1536 lines
		
	
	
		
			39 KiB
		
	
	
	
		
			ArmAsm
		
	
	
	
	
	
			
		
		
	
	
			1536 lines
		
	
	
		
			39 KiB
		
	
	
	
		
			ArmAsm
		
	
	
	
	
	
!	Boothead.s - 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.
 | 
						|
!
 | 
						|
! The primary bootstrap code supplies the following parameters in registers:
 | 
						|
!	dl	= Boot-device.
 | 
						|
!	es:si	= Partition table entry if hard disk.
 | 
						|
!
 | 
						|
.text
 | 
						|
 | 
						|
	o32	    =	  0x66	! This assembler doesn't know 386 extensions
 | 
						|
	BOOTOFF	    =	0x7C00	! 0x0000:BOOTOFF load a bootstrap here
 | 
						|
	LOADSEG     =	0x1000	! Where this code is loaded.
 | 
						|
	BUFFER	    =	0x0600	! First free memory
 | 
						|
	PENTRYSIZE  =	    16	! Partition table entry size.
 | 
						|
	a_flags	    =	     2	! From a.out.h, struct exec
 | 
						|
	a_text	    =	     8
 | 
						|
	a_data	    =	    12
 | 
						|
	a_bss	    =	    16
 | 
						|
	a_total	    =	    24
 | 
						|
	A_SEP	    =	  0x20	! Separate I&D flag
 | 
						|
	K_I386	    =	0x0001	! Call Minix in 386 mode
 | 
						|
	K_RET	    =	0x0020	! Returns to the monitor on reboot
 | 
						|
	K_INT86	    =	0x0040	! Requires generic INT support
 | 
						|
	K_MEML	    =	0x0080	! Pass a list of free memory
 | 
						|
 | 
						|
	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 _device					! BIOS device number
 | 
						|
.extern _rem_part				! To pass partition info
 | 
						|
.extern _k_flags				! Special kernel flags
 | 
						|
.extern _mem					! Free memory list
 | 
						|
 | 
						|
.text
 | 
						|
 | 
						|
! Set segment registers and stack pointer using the programs own header!
 | 
						|
! The header is either 32 bytes (short form) or 48 bytes (long form).  The
 | 
						|
! bootblock will jump to address 0x10030 in both cases, calling one of the
 | 
						|
! two jmpf instructions below.
 | 
						|
 | 
						|
	jmpf	boot, LOADSEG+3	! Set cs right (skipping long a.out header)
 | 
						|
	.space	11		! jmpf + 11 = 16 bytes
 | 
						|
	jmpf	boot, LOADSEG+2	! Set cs right (skipping short a.out header)
 | 
						|
boot:
 | 
						|
	mov	ax, #LOADSEG
 | 
						|
	mov	ds, ax		! ds = header
 | 
						|
 | 
						|
	movb	al, a_flags
 | 
						|
	testb	al, #A_SEP	! Separate I&D?
 | 
						|
	jnz	sepID
 | 
						|
comID:	xor	ax, ax
 | 
						|
	xchg	ax, a_text	! No text
 | 
						|
	add	a_data, ax	! Treat all text as data
 | 
						|
sepID:
 | 
						|
	mov	ax, a_total	! Total nontext memory usage
 | 
						|
	and	ax, #0xFFFE	! Round down to even
 | 
						|
	mov	a_total, ax	! total - text = data + bss + heap + stack
 | 
						|
	cli			! Ignore interrupts while stack in limbo
 | 
						|
	mov	sp, ax		! Set sp at the top of all that
 | 
						|
 | 
						|
	mov	ax, a_text	! Determine offset of ds above cs
 | 
						|
	movb	cl, #4
 | 
						|
	shr	ax, cl
 | 
						|
	mov	cx, cs
 | 
						|
	add	ax, cx
 | 
						|
	mov	ds, ax		! ds = cs + text / 16
 | 
						|
	mov	ss, ax
 | 
						|
	sti			! Stack ok now
 | 
						|
	push	es		! Save es, we need it for the partition table
 | 
						|
	mov	es, ax
 | 
						|
	cld			! C compiler wants UP
 | 
						|
 | 
						|
! Clear bss
 | 
						|
	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
 | 
						|
 | 
						|
! Copy primary boot parameters to variables.  (Can do this now that bss is
 | 
						|
! cleared and may be written into).
 | 
						|
	xorb	dh, dh
 | 
						|
	mov	_device, dx	! Boot device (probably 0x00 or 0x80)
 | 
						|
	mov	_rem_part+0, si	! Remote partition table offset
 | 
						|
	pop	_rem_part+2	! and segment (saved es)
 | 
						|
 | 
						|
! 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
 | 
						|
 | 
						|
! 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
 | 
						|
	push	ds
 | 
						|
	mov	ax, #LOADSEG
 | 
						|
	mov	ds, ax		! Back to the header once more
 | 
						|
	mov	ax, a_total+0
 | 
						|
	mov	dx, a_total+2	! dx:ax = data + bss + heap + stack
 | 
						|
	add	ax, a_text+0
 | 
						|
	adc	dx, a_text+2	! dx:ax = text + data + bss + heap + stack
 | 
						|
	pop	ds
 | 
						|
	mov	_runsize+0, ax
 | 
						|
	mov	_runsize+2, dx	! 32 bit size of this process
 | 
						|
 | 
						|
! Determine available memory as a list of (base,size) pairs as follows:
 | 
						|
! mem[0] = low memory, mem[1] = memory between 1M and 16M, mem[2] = memory
 | 
						|
! above 16M.  Last two coalesced into mem[1] if adjacent.
 | 
						|
	mov	di, #_mem	! di = memory list
 | 
						|
	int	0x12		! Returns low memory size (in K) in ax
 | 
						|
	mul	c1024
 | 
						|
	mov	4(di), ax	! mem[0].size = low memory size in bytes
 | 
						|
	mov	6(di), dx
 | 
						|
	call	_getprocessor
 | 
						|
	cmp	ax, #286	! Only 286s and above have extended memory
 | 
						|
	jb	no_ext
 | 
						|
	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:
 | 
						|
 | 
						|
! Time to switch to a higher level language (not much higher)
 | 
						|
	call	_boot
 | 
						|
 | 
						|
! void ..exit(int status)
 | 
						|
!	Exit the monitor by rebooting the system.
 | 
						|
.define	_exit, __exit, ___exit		! Make various compilers happy
 | 
						|
_exit:
 | 
						|
__exit:
 | 
						|
___exit:
 | 
						|
	mov	bx, sp
 | 
						|
	cmp	2(bx), #0		! Good exit status?
 | 
						|
	jz	reboot
 | 
						|
quit:	mov	ax, #any_key
 | 
						|
	push	ax
 | 
						|
	call	_printf
 | 
						|
	xorb	ah, ah			! Read character from keyboard
 | 
						|
	int	0x16
 | 
						|
reboot:	call	dev_reset
 | 
						|
	call	restore_video
 | 
						|
	int	0x19			! Reboot the system
 | 
						|
.data
 | 
						|
any_key:
 | 
						|
	.ascii	"\nHit any key to reboot\n\0"
 | 
						|
.text
 | 
						|
 | 
						|
! 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
 | 
						|
 | 
						|
! u32_t vec2abs(vector *vec)
 | 
						|
!	8086 interrupt vector to absolute address.
 | 
						|
.define _vec2abs
 | 
						|
_vec2abs:
 | 
						|
	mov	bx, sp
 | 
						|
	mov	bx, 2(bx)
 | 
						|
	mov	ax, (bx)
 | 
						|
	mov	dx, 2(bx)	! dx:ax vector
 | 
						|
	!jmp	seg2abs		! Translate
 | 
						|
 | 
						|
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.
 | 
						|
.data
 | 
						|
	.align	2
 | 
						|
break:	.data2	_end		! A fake heap pointer
 | 
						|
.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
 | 
						|
	lea	dx, -4096(bx)	! A warning when heap+stack goes < 4K
 | 
						|
	cmp	dx, ax
 | 
						|
	jae	plenty		! No reason to complain
 | 
						|
	mov	ax, #memwarn
 | 
						|
	push	ax
 | 
						|
	call	_printf		! Warn about memory running low
 | 
						|
	pop	ax
 | 
						|
	movb	memwarn, #0	! No more warnings
 | 
						|
plenty:	pop	ax		! Return old break (0 for brk)
 | 
						|
	ret
 | 
						|
heaperr:mov	ax, #chmem
 | 
						|
	push	ax
 | 
						|
	mov	ax, #nomem
 | 
						|
	push	ax
 | 
						|
	call	_printf
 | 
						|
	jmp	quit
 | 
						|
.data
 | 
						|
nomem:	.ascii	"\nOut of%s\0"
 | 
						|
memwarn:.ascii	"\nLow on"
 | 
						|
chmem:	.ascii	" memory, use chmem to increase the heap\n\0"
 | 
						|
.text
 | 
						|
 | 
						|
! int dev_open(void);
 | 
						|
!	Given the device "_device" figure out if it exists and what its number
 | 
						|
!	of heads and sectors may be.  Return the BIOS error code on error,
 | 
						|
!	otherwise 0.
 | 
						|
.define	_dev_open
 | 
						|
_dev_open:
 | 
						|
	call	dev_reset	! Optionally reset the disks
 | 
						|
	movb	dev_state, #0	! State is "closed"
 | 
						|
	push	es
 | 
						|
	push	di		! Save registers used by BIOS calls
 | 
						|
	movb	dl, _device	! The default device
 | 
						|
	cmpb	dl, #0x80	! Floppy < 0x80, winchester >= 0x80
 | 
						|
	jae	winchester
 | 
						|
floppy:
 | 
						|
	mov	di, #3		! Three tries to init drive by reading sector 0
 | 
						|
finit0:	xor	ax, ax
 | 
						|
	mov	es, ax
 | 
						|
	mov	bx, #BUFFER	! es:bx = scratch buffer
 | 
						|
	mov	ax, #0x0201	! Read sector, #sectors = 1
 | 
						|
	mov	cx, #0x0001	! Track 0, first sector
 | 
						|
	xorb	dh, dh		! Drive dl, head 0
 | 
						|
	int	0x13
 | 
						|
	jnc	finit0ok	! Sector 0 read ok?
 | 
						|
	cmpb	ah, #0x80	! Disk timed out?  (Floppy drive empty)
 | 
						|
	je	geoerr
 | 
						|
	dec	di
 | 
						|
	jz	geoerr
 | 
						|
	xorb	ah, ah		! Reset drive
 | 
						|
	int	0x13
 | 
						|
	jc	geoerr
 | 
						|
	jmp	finit0		! Retry once more, it may need to spin up
 | 
						|
finit0ok:
 | 
						|
	mov	di, #seclist	! List of per floppy type sectors/track
 | 
						|
flast:	movb	cl, (di)	! Sectors per track to test
 | 
						|
	cmpb	cl, #9		! No need to do the last 720K/360K test
 | 
						|
	je	ftestok
 | 
						|
	xor	ax, ax
 | 
						|
	mov	es, ax
 | 
						|
	mov	bx, #BUFFER	! es:bx = scratch buffer
 | 
						|
	mov	ax, #0x0201	! Read sector, #sectors = 1
 | 
						|
	xorb	ch, ch		! Track 0, last sector
 | 
						|
	xorb	dh, dh		! Drive dl, head 0
 | 
						|
	int	0x13
 | 
						|
	jnc	ftestok		! Sector cl read ok?
 | 
						|
	xorb	ah, ah		! Reset drive
 | 
						|
	int	0x13
 | 
						|
	jc	geoerr
 | 
						|
	inc	di		! Try next sec/track number
 | 
						|
	jmp	flast
 | 
						|
ftestok:
 | 
						|
	movb	dh, #2		! Floppies have two sides
 | 
						|
	jmp	geoboth
 | 
						|
winchester:
 | 
						|
	movb	ah, #0x08	! Code for drive parameters
 | 
						|
	int	0x13		! dl still contains drive
 | 
						|
	jc	geoerr		! No such drive?
 | 
						|
	andb	cl, #0x3F	! cl = max sector number (1-origin)
 | 
						|
	incb	dh		! dh = 1 + max head number (0-origin)
 | 
						|
geoboth:
 | 
						|
	movb	sectors, cl	! Sectors per track
 | 
						|
	movb	al, cl		! al = sectors per track
 | 
						|
	mulb	dh		! ax = heads * sectors
 | 
						|
	mov	secspcyl, ax	! Sectors per cylinder = heads * sectors
 | 
						|
	movb	dev_state, #1	! Device state is "open"
 | 
						|
	xor	ax, ax		! Code for success
 | 
						|
geodone:
 | 
						|
	pop	di
 | 
						|
	pop	es		! Restore di and es registers
 | 
						|
	ret
 | 
						|
geoerr:	movb	al, ah
 | 
						|
	xorb	ah, ah		! ax = BIOS error code
 | 
						|
	jmp	geodone
 | 
						|
.data
 | 
						|
seclist:
 | 
						|
	.data1	18, 15, 9	! 1.44M, 1.2M, and 360K/720K floppy sec/track
 | 
						|
.text
 | 
						|
 | 
						|
! int dev_close(void);
 | 
						|
!	Close the current device.  Under the BIOS this does nothing much.
 | 
						|
.define	_dev_close
 | 
						|
_dev_close:
 | 
						|
	xor	ax, ax
 | 
						|
	movb	dev_state, al	! State is "closed"
 | 
						|
	ret
 | 
						|
 | 
						|
! Reset the disks if needed.  Minix may have messed things up.
 | 
						|
dev_reset:
 | 
						|
	cmpb	dev_state, #0	! Need reset if dev_state < 0
 | 
						|
	jge	0f
 | 
						|
	xorb	ah, ah		! Reset (ah = 0)
 | 
						|
	movb	dl, #0x80	! All disks
 | 
						|
	int	0x13
 | 
						|
	movb	dev_state, #0	! State is "closed"
 | 
						|
0:	ret
 | 
						|
 | 
						|
! int dev_boundary(u32_t sector);
 | 
						|
!	True if a sector is on a boundary, i.e. sector % sectors == 0.
 | 
						|
.define	_dev_boundary
 | 
						|
_dev_boundary:
 | 
						|
	mov	bx, sp
 | 
						|
	xor	dx, dx
 | 
						|
	mov	ax, 4(bx)	! divide high half of sector number
 | 
						|
	div	sectors
 | 
						|
	mov	ax, 2(bx)	! divide low half of sector number
 | 
						|
	div	sectors		! dx = sector % sectors
 | 
						|
	sub	dx, #1		! CF = dx == 0
 | 
						|
	sbb	ax, ax		! ax = -CF
 | 
						|
	neg	ax		! ax = (sector % sectors) == 0
 | 
						|
	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 disk or floppy.  The buffer must
 | 
						|
!	be between 64K boundaries!  Count must fit in a byte.  The external
 | 
						|
!	variables _device, sectors and secspcyl describe the disk and its
 | 
						|
!	geometry.  Returns 0 for success, otherwise the BIOS error code.
 | 
						|
!
 | 
						|
.define _readsectors, _writesectors
 | 
						|
_writesectors:
 | 
						|
	push	bp
 | 
						|
	mov	bp, sp
 | 
						|
	movb	13(bp), #0x03	! Code for a disk write
 | 
						|
	jmp	rwsec
 | 
						|
_readsectors:
 | 
						|
	push	bp
 | 
						|
	mov	bp, sp
 | 
						|
	movb	13(bp), #0x02	! Code for a disk read
 | 
						|
rwsec:	push	si
 | 
						|
	push	di
 | 
						|
	push	es
 | 
						|
	cmpb	dev_state, #0	! Device state?
 | 
						|
	jg	0f		! >0 if open
 | 
						|
	call	_dev_open	! Initialize
 | 
						|
	test	ax, ax
 | 
						|
	jnz	badopen
 | 
						|
0:	mov	ax, 4(bp)
 | 
						|
	mov	dx, 6(bp)
 | 
						|
	call	abs2seg
 | 
						|
	mov	bx, ax
 | 
						|
	mov	es, dx		! es:bx = bufaddr
 | 
						|
	mov	di, #3		! Execute 3 resets on floppy error
 | 
						|
	cmpb	_device, #0x80
 | 
						|
	jb	nohd
 | 
						|
	mov	di, #1		! But only 1 reset on hard disk error
 | 
						|
nohd:	cmpb	12(bp), #0	! count equals zero?
 | 
						|
	jz	done
 | 
						|
more:	mov	ax, 8(bp)
 | 
						|
	mov	dx, 10(bp)	! dx:ax = abs sector.  Divide it by sectors/cyl
 | 
						|
	cmp	dx, #[1024*255*63-255]>>16  ! Near 8G limit?
 | 
						|
	jae	bigdisk
 | 
						|
	div	secspcyl	! ax = cylinder, dx = sector within cylinder
 | 
						|
	xchg	ax, dx		! ax = sector within cylinder, dx = cylinder
 | 
						|
	movb	ch, dl		! ch = low 8 bits of cylinder
 | 
						|
	divb	sectors		! al = head, ah = sector (0-origin)
 | 
						|
	xorb	dl, dl		! About to shift bits 8-9 of cylinder into dl
 | 
						|
	shr	dx, #1
 | 
						|
	shr	dx, #1		! dl[6..7] = high cylinder
 | 
						|
	orb	dl, ah		! dl[0..5] = sector (0-origin)
 | 
						|
	movb	cl, dl		! cl[0..5] = sector, cl[6..7] = high cyl
 | 
						|
	incb	cl		! cl[0..5] = sector (1-origin)
 | 
						|
	movb	dh, al		! dh = head
 | 
						|
	movb	dl, _device	! dl = device to use
 | 
						|
	movb	al, sectors	! Sectors per track - Sector number (0-origin)
 | 
						|
	subb	al, ah		! = Sectors left on this track
 | 
						|
	cmpb	al, 12(bp)	! Compare with # sectors to transfer
 | 
						|
	jbe	doit		! Can't go past the end of a cylinder?
 | 
						|
	movb	al, 12(bp)	! 12(bp) < sectors left on this track
 | 
						|
doit:	movb	ah, 13(bp)	! Code for disk read (0x02) or write (0x03)
 | 
						|
	push	ax		! Save al = sectors to read
 | 
						|
	int	0x13		! call the BIOS to do the transfer
 | 
						|
	pop	cx		! Restore al in cl
 | 
						|
	jmp	rdeval
 | 
						|
bigdisk:
 | 
						|
	mov	si, #ext_rw	! si = extended read/write parameter packet
 | 
						|
	movb	cl, 12(bp)
 | 
						|
	movb	2(si), cl	! Fill in # blocks to transfer
 | 
						|
	mov	4(si), bx	! Buffer address = es:bx
 | 
						|
	mov	6(si), es
 | 
						|
	mov	8(si), ax	! Starting block number = dx:ax
 | 
						|
	mov	10(si), dx
 | 
						|
	movb	dl, _device	! dl = device to use
 | 
						|
	mov	ax, #0x4000	! This, or-ed with 0x02 or 0x03 becomes
 | 
						|
	orb	ah, 13(bp)	! extended read (0x4200) or write (0x4300)
 | 
						|
	int	0x13
 | 
						|
	!jmp	rdeval
 | 
						|
rdeval:
 | 
						|
	jc	ioerr		! I/O error
 | 
						|
	movb	al, cl		! Restore al = sectors read
 | 
						|
	addb	bh, al		! bx += 2 * al * 256 (add bytes transferred)
 | 
						|
	addb	bh, al		! es:bx = where next sector is located
 | 
						|
	add	8(bp), ax	! Update address by sectors transferred
 | 
						|
	adc	10(bp), #0	! Don't forget high word
 | 
						|
	subb	12(bp), al	! Decrement sector count by sectors transferred
 | 
						|
	jnz	more		! Not all sectors have been transferred
 | 
						|
done:	xorb	ah, ah		! No error here!
 | 
						|
	jmp	finish
 | 
						|
ioerr:	cmpb	ah, #0x80	! Disk timed out?  (Floppy drive empty)
 | 
						|
	je	finish
 | 
						|
	cmpb	ah, #0x03	! Disk write protected?
 | 
						|
	je	finish
 | 
						|
	dec	di		! Do we allow another reset?
 | 
						|
	jl	finish		! No, report the error
 | 
						|
	xorb	ah, ah		! Code for a reset (0)
 | 
						|
	int	0x13
 | 
						|
	jnc	more		! Succesful reset, try request again
 | 
						|
finish:	movb	al, ah
 | 
						|
	xorb	ah, ah		! ax = error number
 | 
						|
badopen:pop	es
 | 
						|
	pop	di
 | 
						|
	pop	si
 | 
						|
	pop	bp
 | 
						|
	ret
 | 
						|
.data
 | 
						|
	.align	4
 | 
						|
! Extended read/write commands require a parameter packet.
 | 
						|
ext_rw:
 | 
						|
	.data1	0x10		! Length of extended r/w packet
 | 
						|
	.data1	0		! Reserved
 | 
						|
	.data2	0		! Blocks to transfer (to be filled in)
 | 
						|
	.data2	0		! Buffer address offset (tbfi)
 | 
						|
	.data2	0		! Buffer address segment (tbfi)
 | 
						|
	.data4	0		! Starting block number low 32 bits (tbfi)
 | 
						|
	.data4	0		! Starting block number high 32 bits
 | 
						|
.text
 | 
						|
 | 
						|
! 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
 | 
						|
	jz	0f		! Nothing typed
 | 
						|
	xorb	ah, ah		! Read character from keyboard
 | 
						|
	int	0x16
 | 
						|
	jmp	press		! Keypress
 | 
						|
0:	mov	dx, line	! Serial line?
 | 
						|
	test	dx, dx
 | 
						|
	jz	0f
 | 
						|
	add	dx, #5		! Line Status Register
 | 
						|
	inb	dx
 | 
						|
	testb	al, #0x01	! Data Ready?
 | 
						|
	jz	0f
 | 
						|
	mov	dx, line
 | 
						|
	!add	dx, 0		! Receive Buffer Register
 | 
						|
	inb	dx		! Get character
 | 
						|
	jmp	press
 | 
						|
0:	call	_expired	! Timer expired?
 | 
						|
	test	ax, ax
 | 
						|
	jz	getch
 | 
						|
	mov	ax, #ESC	! Return ESC
 | 
						|
	ret
 | 
						|
press:
 | 
						|
	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, #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
 | 
						|
	mov	bx, line	! Serial line?
 | 
						|
	test	bx, bx
 | 
						|
	jz	nulch
 | 
						|
	push	ax		! Save character to print
 | 
						|
	call	_get_tick	! Current clock tick counter
 | 
						|
	mov	cx, ax
 | 
						|
	add	cx, #2		! Don't want to see it count twice
 | 
						|
1:	lea	dx, 5(bx)	! Line Status Register
 | 
						|
	inb	dx
 | 
						|
	testb	al, #0x20	! Transmitter Holding Register Empty?
 | 
						|
	jnz	0f
 | 
						|
	call	_get_tick
 | 
						|
	cmp	ax, cx		! Clock ticked more than once?
 | 
						|
	jne	1b
 | 
						|
0:	pop	ax		! Restore character to print
 | 
						|
	mov	dx, bx		! Transmit Holding Register
 | 
						|
	outb	dx		! Send character down the serial line
 | 
						|
nulch:	ret
 | 
						|
 | 
						|
! 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:
 | 
						|
	xor	ax, ax
 | 
						|
	mov	es, ax		! es = Vector segment
 | 
						|
	mov	ax, cur_vid_mode
 | 
						|
	movb	ch, ah		! Copy of the special flags
 | 
						|
	andb	ah, #0x0F	! Test bits 8-11, clear special flags
 | 
						|
	jnz	xvesa		! VESA extended mode?
 | 
						|
	int	0x10		! Reset video (ah = 0)
 | 
						|
	jmp	md_480
 | 
						|
xvesa:	mov	bx, ax		! bx = extended mode
 | 
						|
	mov	ax, #0x4F02	! Reset video
 | 
						|
	int	0x10
 | 
						|
md_480:				! Basic video mode is set, now build on it
 | 
						|
	testb	ch, #0x20	! 480 scan lines requested?
 | 
						|
	jz	md_14pt
 | 
						|
	mov	dx, #0x3CC	! Get CRTC port
 | 
						|
	inb	dx
 | 
						|
	movb	dl, #0xD4
 | 
						|
	testb	al, #1		! Mono or color?
 | 
						|
	jnz	0f
 | 
						|
	movb	dl, #0xB4
 | 
						|
0:	mov	ax, #0x110C	! Vertical sync end (also unlocks CR0-7)
 | 
						|
	call	out2
 | 
						|
	mov	ax, #0x060B	! Vertical total
 | 
						|
	call	out2
 | 
						|
	mov	ax, #0x073E	! (Vertical) overflow
 | 
						|
	call	out2
 | 
						|
	mov	ax, #0x10EA	! Vertical sync start
 | 
						|
	call	out2
 | 
						|
	mov	ax, #0x12DF	! Vertical display end
 | 
						|
	call	out2
 | 
						|
	mov	ax, #0x15E7	! Vertical blank start
 | 
						|
	call	out2
 | 
						|
	mov	ax, #0x1604	! Vertical blank end
 | 
						|
	call	out2
 | 
						|
	push	dx
 | 
						|
	movb	dl, #0xCC	! Misc output register (read)
 | 
						|
	inb	dx
 | 
						|
	movb	dl, #0xC2	! (write)
 | 
						|
	andb	al, #0x0D	! Preserve clock select bits and color bit
 | 
						|
	orb	al, #0xE2	! Set correct sync polarity
 | 
						|
	outb	dx
 | 
						|
	pop	dx		! Index register still in dx
 | 
						|
md_14pt:
 | 
						|
	testb	ch, #0x40	! 9x14 point font requested?
 | 
						|
	jz	md_8pt
 | 
						|
	mov	ax, #0x1111	! Load ROM 9 by 14 font
 | 
						|
	xorb	bl, bl		! Load block 0
 | 
						|
	int	0x10
 | 
						|
	testb	ch, #0x20	! 480 scan lines?
 | 
						|
	jz	md_8pt
 | 
						|
	mov	ax, #0x12DB	! VGA vertical display end
 | 
						|
	call	out2
 | 
						|
   eseg	movb	0x0484, #33	! Tell BIOS the last line number
 | 
						|
md_8pt:
 | 
						|
	testb	ch, #0x80	! 8x8 point font requested?
 | 
						|
	jz	setcur
 | 
						|
	mov	ax, #0x1112	! Load ROM 8 by 8 font
 | 
						|
	xorb	bl, bl		! Load block 0
 | 
						|
	int	0x10
 | 
						|
	testb	ch, #0x20	! 480 scan lines?
 | 
						|
	jz	setcur
 | 
						|
	mov	ax, #0x12DF	! VGA vertical display end
 | 
						|
	call	out2
 | 
						|
   eseg	movb	0x0484, #59	! Tell BIOS the last line number
 | 
						|
setcur:
 | 
						|
	xor	dx, dx		! dl = column = 0, dh = row = 0
 | 
						|
	xorb	bh, bh		! Page 0
 | 
						|
	movb	ah, #0x02	! Set cursor position
 | 
						|
	int	0x10
 | 
						|
	push	ss
 | 
						|
	pop	es		! Restore es
 | 
						|
modeok:	ret
 | 
						|
 | 
						|
! Out to the usual [index, data] port pair that are common for VGA devices
 | 
						|
! dx = port, ah = index, al = data.
 | 
						|
out2:
 | 
						|
	push	dx
 | 
						|
	push	ax
 | 
						|
	movb	al, ah
 | 
						|
	outb	dx		! Set index
 | 
						|
	inc	dx
 | 
						|
	pop	ax
 | 
						|
	outb	dx		! Send data
 | 
						|
	pop	dx
 | 
						|
	ret
 | 
						|
 | 
						|
restore_video:			! To restore the video mode on exit
 | 
						|
	mov	ax, old_vid_mode
 | 
						|
	push	ax
 | 
						|
	call	_set_mode
 | 
						|
	pop	ax
 | 
						|
	ret
 | 
						|
 | 
						|
! void serial_init(int line)
 | 
						|
!	Initialize copying console I/O to a serial line.
 | 
						|
.define	_serial_init
 | 
						|
_serial_init:
 | 
						|
	mov	bx, sp
 | 
						|
	mov	dx, 2(bx)	! Line number
 | 
						|
	push	ds
 | 
						|
	xor	ax, ax
 | 
						|
	mov	ds, ax		! Vector and BIOS data segment
 | 
						|
	mov	bx, dx		! Line number
 | 
						|
	shl	bx, #1		! Word offset
 | 
						|
	mov	bx, 0x0400(bx)	! I/O port for the given line
 | 
						|
	pop	ds
 | 
						|
	mov	line, bx	! Remember I/O port
 | 
						|
serial_init:
 | 
						|
	mov	bx, line
 | 
						|
	test	bx, bx		! I/O port must be nonzero
 | 
						|
	jz	0f
 | 
						|
	mov	ax, #0x00E3	! 9600 N-8-1
 | 
						|
	int	0x14		! Initialize serial line dx
 | 
						|
	lea	dx, 4(bx)	! Modem Control Register
 | 
						|
	movb	al, #0x0B	! DTR, RTS, OUT2
 | 
						|
	outb	dx
 | 
						|
0:	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:
 | 
						|
	push	cx
 | 
						|
	xorb	ah, ah		! Code for get tick count
 | 
						|
	int	0x1A
 | 
						|
	mov	ax, dx
 | 
						|
	mov	dx, cx		! dx:ax = cx:dx = tick count
 | 
						|
	pop	cx
 | 
						|
	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	_getprocessor
 | 
						|
	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
 | 
						|
 | 
						|
 | 
						|
! Functions to leave the boot monitor.
 | 
						|
.define _bootstrap		! Call another bootstrap
 | 
						|
.define _minix			! Call Minix
 | 
						|
 | 
						|
! void _bootstrap(int device, struct part_entry *entry)
 | 
						|
!	Call another bootstrap routine to boot MS-DOS for instance.  (No real
 | 
						|
!	need for that anymore, now that you can format floppies under Minix).
 | 
						|
!	The bootstrap must have been loaded at BOOTSEG from "device".
 | 
						|
_bootstrap:
 | 
						|
	call	restore_video
 | 
						|
	mov	bx, sp
 | 
						|
	movb	dl, 2(bx)	! Device to boot from
 | 
						|
	mov	si, 4(bx)	! ds:si = partition table entry
 | 
						|
	xor	ax, ax
 | 
						|
	mov	es, ax		! Vector segment
 | 
						|
	mov	di, #BUFFER	! es:di = buffer in low core
 | 
						|
	mov	cx, #PENTRYSIZE	! cx = size of partition table entry
 | 
						|
 rep	movsb			! Copy the entry to low core
 | 
						|
	mov	si, #BUFFER	! es:si = partition table entry
 | 
						|
	mov	ds, ax		! Some bootstraps need zero segment registers
 | 
						|
	cli
 | 
						|
	mov	ss, ax
 | 
						|
	mov	sp, #BOOTOFF	! This should do it
 | 
						|
	sti
 | 
						|
	jmpf	BOOTOFF, 0	! Back to where the BIOS loads the boot code
 | 
						|
 | 
						|
! void minix(u32_t koff, u32_t kcs, u32_t kds,
 | 
						|
!				char *bootparams, size_t paramsize, u32_t aout);
 | 
						|
!	Call Minix.
 | 
						|
_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 ! Switch to 386 mode?
 | 
						|
	jnz	minix386
 | 
						|
 | 
						|
! Call Minix in real mode.
 | 
						|
minix86:
 | 
						|
	test	_k_flags, #K_MEML ! New memory arrangements?
 | 
						|
	jz	0f
 | 
						|
	push	22(bp)		! Address of a.out headers
 | 
						|
	push	20(bp)
 | 
						|
0:
 | 
						|
	push	18(bp)		! # bytes of boot parameters
 | 
						|
	push	16(bp)		! Address of boot parameters
 | 
						|
 | 
						|
	test	_k_flags, #K_RET ! Can the kernel return?
 | 
						|
	jz	noret86
 | 
						|
	xor	dx, dx		! If little ext mem then monitor not preserved
 | 
						|
	xor	ax, ax
 | 
						|
	cmp	_mon_return, ax	! Minix can return to the monitor?
 | 
						|
	jz	0f
 | 
						|
	mov	dx, cs		! Monitor far return address
 | 
						|
	mov	ax, #ret86
 | 
						|
0:	push	dx		! Push monitor far return address or zero
 | 
						|
	push	ax
 | 
						|
noret86:
 | 
						|
 | 
						|
	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 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
 | 
						|
	.data1	0x0F,0x20,0xC0	! mov	eax, cr0
 | 
						|
	orb	al, #0x01	! Set PE (protection enable) bit
 | 
						|
	.data1	o32
 | 
						|
	mov	msw, ax		! 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
 | 
						|
	test	_k_flags, #K_INT86 ! Generic INT86 support?
 | 
						|
	jz	0f
 | 
						|
	push	#int86		! Far address to INT86 support
 | 
						|
	jmp	1f
 | 
						|
0:	push	#bios13		! Far address to BIOS int 13 support
 | 
						|
1:
 | 
						|
	test	_k_flags, #K_MEML ! New memory arrangements?
 | 
						|
	jz	0f
 | 
						|
	.data1	o32
 | 
						|
	push	20(bp)		! Address of a.out headers
 | 
						|
0:
 | 
						|
	push	#0
 | 
						|
	push	18(bp)		! 32 bit size of parameters on stack
 | 
						|
	push	#0
 | 
						|
	push	16(bp)		! 32 bit address of parameters (ss relative)
 | 
						|
 | 
						|
	test	_k_flags, #K_RET ! Can the kernel return?
 | 
						|
	jz	noret386
 | 
						|
	push	#MCS_SELECTOR
 | 
						|
	push	#ret386		! Monitor far return address
 | 
						|
noret386:
 | 
						|
 | 
						|
	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 ! Kernel data
 | 
						|
	mov	ds, ax
 | 
						|
	mov	ax, #ES_SELECTOR ! Flat 4 Gb
 | 
						|
	mov	es, ax
 | 
						|
	.data1	o32		! Make a far call to the kernel
 | 
						|
	retf
 | 
						|
 | 
						|
! Minix-86 returns here on a halt or reboot.
 | 
						|
ret86:
 | 
						|
	mov	_reboot_code+0, ax
 | 
						|
	mov	_reboot_code+2, dx	! Return value (obsolete method)
 | 
						|
	jmp	return
 | 
						|
 | 
						|
! Minix-386 returns here on a halt or reboot.
 | 
						|
ret386:
 | 
						|
	.data1	o32
 | 
						|
	mov	_reboot_code, ax	! Return value (obsolete method)
 | 
						|
	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
 | 
						|
 | 
						|
	movb	dev_state, #-1	! Minix may have upset the disks, must reset.
 | 
						|
	call	serial_init	! Likewise with our serial console
 | 
						|
 | 
						|
	call	_getprocessor
 | 
						|
	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:
 | 
						|
 | 
						|
	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
 | 
						|
	.data1 0xD5,10 ! aad	! ax = (al >> 4) * 10 + (al & 0x0F)
 | 
						|
	ret			! (BUG: assembler messes up aad & aam!)
 | 
						|
 | 
						|
 | 
						|
! void bootcdinfo(u32_t bufaddr, int *ret, int drive)
 | 
						|
! If booted from CD, do BIOS int 0x13 call to obtain boot CD device.
 | 
						|
.define	_bootcdinfo
 | 
						|
_bootcdinfo:
 | 
						|
	push bp
 | 
						|
	mov bp, sp
 | 
						|
	push ax
 | 
						|
	push bx
 | 
						|
	push cx
 | 
						|
	push dx
 | 
						|
	push si
 | 
						|
	push ds
 | 
						|
	mov	bx, 10(bp)	! drive number
 | 
						|
	mov	cx,  8(bp)
 | 
						|
	mov	ax,  4(bp)	! buffer address from stack
 | 
						|
	mov	dx,  6(bp)
 | 
						|
	call	abs2seg
 | 
						|
	mov	si, ax		! bios will put data in ds:si
 | 
						|
	mov	ds, dx
 | 
						|
!	movb	dl, #0x00
 | 
						|
	movb	dh, #0x00
 | 
						|
	movb	dl, bl
 | 
						|
!	mov	ax, #0x4b01	! command 0x4b, subcommand 0x01
 | 
						|
	movb	ah, #0x4b
 | 
						|
	movb	al, bh
 | 
						|
	int	0x13
 | 
						|
	mov	bp, cx
 | 
						|
	mov	(bp), ax
 | 
						|
	pop ds
 | 
						|
	pop si
 | 
						|
	pop dx
 | 
						|
	pop cx
 | 
						|
	pop bx
 | 
						|
	pop ax
 | 
						|
	pop bp
 | 
						|
	ret
 | 
						|
 | 
						|
! Support function for Minix-386 to make a BIOS int 13 call (disk I/O).
 | 
						|
bios13:
 | 
						|
	mov	bp, sp
 | 
						|
	call	prot2real
 | 
						|
	sti			! Enable interrupts
 | 
						|
 | 
						|
	mov	ax, 8(bp)	! Load parameters
 | 
						|
	mov	bx, 10(bp)
 | 
						|
	mov	cx, 12(bp)
 | 
						|
	mov	dx, 14(bp)
 | 
						|
	mov	es, 16(bp)
 | 
						|
	int	0x13		! Make the BIOS call
 | 
						|
	mov	8(bp), ax	! Save results
 | 
						|
	mov	10(bp), bx
 | 
						|
	mov	12(bp), cx
 | 
						|
	mov	14(bp), dx
 | 
						|
	mov	16(bp), es
 | 
						|
 | 
						|
	cli			! Disable interrupts
 | 
						|
	call	real2prot
 | 
						|
	mov	ax, #DS_SELECTOR ! Kernel data
 | 
						|
	mov	ds, ax
 | 
						|
	.data1	o32
 | 
						|
	retf			! Return to the kernel
 | 
						|
 | 
						|
! Support function for Minix-386 to make an 8086 interrupt call.
 | 
						|
int86:
 | 
						|
	mov	bp, sp
 | 
						|
	call	prot2real
 | 
						|
 | 
						|
	.data1	o32
 | 
						|
	xor	ax, ax
 | 
						|
	mov	es, ax		! Vector & BIOS data segments
 | 
						|
	.data1	o32
 | 
						|
   eseg	mov	0x046C, ax	! 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
 | 
						|
	.data1	o32
 | 
						|
	push	12(bp)		! Far driver address
 | 
						|
	mov	ax, #0x90CB	! RETF; NOP
 | 
						|
0:
 | 
						|
 cseg	cmp	ax, intret	! Needs to be changed?
 | 
						|
	je	0f		! If not then avoid a huge I-cache stall
 | 
						|
   cseg	mov	intret, ax	! Patch `INT n' or `RETF; NOP' into code
 | 
						|
	jmp	.+2		! Clear instruction queue
 | 
						|
0:
 | 
						|
	mov	ds, 16(bp)	! Load parameters
 | 
						|
	mov	es, 18(bp)
 | 
						|
	.data1	o32
 | 
						|
	mov	ax, 20(bp)
 | 
						|
	.data1	o32
 | 
						|
	mov	bx, 24(bp)
 | 
						|
	.data1	o32
 | 
						|
	mov	cx, 28(bp)
 | 
						|
	.data1	o32
 | 
						|
	mov	dx, 32(bp)
 | 
						|
	.data1	o32
 | 
						|
	mov	si, 36(bp)
 | 
						|
	.data1	o32
 | 
						|
	mov	di, 40(bp)
 | 
						|
	.data1	o32
 | 
						|
	mov	bp, 44(bp)
 | 
						|
 | 
						|
intret:	int	0xFF		! Do the interrupt or far call
 | 
						|
 | 
						|
	.data1	o32		! Save results
 | 
						|
	push	bp
 | 
						|
	.data1	o32
 | 
						|
	pushf
 | 
						|
	mov	bp, sp
 | 
						|
	.data1	o32
 | 
						|
	pop	8+8(bp)		! eflags
 | 
						|
	mov	8+16(bp), ds
 | 
						|
	mov	8+18(bp), es
 | 
						|
	.data1	o32
 | 
						|
	mov	8+20(bp), ax
 | 
						|
	.data1	o32
 | 
						|
	mov	8+24(bp), bx
 | 
						|
	.data1	o32
 | 
						|
	mov	8+28(bp), cx
 | 
						|
	.data1	o32
 | 
						|
	mov	8+32(bp), dx
 | 
						|
	.data1	o32
 | 
						|
	mov	8+36(bp), si
 | 
						|
	.data1	o32
 | 
						|
	mov	8+40(bp), di
 | 
						|
	.data1	o32
 | 
						|
	pop	8+44(bp)	! ebp
 | 
						|
 | 
						|
	cli			! Disable interrupts
 | 
						|
 | 
						|
	xor	ax, ax
 | 
						|
	mov	ds, ax		! Vector & BIOS data segments
 | 
						|
	.data1	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
 | 
						|
	.data1	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
 | 
						|
	.data1	o32
 | 
						|
	mov	ax, pdbr	! Load page directory base register
 | 
						|
	.data1	0x0F,0x22,0xD8	! mov	cr3, eax
 | 
						|
	.data1	0x0F,0x20,0xC0	! mov	eax, cr0
 | 
						|
	.data1	o32
 | 
						|
	xchg	ax, msw		! Exchange real mode msw for protected mode msw
 | 
						|
	.data1	0x0F,0x22,0xC0	! mov	cr0, eax
 | 
						|
	jmpf	cs_prot, MCS_SELECTOR ! 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
 | 
						|
	.data1	0x0F,0x20,0xD8	! mov	eax, cr3
 | 
						|
	.data1	o32
 | 
						|
	mov	pdbr, ax	! Save page directory base register
 | 
						|
	.data1	0x0F,0x20,0xC0	! mov	eax, cr0
 | 
						|
	.data1	o32
 | 
						|
	xchg	ax, msw		! Exchange protected mode msw for real mode msw
 | 
						|
	.data1	0x0F,0x22,0xC0	! mov	cr0, eax
 | 
						|
	jmpf	cs_real, 0xDEAD	! 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
 | 
						|
 | 
						|
.data
 | 
						|
	.ascii	"(null)\0"	! Just in case someone follows a null pointer
 | 
						|
	.align	2
 | 
						|
c60:	.data2	60		! Constants for MUL and DIV
 | 
						|
c1024:	.data2	1024
 | 
						|
c1080:	.data2	1080
 | 
						|
c19663:	.data2	19663
 | 
						|
 | 
						|
! 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 (4 Gb flat)
 | 
						|
	.data2	0xFFFF, UNSET
 | 
						|
	.data1	UNSET, 0x92, 0xCF, 0x00
 | 
						|
p_es_desc:
 | 
						|
	! Physical memory descriptor (4 Gb flat)
 | 
						|
	.data2	0xFFFF, 0x0000
 | 
						|
	.data1	0x00, 0x92, 0xCF, 0x00
 | 
						|
p_ss_desc:
 | 
						|
	! Monitor data segment descriptor (64 kb flat)
 | 
						|
	.data2	0xFFFF, UNSET
 | 
						|
	.data1	UNSET, 0x92, 0x00, 0x00
 | 
						|
p_cs_desc:
 | 
						|
	! Kernel code segment descriptor (4 Gb flat)
 | 
						|
	.data2	0xFFFF, UNSET
 | 
						|
	.data1	UNSET, 0x9A, 0xCF, 0x00
 | 
						|
p_mcs_desc:
 | 
						|
	! Monitor code segment descriptor (64 kb flat)
 | 
						|
	.data2	0xFFFF, UNSET
 | 
						|
	.data1	UNSET, 0x9A, 0x00, 0x00
 | 
						|
 | 
						|
.bss
 | 
						|
	.comm	old_vid_mode, 2	! Video mode at startup
 | 
						|
	.comm	cur_vid_mode, 2	! Current video mode
 | 
						|
	.comm	dev_state, 2	! Device state: reset (-1), closed (0), open (1)
 | 
						|
	.comm	sectors, 2	! # sectors of current device
 | 
						|
	.comm	secspcyl, 2	! (Sectors * heads) of current device
 | 
						|
	.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)
 | 
						|
	.comm	line, 2		! Serial line I/O port to copy console I/O to.
 |