ow that the image has grown beyond the 1.44M that fits on a floppy. (previously, the floppy emulation mode was used for cd's.) the boot cd now uses 'no emulation mode,' where an image is provided on the cd that is loaded and executed directly. this is the boot monitor. in order to make this work (the entry point is the same as where the image is loaded, and the boot monitor needs its a.out header too) and keep compatability with the same code being used for regular booting, i prepended 16 bytes that jumps over its header so execution can start there. to be able to read the CD (mostly in order to read the boot image), boot has to use the already present 'extended read' call, but address the CD using 2k sectors.
		
			
				
	
	
		
			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
 | 
						|
.extern _cdbooted				! Whether we booted from CD
 | 
						|
.extern _cddevice				! Whether we booted from CD
 | 
						|
 | 
						|
.text
 | 
						|
 | 
						|
! We assume boot is always linked with a short (32 byte) a.out header and has
 | 
						|
! 16 bytes of its own prefix, so 48 bytes to skip. bootblock jumps into us
 | 
						|
! at offset 0x30, cd boot code at 0x40
 | 
						|
 | 
						|
	! Set cs right
 | 
						|
	! (skip short a.out header plus 16 byte preefix)
 | 
						|
	jmpf	boot, LOADSEG+3		
 | 
						|
	.space	11
 | 
						|
 | 
						|
	! entry point when booting from CD
 | 
						|
	jmpf	cdboot, LOADSEG+3
 | 
						|
	.space	11
 | 
						|
cdboot:
 | 
						|
	mov	bx, #1
 | 
						|
	jmp	commonboot
 | 
						|
boot:
 | 
						|
	mov	bx, #0
 | 
						|
commonboot:
 | 
						|
	mov	ax, #LOADSEG+1
 | 
						|
	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)
 | 
						|
	mov	_cdbooted, bx	! Booted from CD? (bx set above)
 | 
						|
 | 
						|
! 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+1
 | 
						|
	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, _cddevice
 | 
						|
	je	cdopen
 | 
						|
	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)
 | 
						|
	jmp geoboth
 | 
						|
cdopen:
 | 
						|
	movb	cl, #0x3F	! Think up geometry for CD's
 | 
						|
	movb	dh, #0x2
 | 
						|
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 biosreadsectors(u32_t bufaddr, u32_t sector, u8_t count)
 | 
						|
! int writesectors(u32_t bufaddr, u32_t sector, u8_t count)
 | 
						|
!	Read/write several sectors from/to 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 _biosreadsectors, _writesectors
 | 
						|
_writesectors:
 | 
						|
	push	bp
 | 
						|
	mov	bp, sp
 | 
						|
	movb	13(bp), #0x03	! Code for a disk write
 | 
						|
	jmp	rwsec
 | 
						|
_biosreadsectors:
 | 
						|
	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
 | 
						|
	mov	si, _device	
 | 
						|
	cmp	si, _cddevice	! Is it a CD?
 | 
						|
	je	bigdisk		! CD's need extended read.
 | 
						|
	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
 | 
						|
	mov	line, #0
 | 
						|
	test	dx, dx		! Off if line number < 0
 | 
						|
	js	0f
 | 
						|
	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!)
 | 
						|
 | 
						|
! 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
 | 
						|
 | 
						|
! void scan_keyboard(void)
 | 
						|
!	Read keyboard character. Needs to be done in case one is waiting.
 | 
						|
.define _scan_keyboard
 | 
						|
_scan_keyboard:
 | 
						|
	inb	0x60
 | 
						|
	inb	0x61
 | 
						|
	movb	ah, al
 | 
						|
	orb	al, #0x80
 | 
						|
	outb	0x61
 | 
						|
	movb	al, ah
 | 
						|
	outb	0x61
 | 
						|
	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.
 |