📄 mem32.asm
字号:
;---------------------------------------------------------------
; Assembler code to provide 32bit flat memory access
;---------------------------------------------------------------
.MODEL MEDIUM, C
;---------------------------------------------------------------
; I/O Ports
;---------------------------------------------------------------
KBC_CTL equ 060h ; 8042 control port
KBC_STAT equ 064h ; 8042 status port
;---------------------------------------------------------------
; Keyboard Controller
;---------------------------------------------------------------
inpt_buf_full equ 2 ; Input buffer full
enable_a20 equ 0dfh ; enable A20 command
disable_a20 equ 0ddh ; disable A20 command
;---------------------------------------------------------------
; Segment access
;---------------------------------------------------------------
CS_access equ 10011011b ; EXE/READ-only access
DS_access equ 10010011b ; R/W Data segment
;---------------------------------------------------------------
; Macro definitions
;---------------------------------------------------------------
Minit_descriptor macro segment,offset,desc_name
mov dx,&segment ;; get segment name
mov ax,&offset ;; to form 32 bit addr
Call Calc_pm_address ;; calculate 32 bit addr
mov &desc_name.Base_A15_A00,ax ;; Save 32-bit linear
mov &desc_name.Base_A23_A16,dl ;; segment address in
mov &desc_name.Base_A31_A24,dh ;; GDT entry
endm
Mfarjmp macro destination,selector ;dynamic JMP FAR SEG:OFF
db 0eah ;; jmp instruction
dw offset destination ;; offset word
dw selector ;; segment selector word
endm
;---------------------------------------------------------------
; Structure definitions
;---------------------------------------------------------------
Descriptor STRUC
Seg_limit dw ? ; Segment limit
Base_A15_A00 dw ? ; A00..A15 of base addr
Base_A23_A16 db ? ; A16..A23 of base addr
Access_rights db ? ; Segment access rights
GDLimit_A19_A16 db ? ; Granularity, Op-size,
; Limit A16..A19
Base_A31_A24 db ? ; A24..A31 of base addr
Descriptor ENDS
.DATA
;---------------------------------------------------------------
; Global Descriptor Table & pointers
;---------------------------------------------------------------
GDT_386 Descriptor <> ; NULL DESCRIPTOR
CSEG2 Descriptor <0ffffh,,,CS_access> ; CS
DSEG2 Descriptor <0ffffh,,,DS_access> ; DS
SSEG2 Descriptor <0ffffh,,,DS_access> ; SS
ESEG2 Descriptor <0ffffh,0,0,DS_access,0dfh,0> ; ES
Gdt386_len equ $-Gdt_386
GDT_PTR Label Fword
dw Gdt386_len-1 ; length of table
GDT_LW dw 0
GDT_HW dw 0
;---------------------------------------------------------------
; XMS call address
;---------------------------------------------------------------
XMS_CALL dw 0,0
.CODE
;---------------------------------------------------------------
; Stack segment backup location (must be in code space)
;---------------------------------------------------------------
stack_seg dw ?
;---------------------------------------------------------------
; CALC_PM_ADDRESS: Calculate 32-bit protected mode address.
; Used for building descriptor tables.
;---------------------------------------------------------------
; Input: DX:AX = Real mode address
; Output: DX:AX = 32-bit linear address
; Register(s) modified: AX, CX, DX
;---------------------------------------------------------------
Calc_pm_address proc near
;---------------------------------------------------------------
mov cx,ax ; point to control block
mov ax,dx ; load segment into AX
xor dh,dh ; clear upper register
mov dl,ah ; build high byte of 32-bit addr
shr dl,4 ; use only high nibble from (AX)
shl ax,4 ; strip high nibble from segment
add ax,cx ; add GDT offset for low word
adc dx,0 ; adj high byte if CY from low
ret ; back to calling program
calc_pm_address endp
;---------------------------------------------------------------
; Wait_KBC: This routine waits for the 8042 buffer to empty.
;---------------------------------------------------------------
; Input: None
; Output: AL = 0, 8042 input buffer empty: ZF
; AL = 2, Time out; 8042 buffer full: NZ
; Register(s) modified: AX, CX
;---------------------------------------------------------------
Wait_KBC proc near
;---------------------------------------------------------------
xor cx,cx ; CX=0: timeout value
Try_KBC:
in al,KBC_STAT ; read 8042 status port
and al,inpt_buf_full ; input buffer full flag
loopnz Try_KBC ; loop until buffer empty
ret
Wait_KBC endp
;---------------------------------------------------------------
; Test_A20: This routine will test if the A20 address
; line is REALLY active
;---------------------------------------------------------------
Test_A20 proc near
;---------------------------------------------------------------
push ds ; save segment registers
push es
xor ax,ax ; set A20 test segments
mov ds,ax ; DS = 0000H
dec ax
mov es,ax ; ES = FFFFH
mov al,ds:[0] ; get byte from 0:0
mov ah,al ; preserve old byte
not al ; modify byte
xchg al,es:[10h] ; put modified byte to 0ffffh:10h
cmp ah,ds:[0] ; set zero if byte at 0:0 not modified
mov es:[10h],al ; put back old byte at 0ffffh:10h
pop es ; restore segment registers
pop ds
ret
Test_A20 endp
;---------------------------------------------------------------
; Wait_A20: This routine waits for gate A20 address line
; activation
;---------------------------------------------------------------
Wait_A20 proc near
;---------------------------------------------------------------
xor cx,cx ; CX=0: timeout value
Try_A20:
Call Test_A20 ; test A20 state
loopnz Try_A20 ; loop until A20 is enabled
ret
Wait_A20 endp
;---------------------------------------------------------------
; XMS_A20: This routine enables the A20 address line using
; an XMS memory manager (himem.sys) if present
;---------------------------------------------------------------
XMS_A20 proc near
;---------------------------------------------------------------
push es ; preserve ES, INT 2Fh destroys it
mov ax,4300h ; check for XMS
int 2fh
cmp al,80h ; XMS present?
jnz XMS_Err
mov ax,4310h ; get XMS driver address
int 2fh
mov XMS_CALL,bx ; store XMS driver address
mov XMS_CALL+2,es
mov ah,3 ; enable A20
call dword ptr XMS_CALL
XMS_Err:
pop es ; restore ES (buffer segment)
ret
XMS_A20 endp
;---------------------------------------------------------------
; KBC_A20: This routine controls a signal which gates address
; line 20 (A20). The gate A20 signal is an output of
; of the 8042 slave processor (keyboard controller).
; A20 should be gated on before entering protected
; mode, to allow addressing of the entire 4G address
; space of the 80386 & 80486.
;---------------------------------------------------------------
; Register(s) modified: AX, CX
;---------------------------------------------------------------
KBC_A20 proc near
;---------------------------------------------------------------
Call Wait_KBC ; insure 8042 input buffer empty
mov al,0D1h ; 8042 cmd to write output port
out KBC_STAT,al ; output cmd to 8042
Call Wait_KBC ; wait for 8042 to accept cmd
mov al,enable_a20 ; gate address bit 20 on
out KBC_CTL,al ; output port data to 8042
Call Wait_KBC ; wait for 8042 to port data
ret
KBC_A20 endp
.386P
;---------------------------------------------------------------
; WriteL: This routine writes a 32 bit value at a 32 bit
; physical address.
;---------------------------------------------------------------
PUBLIC WriteL
WriteL proc far
;---------------------------------------------------------------
push bp ; set up stack and
mov bp,sp ; local pointer
push ds
push es
;---------------------------------------------------------------
; 1) Disable interrupts
; 2) Set up descriptor table entries w/ run time addresses
; 3) Initialize pointer to GDT
; 4) Enable A20 on the address bus
;---------------------------------------------------------------
; (1)
pushf ; save flags
cli ; disable interrupts
; (2)
Minit_descriptor cs,0,CSEG2
Minit_descriptor ss,0,SSEG2
Minit_descriptor ds,0,DSEG2
; (3)
mov cs:stack_seg,SS ; save SS
mov dx,ds ; GDT linear address
mov ax,offset GDT_386
Call Calc_PM_Address
mov GDT_LW,ax
mov GDT_HW,dx
Lgdt GDT_PTR ; load GDT register
; (4)
Call Test_A20 ; A20 already active ?
jz @OK_A20W
Call XMS_A20 ; set A20 line via XMS driver
Call Test_A20
jz @OK_A20W
Call KBC_A20 ; set A20 line via KBC
Call Wait_A20 ; wait for activation
jnz @RM_386W
@OK_A20W:
;---------------------------------------------------------------
; 1) Enable protected mode
; 2) FAR JUMP to protected mode routine
;---------------------------------------------------------------
; (1)
mov eax,cr0 ; get CPU control register
or al,1 ; enable protected mode bit
mov cr0,eax ; now in protected mode
; (2)
Mfarjmp <@PM_386W>,<CSEG2-GDT_386>
;---------------------------------------------------------------
; Now, fully in protected mode: Initialize protected mode
; segment registers.
;---------------------------------------------------------------
@PM_386W:
mov ax,ESEG2-GDT_386 ; Pointer to extra segment
mov es,ax
mov ax,DSEG2-GDT_386 ; Pointer to current data seg
mov ds,ax
mov ax,SSEG2-GDT_386 ; Pointer to stack segment
mov ss,ax
;---------------------------------------------------------------
; Write data in protected mode
;---------------------------------------------------------------
mov eax,[bp+10]
mov ebx,[bp+6]
mov es:[ebx],eax
;---------------------------------------------------------------
; Now we are done and can begin the process of exiting
; protected mode.
;---------------------------------------------------------------
mov eax,cr0 ; get CPU control register
and al,0feh ; clear protected mode bit
mov cr0,eax ; now, we are out of prot mode
Mfarjmp <@RM_386W>,<@CODE>
;---------------------------------------------------------------
; Restore segment registers w/ real mode compatible values
;---------------------------------------------------------------
@RM_386W:
mov ss,cs:stack_seg ; restore SS
popf ; restore interrupt flag
pop es ; restore ES,DS,BP
pop ds
pop bp
retf
WriteL endp
;---------------------------------------------------------------
; ReadL: This routine reads a 32 bit value from a 32 bit
; physical address.
;---------------------------------------------------------------
PUBLIC ReadL
ReadL proc far
;---------------------------------------------------------------
push bp ; set up stack and
mov bp,sp ; local pointer
push ds
push es
;---------------------------------------------------------------
; 1) Disable interrupts
; 2) Set up descriptor table entries w/ run time addresses
; 3) Initialize pointer to GDT
; 4) Enable A20 on the address bus
;---------------------------------------------------------------
; (1)
pushf ; save flags
cli ; disable interrupts
; (2)
Minit_descriptor cs,0,CSEG2
Minit_descriptor ss,0,SSEG2
Minit_descriptor ds,0,DSEG2
; (3)
mov cs:stack_seg,SS ; save SS
mov dx,ds ; GDT linear address
mov ax,offset GDT_386
Call Calc_PM_Address
mov GDT_LW,ax
mov GDT_HW,dx
Lgdt GDT_PTR ; load GDT register
; (4)
Call Test_A20 ; A20 already active ?
jz @OK_A20R
Call XMS_A20 ; set A20 line via XMS driver
Call Test_A20
jz @OK_A20R
Call KBC_A20 ; set A20 line via KBC
Call Wait_A20 ; wait for activation
jnz @RM_386R
@OK_A20R:
;---------------------------------------------------------------
; 1) Enable protected mode
; 2) FAR JUMP to protected mode routine
;---------------------------------------------------------------
; (1)
mov eax,cr0 ; get CPU control register
or al,1 ; enable protected mode bit
mov cr0,eax ; now in protected mode
; (2)
Mfarjmp <@PM_386R>,<CSEG2-GDT_386>
;---------------------------------------------------------------
; Now, fully in protected mode: Initialize protected mode
; segment registers.
;---------------------------------------------------------------
@PM_386R:
mov ax,ESEG2-GDT_386 ; Pointer to extra segment
mov es,ax
mov ax,DSEG2-GDT_386 ; Pointer to current data seg
mov ds,ax
mov ax,SSEG2-GDT_386 ; Pointer to stack segment
mov ss,ax
;---------------------------------------------------------------
; Read data in protected mode
;---------------------------------------------------------------
mov ebx,[bp+6]
mov eax,es:[ebx]
mov edx,eax
and eax,0ffffh ; AX = low word
shr edx,16 ; DX = high word
;---------------------------------------------------------------
; Now we are done and can begin the process of exiting
; protected mode.
;---------------------------------------------------------------
mov ebx,cr0 ; get CPU control register
and bl,0feh ; clear protected mode bit
mov cr0,ebx ; now, we are out of prot mode
Mfarjmp <@RM_386R>,<@CODE>
;---------------------------------------------------------------
; Restore segment registers w/ real mode compatible values
;---------------------------------------------------------------
@RM_386R:
mov ss,cs:stack_seg ; Restore SS,ES,DS
popf ; restore interrupt flag
pop es ; restore ES,DS,BP
pop ds
pop bp
retf
ReadL endp
END
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -