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