//-----------------------------------------------------------------
//	usevm86.s	(A modification of our 'vm86demo.s' demo)
//
//	Here we have modified our 'vm86demo.s' program so that now
//	some i/o-sensitive instructions are encountered within our 
//	virtual-8086 mode subroutine (specifically 'int $0x1C' and 
//	'iret').  These will trigger General Progection faults and
//	their actions must be 'emulated' by our software if we are
//	to complete this new virtual-8086 subroutine successfully.
//
//	  to assemble: $ as usevm86.s -o usevm86.o	
//	  and to link: $ ld usevm86.o -T ldscript -o usevm86.b
//	  and install: $ dd if=usevm86.b of=/dev/sda4 seek=1
//
//	NOTE: This code begins executing with CS:IP = 1000:0002.
//
//	programmer: ALLAN CRUSE
//	written on: 04 DEC 2008
//-----------------------------------------------------------------


	.section	.text
#------------------------------------------------------------------
	.word	0xABCD
#------------------------------------------------------------------
main:	.code16

	mov	%sp, %cs:ipltos+0
	mov	%ss, %cs:ipltos+2

	mov	%cs, %ax
	mov	%ax, %ss
	lea	tos0, %sp

	call	enter_protected_mode
	call	execute_program_demo
	call	leave_protected_mode

	lss	%cs:ipltos, %sp
	lret
#------------------------------------------------------------------
ipltos:	.word	0, 0
#------------------------------------------------------------------
theTSS:	.space	0x68
	.equ	limTSS, (.-theTSS)-1
#------------------------------------------------------------------
theIDT:	.space	13 * 8
	.word	isrGPF, privCS, 0x8E00, 0x0000	
	.equ	limIDT, (.-theIDT)-1
#------------------------------------------------------------------
theGDT:	.quad	0x0000000000000000
	
	.equ	privCS, (.-theGDT)+0
	.quad	0x00409A010000FFFF

	.equ	privSS, (.-theGDT)+0
	.quad	0x004092010000FFFF

	.equ	sel_cs, (.-theGDT)+0
	.quad	0x00009A010000FFFF

	.equ	sel_ds, (.-theGDT)+0
	.quad	0x000092010000FFFF

	.equ	sel_es, (.-theGDT)+0
	.quad	0x0000920B8000FFFF

	.equ	sel_fs, (.-theGDT)+0
	.quad	0x008F92000000FFFF

	.equ	selTSS, (.-theGDT)
	.word	limTSS, theTSS, 0x8901, 0x0000
	
	.equ	limGDT, (.-theGDT)-1
#------------------------------------------------------------------
regGDT:	.word	limGDT, theGDT, 0x0001
regIDT:	.word	limIDT, theIDT, 0x0001
regIVT:	.word	0x03FF, 0x0000, 0x0000
#------------------------------------------------------------------
enter_protected_mode:

	cli
	mov	%cr0, %eax
	bts	$0, %eax
	mov	%eax, %cr0

	lgdt	%cs:regGDT
	lidt	%cs:regIDT

	ljmp	$sel_cs, $pm
pm:	mov	$sel_ds, %ax
	mov	%ax, %ss

	xor	%ax, %ax
	mov	%ax, %ds
	mov	%ax, %es
	mov	%ax, %fs
	mov	%ax, %gs

	ret
#------------------------------------------------------------------
execute_program_demo:

	# preserve our current stacktop address
	mov	%esp, %ss:tossav+0
	mov	%ss,  %ss:tossav+4

	# switch to a 32-bit stack
	mov	$privSS, %ax
	mov	%ax, %ss
	and	$~0x3, %esp

	# establish the Task-State Segment
	mov	$selTSS, %ax
	ltr	%ax

	# initialize the SS0:ESP0 fields in our TSS 
	mov	%esp, %ss:theTSS+4
	mov	%ss,  %ss:theTSS+8

	# prepare the stack for a transfer to Virtual-8086 mode
	pushl	$0			# image for GS
	pushl	$0			# image for FS
	pushl	$0			# image for DS
	pushl	$0			# image for ES
	pushl	$0x1000			# image for SS
	pushl	$tos3			# image for SP
	pushl	$0x00020000		# image for EFLAGS
	pushl	$0x1000			# image for CS
	pushl	$vm86			# image for IP

	# transfer to Virtual-8086 mode (in ring3)
	iretl
#------------------------------------------------------------------
tossav:	.long	0, 0			# area for holding SS:ESP 
#------------------------------------------------------------------
back_to_main_routine:
	lss	%cs:tossav, %esp	# recover former stacktop
	ret				#  and go back to 'main'
#------------------------------------------------------------------
vm86:	.code16

	# let's try executing a software interrupt

	int	$0x1C			# usually just returns

	# set all text-mode video attributes to a blue-background
	
	mov	$0xB800, %ax		# address video memory
	mov	%ax, %ds		#   with DS register
	mov	%ax, %es		#   also ES register
	xor	%si, %si		# point DS:SI to start
	xor	%di, %di		# point ES:DI to start
	cld				# do forward processing
	mov	$0x4000, %cx		# number of video words
nxpel:	lodsw				# fetch picture-element
	mov	$0x17, %ah		# set attribute-byte
	stosw				# store picture-element
	loop	nxpel			# again for entire vram

	hlt				# generate a GP-exception
#------------------------------------------------------------------
isrGPF:	.code32	# this is our exception-handler for GP-exceptions

	# verify that this exception occurred in Virtual-8086 mode
	btl	$17, 12(%esp)		# was VM-bit set?
	jc	emulate			# yes, do emulation
	ljmp	$sel_cs, $back_to_main_routine

emulate:
#
# Here we carefully preserve registers while we are determining
# which opcode has triggered this General Protection Fault, and
# then we will 'emulate' the usual actions for that instuction. 
# (Note that segment-registers already got saved on the stack.)
#
	enter	$0, $0			# setup stack access
	pushal				# preserve registers
	mov	$sel_fs, %ax		# address flat memory
	mov	%ax, %ds		#   with DS register
	
	# compute address of the faulting instruction in EBX

	movzxw	8(%ebp), %ebx		# fetch the IP-image
	movzxw	12(%ebp), %eax		# fetch the CS-image
	shl	$4, %eax		#  sixteen times CS
	add	%eax, %ebx		#   is added to IP

	# now we 'switch' on the faulting instruction's opcode

	cmpb	$0xCD, (%ebx)		# was it 'int-nn'?
	je	emulate_int		# yes, then enulate

	cmpb	$0xCF, (%ebx)		# was it 'iret'?
	je	emulate_iret		# yes, then emulate

	# --- check for other i/o-sensitive opcodes here ---
	# --- i.e., cli, sti, pushf, popf, pushfl, popfl --- 
	# --- and add code that 'emulates' their actions ---

	# else we terminate this demo by returning to 'main'
	ljmp	$sel_cs, $back_to_main_routine

em_done:
	popal				# restore saved registers
	leave				# and former base-pointer
	add	$4, %esp		# discard the error-code
	iret				# resume the VM86 task

emulate_int:
	# decrement the SP-image to make room for three words	
	subw	$6, 20(%ebp)		# make room for 3 words
	
	# compute address of the ring3 stacktop in register EDI
	movzxw	20(%ebp), %edi		# fetch the SP-image
	movzxw	24(%ebp), %eax		# fetch the SS-image
	shl	$4, %eax		#  sixteen times SS
	add	%eax, %edi		#   is added to SP

	# advance the IP-image past the 'int-nn' instruction
	addw	$2, 8(%ebp)		# advance the IP-image

	# transfer the IP, CS, FLAGS images to the ring3 stack
	mov	8(%ebp), %ax		# fetch IP-image	
	mov	%ax, 0(%edi)		# store IP-image
	mov	12(%ebp), %ax		# fetch CS-image	
	mov	%ax, 2(%edi)		# store CS-image
	mov	16(%ebp), %ax		# fetch FLAGS-image	
	mov	%ax, 4(%edi)		# store FLAGS-image

	# clear the IF-bit and TF-bit in the EFLAGS image
	btrw	$9, 16(%ebp)		# clear the IF-image
	btrw	$8, 16(%ebp)		# clear the TF-image

	# use real-mode interrupt-vector as the 'return' address	
	movzxb	1(%ebx), %edx		# vector-number in EDX
	mov	0(, %edx, 4), %ax	# fetch vector loword
	mov	%ax, 8(%ebp)		# store as IP-image
	mov	2(, %edx, 4), %ax	# fetch vector hiword
	mov	%ax, 12(%ebp)		# store as CS-image

	# ok, we're ready to resume the ring3 task
	jmp	em_done

emulate_iret:
	# compute address of ring3 stacktop in register ESI
	movzxw	20(%ebp), %esi		# fetch the SP-image
	movzxw	24(%ebp), %eax		# fetch the SS-image
	shl	$4, %eax		#  sixteen times SS
	add	%eax, %esi		#   is added to SP	

	# copy IP, CS, FLAGS from ring3 stack to ring0 stack
	mov	0(%esi), %ax		# fetch IP-image
	mov	%ax, 8(%ebp)		# store IP-image
	mov	2(%esi), %ax		# fetch CS-image
	mov	%ax, 12(%ebp)		# store CS-image
	mov	4(%esi), %ax		# fetch FLAGS-image
	mov	%ax, 16(%ebp)		# store FLAGS-image

	# increment the SP-image to discard those three words
	addw	$6, 20(%ebp)		# discard three words

	# ok, we're ready to resume the ring3 task
	jmp	em_done
#------------------------------------------------------------------
leave_protected_mode:
	.code16
	mov	$sel_ds, %ax
	mov	%ax, %ds
	mov	%ax, %es
	mov	%ax, %fs
	mov	%ax, %gs
		
	mov	%cr0, %eax
	btr	$0, %eax
	mov	%eax, %cr0

	ljmp	$0x1000, $rm
rm:	mov	%cs, %ax
	mov	%ax, %ss

	lidt	%cs:regIVT
	sti

	ret
#------------------------------------------------------------------
	.align	16
	.space	256
tos3:
	.space	256
tos0:	
#------------------------------------------------------------------
	.end