include(asmacs)		# simple verbosifiers; to=,  copy=mov  etc.

# m4 cut with sed

.org 0x7c00		# This means we'll have to trim the 
			#    binary with dd or something. Well worth it.    
			#    After   as   and   objcopy -O binary   do
			#    dd < a.out > whatever bs=512 skip=62

_assembling_16
.global _start
_start:

nointerrupts

movw $gdt_entries to %si	# copy initial GDT descriptors to  x2000
movw $0x2008 to %di		#	skip and thus allocate entry 0
copy %di to %bx
add $0x100 to %bx
for_128_duals:			# move 256 bytes
	copy  (%si) to %ax
	copy  %ax, %es:(%di)
	add $2 to %di
	add $2 to %si
	ifsubtract %bx to %di
jnz for_128_duals
				# 	assuming %es=0 and ss makes sense.
				#	My P166 BIOS allows this. YMMV.

lgdt initial_gdtr		# This is one reason for the .org 0x7c00

copy %cr0 to counter		
increment %cx
copy counter to %cr0		# protection is on, but we need to get into
				# 32 bit segments. You have to branch to 
				# a new cs, mov won't do cs.

				# hand assembled jmp sd1:cs32
_byte   0x66			# we're still IN 16 bit 
_byte	0xea
_quad   cs32 
_dual 	0x08			# 8 is GDT index 1, our code seg. selector

cs32:				

_assembling_32			# since we just jumped to a 32 bit segment

mov $0x10 to %eax		# selector 0x10 is index 2, our 32 bit data
movw %ax to %ds
movw %ax to %ss			# This might help with the 32/16/32 stunts.

mov $0x9000 to %esp		# first 64k of second meg is our stack


#
## BUILD Interrupt Descriptor Table at 0x1000 physical
#	256 interrupt gate descriptors that call sequential IIT 
#	calls, which all call INTCALLER. 


movw $0x7003 to A
movl $0x1000 to dest
per_IDT_descriptor:			# Our first (example) entry is...
	movw A to (dest)		# 03 70       starting at 0x1000
	increment dest			# offset
	increment dest
	movw $0x08 to (dest)		
	increment dest			# 03 70 	08 00 
	increment dest			# 		pmode code sel.
	movl $0x00008e00 to (dest)	
	increment dest			# 03 70 20 00 	00 8e 00 00
	increment dest			# 		present    intrrgate 
	increment dest
	increment dest
	add $8 to A
	ifsubtract $0x7803, A
jnz per_IDT_descriptor

lidt initial_idtr


#
## BUILD Int Indirection Table at 0x7000 <-> 0x7800. This is a bunch of 
#	calls that INTCALLER can pop the return address of to find out 
#	what int is happening.


copy	$0xff to B
copy 	$0x7000 to dest
copy	$(INTCALLER - 0x7008)  to A		# first relative offset
						# byte following 
while_B:
		# dest is cursor into IIT
		# eax is current call offset 
		# ebx is an entry counter, and is extraneous

	movl	$0xe8909090 to (dest)	# e8 opcode for CALL rel32
					# prefixed with NOPs. Note endianism.
	add $4 to  dest			
	movl	A to (dest)		# append 4 byte relative offset
	add $4 to  dest		
	subl	$8, A			# relative offset from next IIT call to 
					# INTCALLER will be 8 less.
	decrement B
jnz while_B


interrupts


HANG: nop  ;

	mov  0xb8060 to A
	inc A
	inc A
	mov A to 0xb8060
	int $0x40


jmp HANG ###########
#################################

INTCALLER:		# System code, so to speak. 

			# we are in the system 32-bit segments

mov A to 0x6300
pop A			# gotta pop the I.I.T. return value to calculate
			#   which int we are. 



	mov  0xb8060 + 320 to A
	inc A
	inc A
	mov A to 0xb8060 +320


_byte   0xea			# far jmp to a suitable-for-real-mode cs
_quad	small_code_segment
_dual	0x20
small_code_segment:		# we are a 16-bit machine with no useful
				# 	segments.

_assembling_16


movl $0x18 to A			# upgrade to an 8086

movw A to %ds
movw A to %ss


copy %cr0 to counter
decrement %cx
copy counter to %cr0		# turn off protection, losing the segments 
				# 	again.


_byte   0xea
_dual   rereal
_dual   0x0                    	# far jmp to an 8086 cs, NOT a dscriptor

rereal:


mov 0xb800 to %ax		# real mode demo code. This could be
mov %ax to %ds			#   an 8086 int caller.
sub $1, %ax
mov %ax, 0x500


copy %cr0 to %ecx
increment %ecx
copy %ecx to %cr0


_byte   0xea
_dual   recs32
_dual   0x08			# 8 is GDT index 1, our code seg. selector

recs32:

_assembling_32

movw $0x10 to A

mov A to %ds
mov A to %ss


iret



############### initialized DATA allocations      ####################

gdt_entries:
	# The GDT copy routine skip-allocates index 0.
	# We initialize from index 1 on. Index 1 = selector 8. 
	# This template is for typical segments, not gates and such. 

        _dual 	0xFFFF	# low dual of limit
	_dual	0	# low dual of base
	_byte	0	# bits 16<->23 of base
			
				# The DFbyte, see also the DFnybble
				# 
				# bit	on=	description	  bit of 64 
				# 0	0x1	accessed		40
				# 1	0x2	read/write		41
				# 2	0x4	expandup/conform	42
				# 3	0x8	executable?		43
				# 4	0x10	typical			44
				# 5	0x20	DPL lo			45
				# 6	0x40	DPL hi			46
				# 7	0x80	present			47
	_byte	0x9A	# = present typical executable readable  	

				# bit	on=     description	  bit of 64
                                # 0	0x1    	limit hi nybble		48
                                # 1     0x2     	"		49
                                # 2     0x4     	"		50
                                # 3     0x8     	"		51
				# The DFnybble(TM)
				# 4     0x10    AVL			52
                                # 5     0x20    0			53
                                # 6     0x40    Big/D-efault/X		54
				# D-bit true = 32 bit addy/operands in pmode	
                                # 7     0x80    4k Granularity		55
	_byte	0xCF	# = *4k Big   f-nybble	
	_byte	0x00	# hi byte of base

# index 2, 32 bit data, selector val = 0x10, 4 gig limit, 0 base 
        _dual   0xFFFF
	_dual	0
	_byte   0x00	
	_byte	0x92 	# present typical writable
	_byte	0xcf    # *4k  BIG  f-nybble 
	_byte	0x00


# index 3, 16 bit data, selector val = 0x18, 64k limit, 0 base
        _dual   0xFFFF	# limit
        _dual   0
        _byte   0		
        _byte   0x92    # present typical writable
        _byte   0x0    	# (bytegrain) 0-max-nybble-of-limit
        _byte   0

# index 4, 16 bit code, selector val = 0x20, 64k lim/gran, 0000 base
# this is base 0 real-ish, for INTCALLER , ffff limit

        _dual   0xFFFF  # limit
        _dual   0       # this is physical 
	_byte   0x0	#     this OR previous dual = 0000
	_byte   0x9a    # present typical executable readable
	_byte   0x0     # (smallgrain) (notBIG) 0-max-nybble-of-limit
        _byte   0

#,.....................................................

## 6 byte limits/pointers for IDTR & GDTR

initial_idtr:
	_dual  0x800		# 2k, 256 * 8
	_quad  0x1000		# gdt physical address

initial_gdtr:
	_dual  0x400		# gdt limit
	_quad  0x2000		# gdt physical address
			
.org 0x7dfe
.word  0xAA55 	# Claim to be a PEEEEEEE.CEE bootsector.

STAGE_II:
# get a floppy load on, put your OS here, and jmp here.


# Copyright 2000 Rick Hohensee
# References/keys: Intel 386 manuals, John Fine, GNU as.info, gazos
#	Ralf Brown's, Linux
