+++ /dev/null
-! 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 biosreadsectors(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 _biosreadsectors, _writesectors
-_writesectors:
- push bp
- mov bp, sp
- movb 13(bp), 0x40 ! Code for a file write
- jmp rwsec
-_biosreadsectors:
- 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
-
-! void scan_keyboard(void)
-! Read keyboard character. Needs to be done in case one is waiting.
-.define _scan_keyboard
-_scan_keyboard:
- movb ah, 1 ! Check keyboard
- int 0x16
- jz no_key
- movb ah, 0 ! Empty it
- int 0x16
-no_key: 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 $