Page 1 of 2

A20 Gate

Posted: Sun Jun 23, 2002 5:59 am
by drizzt
mov ah, 2401h
int 15h

Is it possible to use this code to enable A20 Gate whitout switching in PMode?!

Re:A20 Gate

Posted: Sun Jun 23, 2002 7:01 am
by crazybuddha
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.

Re:A20 Gate

Posted: Sun Jun 23, 2002 1:40 pm
by Pype.Clicker
there is even a XMS command to turn on A20 gate ... if xms is usable for you ;)

Re:A20 Gate

Posted: Mon Jun 24, 2002 1:37 am
by drizzt
...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!

Re:A20 Gate

Posted: Mon Jun 24, 2002 6:52 am
by crazybuddha
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.

Re:A20 Gate

Posted: Mon Jun 24, 2002 2:15 pm
by Pype.Clicker
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 ...

Re:A20 Gate

Posted: Fri Jun 28, 2002 11:53 am
by drizzt
; 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...

Re:A20 Gate

Posted: Fri Jun 28, 2002 12:42 pm
by crazybuddha
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?".

Re:A20 Gate

Posted: Fri Jun 28, 2002 1:06 pm
by Pype.Clicker
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 :)

Re:A20 Gate

Posted: Fri Jun 28, 2002 7:12 pm
by Schol-R-LEA
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 ...
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.

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

Posted: Sat Jun 29, 2002 1:24 pm
by Peter_Vigren
Schol-R-LEA wrote:
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 ...
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.

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...
So the code can sometimes be att xxxx:0000?? Unpleasant if "Org 7C00h" is in use...

Re:A20 Gate

Posted: Sun Jun 30, 2002 1:01 am
by Pype.Clicker
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 8)

Re:A20 Gate

Posted: Sun Jun 30, 2002 8:14 am
by drizzt
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...)

Re:A20 Gate

Posted: Sun Jun 30, 2002 8:16 am
by drizzt
;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...)

Re:A20 Gate

Posted: Sun Jun 30, 2002 8:17 am
by drizzt
; 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: