//-----------------------------------------------------------------
//	vm86demo.s
//
//	This example shows the steps needed to enter the x86 CPU's 
//	'Virtual-8086' mode, take some kind of perceptible action,
//	and then return to real-mode for a restart of the machine. 
//
//	  to assemble: $ as vm86demo.s -o vm86demo.o	
//	  and to link: $ ld vm86demo.o -T ldscript -o vm86demo.b
//	  and install: $ dd if=vm86demo.b of=/dev/sda4 seek=1
//
//	NOTE: This code begins executing with CS:IP = 1000:0002.
//
//	programmer: ALLAN CRUSE
//	written on: 03 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

	# 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
	ljmp	$sel_cs, $back_to_main_routine
#------------------------------------------------------------------
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