193 lines
4.2 KiB
NASM
193 lines
4.2 KiB
NASM
;
|
|
; This file is designed to run as a ROM replacement, but it has a .COM extension because it's
|
|
; also designed to run as a COM file under DOS (hence the "org 0x100").
|
|
;
|
|
cpu 386
|
|
org 0x100
|
|
section .text
|
|
|
|
bits 16
|
|
|
|
DPL0 equ 0
|
|
DPL1 equ 1
|
|
DPL2 equ 2
|
|
DPL3 equ 3
|
|
|
|
ACC_TYPE_SEG equ 0x1000
|
|
ACC_PRESENT equ 0x8000
|
|
ACC_TYPE_CODE equ 0x0800
|
|
ACC_TYPE_READABLE equ 0x0200
|
|
ACC_TYPE_WRITABLE equ 0x0200
|
|
ACC_TYPE_CODE_READABLE equ (0x1a00 | ACC_PRESENT)
|
|
ACC_TYPE_DATA_WRITABLE equ (0x1200 | ACC_PRESENT)
|
|
|
|
EXT_NONE equ 0x0000
|
|
EXT_BIG equ 0x0040
|
|
|
|
;
|
|
; We build some data structures in the first page (0x0000-0x0fff) of RAM:
|
|
;
|
|
; 0x0000-0x03ff Real-mode IDT (256*4)
|
|
; 0x0400-0x0bff Prot-mode IDT (256*8)
|
|
; 0x0c00-0x0cff GDT (enough room for 32 selectors)
|
|
; 0x0d00-0x0d07 IDTR
|
|
; 0x0d08-0x0d0f GDTR
|
|
; 0x0d10-0x0fff reserved
|
|
;
|
|
; And in the second page (0x1000-0x1fff), we build a page directory, followed by a single page table that
|
|
; will allow us to map up to 4Mb (although we'll only create entries for the first 1Mb).
|
|
;
|
|
RAM_GDT equ 0x0c00
|
|
RAM_IDTR equ 0x0d00
|
|
RAM_GDTR equ 0x0d08
|
|
|
|
CSEG_REAL equ 0xf000
|
|
CSEG_PROT equ 0x0008
|
|
DSEG_PROT equ 0x0010
|
|
|
|
CR0_MSW_PE equ 0x0001
|
|
|
|
;
|
|
; set initializes a register to the specified value (eg, "set eax,0")
|
|
;
|
|
%macro set 2
|
|
%ifnum %2
|
|
%if %2 = 0
|
|
xor %1,%1
|
|
%else
|
|
mov %1,%2
|
|
%endif
|
|
%else
|
|
mov %1,%2
|
|
%endif
|
|
%endmacro
|
|
|
|
;
|
|
; defDesc defines a descriptor, given a base (%1), limit (%2), type (%3), dpl (%4), and ext (%5)
|
|
;
|
|
%macro defDesc 1-5 0,0,0,0,0
|
|
dw (%2 & 0x0000ffff)
|
|
dw (%1 & 0x0000ffff)
|
|
dw ((%1 & 0x00ff0000) >> 16) | %3 | (%4 << 13)
|
|
dw ((%2 & 0x000f0000) >> 16) | %5 | ((%1 & 0xff000000) >> 16)
|
|
%endmacro
|
|
|
|
;
|
|
; setDesc creates a descriptor, given a base (%1), limit (%2), type (%3), ext (%4), and selector (%5)
|
|
;
|
|
%assign selDesc 0
|
|
%macro setDesc 1-5 0,0,0,0,none
|
|
set ebx,%1
|
|
set ecx,%2
|
|
set dx,%3
|
|
set ax,%4
|
|
call storeDesc
|
|
%assign %5 selDesc
|
|
%assign selDesc selDesc+8
|
|
%endmacro
|
|
|
|
start: cli ; disable all interrupts
|
|
mov al,0xff ; and ensure that no hardware interrupts can sneak in
|
|
out 0x21,al ; if interrupts become enabled later
|
|
out 0xa1,al
|
|
|
|
sub ax,ax
|
|
mov ds,ax
|
|
mov es,ax
|
|
mov ss,ax
|
|
mov sp,0x1000
|
|
|
|
mov eax,0x44332211
|
|
mov ebx,eax
|
|
mov ecx,0x88776655
|
|
mul ecx
|
|
div ecx
|
|
cmp eax,ebx
|
|
je near initRAM ; apparently we have to tell NASM "near" because this is a forward reference
|
|
times 32768 nop ; lots of NOPs to force a 16-bit conditional jump
|
|
|
|
;
|
|
; storeDesc(EBX=base, ECX=limit, DX=type, AX=ext, DI=address of descriptor)
|
|
;
|
|
storeDesc:
|
|
cld
|
|
push ax
|
|
mov ax,cx
|
|
stosw ; store the low 16 bits of limit from ECX
|
|
mov ax,bx
|
|
stosw ; store the low 16 bits of base from EBX
|
|
mov ax,dx
|
|
shr ebx,16
|
|
mov al,bl
|
|
stosw
|
|
pop ax
|
|
shr ecx,16
|
|
and cl,0xf
|
|
or al,cl
|
|
mov ah,bh
|
|
stosw
|
|
ret
|
|
|
|
;
|
|
; The following ROM-based data structures are obsolete, because we build these data structures in RAM now.
|
|
;
|
|
addrGDT:dw romGDTEnd - romGDT - 1 ; 16-bit limit of romGDT
|
|
dw romGDT, 0xffff ; 32-bit base address of romGDT (works as long as we're aliased at 0xffff0000)
|
|
|
|
romGDT: defDesc 0 ; the first descriptor in any descriptor table is always a dud (it corresponds to the null descriptor)
|
|
defDesc 0x000f0000,0x0000ffff,ACC_TYPE_CODE_READABLE
|
|
defDesc 0x00000000,0x000fffff,ACC_TYPE_DATA_WRITABLE
|
|
romGDTEnd:
|
|
;
|
|
; End of ROM-based data structures
|
|
;
|
|
|
|
initRAM:
|
|
set edi,RAM_GDT
|
|
mov [RAM_GDTR+2],edi
|
|
setDesc 0,0,0,0,NULL
|
|
mov eax,cs
|
|
shl eax,4
|
|
setDesc eax,0x0000ffff,ACC_TYPE_CODE_READABLE,EXT_BIG,CSEG_PROT
|
|
setDesc 0x0,0x000fffff,ACC_TYPE_DATA_WRITABLE,EXT_BIG,DSEG_PROT
|
|
sub edi,RAM_GDT
|
|
dec edi
|
|
mov [RAM_GDTR],di
|
|
|
|
goProt: o32 lgdt [RAM_GDTR]
|
|
mov eax,cr0
|
|
or eax,CR0_MSW_PE
|
|
mov cr0,eax
|
|
jmp CSEG_PROT:inProt
|
|
inProt:
|
|
bits 32
|
|
mov ax,DSEG_PROT
|
|
mov ds,ax
|
|
mov es,ax
|
|
mov ss,ax
|
|
|
|
;
|
|
; Do some protected-mode tests now...
|
|
;
|
|
|
|
goReal: mov eax,cr0
|
|
and eax,~CR0_MSW_PE
|
|
mov cr0,eax
|
|
bits 16
|
|
jmp CSEG_REAL:inReal
|
|
inReal:
|
|
or ax,1
|
|
jnz start ; apparently we do NOT have to say "near" here since this is a backward reference
|
|
|
|
;
|
|
; Fill the remaining space with NOPs until we get to target offset 0xFFF0.
|
|
; Note that we subtract 0x100 from the target offset because we're ORG'ed at 0x100.
|
|
;
|
|
times 0xfff0-0x100-($-$$) nop
|
|
|
|
jmp CSEG_REAL:start
|
|
|
|
db 0x20
|
|
db '04/04/15'
|
|
db 0xFC ; 0000FFFE FC (Model ID byte)
|
|
db 0x00 ; 0000FFFF 00 (location of checksum byte)
|