|
|
%define GDT_Paragraph 0x1000
|
|
%define GDT (GDT_Paragraph*16)
|
|
%define GDT_CodeSegIndex 1
|
|
%define GDT_DataSegIndex 2
|
|
%define GDT_CodeSeg16Index 3
|
|
%define GDT_DataSeg16Index 4
|
|
%define GDT_Selector(DescriptorIndex, TableIndicator, RequestorPrivLevel) ((DescriptorIndex << 3) | ((TableIndicator & 1) << 2) | (RequestorPrivLevel & 0x3))
|
|
%define GDT_CodeSegSelector GDT_Selector(GDT_CodeSegIndex, 0, 0)
|
|
%define GDT_DataSegSelector GDT_Selector(GDT_DataSegIndex, 0, 0)
|
|
%define GDT_CodeSeg16Selector GDT_Selector(GDT_CodeSeg16Index, 0, 0)
|
|
%define GDT_DataSeg16Selector GDT_Selector(GDT_DataSeg16Index, 0, 0)
|
|
|
|
%define VIDEO 0xB8000
|
|
%define VIDEO_X_RES 80
|
|
%define VIDEO_Y_RES 25
|
|
%define ScreenCoord(x, y) (2*(VIDEO_X_RES*(y)+(x)))
|
|
|
|
; GDT Stuff
|
|
%define Data 0000b
|
|
%define DataMutable 0010b
|
|
%define DataShrink 0100b
|
|
%define DataShrinkMutable 0110b
|
|
%define Code 1000b
|
|
%define CodeRead 1010b
|
|
%define CodeOwnRing 1100b
|
|
%define CodeOwnRingRead 1110b
|
|
|
|
%macro GDT_NULL_ENTRY 1 ; address
|
|
%assign address %1
|
|
lea di, [address]
|
|
xor ax, ax
|
|
mov cx, 4
|
|
rep stosw
|
|
|
|
%assign GDT_COUNT (GDT_COUNT+1)
|
|
%endmacro
|
|
|
|
%macro GDT_ENTRY 6 ; address base limit type privilege bits32
|
|
%assign address %1
|
|
|
|
%assign base %2
|
|
%assign limit %3
|
|
|
|
%assign type (%4 & 0xf)
|
|
%assign reserved (1<<4)
|
|
%assign privilege ((%5 & 0x3) << 5)
|
|
%assign present (1<<7)
|
|
%assign access (present | privilege | reserved | type)
|
|
|
|
%assign bits32 ((%6 & 1) << 2)
|
|
%assign granularity (1<<3)
|
|
%assign flags (granularity | bits32)
|
|
|
|
%assign limit_0_15 (limit & 0xffff)
|
|
%assign limit_16_19 ((limit >> 16) & 0xf)
|
|
%assign base_0_15 (base & 0xffff)
|
|
%assign base_16_23 ((base >> 16) & 0xff)
|
|
%assign base_24_31 ((base >> 24) & 0xff)
|
|
|
|
mov WORD [es:(address + 0)], limit_0_15
|
|
mov WORD [es:(address + 2)], base_0_15
|
|
mov WORD [es:(address + 4)], base_16_23 | (access << 8)
|
|
mov WORD [es:(address + 6)], limit_16_19 | (flags << 4) | (base_24_31 << 8)
|
|
|
|
%assign GDT_COUNT (GDT_COUNT+1)
|
|
%endmacro
|
|
|
|
%define IOKeyData 0x60
|
|
%define InKeyStatus 0x64
|
|
%define OutKeyCommand 0x64
|
|
|
|
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
Enable_A20:
|
|
; Enables A20 gate using:the BIOS, keyboard controller, or Fast A20
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
pushad ;Preserve registers
|
|
pushfd ;Preserve EFLAGS because we might disable interrupts
|
|
mov ax,0x2402 ;INT 15h AX=2402h: Query A20 status
|
|
int 0x15 ;Check to see if A20 gate is enabled
|
|
jc A20_No_BIOS ;Error? Don't use BIOS to enable gate
|
|
test al,1 ;Test Bit 0 of AL
|
|
je A20_Enabled ;A20 gate already enabled
|
|
mov ax,0x2401 ;INT 15h AX=2401h: Enable A20 gate
|
|
int 0x15 ;Use BIOS to enable A20 gate
|
|
jc A20_No_BIOS ;Error? Don't use BIOS to enable gate
|
|
or ah,ah ;Test AH
|
|
jnz A20_No_BIOS ;Non-zero? A20 gate may not be enabled
|
|
A20_Enabled:
|
|
popfd ;Restore EFLAGS
|
|
popad ;Restore registers
|
|
ret ;Return
|
|
A20_No_BIOS:
|
|
mov ax,0x2403 ;INT 15h AX=2403h: Query A20 support
|
|
int 0x15 ;Call BIOS to find out how the A20 gate can be enabled
|
|
jc A20_KBD ;Error? Assume that keyboard controller is the only option
|
|
test bx,1 ;Bit 0:Keyboard controller supported
|
|
je A20_KBD ;BIOS indicates that keyboard controller is supported
|
|
test bx,2 ;Bit 1:Fast A20 supported
|
|
je FAST_A20 ;BIOS indicated that Fast A20 is supported
|
|
call Check_A20 ;Test A20 gate manually
|
|
jc A20_Enabled ;A20 gate already enabled
|
|
A20_Fail:
|
|
call A20_OUT_Wait ;"Unable to enable A20 gate!"
|
|
cli ;Disable interrupts
|
|
hlt ;Halt machine
|
|
FAST_A20:
|
|
in al,0x92 ;Read System Control Port A
|
|
test al,2 ;Test Fast A20 bit
|
|
jnz A20_Fail ;Bit already set, failed to enable A20 gate
|
|
or al,2 ;Set Bit 1:Enable Fast A20
|
|
and al,0xFE ;Always clear Bit 0 so that machine does not reboot
|
|
out 0x92,al ;Write to port 92h
|
|
call Check_A20 ;Check A20 gate
|
|
jc A20_Enabled ;Success?
|
|
cli ;No? something must have gone wrong
|
|
hlt ;Clear interrupts and halt machine
|
|
A20_KBD:
|
|
cli ;Disable interrupts
|
|
mov cx,50 ;Number of attempts to enable gate
|
|
Use_KBD:
|
|
call A20_OUT_Wait ;Wait for keyboard
|
|
mov al,0xAD ;Disable Keyboard
|
|
out 0x64,al ;Wait for Keyboard
|
|
mov al,0xD0 ;Read Controller Output Port
|
|
out 0x64,al
|
|
call A20_IN_Wait ;Wait for Keyboard
|
|
in al,0x60 ;Read Data Port
|
|
push ax ;Save
|
|
call A20_OUT_Wait ;Wait for Keyboard
|
|
mov al,0xD1 ;Write Controller Output Port
|
|
out 0x64,al
|
|
call A20_OUT_Wait ;Wait for Keyboard
|
|
pop ax ;Get port data back
|
|
or al,10b ;Bit 1: Enable A20 gate
|
|
out 0x60,al ;Write to data port
|
|
call A20_OUT_Wait ;Wait for Keyboard
|
|
mov al,0xAE ;Enable Keyboard
|
|
out 0x64,al
|
|
call A20_OUT_Wait ;Wait for Keyboard
|
|
call Check_A20 ;Verify that gate is enabled
|
|
jc A20_Enabled ;Yes? Return
|
|
loop Use_KBD ;No? Keep trying
|
|
jmp A20_Fail ;Could not enable gate after 50 tries
|
|
A20_OUT_Wait:
|
|
in al,0x64 ;Read status register
|
|
test al,10b ;Is port ready?
|
|
jnz A20_OUT_Wait ;Nope, wait
|
|
ret ;Yep, continue
|
|
A20_IN_Wait:
|
|
in al,0x64 ;Read status register
|
|
test al,1b ;Is port ready?
|
|
jz A20_IN_Wait ;Nope, wait
|
|
ret ;Yep, continue
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
Check_A20:
|
|
; Test if A20 gate is enabled by comaparing the value in FFFF:7E0E
|
|
; with the VBR boot signature at 0000:7DFE
|
|
; Out: Carry flag set if A20 gate is enabled
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
pushad ;Preserve registers
|
|
push es
|
|
mov ax, 0xFFFF
|
|
mov es, ax ;Extra segment:FFFF
|
|
cmp WORD [es:0x7DFE + 16], 0xAAFF ;See if boot signature is wrapped at FFFF:7E0E (Meaning FFFF:7E0E = 0:7DFE)
|
|
stc ; Assume we didn't wrap, yes A20
|
|
jne .End
|
|
clc ; We did wrap, no A20
|
|
.End:
|
|
pop es
|
|
popad
|
|
ret
|
|
|
|
|
|
|
|
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
Go32Bit:
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
call Enable_A20
|
|
|
|
; Set screen mode
|
|
mov ax, 0x0003 ; AH=0 (Change video mode), AL=13h (Mode) = 320x200 - 256 colors
|
|
mov bx, 0x0000
|
|
int 0x10 ; Video BIOS interrupt
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
; Global Descriptor Table ;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
mov ax, GDT_Paragraph
|
|
mov es, ax
|
|
mov cx, 4096
|
|
lea si, [GO32_COPY_BEGIN]
|
|
xor di, di
|
|
rep movsb
|
|
|
|
%assign GDT_COUNT 0
|
|
|
|
GDT_NULL_ENTRY 0
|
|
GDT_ENTRY (GDT_CodeSegIndex*8), 0x00000000, 0xffffffff, CodeRead, 0, 1
|
|
GDT_ENTRY (GDT_DataSegIndex*8), 0x00000000, 0xffffffff, DataMutable, 0, 1
|
|
GDT_ENTRY (GDT_CodeSeg16Index*8), 0x00000000, 0xffffffff, CodeRead, 0, 0
|
|
GDT_ENTRY (GDT_DataSeg16Index*8), 0x00000000, 0xffffffff, DataMutable, 0, 0
|
|
cli
|
|
lgdt [GDT_Record]
|
|
|
|
mov eax, cr0
|
|
or al, 1 ; set PE (Protection Enable) bit in CR0 (Control Register 0)
|
|
mov cr0, eax
|
|
|
|
jmp ClearPrefetchQueue
|
|
nop
|
|
nop
|
|
ClearPrefetchQueue:
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; Manually assembled long jump ;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
db 0x66 ; 32bit override
|
|
db 0xea ; Long jump
|
|
dd ProtectedModeBaby ; Absolute address
|
|
dw GDT_CodeSegSelector ; Descriptor Selector
|
|
|
|
GDT_Record:
|
|
dw (GDT_COUNT*8) - 1 ; Size of GDT in bytes minus 1
|
|
dd 0x1000*16 ; Linear address of GDT
|
|
|
|
|
|
|
|
|
|
align 512
|
|
BITS 32
|
|
GO32_COPY_BEGIN:
|
|
|
|
segment Go32Bit vstart=GDT
|
|
GDT_SPACE: times (GDT_COUNT*8) db 0
|
|
|
|
ProtectedModeBaby:
|
|
|
|
; Update Other Segments
|
|
mov ax, GDT_DataSegSelector
|
|
mov ds, ax
|
|
mov ss, ax
|
|
mov es, ax
|
|
mov fs, ax
|
|
mov gs, ax
|
|
|
|
|
|
; Setup stack
|
|
mov esp, (100 << 20) - 1 ; 100M Addressable
|
|
mov ebp, esp
|
|
|
|
; Space for dynamic variables
|
|
sub esp, 200*4 ; 200 32-bit integers
|
|
|
|
|
|
push ebp
|
|
|
|
DrawStuff:
|
|
xor ecx, ecx
|
|
xor edx, edx
|
|
lea edi, [VIDEO]
|
|
|
|
mov ebp, dword [Zoom]
|
|
sar ebp, 9
|
|
jnz .DrawLoop
|
|
|
|
mov ebp, 0x7fffffff
|
|
|
|
.DrawLoop:
|
|
mov eax, [Px]
|
|
shr eax, 4
|
|
add eax, ecx
|
|
imul eax, eax
|
|
|
|
mov ebx, eax
|
|
|
|
mov eax, [Py]
|
|
shr eax, 4
|
|
add eax, edx
|
|
add eax, edx
|
|
imul eax, eax
|
|
|
|
add ebx, eax
|
|
|
|
mov eax, [ColorOffset]
|
|
push edx
|
|
xor edx, edx
|
|
idiv ebp
|
|
pop edx
|
|
|
|
xchg eax, ebx
|
|
|
|
add ax, bx
|
|
|
|
mov al, ah
|
|
|
|
stosw
|
|
|
|
inc ecx
|
|
cmp ecx, VIDEO_X_RES
|
|
jne .DrawLoop
|
|
|
|
xor ecx, ecx
|
|
inc edx
|
|
cmp edx, VIDEO_Y_RES-2
|
|
jne .DrawLoop
|
|
|
|
pop ebp
|
|
|
|
|
|
cld
|
|
|
|
xor eax, eax
|
|
|
|
in al, IOKeyData ; Get the scan code from the keyboard
|
|
|
|
test al, 0x80
|
|
jnz .ThereWasNothing
|
|
|
|
cmp al, 75; left
|
|
je .KeyLeft
|
|
|
|
cmp al, 72; up
|
|
je .KeyUp
|
|
|
|
cmp al, 80; right
|
|
je .KeyDown
|
|
|
|
cmp al, 77; down
|
|
je .KeyRight
|
|
|
|
cmp al, 51; <
|
|
je .KeyLess
|
|
|
|
cmp al, 52; >
|
|
je .KeyGreater
|
|
|
|
cmp al, 1 ; Escape
|
|
je .KeyEscape
|
|
|
|
|
|
|
|
.ThereWasNothing:
|
|
mov [Key], byte ' '
|
|
jmp .PrintKeyLegend
|
|
; lea esi, [ProtectedWelcomeStr]
|
|
|
|
.KeyEscape:
|
|
call Reboot
|
|
jmp .PrintKeyLegend
|
|
|
|
.KeyLeft:
|
|
mov [Key], byte 'L'
|
|
add [Px], dword -1
|
|
jmp .PrintKeyLegend
|
|
.KeyUp:
|
|
mov [Key], byte 'U'
|
|
add [Py], dword -1
|
|
jmp .PrintKeyLegend
|
|
.KeyRight:
|
|
mov [Key], byte 'R'
|
|
add [Px], dword 1
|
|
jmp .PrintKeyLegend
|
|
.KeyDown:
|
|
mov [Key], byte 'D'
|
|
add [Py], dword 1
|
|
jmp .PrintKeyLegend
|
|
.KeyLess:
|
|
mov [Key], byte '<'
|
|
add [Zoom], dword -1
|
|
jmp .PrintKeyLegend
|
|
.KeyGreater:
|
|
mov [Key], byte '>'
|
|
add [Zoom], dword 1
|
|
|
|
|
|
|
|
.PrintKeyLegend:
|
|
lea esi, [ThereWasSomethingStr]
|
|
|
|
lea edi, [VIDEO + ScreenCoord((80-11), 24)]
|
|
mov ah, 0x2f
|
|
.print_loop:
|
|
mov al, BYTE [esi]
|
|
test al, al
|
|
jz .break_print_loop
|
|
stosw
|
|
inc esi
|
|
jmp .print_loop
|
|
.break_print_loop:
|
|
|
|
lea esi, [VarLabels]
|
|
lea edi, [ConversionBuffer]
|
|
mov ecx, VarLabelsLength
|
|
call CopyData
|
|
|
|
lea esi, [ConversionBuffer]
|
|
lea edi, [VIDEO + ScreenCoord(0, 24)]
|
|
mov ah, 0x0f
|
|
call PrintString
|
|
|
|
|
|
; lea esi, [ConversionBuffer]
|
|
mov eax, dword [Px]
|
|
sar eax, 4
|
|
call IntToString
|
|
|
|
; lea esi, [ConversionBuffer]
|
|
; ecx set by IntToString
|
|
mov edx, 7
|
|
mov al, ' '
|
|
call RightJustifyString
|
|
|
|
; esi still ConversionBuffer
|
|
mov ecx, edx
|
|
lea edi, [VIDEO + ScreenCoord(StrXStart, 24)]
|
|
mov ah, 0x70
|
|
call PrintString
|
|
|
|
|
|
; lea esi, [ConversionBuffer]
|
|
mov eax, dword [Py]
|
|
sar eax, 4
|
|
call IntToString
|
|
|
|
; lea esi, [ConversionBuffer]
|
|
; ecx set by IntToString
|
|
mov edx, 7
|
|
mov al, ' '
|
|
call RightJustifyString
|
|
|
|
; esi still ConversionBuffer
|
|
mov ecx, edx
|
|
lea edi, [VIDEO + ScreenCoord(StrYStart, 24)]
|
|
mov ah, 0x70
|
|
call PrintString
|
|
|
|
|
|
|
|
add [ColorOffset], dword 1
|
|
|
|
jmp DrawStuff
|
|
|
|
|
|
|
|
|
|
|
|
%include "Strings32.nasm"
|
|
|
|
Halt:
|
|
cli
|
|
hlt
|
|
jmp Halt
|
|
|
|
; ------------------------------------------------------------------------------
|
|
|
|
Reboot:
|
|
in al, 0x64
|
|
test al, 0x2 ; Wait for an empty Input Buffer
|
|
jne Reboot
|
|
mov al, 0xFE
|
|
out 0x64, al ; Send the reboot call to the keyboard controller
|
|
jmp Reboot
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; Strings
|
|
;;;;;;;;;;;;;;;;;;;;;;;;
|
|
VarLabels: db "X:"
|
|
StrXStart equ $-VarLabels
|
|
db " "
|
|
db "Y:"
|
|
StrYStart equ $-VarLabels
|
|
db " "
|
|
VarLabelsLength equ $-VarLabels
|
|
ProtectedWelcomeStr: db " Placeholder for SingOS - 32 bit edition! ", 0
|
|
ProtectedWelcomeStrLength equ $-ProtectedWelcomeStr
|
|
TestStr: db "hello, world"
|
|
TestStrLength equ $-TestStr
|
|
ThereWasSomethingStr: db " KEY = '"
|
|
Key: db " ' ", 0
|
|
Px: dd 0
|
|
Py: dd 0
|
|
ColorOffset: dd 0
|
|
Zoom: dd (4<<9)
|
|
ConversionBuffer: times 40 db 0
|
|
align 512
|