A20 Gate
Re:A20 Gate
I don't know anything about that BIOS routine, but I think you have to keep in mind what the A20 gate actually is. It's purpose is to wrap addresses beyond 20 bits back down to the bottom of memory. A lot of software written before the 286's protected mode assumed this is how addressing worked and would break on the "new" 286.
Setting A20 has nothing to do with protected mode. You can enable it in real mode. It makes no difference.
However, you should also be aware that the "limit" for offsets in real mode is still 0xFFFF. This is the trick of "unreal" mode. By switching to pmode momemtarily, and loading a descriptor with a larger limit, you fool the processor into accepting a higher offset limit in real mode.
Setting A20 has nothing to do with protected mode. You can enable it in real mode. It makes no difference.
However, you should also be aware that the "limit" for offsets in real mode is still 0xFFFF. This is the trick of "unreal" mode. By switching to pmode momemtarily, and loading a descriptor with a larger limit, you fool the processor into accepting a higher offset limit in real mode.
- Pype.Clicker
- Member
- Posts: 5964
- Joined: Wed Oct 18, 2006 2:31 am
- Location: In a galaxy, far, far away
- Contact:
Re:A20 Gate
there is even a XMS command to turn on A20 gate ... if xms is usable for you
Re:A20 Gate
...but is it possible to access to all or part of my memory in real mode??!
My problem is that i would load data in extended memory, for example load all data from a floppy into mem... but i'm in real mode... it's possible? and if so, could anyone send me the code or an example to do this???
Thanx!
My problem is that i would load data in extended memory, for example load all data from a floppy into mem... but i'm in real mode... it's possible? and if so, could anyone send me the code or an example to do this???
Thanx!
Re:A20 Gate
You have to switch to protected mode, load a descriptor with a larger limit, then switch back to real mode. You can see an example in 'boot.asm' on my web page.
Otherwise, the largest offset you can have is 0xFFFF.
Otherwise, the largest offset you can have is 0xFFFF.
- Pype.Clicker
- Member
- Posts: 5964
- Joined: Wed Oct 18, 2006 2:31 am
- Location: In a galaxy, far, far away
- Contact:
Re:A20 Gate
There is also a service in bios (int 15h, function 87h) that performs the following:
- switch into pmode
- performs a memcopy between 2 places you've defined
- comes back to realmode.
You can transfer a grand maximum of 64K at once, but it will surely simplify your boot loader if you _really_ need to load stuffs >1Mb as you won't have to handle all those "to the DPL0 and back" switches.
It worked on my Pentium 120 when i started with pmode (about 6 yr ago), but i dunno whether it's still implemented nowadays or not ...
- switch into pmode
- performs a memcopy between 2 places you've defined
- comes back to realmode.
You can transfer a grand maximum of 64K at once, but it will surely simplify your boot loader if you _really_ need to load stuffs >1Mb as you won't have to handle all those "to the DPL0 and back" switches.
It worked on my Pentium 120 when i started with pmode (about 6 yr ago), but i dunno whether it's still implemented nowadays or not ...
Re:A20 Gate
; Thanx to crazybuddha & Pype.Clicker.. I try to write
; this code for my kernel, but it doesn't work... where is
; the problem?? Could you help me again???
; (...to compile I use Nasmw and to boot I use a boot-loader that simply
; load kernel at 1000h:0000h)
SEGMENT START Para Public Use32
xor ax, ax
mov ss, ax ; Stack Pointer
mov sp, 7c00h
mov ax, cs ; Update segment register
mov ds, ax
mov es, ax
call enable_a20
call PMode
mov dword ebx, 0xA00000 ; 32 bit offset (= 10MB)
mov al, 36
mov byte [ebx], 36 ; I write 36 at 10MB in ext. memory (for example...)
xor ax, ax
mov byte al, [ebx]
cmp al, 36
je ok
error:
mov ah, 0Eh
mov al, 35
int 10h
jmp error ; if it doesn't work => print '#' forever...
ok:
mov ah, 0Eh
mov al, 36
int 10h
jmp ok ; if it work => print '$' forever...
enable_a20:
mov al, 0D1h
out 64h, al
mov al, 0DFh
out 60h, al
ret
; switch in PM & back to real mode
PMode:
xor eax, eax
mov ax, cs
shl eax, 4
lea edx, [GDT]
add eax, edx
mov DWord [Cs:GDTR+2], eax
push ds
cli
lgdt [GDTR]
mov eax, 1
mov cr0, eax ; in PM
xor eax, eax ; back to real Mode
mov cr0, eax
sti
pop ds
ret
GDT dd 0, 10000000h ; ??? 256MB ???
GDTR dw 4
; PS Sorry i'm a newbie about os development...
; this code for my kernel, but it doesn't work... where is
; the problem?? Could you help me again???
; (...to compile I use Nasmw and to boot I use a boot-loader that simply
; load kernel at 1000h:0000h)
SEGMENT START Para Public Use32
xor ax, ax
mov ss, ax ; Stack Pointer
mov sp, 7c00h
mov ax, cs ; Update segment register
mov ds, ax
mov es, ax
call enable_a20
call PMode
mov dword ebx, 0xA00000 ; 32 bit offset (= 10MB)
mov al, 36
mov byte [ebx], 36 ; I write 36 at 10MB in ext. memory (for example...)
xor ax, ax
mov byte al, [ebx]
cmp al, 36
je ok
error:
mov ah, 0Eh
mov al, 35
int 10h
jmp error ; if it doesn't work => print '#' forever...
ok:
mov ah, 0Eh
mov al, 36
int 10h
jmp ok ; if it work => print '$' forever...
enable_a20:
mov al, 0D1h
out 64h, al
mov al, 0DFh
out 60h, al
ret
; switch in PM & back to real mode
PMode:
xor eax, eax
mov ax, cs
shl eax, 4
lea edx, [GDT]
add eax, edx
mov DWord [Cs:GDTR+2], eax
push ds
cli
lgdt [GDTR]
mov eax, 1
mov cr0, eax ; in PM
xor eax, eax ; back to real Mode
mov cr0, eax
sti
pop ds
ret
GDT dd 0, 10000000h ; ??? 256MB ???
GDTR dw 4
; PS Sorry i'm a newbie about os development...
Re:A20 Gate
I have a number of observations.
You say you have a boot loader to put the 32-bit code at 1000:0000h. You have to realize that if you are in real mode, a physical memory location is arrived at by shifting the segment left by 4, then adding the offset. As you can see, if you shift 1000h left, you have a problem.
You don't need "SEGMENT START Para Public". If you are using NASMW, and use want to generate 32-bit byte object code, just say [BITS 32].
However, this shouldn't be here either. It should be [BITS 16] or left blank, since nasm will default it to 16-bit if producing a binary file. You are in real mode, and although you can use the 32-bit registers, they don't help for memory addressing, and will generate the wrong object code, causing the processor to "mis-read" the instructions.
The GDT is completely wrong. There is no short cut to this. Go through my previous "baby steps - descriptor" to figure out what you want it to be. You can also find it on my webpage, along with an example. Step through it.
Basically, you need to back up a bit and do one thing at a time. Ask yourself, "how will I test this step?".
You say you have a boot loader to put the 32-bit code at 1000:0000h. You have to realize that if you are in real mode, a physical memory location is arrived at by shifting the segment left by 4, then adding the offset. As you can see, if you shift 1000h left, you have a problem.
You don't need "SEGMENT START Para Public". If you are using NASMW, and use want to generate 32-bit byte object code, just say [BITS 32].
However, this shouldn't be here either. It should be [BITS 16] or left blank, since nasm will default it to 16-bit if producing a binary file. You are in real mode, and although you can use the 32-bit registers, they don't help for memory addressing, and will generate the wrong object code, causing the processor to "mis-read" the instructions.
The GDT is completely wrong. There is no short cut to this. Go through my previous "baby steps - descriptor" to figure out what you want it to be. You can also find it on my webpage, along with an example. Step through it.
Basically, you need to back up a bit and do one thing at a time. Ask yourself, "how will I test this step?".
- Pype.Clicker
- Member
- Posts: 5964
- Joined: Wed Oct 18, 2006 2:31 am
- Location: In a galaxy, far, far away
- Contact:
Re:A20 Gate
According to what i see, you assume your code is loading in 16 bits mode, and not in 32 bits (call PMode and xor ax,ax; mov ss,ax seems to be evidences of this)
Does BIOS start the boot sector with 0000:7c00 or with 07c0:0000 ? i can't remember, and it's going to have lot of importance for your mov ds,cs stuff, as far as i can guess ...
mov eax,1; mov cr0,eax seems dangerous to me as you have plenty other bits in CR0 that you might not want to change such as x87 presence, etc (might be hardwired, but still ...)
problem with your GDT, as CrazyBuddah said. remember descriptor 0 is ALWAYS reserved in IA-32 in pmode, so having xor eax,eax; mov cs,ax before entering PMODE is certainly no good idea
Does BIOS start the boot sector with 0000:7c00 or with 07c0:0000 ? i can't remember, and it's going to have lot of importance for your mov ds,cs stuff, as far as i can guess ...
mov eax,1; mov cr0,eax seems dangerous to me as you have plenty other bits in CR0 that you might not want to change such as x87 presence, etc (might be hardwired, but still ...)
problem with your GDT, as CrazyBuddah said. remember descriptor 0 is ALWAYS reserved in IA-32 in pmode, so having xor eax,eax; mov cs,ax before entering PMODE is certainly no good idea
Re:A20 Gate
Good question; unfortunately, it's not one with a straightforward answer. The standard which most PC BIOS's follow is to use a zero-base segment (0000:7C00), but not all follow that. Some, especially older ones, jump around quite a bit without resetting it, so you can't asume the location of the code segment entry point at boot time. This is the reason DOS switched from a NEAR (16-bit offset address) JMP at the start of the code to a SHORT (8-bit relative) JMP followed by a NOP - they couldn't assume that the usual addressing would work in all cases. It is best to set it explicitly yourself, as the first thing the code does.Pype.Clicker wrote: Does BIOS start the boot sector with 0000:7c00 or with 07c0:0000 ? i can't remember, and it's going to have lot of importance for your mov ds,cs stuff, as far as i can guess ...
Generally, 0000:7C00 is a right, but you should be aware that it might not work on all machines.
That reminds me, I've got change my own code for that...
Re:A20 Gate
So the code can sometimes be att xxxx:0000?? Unpleasant if "Org 7C00h" is in use...Schol-R-LEA wrote:Good question; unfortunately, it's not one with a straightforward answer. The standard which most PC BIOS's follow is to use a zero-base segment (0000:7C00), but not all follow that. Some, especially older ones, jump around quite a bit without resetting it, so you can't asume the location of the code segment entry point at boot time. This is the reason DOS switched from a NEAR (16-bit offset address) JMP at the start of the code to a SHORT (8-bit relative) JMP followed by a NOP - they couldn't assume that the usual addressing would work in all cases. It is best to set it explicitly yourself, as the first thing the code does.Pype.Clicker wrote: Does BIOS start the boot sector with 0000:7c00 or with 07c0:0000 ? i can't remember, and it's going to have lot of importance for your mov ds,cs stuff, as far as i can guess ...
Generally, 0000:7C00 is a right, but you should be aware that it might not work on all machines.
That reminds me, I've got change my own code for that...
- Pype.Clicker
- Member
- Posts: 5964
- Joined: Wed Oct 18, 2006 2:31 am
- Location: In a galaxy, far, far away
- Contact:
Re:A20 Gate
Well, that's what i fear. Therefore, i think an "absolute jump" would be welcome in a reliable boot sector:
org 7c00
jmp short start (cs=07c0 or 0000 ??)
; boot sector datas would come here (BPB, etc.)
start:
jmp 0000:continue
continue: (now we're sure cs=0000
I think you will find something like this in a lot of boot sectors, including "trampolines" that just load another sector at 0000:7c00 and delegate the boot (multi-boot, etc). Those also have a copy routine, or simply does weird things as push 0000; push continue; retf
org 7c00
jmp short start (cs=07c0 or 0000 ??)
; boot sector datas would come here (BPB, etc.)
start:
jmp 0000:continue
continue: (now we're sure cs=0000
I think you will find something like this in a lot of boot sectors, including "trampolines" that just load another sector at 0000:7c00 and delegate the boot (multi-boot, etc). Those also have a copy routine, or simply does weird things as push 0000; push continue; retf
Re:A20 Gate
Well, now i try to be more clear as possible...
first of all, thanks to pype.clicker & crazybuddha for their helps...
but i'm not expert in os development and i continue to have problems to access
in memory above 1MB.
So i think to include my boot.asm, kernel.asm & main.c programs, that constitute
a simplified version of my os.. (maybe it requires many space... sorry!)
I have gone in crazybuddha web site and in other site to find specifications about
GDT descriptor & unreal mode... now i'm able to allow use of 32bit offset (i hope),
and thanx to pype.clicker i'm able to enable a20 line... (i re-hope)
I also read baby-steps (very good!) and rookie step #1... i think are very useful..
i suggest you to continue...
So, the problem is that when main.c is executed it should have to fill the screen with
"OK!" messages... but it isn't so :(
I compile asm progs with NASMW, and main.c whit TCC, generating boot.bin, kernel.bin &
main.bin; then i link kernel.bin and main.bin with JLOC.
I think the problem came when jmp _main is invoked... but i'm not very expert in segment
and i don't know well...
Thanx to all that want to help me... (continue...)
first of all, thanks to pype.clicker & crazybuddha for their helps...
but i'm not expert in os development and i continue to have problems to access
in memory above 1MB.
So i think to include my boot.asm, kernel.asm & main.c programs, that constitute
a simplified version of my os.. (maybe it requires many space... sorry!)
I have gone in crazybuddha web site and in other site to find specifications about
GDT descriptor & unreal mode... now i'm able to allow use of 32bit offset (i hope),
and thanx to pype.clicker i'm able to enable a20 line... (i re-hope)
I also read baby-steps (very good!) and rookie step #1... i think are very useful..
i suggest you to continue...
So, the problem is that when main.c is executed it should have to fill the screen with
"OK!" messages... but it isn't so :(
I compile asm progs with NASMW, and main.c whit TCC, generating boot.bin, kernel.bin &
main.bin; then i link kernel.bin and main.bin with JLOC.
I think the problem came when jmp _main is invoked... but i'm not very expert in segment
and i don't know well...
Thanx to all that want to help me... (continue...)
Re:A20 Gate
;Here is my whole code:
; ====================== BOOT.ASM ===================
[org 0x7C00]
start:
xor ax, ax ; update segment register
mov ds, ax
mov ss, ax
mov sp, 0x9C00 ; stack pointer at 0000h:09c00
call clrscr
mov si, msg
call print
mov ah, 03h
mov al, 05h
mov bh, 00h ; Delay
mov bl, 00h ; Char/sec
int 16h ; Max speed of keyboard
cli
;Enable a20 gate (real mode) ... thanx to pype.clycker!
push ax
push cx
call A20@wt ;wait 8042 to be ready
mov al, 0D1h ;message: writing on port 0x60
out 64h, al ;sent to COMMAND PORT
call A20@wt
mov al, 0DFh ;enable 32 bits addresses
out 60h, al
call A20@wt
pop cx
pop ax
jmp load_kernel
A20@wt:
xor cx, cx ;WAIT 8042 STATE
A20@00:
in al, 64h
test al, 2
loopne A20@00
ret
load_kernel:
sti
mov ah, 0 ; Reset the floppy drive
mov dl, 0
int 13h
jc load_kernel
mov ax, 1000h ; ES:BX = 1000:0000 <-- Here is the kernel
mov es, ax ;
xor bx, bx ;
read_sec:
mov ah, 2 ; Read
mov al, 18 ; # of sectors
mov ch, 0 ; Track
mov cl, 2 ; sector
mov dh, 0 ; Head
mov dl, 0 ; Drive
int 13h ; Read !
jc read_sec
jmp 1000h:0000h ; <-- Put the kernel here!
echo:
mov ah, 0Eh ; Print a char
mov bx, 0007h
int 10h
ret
clrscr:
mov ax, 0003h
int 10h
ret
print:
lodsb
call echo
cmp al, 0
jnz print
ret
; VARS
msg db 'I love turkey!', 0
;Riempe il file fino a 512byte
times 510-($-$$) db 0
dw 0AA55h
(continue...)
; ====================== BOOT.ASM ===================
[org 0x7C00]
start:
xor ax, ax ; update segment register
mov ds, ax
mov ss, ax
mov sp, 0x9C00 ; stack pointer at 0000h:09c00
call clrscr
mov si, msg
call print
mov ah, 03h
mov al, 05h
mov bh, 00h ; Delay
mov bl, 00h ; Char/sec
int 16h ; Max speed of keyboard
cli
;Enable a20 gate (real mode) ... thanx to pype.clycker!
push ax
push cx
call A20@wt ;wait 8042 to be ready
mov al, 0D1h ;message: writing on port 0x60
out 64h, al ;sent to COMMAND PORT
call A20@wt
mov al, 0DFh ;enable 32 bits addresses
out 60h, al
call A20@wt
pop cx
pop ax
jmp load_kernel
A20@wt:
xor cx, cx ;WAIT 8042 STATE
A20@00:
in al, 64h
test al, 2
loopne A20@00
ret
load_kernel:
sti
mov ah, 0 ; Reset the floppy drive
mov dl, 0
int 13h
jc load_kernel
mov ax, 1000h ; ES:BX = 1000:0000 <-- Here is the kernel
mov es, ax ;
xor bx, bx ;
read_sec:
mov ah, 2 ; Read
mov al, 18 ; # of sectors
mov ch, 0 ; Track
mov cl, 2 ; sector
mov dh, 0 ; Head
mov dl, 0 ; Drive
int 13h ; Read !
jc read_sec
jmp 1000h:0000h ; <-- Put the kernel here!
echo:
mov ah, 0Eh ; Print a char
mov bx, 0007h
int 10h
ret
clrscr:
mov ax, 0003h
int 10h
ret
print:
lodsb
call echo
cmp al, 0
jnz print
ret
; VARS
msg db 'I love turkey!', 0
;Riempe il file fino a 512byte
times 510-($-$$) db 0
dw 0AA55h
(continue...)
Re:A20 Gate
; And now my kernel...
; ==================== KERNEL.ASM =====================
[BITS 16]
SEGMENT START
GLOBAL _printf
GLOBAL _read_mem_32
GLOBAL _write_mem_32
EXTERN _main
; Update segment register
xor???ax, ax
mov???ss, ax
mov ax, cs
mov ds, ax
mov es, ax
;Switch in unreal mode
???cli
???push ds
???push es
xor eax, eax ; point gdt_ptr to gdt
??????mov ax, ds
??????shl eax, 4
add eax, gdt ; EAX=linear address of gdt
mov [gdt_ptr + 2], eax
lgdt [gdt_ptr]
??????mov eax, cr0
??????or al, 1
mov cr0, eax ; partial switch to 32-bit pmode
mov bx, DATA_SEL ; selector to segment w/ 4G limit
??????mov ds, bx
mov es, bx ??? ; set seg limits in descriptor caches
??????dec al
mov cr0, eax??? ; back to (un)real mode
pop es ; segment regs back to old values,
pop ds ; but now 32-bit addresses are OK
???sti
jmp ???_main ??????; Jump to C main program
; --------- INTERNAL FUNCTIONS ----------
echo:????????????; Stampa il carattere in al
mov ah, 0Eh
???mov???bx, 0007h
int???10h
???ret???
print:????????????; Stampa una stringa
???lodsb
cmp al, 0
jz pret
???call???echo
???jmp???print
pret:???ret
; ----------------------------------------
_printf:
push bp ; C printf...
mov bp, sp ;
???mov si, [bp+4]???
???call ???print
mov sp, bp
pop bp
ret
_write_mem_32:?????????; void write_mem_32(unsigned long i, char c)
????????????;
push bp ;
mov bp, sp ;
???mov dword ebx, [bp+4]???; OFFSET (32 BIT)
mov al, [bp+6]
mov [es:ebx], al
mov sp, bp
pop bp
ret
_read_mem_32: ; char read_mem_32(unsigned long i)
????????????;
push bp ;
mov bp, sp ;
???mov dword ebx, [bp+4]???; OFFSET (32 BIT)
mov dl, [es:ebx]
mov sp, bp
pop bp
xor ax, ax
mov al, dl
mov dx, ax??????; Result is in ax & dx
ret
; This is my gdt... i hope it's ok now...
gdt_ptr:
dw gdt_end - gdt - 1 ; GDT limit
dd 0 ; linear adr of GDT
gdt: dw 0 ; limit 15:0
dw 0 ; base 15:0
db 0 ; base 23:16
db 0 ; access byte (descriptor type)
db 0 ; limit 19:16, flags
db 0 ; base 31:24
DATA_SEL equ $-gdt
???dw 0FFFFh
???dw 0
???db 0
db 92h ; present, ring 0, data, expand-up, writable
???db 0CFh??????; page-granular, 32-bit
???db 0
gdt_end:
; ==================== KERNEL.ASM =====================
[BITS 16]
SEGMENT START
GLOBAL _printf
GLOBAL _read_mem_32
GLOBAL _write_mem_32
EXTERN _main
; Update segment register
xor???ax, ax
mov???ss, ax
mov ax, cs
mov ds, ax
mov es, ax
;Switch in unreal mode
???cli
???push ds
???push es
xor eax, eax ; point gdt_ptr to gdt
??????mov ax, ds
??????shl eax, 4
add eax, gdt ; EAX=linear address of gdt
mov [gdt_ptr + 2], eax
lgdt [gdt_ptr]
??????mov eax, cr0
??????or al, 1
mov cr0, eax ; partial switch to 32-bit pmode
mov bx, DATA_SEL ; selector to segment w/ 4G limit
??????mov ds, bx
mov es, bx ??? ; set seg limits in descriptor caches
??????dec al
mov cr0, eax??? ; back to (un)real mode
pop es ; segment regs back to old values,
pop ds ; but now 32-bit addresses are OK
???sti
jmp ???_main ??????; Jump to C main program
; --------- INTERNAL FUNCTIONS ----------
echo:????????????; Stampa il carattere in al
mov ah, 0Eh
???mov???bx, 0007h
int???10h
???ret???
print:????????????; Stampa una stringa
???lodsb
cmp al, 0
jz pret
???call???echo
???jmp???print
pret:???ret
; ----------------------------------------
_printf:
push bp ; C printf...
mov bp, sp ;
???mov si, [bp+4]???
???call ???print
mov sp, bp
pop bp
ret
_write_mem_32:?????????; void write_mem_32(unsigned long i, char c)
????????????;
push bp ;
mov bp, sp ;
???mov dword ebx, [bp+4]???; OFFSET (32 BIT)
mov al, [bp+6]
mov [es:ebx], al
mov sp, bp
pop bp
ret
_read_mem_32: ; char read_mem_32(unsigned long i)
????????????;
push bp ;
mov bp, sp ;
???mov dword ebx, [bp+4]???; OFFSET (32 BIT)
mov dl, [es:ebx]
mov sp, bp
pop bp
xor ax, ax
mov al, dl
mov dx, ax??????; Result is in ax & dx
ret
; This is my gdt... i hope it's ok now...
gdt_ptr:
dw gdt_end - gdt - 1 ; GDT limit
dd 0 ; linear adr of GDT
gdt: dw 0 ; limit 15:0
dw 0 ; base 15:0
db 0 ; base 23:16
db 0 ; access byte (descriptor type)
db 0 ; limit 19:16, flags
db 0 ; base 31:24
DATA_SEL equ $-gdt
???dw 0FFFFh
???dw 0
???db 0
db 92h ; present, ring 0, data, expand-up, writable
???db 0CFh??????; page-granular, 32-bit
???db 0
gdt_end: