! Doshead.s - DOS & BIOS support for boot.c Author: Kees J. Bot ! ! ! This file contains the startup and low level support for the secondary ! boot program. It contains functions for disk, tty and keyboard I/O, ! copying memory to arbitrary locations, etc. ! ! This runs under MS-DOS as a .COM file. A .COM file is what Minix calls ! a common I&D executable, except that the first 256 bytes contains DOS ! thingies. ! .sect .text; .sect .rom; .sect .data; .sect .bss K_I386 = 0x0001 ! Call Minix in 386 mode STACK = 16384 ! Number of bytes for the stack DS_SELECTOR = 3*8 ! Kernel data selector ES_SELECTOR = 4*8 ! Flat 4 Gb SS_SELECTOR = 5*8 ! Monitor stack CS_SELECTOR = 6*8 ! Kernel code MCS_SELECTOR= 7*8 ! Monitor code ESC = 0x1B ! Escape character ! Imported variables and functions: .extern _caddr, _daddr, _runsize, _edata, _end ! Runtime environment .extern _k_flags ! Special kernel flags .extern _mem ! Free memory list .extern _vdisk ! Name of the virtual disk .sect .text .use16 ! Tell 386 assembler we're in 16-bit mode .define _PSP _PSP: .space 256 ! Program Segment Prefix dosboot: cld ! C compiler wants UP xor ax, ax ! Zero mov di, _edata ! Start of bss is at end of data mov cx, _end ! End of bss (begin of heap) sub cx, di ! Number of bss bytes shr cx, 1 ! Number of words rep stos ! Clear bss cmp sp, _end+STACK jb 0f mov sp, _end+STACK ! "chmem" to 16 kb 0: ! Are we called with the /U option? movb cl, (_PSP+0x80) ! Argument byte count xorb ch, ch mov bx, _PSP+0x81 ! Argument string 0: jcxz notuflag cmpb (bx), 0x20 ! Whitespace? ja 1f inc bx dec cx jmp 0b 1: cmp cx, 2 ! '/U' is two bytes jne notuflag cmpb (bx), 0x2F ! '/'? jne notuflag movb al, 1(bx) andb al, ~0x20 ! Ignore case cmpb al, 0x55 ! 'U'? jne notuflag jmp keepumb ! Go grab an UMB notuflag: ! Remember the current video mode for restoration on exit. movb ah, 0x0F ! Get current video mode int 0x10 andb al, 0x7F ! Mask off bit 7 (no blanking) movb (old_vid_mode), al movb (cur_vid_mode), al ! We require at least MS-DOS 3.0. mov ax, 0x3000 ! Get DOS version int 0x21 cmpb al, 3 ! DOS 3.0+ ? jae dosok push tellbaddos call _printf jmp quit .sect .rom tellbaddos: .ascii "MS-DOS 3.0 or better required\n\0" .sect .text dosok: ! Find out how much "low" memory there is available, where it starts and ! where it ends. mov di, _mem ! di = memory list mov ax, _PSP+0x80 ! From PSP:80 to next PSP is ours mov dx, ds call seg2abs mov (di), ax mov 2(di), dx ! mem[0].base = ds * 16 + 0x80 xor ax, ax mov dx, (_PSP+2) ! First in-use segment far above us call seg2abs sub ax, (di) sbb dx, 2(di) ! Minus base gives size mov 4(di), ax mov 6(di), dx ! mem[1].size = free low memory size ! Give C code access to the code segment, data segment and the size of this ! process. xor ax, ax mov dx, cs call seg2abs mov (_caddr+0), ax mov (_caddr+2), dx xor ax, ax mov dx, ds call seg2abs mov (_daddr+0), ax mov (_daddr+2), dx mov ax, sp mov dx, ss ! End of stack = end of program call seg2abs sub ax, (_caddr+0) sbb dx, (_caddr+2) ! Minus start of our code mov (_runsize+0), ax mov (_runsize+2), dx ! Is our size ! Patch the regular _getprocessor library routine to jump to 'getprocessor', ! that checks if we happen to be in a V8086 straightjacket by returning '86'. cseg movb (_getprocessor+0), 0xE9 mov ax, getprocessor sub ax, _getprocessor+3 cseg mov (_getprocessor+1), ax ! Grab the largest chunk of extended memory available. call _getprocessor cmp ax, 286 ! Only 286s and above have extended memory jb no_ext mov ax, 0x4300 ! XMS driver check int 0x2F cmpb al, 0x80 ! XMS driver exists? je xmsthere get_ext: ! No driver, so can use all ext memory directly call _getprocessor cmp ax, 486 ! Assume 486s were the first to have >64M jb small_ext ! (It helps to be paranoid when using the BIOS) big_ext: mov ax, 0xE801 ! Code for get memory size for >64M int 0x15 ! ax = mem at 1M per 1K, bx = mem at 16M per 64K jnc got_ext small_ext: movb ah, 0x88 ! Code for get extended memory size clc ! Carry will stay clear if call exists int 0x15 ! Returns size (in K) in ax for AT's jc no_ext test ax, ax ! An AT with no extended memory? jz no_ext xor bx, bx ! bx = mem above 16M per 64K = 0 got_ext: mov cx, ax ! cx = copy of ext mem at 1M mov 10(di), 0x0010 ! mem[1].base = 0x00100000 (1M) mul (c1024) mov 12(di), ax ! mem[1].size = "ext mem at 1M" * 1024 mov 14(di), dx test bx, bx jz no_ext ! No more ext mem above 16M? cmp cx, 15*1024 ! Chunks adjacent? (precisely 15M at 1M?) je adj_ext mov 18(di), 0x0100 ! mem[2].base = 0x01000000 (16M) mov 22(di), bx ! mem[2].size = "ext mem at 16M" * 64K jmp no_ext adj_ext: add 14(di), bx ! Add ext mem above 16M to mem below 16M no_ext: jmp gotxms xmsthere: mov ax, 0x4310 ! Get XMS driver address int 0x2F mov (xms_driver+0), bx mov (xms_driver+2), es push ds pop es movb ah, 0x08 ! Query free extended memory xorb bl, bl callf (xms_driver) testb bl, bl jnz xmserr push ax ! ax = size of largest block in kb mul (c1024) mov 12(di), ax mov 14(di), dx ! mem[1].size = ax * 1024 pop dx ! dx = size of largest block in kb movb ah, 0x09 ! Allocate XMS block of size dx callf (xms_driver) test ax, ax jz xmserr mov (xms_handle), dx ! Save handle movb ah, 0x0C ! Lock XMS block (handle in dx) callf (xms_driver) test ax, ax jz xmserr mov 8(di), bx mov 10(di), dx ! mem[1].base = Address of locked block gotxms: ! If we're running in a DOS box then they're might be an Upper Memory Block ! we can use. Every little bit helps when in real mode. mov ax, 20(di) or ax, 22(di) ! Can we use mem[2]? jnz gotumb mov dx, 0xFFFF ! dx = Maximum size, i.e. gimme all call getumb ! Get UMB, dx = segment, cx = length test cx, cx ! Did we get a block? jz gotumb xor ax, ax ! dx:ax = memory block call seg2abs mov 16(di), ax mov 18(di), dx ! mem[2].base = memory block base mov dx, cx xor ax, ax ! dx:ax = length of memory block call seg2abs mov 20(di), ax mov 22(di), dx ! mem[2].size = memory block length gotumb: ! Set up an INT 24 "critical error" handler that returns "fail". This way ! Minix won't suffer from "(A)bort, (R)etry, (I)nfluence with a large hammer?". mov (0x007C), 0x03B0 ! movb al, 0x03 (fail code) movb (0x007E), 0xCF ! iret movb ah, 0x25 ! Set interrupt vector mov dx, 0x007C ! ds:dx = ds:0x007C = interrupt handler int 0x21 ! Time to switch to a higher level language (not much higher) call _boot ! void ..exit(int status) ! Exit the monitor by returning to DOS. .define _exit, __exit, ___exit ! Make various compilers happy _exit: __exit: ___exit: mov dx, (xms_handle) cmp dx, -1 ! Is there an ext mem block in use? je nohandle movb ah, 0x0D ! Unlock extended memory block callf (xms_driver) mov dx, (xms_handle) movb ah, 0x0A ! Free extended memory block callf (xms_driver) nohandle: call restore_video pop ax pop ax ! Return code in al movb ah, 0x4C ! Terminate with return code int 0x21 quit: ! exit(1) movb al, 1 push ax call _exit xmserr: xorb bh, bh push bx push tellxmserr call _printf jmp quit .sect .rom tellxmserr: .ascii "Extended memory problem, error 0x%02x\n\0" .sect .text ! int getprocessor(void) ! Prefix for the regular _getprocessor call that first checks if we're ! running in a virtual 8086 box. getprocessor: push sp ! Is pushed sp equal to sp? pop ax cmp ax, sp jne gettrueproc ! If not then it's a plain 8086 or 80186 .data1 0x0F,0x01,0xE0 ! Use old 286 SMSW instruction to get the MSW testb al, 0x01 ! Protected mode enabled? jz gettrueproc ! If not then a 286 or better in real mode mov ax, 86 ! Forget fancy tricks, say it's an 8086 ret gettrueproc: ! Get the true processor type push bp ! _getprocessor prologue that is patched over. mov bp, sp jmp _getprocessor+3 ! Try to get an Upper Memory Block under MS-DOS 5+. Try to get one up to size ! dx, return segment of UMB found in dx and size in paragraphs in cx. getumb: xor cx, cx ! Initially nothing found mov ax, 0x3000 ! Get DOS version int 0x21 cmpb al, 5 ! MS-DOS 5.0 or better? jb retumb mov ax, 0x544D ! Get UMB kept by BOOT /U int 0x15 ! Returns dx = segment, cx = size jc 0f cmp ax, 0x4D54 ! Carry clear and ax byte swapped? je retumb 0: mov ax, 0x5802 ! Get UMB link state int 0x21 xorb ah, ah push ax ! Save UMB link state mov ax, 0x5803 ! Set UMB link state mov bx, 0x0001 ! Add UMBs to DOS memory chain int 0x21 mov ax, 0x5800 ! Get memory allocation strategy int 0x21 push ax ! Save allocation strategy mov ax, 0x5801 ! Set memory allocation strategy mov bx, 0x0080 ! First fit, try high then low memory int 0x21 movb ah, 0x48 ! Allocate memory mov bx, dx ! Number of paragraphs wanted int 0x21 ! Fails with bx = size of largest jnc 0f ! Succeeds with ax = allocated block test bx, bx ! Is there any? jz no_umb movb ah, 0x48 ! Allocate memory int 0x21 jc no_umb ! Did we get some? 0: mov dx, ax ! dx = segment mov cx, bx ! cx = size no_umb: mov ax, 0x5801 ! Set memory allocation strategy pop bx ! bx = saved former strategy int 0x21 mov ax, 0x5803 ! Set UMB link state pop bx ! bx = saved former link state int 0x21 retumb: ret ! 'BOOT /U' instructs this program to grab the biggest available UMB and to ! sit on it until the next invocation of BOOT wants it back. These shenanigans ! are necessary because Windows 95 keeps all UMBs to itself unless you get hold ! of them first. umb = 0x80 ! UMB base and size old15 = 0x84 ! Old 15 interrupt vector new15 = 0x88 ! New 15 interrupt handler keepumb: mov ax, 0x544D ! "Keep UMB" handler already present? int 0x15 jc 0f cmp ax, 0x4D54 je exitumb ! Already present, so quit 0: mov si, new15start mov di, new15 mov cx, new15end sub cx, si rep movsb ! Copy handler into place add di, 15 movb cl, 4 shr di, cl ! di = first segment above handler mov cx, cs cmp cx, 0xA000 ! Are we loaded high perchance? jb nothigh werehigh: add cx, di mov (umb+0), cx ! Use my own memory as the UMB to keep mov ax, (_PSP+2) ! Up to the next in-use segment sub ax, dx ! ax = size of my free memory cmp ax, 0x1000 ! At least 64K? jb exitumb ! Don't bother if less mov (umb+2), 0x1000 ! Size of UMB add di, 0x1000 ! Keep my code plus 64K when TSR jmp hook15 nothigh: mov dx, 0x1000 call getumb ! Grab an UMB of at most 64K cmp cx, 0x1000 ! Did we get 64K? jb exitumb ! Otherwise don't bother mov (umb+0), dx mov (umb+2), cx hook15: mov ax, 0x3515 ! Get interrupt vector int 0x21 mov (old15+0), bx mov (old15+2), es ! Old 15 interrupt mov ax, 0x2515 ! Set interrupt vector mov dx, new15 ! ds:dx = new 15 handler int 0x21 mov ax, 0x3100 ! Terminate and stay resident mov dx, di ! dx = di = paragraphs we keep int 0x21 exitumb: mov ax, 0x4C00 ! exit(0) int 0x21 new15start: ! New interrupt 15 handler pushf cmp ax, 0x544D ! Is it my call? je my15 popf cseg jmpf (old15) ! No, continue with old 15 my15: popf push bp mov bp, sp andb 6(bp), ~0x01 ! clear carry, call will succeed xchgb al, ah ! ax = 4D54, also means call works cseg mov dx, (umb+0) ! dx = base of UMB cseg mov cx, (umb+2) ! cx = size of UMB pop bp iret ! return to caller new15end: ! u32_t mon2abs(void *ptr) ! Address in monitor data to absolute address. .define _mon2abs _mon2abs: mov bx, sp mov ax, 2(bx) ! ptr mov dx, ds ! Monitor data segment !jmp seg2abs seg2abs: ! Translate dx:ax to the 32 bit address dx-ax push cx movb ch, dh movb cl, 4 shl dx, cl shrb ch, cl ! ch-dx = dx << 4 add ax, dx adcb ch, 0 ! ch-ax = ch-dx + ax movb dl, ch xorb dh, dh ! dx-ax = ch-ax pop cx ret abs2seg: ! Translate the 32 bit address dx-ax to dx:ax push cx movb ch, dl mov dx, ax ! ch-dx = dx-ax and ax, 0x000F ! Offset in ax movb cl, 4 shr dx, cl shlb ch, cl orb dh, ch ! dx = ch-dx >> 4 pop cx ret ! void raw_copy(u32_t dstaddr, u32_t srcaddr, u32_t count) ! Copy count bytes from srcaddr to dstaddr. Don't do overlaps. ! Also handles copying words to or from extended memory. .define _raw_copy _raw_copy: push bp mov bp, sp push si push di ! Save C variable registers copy: cmp 14(bp), 0 jnz bigcopy mov cx, 12(bp) jcxz copydone ! Count is zero, end copy cmp cx, 0xFFF0 jb smallcopy bigcopy:mov cx, 0xFFF0 ! Don't copy more than about 64K at once smallcopy: push cx ! Save copying count mov ax, 4(bp) mov dx, 6(bp) cmp dx, 0x0010 ! Copy to extended memory? jae ext_copy cmp 10(bp), 0x0010 ! Copy from extended memory? jae ext_copy call abs2seg mov di, ax mov es, dx ! es:di = dstaddr mov ax, 8(bp) mov dx, 10(bp) call abs2seg mov si, ax mov ds, dx ! ds:si = srcaddr shr cx, 1 ! Words to move rep movs ! Do the word copy adc cx, cx ! One more byte? rep movsb ! Do the byte copy mov ax, ss ! Restore ds and es from the remaining ss mov ds, ax mov es, ax jmp copyadjust ext_copy: mov (x_dst_desc+2), ax movb (x_dst_desc+4), dl ! Set base of destination segment mov ax, 8(bp) mov dx, 10(bp) mov (x_src_desc+2), ax movb (x_src_desc+4), dl ! Set base of source segment mov si, x_gdt ! es:si = global descriptor table shr cx, 1 ! Words to move movb ah, 0x87 ! Code for extended memory move int 0x15 copyadjust: pop cx ! Restore count add 4(bp), cx adc 6(bp), 0 ! srcaddr += copycount add 8(bp), cx adc 10(bp), 0 ! dstaddr += copycount sub 12(bp), cx sbb 14(bp), 0 ! count -= copycount jmp copy ! and repeat copydone: pop di pop si ! Restore C variable registers pop bp ret ! u16_t get_word(u32_t addr); ! void put_word(u32_t addr, u16_t word); ! Read or write a 16 bits word at an arbitrary location. .define _get_word, _put_word _get_word: mov bx, sp call gp_getaddr mov ax, (bx) ! Word to get from addr jmp gp_ret _put_word: mov bx, sp push 6(bx) ! Word to store at addr call gp_getaddr pop (bx) ! Store the word jmp gp_ret gp_getaddr: mov ax, 2(bx) mov dx, 4(bx) call abs2seg mov bx, ax mov ds, dx ! ds:bx = addr ret gp_ret: push es pop ds ! Restore ds ret ! void relocate(void); ! After the program has copied itself to a safer place, it needs to change ! the segment registers. Caddr has already been set to the new location. .define _relocate _relocate: pop bx ! Return address mov ax, (_caddr+0) mov dx, (_caddr+2) call abs2seg mov cx, dx ! cx = new code segment mov ax, cs ! Old code segment sub ax, cx ! ax = -(new - old) = -Moving offset mov dx, ds sub dx, ax mov ds, dx ! ds += (new - old) mov es, dx mov ss, dx xor ax, ax call seg2abs mov (_daddr+0), ax mov (_daddr+2), dx ! New data address push cx ! New text segment push bx ! Return offset of this function retf ! Relocate ! void *brk(void *addr) ! void *sbrk(size_t incr) ! Cannot fail implementations of brk(2) and sbrk(3), so we can use ! malloc(3). They reboot on stack collision instead of returning -1. .sect .data .align 2 break: .data2 _end ! A fake heap pointer .sect .text .define _brk, __brk, _sbrk, __sbrk _brk: __brk: ! __brk is for the standard C compiler xor ax, ax jmp sbrk ! break= 0; return sbrk(addr); _sbrk: __sbrk: mov ax, (break) ! ax= current break sbrk: push ax ! save it as future return value mov bx, sp ! Stack is now: (retval, retaddr, incr, ...) add ax, 4(bx) ! ax= break + increment mov (break), ax ! Set new break lea dx, -1024(bx) ! sp minus a bit of breathing space cmp dx, ax ! Compare with the new break jb heaperr ! Suffocating noises pop ax ! Return old break (0 for brk) ret heaperr:push nomem call _printf call quit .sect .rom nomem: .ascii "\nOut of memory\n\0" .sect .text ! int dev_open(void); ! Open file 'vdisk' to use as the Minix virtual disk. Store handle in ! vfd. Returns 0 for success, otherwise the DOS error code. .define _dev_open _dev_open: call _dev_close ! If already open then first close mov dx, (_vdisk) ! ds:dx = Address of file name mov ax, 0x3D22 ! Open file read-write & deny write int 0x21 jnc opok ! Open succeeded? cmp ax, 5 ! Open failed, "access denied"? jne opbad mov ax, 0x3D40 ! Open file read-only int 0x21 jc opbad opok: mov (vfd), ax ! File handle to open file xor ax, ax ! Zero for success opbad: ret ! int dev_close(void); ! Close the dos virtual disk. .define _dev_close _dev_close: mov bx, -1 cmp (vfd), bx ! Already closed? je 1f movb ah, 0x3E ! Close file xchg bx, (vfd) ! bx = vfd; vfd = -1; int 0x21 jc 0f 1: xor ax, ax 0: ret ! int dev_boundary(u32_t sector); ! Returns false; files have no visible boundaries. .define _dev_boundary _dev_boundary: xor ax, ax ret ! int readsectors(u32_t bufaddr, u32_t sector, u8_t count) ! int writesectors(u32_t bufaddr, u32_t sector, u8_t count) ! Read/write several sectors from/to the Minix virtual disk. Count ! must fit in a byte. The external variable vfd is the file handle. ! Returns 0 for success, otherwise the DOS error code. ! .define _readsectors, _writesectors _writesectors: push bp mov bp, sp movb 13(bp), 0x40 ! Code for a file write jmp rwsec _readsectors: push bp mov bp, sp movb 13(bp), 0x3F ! Code for a file read rwsec: cmp (vfd), -1 ! Currently closed? jne 0f call _dev_open ! Open file if needed test ax, ax jnz rwerr 0: mov dx, 8(bp) mov bx, 10(bp) ! bx-dx = Sector number mov cx, 9 mul512: shl dx, 1 rcl bx, 1 ! bx-dx *= 512 loop mul512 mov cx, bx ! cx-dx = Byte position in file mov bx, (vfd) ! bx = File handle mov ax, 0x4200 ! Lseek absolute int 0x21 jb rwerr mov bx, (vfd) ! bx = File handle mov ax, 4(bp) mov dx, 6(bp) ! dx-ax = Address to transfer data to/from call abs2seg mov ds, dx mov dx, ax ! ds:dx = Address to transfer data to/from xorb cl, cl movb ch, 12(bp) ! ch = Number of sectors to transfer shl cx, 1 ! cx = Number of bytes to transfer push cx ! Save count movb ah, 13(bp) ! Read or write int 0x21 pop cx ! Restore count push es pop ds ! Restore ds jb rwerr cmp ax, cx ! All bytes transferred? je rwall mov ax, 0x05 ! The DOS code for "I/O error", but different jmp rwerr rwall: call wheel ! Display tricks xor ax, ax rwerr: pop bp ret ! int getch(void); ! Read a character from the keyboard, and check for an expired timer. ! A carriage return is changed into a linefeed for UNIX compatibility. .define _getch _getch: xor ax, ax xchg ax, (unchar) ! Ungotten character? test ax, ax jnz gotch getch: hlt ! Play dead until interrupted (see pause()) movb ah, 0x01 ! Keyboard status int 0x16 jnz press ! Keypress? call _expired ! Timer expired? test ax, ax jz getch mov ax, ESC ! Return ESC ret press: xorb ah, ah ! Read character from keyboard int 0x16 cmpb al, 0x0D ! Carriage return? jnz nocr movb al, 0x0A ! Change to linefeed nocr: cmpb al, ESC ! Escape typed? jne noesc inc (escape) ! Set flag noesc: xorb ah, ah ! ax = al gotch: ret ! int ungetch(void); ! Return a character to undo a getch(). .define _ungetch _ungetch: mov bx, sp mov ax, 2(bx) mov (unchar), ax ret ! int escape(void); ! True if ESC has been typed. .define _escape _escape: movb ah, 0x01 ! Keyboard status int 0x16 jz escflg ! Keypress? cmpb al, ESC ! Escape typed? jne escflg xorb ah, ah ! Discard the escape int 0x16 inc (escape) ! Set flag escflg: xor ax, ax xchg ax, (escape) ! Escape typed flag ret ! int putch(int c); ! Write a character in teletype mode. The putk synonym is ! for the kernel printf function that uses it. ! Newlines are automatically preceded by a carriage return. ! .define _putch, _putk _putch: _putk: mov bx, sp movb al, 2(bx) ! al = character to be printed testb al, al ! Kernel printf adds a null char to flush queue jz nulch cmpb al, 0x0A ! al = newline? jnz putc movb al, 0x20 ! Erase wheel and do a carriage return call plotc ! plotc(' '); nodirt: movb al, 0x0D call putc ! putc('\r') movb al, 0x0A ! Restore the '\n' and print it putc: movb ah, 0x0E ! Print character in teletype mode mov bx, 0x0001 ! Page 0, foreground color int 0x10 ! Call BIOS VIDEO_IO nulch: ret ! |/-\|/-\|/-\|/-\|/-\ (playtime) wheel: mov bx, (gp) movb al, (bx) inc bx ! al = *gp++; cmp bx, glyphs+4 jne 0f mov bx, glyphs 0: mov (gp), bx ! gp= gp == glyphs + 4 ? glyphs : gp; !jmp plotc plotc: movb ah, 0x0A ! 0x0A = write character at cursor mov bx, 0x0001 ! Page 0, foreground color mov cx, 0x0001 ! Just one character int 0x10 ret .sect .data .align 2 gp: .data2 glyphs glyphs: .ascii "|/-\\" .sect .text ! void pause(void); ! Wait for an interrupt using the HLT instruction. This either saves ! power, or tells an x86 emulator that nothing is happening right now. .define _pause _pause: hlt ret ! void set_mode(unsigned mode); ! void clear_screen(void); ! Set video mode / clear the screen. .define _set_mode, _clear_screen _set_mode: mov bx, sp mov ax, 2(bx) ! Video mode cmp ax, (cur_vid_mode) je modeok ! Mode already as requested? mov (cur_vid_mode), ax _clear_screen: mov ax, (cur_vid_mode) andb ah, 0x7F ! Test bits 8-14, clear bit 15 (8x8 flag) jnz xvesa ! VESA extended mode? int 0x10 ! Reset video (ah = 0) jmp mdset xvesa: mov bx, ax ! bx = extended mode mov ax, 0x4F02 ! Reset video int 0x10 mdset: testb (cur_vid_mode+1), 0x80 jz setcur ! 8x8 font requested? mov ax, 0x1112 ! Load ROM 8 by 8 double-dot patterns xorb bl, bl ! Load block 0 int 0x10 setcur: xor dx, dx ! dl = column = 0, dh = row = 0 xorb bh, bh ! Page 0 movb ah, 0x02 ! Set cursor position int 0x10 modeok: ret restore_video: ! To restore the video mode on exit movb al, 0x20 call plotc ! Erase wheel push (old_vid_mode) call _set_mode pop ax ret ! u32_t get_tick(void); ! Return the current value of the clock tick counter. This counter ! increments 18.2 times per second. Poll it to do delays. Does not ! work on the original PC, but works on the PC/XT. .define _get_tick _get_tick: xorb ah, ah ! Code for get tick count int 0x1A mov ax, dx mov dx, cx ! dx:ax = cx:dx = tick count ret ! Functions used to obtain info about the hardware. Boot uses this information ! itself, but will also pass them on to a pure 386 kernel, because one can't ! make BIOS calls from protected mode. The video type could probably be ! determined by the kernel too by looking at the hardware, but there is a small ! chance on errors that the monitor allows you to correct by setting variables. .define _get_bus ! returns type of system bus .define _get_video ! returns type of display ! u16_t get_bus(void) ! Return type of system bus, in order: XT, AT, MCA. _get_bus: call gettrueproc xor dx, dx ! Assume XT cmp ax, 286 ! An AT has at least a 286 jb got_bus inc dx ! Assume AT movb ah, 0xC0 ! Code for get configuration int 0x15 jc got_bus ! Carry clear and ah = 00 if supported testb ah, ah jne got_bus eseg movb al, 5(bx) ! Load feature byte #1 inc dx ! Assume MCA testb al, 0x02 ! Test bit 1 - "bus is Micro Channel" jnz got_bus dec dx ! Assume AT testb al, 0x40 ! Test bit 6 - "2nd 8259 installed" jnz got_bus dec dx ! It is an XT got_bus: push ds pop es ! Restore es mov ax, dx ! Return bus code mov (bus), ax ! Keep bus code, A20 handler likes to know ret ! u16_t get_video(void) ! Return type of display, in order: MDA, CGA, mono EGA, color EGA, ! mono VGA, color VGA. _get_video: mov ax, 0x1A00 ! Function 1A returns display code int 0x10 ! al = 1A if supported cmpb al, 0x1A jnz no_dc ! No display code function supported mov ax, 2 cmpb bl, 5 ! Is it a monochrome EGA? jz got_video inc ax cmpb bl, 4 ! Is it a color EGA? jz got_video inc ax cmpb bl, 7 ! Is it a monochrome VGA? jz got_video inc ax cmpb bl, 8 ! Is it a color VGA? jz got_video no_dc: movb ah, 0x12 ! Get information about the EGA movb bl, 0x10 int 0x10 cmpb bl, 0x10 ! Did it come back as 0x10? (No EGA) jz no_ega mov ax, 2 cmpb bh, 1 ! Is it monochrome? jz got_video inc ax jmp got_video no_ega: int 0x11 ! Get bit pattern for equipment and ax, 0x30 ! Isolate color/mono field sub ax, 0x30 jz got_video ! Is it an MDA? mov ax, 1 ! No it's CGA got_video: ret ! Function to leave the boot monitor and run Minix. .define _minix ! void minix(u32_t koff, u32_t kcs, u32_t kds, ! char *bootparams, size_t paramsize, u32_t aout); _minix: push bp mov bp, sp ! Pointer to arguments mov dx, 0x03F2 ! Floppy motor drive control bits movb al, 0x0C ! Bits 4-7 for floppy 0-3 are off outb dx ! Kill the motors push ds xor ax, ax ! Vector & BIOS data segments mov ds, ax andb (0x043F), 0xF0 ! Clear diskette motor status bits of BIOS pop ds cli ! No more interruptions test (_k_flags), K_I386 ! Minix-386? jnz minix386 ! Call Minix in real mode. minix86: push 22(bp) ! Address of a.out headers push 20(bp) push 18(bp) ! # bytes of boot parameters push 16(bp) ! Address of boot parameters mov dx, cs ! Monitor far return address mov ax, ret86 cmp (_mem+14), 0 ! Any extended memory? (mem[1].size > 0 ?) jnz 0f xor dx, dx ! If no ext mem then monitor not preserved xor ax, ax 0: push dx ! Push monitor far return address or zero push ax mov ax, 8(bp) mov dx, 10(bp) call abs2seg push dx ! Kernel code segment push 4(bp) ! Kernel code offset mov ax, 12(bp) mov dx, 14(bp) call abs2seg mov ds, dx ! Kernel data segment mov es, dx ! Set es to kernel data too retf ! Make a far call to the kernel ! Call 386 Minix in 386 mode. minix386: cseg mov (cs_real-2), cs ! Patch CS and DS into the instructions that cseg mov (ds_real-2), ds ! reload them when switching back to real mode mov eax, cr0 orb al, 0x01 ! Set PE (protection enable) bit o32 mov (msw), eax ! Save as protected mode machine status word mov dx, ds ! Monitor ds mov ax, p_gdt ! dx:ax = Global descriptor table call seg2abs mov (p_gdt_desc+2), ax movb (p_gdt_desc+4), dl ! Set base of global descriptor table mov ax, 12(bp) mov dx, 14(bp) ! Kernel ds (absolute address) mov (p_ds_desc+2), ax movb (p_ds_desc+4), dl ! Set base of kernel data segment mov dx, ss ! Monitor ss xor ax, ax ! dx:ax = Monitor stack segment call seg2abs ! Minix starts with the stack of the monitor mov (p_ss_desc+2), ax movb (p_ss_desc+4), dl mov ax, 8(bp) mov dx, 10(bp) ! Kernel cs (absolute address) mov (p_cs_desc+2), ax movb (p_cs_desc+4), dl mov dx, cs ! Monitor cs xor ax, ax ! dx:ax = Monitor code segment call seg2abs mov (p_mcs_desc+2), ax movb (p_mcs_desc+4), dl push MCS_SELECTOR push int86 ! Far address to INT86 support o32 push 20(bp) ! Address of a.out headers push 0 push 18(bp) ! 32 bit size of parameters on stack push 0 push 16(bp) ! 32 bit address of parameters (ss relative) push MCS_SELECTOR push ret386 ! Monitor far return address push 0 push CS_SELECTOR push 6(bp) push 4(bp) ! 32 bit far address to kernel entry point call real2prot ! Switch to protected mode mov ax, DS_SELECTOR mov ds, ax ! Kernel data mov ax, ES_SELECTOR mov es, ax ! Flat 4 Gb o32 retf ! Make a far call to the kernel ! Minix-86 returns here on a halt or reboot. ret86: mov 8(bp), ax mov 10(bp), dx ! Return value jmp return ! Minix-386 returns here on a halt or reboot. ret386: o32 mov 8(bp), eax ! Return value call prot2real ! Switch to real mode return: mov sp, bp ! Pop parameters sti ! Can take interrupts again call _get_video ! MDA, CGA, EGA, ... movb dh, 24 ! dh = row 24 cmp ax, 2 ! At least EGA? jb is25 ! Otherwise 25 rows push ds xor ax, ax ! Vector & BIOS data segments mov ds, ax movb dh, (0x0484) ! Number of rows on display minus one pop ds is25: xorb dl, dl ! dl = column 0 xorb bh, bh ! Page 0 movb ah, 0x02 ! Set cursor position int 0x10 xorb ah, ah ! Whack the disk system, Minix may have messed movb dl, 0x80 ! it up int 0x13 call gettrueproc cmp ax, 286 jb noclock xorb al, al tryclk: decb al jz noclock movb ah, 0x02 ! Get real-time clock time (from CMOS clock) int 0x1A jc tryclk ! Carry set, not running or being updated movb al, ch ! ch = hour in BCD call bcd ! al = (al >> 4) * 10 + (al & 0x0F) mulb (c60) ! 60 minutes in an hour mov bx, ax ! bx = hour * 60 movb al, cl ! cl = minutes in BCD call bcd add bx, ax ! bx = hour * 60 + minutes movb al, dh ! dh = seconds in BCD call bcd xchg ax, bx ! ax = hour * 60 + minutes, bx = seconds mul (c60) ! dx-ax = (hour * 60 + minutes) * 60 add bx, ax adc dx, 0 ! dx-bx = seconds since midnight mov ax, dx mul (c19663) xchg ax, bx mul (c19663) add dx, bx ! dx-ax = dx-bx * (0x1800B0 / (2*2*2*2*5)) mov cx, ax ! (0x1800B0 = ticks per day of BIOS clock) mov ax, dx xor dx, dx div (c1080) xchg ax, cx div (c1080) ! cx-ax = dx-ax / (24*60*60 / (2*2*2*2*5)) mov dx, ax ! cx-dx = ticks since midnight movb ah, 0x01 ! Set system time int 0x1A noclock: mov ax, 8(bp) mov dx, 10(bp) ! dx-ax = return value from the kernel pop bp ret ! Return to monitor as if nothing much happened ! Transform BCD number in al to a regular value in ax. bcd: movb ah, al shrb ah, 4 andb al, 0x0F aad ! ax = (al >> 4) * 10 + (al & 0x0F) ret ! Support function for Minix-386 to make an 8086 interrupt call. int86: mov bp, sp call prot2real o32 xor eax, eax mov es, ax ! Vector & BIOS data segments o32 eseg mov (0x046C), eax ! Clear BIOS clock tick counter sti ! Enable interrupts movb al, 0xCD ! INT instruction movb ah, 8(bp) ! Interrupt number? testb ah, ah jnz 0f ! Nonzero if INT, otherwise far call push cs push intret+2 ! Far return address o32 push 12(bp) ! Far driver address mov ax, 0x90CB ! RETF; NOP 0: cseg mov (intret), ax ! Patch 'INT n' or 'RETF; NOP' into code mov ds, 16(bp) ! Load parameters mov es, 18(bp) o32 mov eax, 20(bp) o32 mov ebx, 24(bp) o32 mov ecx, 28(bp) o32 mov edx, 32(bp) o32 mov esi, 36(bp) o32 mov edi, 40(bp) o32 mov ebp, 44(bp) intret: int 0xFF ! Do the interrupt or far call o32 push ebp ! Save results o32 pushf mov bp, sp o32 pop 8+8(bp) ! eflags mov 8+16(bp), ds mov 8+18(bp), es o32 mov 8+20(bp), eax o32 mov 8+24(bp), ebx o32 mov 8+28(bp), ecx o32 mov 8+32(bp), edx o32 mov 8+36(bp), esi o32 mov 8+40(bp), edi o32 pop 8+44(bp) ! ebp cli ! Disable interrupts xor ax, ax mov ds, ax ! Vector & BIOS data segments o32 mov cx, (0x046C) ! Collect lost clock ticks in ecx mov ax, ss mov ds, ax ! Restore monitor ds call real2prot mov ax, DS_SELECTOR ! Kernel data mov ds, ax o32 retf ! Return to the kernel ! Switch from real to protected mode. real2prot: movb ah, 0x02 ! Code for A20 enable call gate_A20 lgdt (p_gdt_desc) ! Global descriptor table o32 mov eax, (pdbr) ! Load page directory base register mov cr3, eax mov eax, cr0 o32 xchg eax, (msw) ! Exchange real mode msw for protected mode msw mov cr0, eax jmpf MCS_SELECTOR:cs_prot ! Set code segment selector cs_prot: mov ax, SS_SELECTOR ! Set data selectors mov ds, ax mov es, ax mov ss, ax ret ! Switch from protected to real mode. prot2real: lidt (p_idt_desc) ! Real mode interrupt vectors mov eax, cr3 o32 mov (pdbr), eax ! Save page directory base register mov eax, cr0 o32 xchg eax, (msw) ! Exchange protected mode msw for real mode msw mov cr0, eax jmpf 0xDEAD:cs_real ! Reload cs register cs_real: mov ax, 0xBEEF ds_real: mov ds, ax ! Reload data segment registers mov es, ax mov ss, ax xorb ah, ah ! Code for A20 disable !jmp gate_A20 ! Enable (ah = 0x02) or disable (ah = 0x00) the A20 address line. gate_A20: cmp (bus), 2 ! PS/2 bus? je gate_PS_A20 call kb_wait movb al, 0xD1 ! Tell keyboard that a command is coming outb 0x64 call kb_wait movb al, 0xDD ! 0xDD = A20 disable code if ah = 0x00 orb al, ah ! 0xDF = A20 enable code if ah = 0x02 outb 0x60 call kb_wait movb al, 0xFF ! Pulse output port outb 0x64 call kb_wait ! Wait for the A20 line to settle down ret kb_wait: inb 0x64 testb al, 0x02 ! Keyboard input buffer full? jnz kb_wait ! If so, wait ret gate_PS_A20: ! The PS/2 can twiddle A20 using port A inb 0x92 ! Read port A andb al, 0xFD orb al, ah ! Set A20 bit to the required state outb 0x92 ! Write port A jmp .+2 ! Small delay A20ok: inb 0x92 ! Check port A andb al, 0x02 cmpb al, ah ! A20 line settled down to the new state? jne A20ok ! If not then wait ret ! void int15(bios_env_t *ep) ! Do an "INT 15" call, primarily for APM (Power Management). .define _int15 _int15: push si ! Save callee-save register si mov si, sp mov si, 4(si) ! ep mov ax, (si) ! ep->ax mov bx, 2(si) ! ep->bx mov cx, 4(si) ! ep->cx int 0x15 ! INT 0x15 BIOS call pushf ! Save flags mov (si), ax ! ep->ax mov 2(si), bx ! ep->bx mov 4(si), cx ! ep->cx pop 6(si) ! ep->flags pop si ! Restore ret .sect .rom .align 4 c60: .data2 60 ! Constants for MUL and DIV c1024: .data2 1024 c1080: .data2 1080 c19663: .data2 19663 .sect .data .align 4 ! Global descriptor tables. UNSET = 0 ! Must be computed ! For "Extended Memory Block Move". x_gdt: x_null_desc: ! Null descriptor .data2 0x0000, 0x0000 .data1 0x00, 0x00, 0x00, 0x00 x_gdt_desc: ! Descriptor for this descriptor table .data2 6*8-1, UNSET .data1 UNSET, 0x00, 0x00, 0x00 x_src_desc: ! Source segment descriptor .data2 0xFFFF, UNSET .data1 UNSET, 0x92, 0x00, 0x00 x_dst_desc: ! Destination segment descriptor .data2 0xFFFF, UNSET .data1 UNSET, 0x92, 0x00, 0x00 x_bios_desc: ! BIOS segment descriptor (scratch for int 0x15) .data2 UNSET, UNSET .data1 UNSET, UNSET, UNSET, UNSET x_ss_desc: ! BIOS stack segment descriptor (scratch for int 0x15) .data2 UNSET, UNSET .data1 UNSET, UNSET, UNSET, UNSET ! Protected mode descriptor table. p_gdt: p_null_desc: ! Null descriptor .data2 0x0000, 0x0000 .data1 0x00, 0x00, 0x00, 0x00 p_gdt_desc: ! Descriptor for this descriptor table .data2 8*8-1, UNSET .data1 UNSET, 0x00, 0x00, 0x00 p_idt_desc: ! Real mode interrupt descriptor table descriptor .data2 0x03FF, 0x0000 .data1 0x00, 0x00, 0x00, 0x00 p_ds_desc: ! Kernel data segment descriptor (4Gb flat) .data2 0xFFFF, UNSET .data1 UNSET, 0x92, 0xCF, 0x00 p_es_desc: ! Physical memory descriptor (4Gb flat) .data2 0xFFFF, 0x0000 .data1 0x00, 0x92, 0xCF, 0x00 p_ss_desc: ! Monitor data segment descriptor (64Kb flat) .data2 0xFFFF, UNSET .data1 UNSET, 0x92, 0x00, 0x00 p_cs_desc: ! Kernel code segment descriptor (4Gb flat) .data2 0xFFFF, UNSET .data1 UNSET, 0x9A, 0xCF, 0x00 p_mcs_desc: ! Monitor code segment descriptor (64 kb flat) (unused) .data2 0xFFFF, UNSET .data1 UNSET, 0x9A, 0x00, 0x00 xms_handle: .data2 -1 ! Handle of allocated XMS block vfd: .data2 -1 ! Virtual disk file handle .sect .bss .comm xms_driver, 4 ! Vector to XMS driver .comm old_vid_mode, 2 ! Video mode at startup .comm cur_vid_mode, 2 ! Current video mode .comm msw, 4 ! Saved machine status word (cr0) .comm pdbr, 4 ! Saved page directory base register (cr3) .comm escape, 2 ! Escape typed? .comm bus, 2 ! Saved return value of _get_bus .comm unchar, 2 ! Char returned by ungetch(c) ! ! $PchId: doshead.ack.s,v 1.7 2002/02/27 19:37:52 philip Exp $