	PAGE	,132
	TITLE	Method 2
	.286C	; Tell MASM 2.0 about 286 instructions
;--------------------------------------------------------------:
;	Sample Program 2				       :
;							       :
; This program switches into Protected Virtual Mode, changes   :
; the display attribute to reverse video, and returns to Real  :
; Mode to exit to DOS					       :
;							       :
; Once entered into a file, do the following:		       :
;	MASM SAMPLE2;					       :
;	LINK SAMPLE2;					       :
;	EXE2BIN SAMPLE2 SAMPLE2.COM			       :
;	DEL SAMPLE2.EXE 				       :
;							       :
; WARNING: This program will "kill" a PC.  It should only      :
; be run on an AT.					       :
;--------------------------------------------------------------:

bios_data_seg	SEGMENT at	0040h
		ORG		0067h
io_rom_init	dw ?	; dword variable in BIOS data segment
io_rom_seg	dw ?	;  used to store a dword address
bios_data_seg	ENDS

descriptor	STRUC
seg_limit	dw	0	; segment limit (1-65536 bytes)
base_lo_word	dw	0	; 24 bit physical address
base_hi_byte	db	0	;	(0 - (16M-1))
access_rights	db	0	; access rights byte
		dw	0	; reserved_386
descriptor	ENDS

cmos_port	equ 070h
code_seg_access equ 10011011b	;access rights byte for code seg
data_seg_access equ 10010011b	;access rights byte for data seg
enable_bit20	equ 11011111b	;8042 function code to gate A20
port_a		equ 060h	;8042 port A
shut_cmd	equ 0FEh	;cmd to 8042: shut down AT
shut_down	equ 00Fh	;CMOS shut down byte index
status_port	equ 064h	;8042 status port
virtual_enable	equ 0001h	;LSB=1: Protected Virtual Mode

	SUBTTL	Macro Definitions
	PAGE
;--------------------------------------------------------------:
; These mnemonics are not supported in MASM 2.0 therefore      :
; they are supplied here as MACROS.			       :
;--------------------------------------------------------------:
lgdt	MACRO	lgdt1
	LOCAL	lgdt2,lgdt3
	db	00Fh
lgdt2	label	byte
	mov	dx,word ptr lgdt1
lgdt3	label	byte
	org	offset lgdt2
	db	001h
	org	offset lgdt3
	ENDM
	page

lmsw	MACRO	lmsw1
	LOCAL	lmsw2,lmsw3
	db	00Fh
lmsw2	label	byte
	mov	si,ax
lmsw3	label	byte
	org	offset lmsw2
	db	001h
	org	offset lmsw3
	ENDM

jumpfar MACRO	jumpfar1,jumpfar2
	db	0EAh
	dw	(offset jumpfar1)
	dw	jumpfar2
	ENDM

	SUBTTL	Program entry point and data area
	PAGE
cseg	SEGMENT para		public		'code'
	ASSUME	cs:cseg

	ORG	100h
start:	jmp	short	main

	EVEN
gdt		LABEL	word

gdt_desc	EQU	(($-gdt)/8)*8 + 0000000000000000b
gdt1	descriptor	<gdt_leng,,,data_seg_access,>
cs_code 	EQU	(($-gdt)/8)*8 + 0000000000000000b
gdt2	descriptor	<cseg_leng,,,code_seg_access,>
cs_data 	EQU	(($-gdt)/8)*8 + 0000000000000000b
gdt3	descriptor	<cseg_leng,,,data_seg_access,>
ss_desc 	EQU	(($-gdt)/8)*8 + 0000000000000000b
gdt4	descriptor	<0FFFFh,,,data_seg_access,>
ds_desc 	equ	(($-gdt)/8)*8 + 0000000000000000b
gdt5	descriptor	<0FFFFh,,,data_seg_access,>
es_desc 	equ	(($-gdt)/8)*8 + 0000000000000000b
gdt6	descriptor	<0FFFFh,,,data_seg_access,>
gdt_leng	EQU	$-gdt
		PAGE
;--------------------------------------------------------------:
; Format of the Segment Selector Component:		       :
;							       :
; +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+	       :
; |		INDEX			 +TI+ RPL +	       :
; +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+	       :
;							       :
; TI = Table Indicator (0=GDT, 1=LDT)			       :
; RPL = Requested Privelege Level (00 = highest; 11 = Lowest)  :
;--------------------------------------------------------------:
; Format of the Global Descriptor Table 		       :
;		  .-----------+ 	     +---> TI	       :
;		  V	      | 	     |++-> RPL	       :
; GDT ==> +---------------+   | 	     |||	       :
;	  |   GDT_DESC	  | --+ 0000000000000000b	       :
;	  +---------------+				       :
;	  |    CS_CODE	  |	0000000000001000b	       :
;	  +---------------+				       :
;	  |    CS_DATA	  |	0000000000010000b	       :
;	  +---------------+				       :
;	  |    SS_DESC	  |	0000000000011000b	       :
;	  +---------------+				       :
;	  |    DS_DESC	  |	0000000000100000b	       :
;	  +---------------+				       :
;	  |    ES_DESC	  |	0000000000101000b	       :
;	  +---------------+				       :
;--------------------------------------------------------------:

	SUBTTL	Program Main
	PAGE
;--------------------------------------------------------------:
; MAIN							       :
;--------------------------------------------------------------:
	ASSUME	ds:cseg
main	PROC			      ;ES=DS=CS
	cld			      ;forward

	mov	dx,cs		      ;form 24bit address out of
	mov	cx,offset gdt	      ;CS:GDT
	call	form_24bit_address
	mov	gdt1.base_lo_word,dx  ;DESC now points to gdt
	mov	gdt1.base_hi_byte,cl

	mov	dx,cs		      ;form 24bit address out of
	xor	cx,cx		      ; CS:0000
	call	form_24bit_address
	mov	gdt2.base_lo_word,dx  ;CS_CODE now points to
	mov	gdt2.base_hi_byte,cl  ; CSEG as a code segment
	mov	gdt3.base_lo_word,dx  ;CS_DATA now points to
	mov	gdt3.base_hi_byte,cl  ; CSEG as a data segment

	mov	dx,ss		      ;form 24bit address out of
	xor	cx,cx		      ;SS:0000
	call	form_24bit_address
	mov	gdt4.base_lo_word,dx  ;SS_DESC now points to
				      ; stack segment
	mov	gdt4.base_hi_byte,cl

	lgdt	gdt		      ;Load the GDTR

	mov	ah,enable_bit20       ;gate address bit 20 on
	call	gate_a20
	or	al,al		      ;was the command accepted?
	jz	m_10		      ;go if yes
	mov	dx,offset gate_failure	;print error msg
	mov	ah,9			; and terminate
	int	21h
	int	20h

gate_failure	db	"Address line A20 failed to Gate open$"

m_10:	cli			;No interrupts
	pushf			;Simulate INT by pushing Flags,
	push	cs		; CS,
	mov	ax,offset real	; and offset of return address.
	push	ax

	pusha		;Now, set up stack the way BIOS block-
	push	es	; move logic will expect it.
	push	ds	;On return, this is the way
			; the regs will be.

	ASSUME	ds:bios_data_seg
	mov	dx,bios_data_seg   ;Place on Stack current SS:SP
	mov	ds,dx
	mov	io_rom_seg,ss
	mov	io_rom_init,sp

	mov	al,shut_down	   ;Set shutdown byte to
	out	cmos_port,al	   ; shut down code x"09"
	jmp	short	$+2	   ;I/O delay
	mov	al,9
	out	cmos_port+1,al

	mov	ax,virtual_enable  ;machine status word needed
	lmsw	ax		   ; to switch to virtual mode
	jumpfar m_20,cs_code	   ;Must purge prefetch queue

m_20:	ASSUME	ds:cseg 	   ;IN VIRTUAL MODE ...
	mov	ax,ss_desc	   ;stack segment selector
	mov	ss,ax		   ;user's ss+sp
				   ; is not a descriptor

	mov	ax,cs_data
	mov	ds,ax		   ;DS = CSEG as data

	mov	gdt5.base_lo_word,0000h  ;use 8000 for COLOR
	mov	gdt5.base_hi_byte,0Bh
	mov	gdt6.base_lo_word,0000h
	mov	gdt6.base_hi_byte,0Bh

	mov	ax,ds_desc
	mov	ds,ax
	mov	ax,es_desc
	mov	es,ax
	mov	cx,80*25
	xor	si,si
	xor	di,di
m_30:	lodsw
	mov	ah,70h		   ;attribute reverse video
	stosw
	loop	m_30

	mov	al,shut_cmd	   ;shutdown cmd
	out	status_port,al	   ;get back into REAL mode
m_40:	hlt
	jmp	short	m_40

	SUBTTL	Gate A20
	PAGE
;--------------------------------------------------------------:
; GATE_A20						       :
; This routine controls a signal which gates address bit 20.   :
; The gate A20 signal is an output of the 8042 slave processor.:
; Address bit 20 should be gated on before entering protected  :
; mode.  It should be gated off after entering real mode from  :
; protected mode.					       :
; Input:  (AH)=0DDh addr bit 20 gated off (A20 always 0)       :
;	  (AH)=0DFh addr bit 20 gated on (286 controls A20)    :
; Output: (AL)=0 operation successful.	8042 has accepted cmd  :
;	  (AL)=2 Failure -- 8042 unable to accept command.     :
;--------------------------------------------------------------:
gate_a20	PROC
	cli		     ;disable ints while using 8042
	call	empty_8042   ;insure 8042 input buffer empty
	jnz	gate_a20_01  ;ret if 8042 unable to accept cmd
	mov	al,0D1h      ;8042 command to write output port
	out	status_port,al	;output cmd to 8042
	call	empty_8042   ;wait for 8042 to accept command
	jnz	gate_a20_01  ;ret if 8042 unable to accept cmd
	mov	al,ah	     ;8042 port data
	out	port_a,al    ;output port data to 8042
	call	empty_8042   ;wait for 8042 to port data
gate_a20_01:
	ret
gate_a20	ENDP
;--------------------------------------------------------------:
; EMPTY_8042						       :
;	This routine waits for the 8042 buffer to empty        :
;	Input:	None					       :
;	Output: (AL)=0 8042 input buffer empty (ZF=1)	       :
;		(AL)=2 Time out, 8042 buffer full (ZF=0)       :
;--------------------------------------------------------------:
empty_8042	PROC
	push	cx		;save CX
	sub	cx,cx		;CX=0 will be time out value
empty_8042_01:
	in	al,status_port	;read 8042 status port
	and	al,00000010b	;test inp. buffer full flag (D1)
	loopnz	empty_8042_01	;loop until input buffer empty
				; or time out
	pop	cx		;restore CX
	ret
empty_8042	ENDP

	SUBTTL	form_24bit_address
	PAGE
;--------------------------------------------------------------:
; FORM_24BIT_ADDRESS					       :
;	Input:	DX has some segment			       :
;		CX has some offset			       :
;	Output: DX has base_lo_word			       :
;		CL has base_hi_byte			       :
;--------------------------------------------------------------:
form_24bit_address	PROC
	push	ax
	     ;DX == s15 s14 s13 s12 s11 ... s04 s03 s02 s01 s00
	rol	dx,4
	     ;DX == s11 ... s04 s03 s02 s01 s00 s15 s14 s13 s12
	mov	ax,dx
	     ;AX == s11 ... s04 s03 s02 s01 s00 s15 s14 s13 s12
	and	dl,0F0h
	     ;DX == s11 ... s04 s03 s02 s01 s00   0   0   0   0
	and	ax,0Fh
	     ;AX ==   0 ...   0   0   0   0   0 s15 s14 s13 s12
	add	dx,cx	;form_24bit_address
	mov	cx,ax	;get base_hi_byte in CL
	adc	cl,ch	;carry in (CH=0)
	pop	ax
	ret
form_24bit_address	ENDP

	SUBTTL	Real Mode re-entry point.
	PAGE
	ASSUME	ds:cseg 		; IN REAL MODE ...
real:	sti				; turn the int's on
	int	20h			; back to DOS

main		ENDP

cseg_leng	EQU	$
cseg		ENDS
		END	start
