minix/boot/doshead.s
2005-04-21 14:53:53 +00:00

1370 lines
34 KiB
ArmAsm
Executable file

! 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 $