flat assembler
Message board for the users of flat assembler.

Index > OS Construction > Going back to 16bit mode in my bootstrap

Author
Thread Post new topic Reply to topic
calpol2004



Joined: 16 Dec 2004
Posts: 110
calpol2004 01 Aug 2007, 14:09
I'm having problems loading my kernel with my bootstrap loader, it's loaded by the bootsector at 0x500 underneath the bootsector where there's about 30kb to play with. I've entered pmode and i'm pretty sure the code for enabling the A20 gate works. What my problem is, is that in order to load the kernel i need to switch 16bit mode again but in such a way that i can retain the 4GB addressable memory that i acquired by enabling the A20 (unreal mode i think it's called).

Code:
org 0x500
 ;located in unused area below bootloader, 30kb to work with Smile
use16
jmp main

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                 DATA                     ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
LoadingMsg db "Preparing to load Kernel...", 0x0D, 0x0A, 0x00  ;just confirm the kernel loader actually started
msgFailure  db 0x0D, 0x0A, "Couldn't find the Kernel :'(. Press Any Key to Reboot", 0x0D, 0x0A, 0x00     ;our error message

;Global Descriptor Table (GDT)
gdt_data:
dd 0                            ; null descriptor
dd 0
;gdt code:                     ; code descriptor
dw 0FFFFh                       ; limit low
dw 0                            ; base low
db 0                            ; base middle
db 10011010b                    ; access
db 11001111b                    ; granularity
db 0                            ; base high
;gdt data:                      ; data descriptor
dw 0FFFFh                       ; limit low (Same as code)10:56 AM 7/8/2007
dw 0                            ; base low
db 0                            ; base middle
db 10010010b                    ; access
db 11001111b                    ; granularity
db 0                            ; base high
end_of_gdt:
toc: 
dw end_of_gdt - gdt_data - 1    ; limit (Size of GDT)
dd gdt_data                     ; base of GDT

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;      Procedures              ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Print:                                    ;our basic screen output for 16bit mode
lodsb                          ;loads next char, increments cx n stuff
or              al, al         ;returns 1 if al is 0, all strings used must be 0 terminated so this doesnt loop forever.
jz              PrintDone
mov             ah,0eh         ;output character teletype BIOS interrupt.
int             10h            ;10h video interupts, where the 0Eh teletype write char is located.
jmp             Print          ;loop back to start for next char
PrintDone:
ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;   Lets enter Pmode. Joy :/    ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
main:
cli
xor     ax, ax                  ; null segments
mov     ds, ax
mov     es, ax
mov     ax, 0x9000              ; stack begins at 0x9000-0xffff
mov     ss, ax
mov     sp, 0xFFFF
sti
mov     si, LoadingMsg
call    Print
cli
lgdt    [toc] ;set up gdt, toc is pointer defined in the date section
mov     eax, cr0                ; set bit 0 in cr0--enter pmode, note to self: what fuck is cr0 register used for normally?
or      eax, 1                  ;mov doesnt let you do mov cr0,1 you have to put it in eax first, sucks tbh.
mov     cr0, eax
jmp     08h:pmode              ; far jump to fix CS. Remember that the code selector is 0x8!
;Note: Do NOT re-enable interrupts! Doing so will triple fault!

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                  ;
;                     32bit PMODE, about time Smile                   ;
;                                                                  ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
use32 ; <- remember to use these mmk?
pmode:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;          Set up the Stack          ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
        mov             ax, 0x10                ; set data segments to data selector (0x10) - note to self, what the fuck is a selector? Note to self2: it's another word for a segment.
        mov             ds, ax
        mov             ss, ax
        mov             es, ax
        mov             esp, 90000h             ; stack begins from 90000h    ;same place as before, well i think so anyway, god i cant wait till this shits finished.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; set up A20 Gate for 4GB of memory  ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
        pusha
        mov cx,50   ;5 tries
        .EnableA20start:     ;start of loop
        .KKbrdCntrlWait:        ;start of status register wait
        xor ax,ax
        in  al,64h             ;try to get the status byte
        bt  ax,1               ;did we get something?
        jc      .KKbrdCntrlWait  ;try again, GO FISH!
        mov al,0DFh ;enable A20 command
        out 64h,al ;why do you enable 4GB of memory with the keyboard controller?!? seems bodged to me :p, seriously though, who at intel came up with this?
        .KKbrdCntrlWait2:       ;now lets wait for the status register to fill, i think its so we get confirmation it's now about to find out if it can actually enable A20
        xor ax,ax
        in  al,64h
        bt  ax,1          ;check if low bit 1 is true confirming it worked
        jc      .KKbrdCntrlWait2      ;check again, usually doesnt work, keyboard controller is ftl.
        .KKbrdCntrlGetStat2:
        xor ax,ax
        in  al,60h ;read the status byte, see if it worked
        bt  ax,1 ;check if bit 1 is set, if so it worked
        popa
        jc  loadkernel ;carry on to load Kernel, bt puts the result bool value in carry flag btw, rather not say how long it took till i figured that out.
        loop .EnableA20start ;lets try again till cx equals 0, currently set for 50 tries, if it doesnt work the person doesn't deserve to use my OS with they're shitty computer Smile.
        popa
        error:
        ;show error message here
        hlt

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;          Load Kernel               ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
        loadkernel:
                    and   al,0xfe        ; switch back to real mode
                    mov   cr0,eax        ;
                    jmp   0x500:unreal-0x500 ; actually go back to 16-bit 
                    hlt
         use16
         unreal:
         mov   bx,0x10        ; load all of the data segments with large 32-bit segment
         mov   ds,bx          ;
         mov   es,bx          ;
         mov   ss,bx          ;      

         ;code here
;need to go back to 16bit mode so i can access use interrupts but this time round i have up to 4GB addressable memory
cli
hlt
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;        End, Thank **** for that    ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
    


The code to go back into 16bit mode is currently located right at the end in loadkernel:, so far i've tried setting bit 0 of cr0 to 0 and jumping to a 16bit section of code with 0x500:unreal-0x500 very much like JMD's KolibroOS's usb bootsector does, but either the code doesn't run or the code triple faults and bochs just resets constantly. If someone has any examples which switch between 16 and 32bit mode or can see an error in my code i'd appreciate your help.
Post 01 Aug 2007, 14:09
View user's profile Send private message MSN Messenger Reply with quote
mkriegel



Joined: 15 Jul 2007
Posts: 19
Location: Germany
mkriegel 01 Aug 2007, 14:56
Why do you need to switch to 16 bit mode in order to load the rest of your kernel. Can't you do this by calling a DMA-driver or sth. like this.???
Post 01 Aug 2007, 14:56
View user's profile Send private message Reply with quote
calpol2004



Joined: 16 Dec 2004
Posts: 110
calpol2004 01 Aug 2007, 15:31
Well i can use BIOS interrupts to load the file from the floppy in 16bit mode and because i went in the 32bit mode temporarily and enabled the a20 gate i can load a kernel over 1MB.

I guess it's possible to interface with the floppy controller and load sectors manually but it's so much more convenient to use the bios, more portable too.

I can't see how i'd go about using DMA channels to load sectors from a floppy disk, seems a much harder feat.


Last edited by calpol2004 on 01 Aug 2007, 15:33; edited 1 time in total
Post 01 Aug 2007, 15:31
View user's profile Send private message MSN Messenger Reply with quote
Dex4u



Joined: 08 Feb 2005
Posts: 1601
Location: web
Dex4u 01 Aug 2007, 15:33
I written some code to goto and from realmode for vesa mode changing, see here:

http://www.dex4u.com/demos/DemoVesa.zip

NOTE: I was called "ASHLEY4" when i wrote it.
The first thing to remember is you need to set realmode and pmode address the same before goint to pmode and save some realmode settings as here:
Code:
        xor   ebx,ebx      mov   bx,ds                                   shl   ebx,4                                   mov   eax,ebx mov   [sys_code_1 + 2],ax                     mov   [sys_data_1 + 2],ax     mov   [Real_code_1 + 2],ax                    mov   [Real_data_1 + 2],ax    shr   eax,16  mov   [sys_code_1 + 4],al     mov   [sys_data_1 + 4],al     mov   [Real_code_1 + 4],al    mov   [Real_data_1 + 4],al  mov   [sys_code_1 + 7],ah     mov   [sys_data_1 + 7],ah     mov   [Real_code_1 + 7],ah    mov   [Real_data_1 + 7],ah        add   ebx,gdt                                   mov   [gdtr + 2],ebx        cli                                            mov   ax,cs  mov   [RealModeCS],ax    

Now the next thing to do is go back and forth to realmode for every 512bytes loaded, this sounds slow, but i can tell you it the same speed as if you made a pmode driver.
In my OS i have two drivers for floppy and hdd, one a Pmode to real, and one a Pmode driver and theres very little between them, but by going to and from pmode you can load from usb key fob etc, if your bios can boot from USB.
Post 01 Aug 2007, 15:33
View user's profile Send private message Reply with quote
mkriegel



Joined: 15 Jul 2007
Posts: 19
Location: Germany
mkriegel 01 Aug 2007, 18:11
Isn't this switching a danger for a well-structured OS?
Post 01 Aug 2007, 18:11
View user's profile Send private message Reply with quote
Gizmo



Joined: 19 Jul 2007
Posts: 25
Gizmo 01 Aug 2007, 18:37
Correct me if I'm wrong, but you can enable the a20 gate in real mode since its the keyboard controller your talking to and not the processor.

You can switch modes on 386 and other cpus as much as you want, just store the ivt and restore the pic when you head back to real mode (if you change them any).
The 286 cannot go back to real mode.
Long mode cannot go back to real or protected mode, but you can use legacy mode.
Switching modes is slow so you shouldnt do it alot, but a few places here and there will cause no harm.
Post 01 Aug 2007, 18:37
View user's profile Send private message Reply with quote
calpol2004



Joined: 16 Dec 2004
Posts: 110
calpol2004 01 Aug 2007, 18:50
Well considering this is a bootstrap...a second part to the bootloader, the switches only take place once. I'm hoping all the switches i'll ever have to do is once 32bit protected provided gizmo is right that it's unnecesary for me to enter 32bit just to enable A20 then come back out.

Because my kernel doesn't multi-task or anything (yet) so even though BIOS interrupts are slow they're still pretty fast and using them is only going to add like 0.1 seconds to a process which no one is going to notice (i may go through it afterwards and re-write it using the controllers instead), it's often worth using them sometimes for extra portability and not having to write as much code :p.

I'm currently looking through the bochs documention to find out how to use the debugger, i could then go through the program instruction by instruction and get a better idea what going wrong and test my A20 procedure as i'm not 100% it's functional.

Either way, thanks for the help. DexOS Even if your Vesa example doesn't help me with this problem i have been looking for a coded example which uses VESA so you've helped me out there Smile.
Post 01 Aug 2007, 18:50
View user's profile Send private message MSN Messenger Reply with quote
Gizmo



Joined: 19 Jul 2007
Posts: 25
Gizmo 01 Aug 2007, 19:55
To test if your a20 works, compare a known value from 1 address to the value stored at that address + 1meg. If it wraps around and they are equal, then your a20 code didnt work.
Some keyboard controllers are a bit different and your code may not work on every pc.
Here is the code I use that I found in a tutorial that trys 2 methods (note its 16 bit):
(I havent tried it this way, but it looks like it could be called in C as
signed long enableA20(void)
)
Code:
                ;;
                ;; enableA20.s (adapted from Visopsys OS-loader)
                ;;
                ;; Copyright (c) 2000, J. Andrew McLaughlin
                ;; You're free to use this code in any manner you like, as long as this
                ;; notice is included (and you give credit where it is due), and as long
                ;; as you understand and accept that it comes with NO WARRANTY OF ANY KIND.
                ;; Contact me at jamesamc@yahoo.com about any bugs or problems.
                ;;

        use16
        Enable_A20:
        ;store all registers
                pusha
        ;disable interupts
                cli
        ;5 attempts to enable a20 line
                mov CX, 5
        ;first method of enabling a20
                .startAttempt1:
                ; Wait for the controller to be ready for a command
                       .commandWait1:
                                xor AX, AX
                                in AL, 64h
                                bt AX, 1
                                jc .commandWait1
                ; Tell the controller we want to read the current status.
                ; Send the command D0h: read output port.
                       mov AL, 0D0h
                       out 64h, AL
                ;Wait for the controller to be ready with a byte of data
                      .dataWait1:
                                xor AX, AX
                                in AL, 64h
                                bt AX, 0
                                jnc .dataWait1
                ; Read the current port status from port 60h
                       xor AX, AX
                       in AL, 60h
                ; Save the current value of AX
                       push AX
                ; Wait for the controller to be ready for a command
                       .commandWait2:
                                in AL, 64h
                                bt AX, 1
                                jc .commandWait2
                ; Tell the controller we want to write the status byte again
                       mov AL, 0D1h
                       out 64h, AL
                ; Wait for the controller to be ready for the data
                       .commandWait3:
                                xor AX, AX
                                in AL, 64h
                                bt AX, 1
                                jc .commandWait3
                ; Write the new value to port 60h.  Remember we saved the old value on the stack
                       pop AX
                ; Turn on the A20 enable bit
                       or AL, 00000010b
                       out 60h, AL
                ; Finally, we will attempt to read back the A20 status to ensure it was enabled
                ; Wait for the controller to be ready for a command
                       .commandWait4:
                                xor AX, AX
                                in AL, 64h
                                bt AX, 1
                                jc .commandWait4
                ; Send the command D0h: read output port.
                       mov AL, 0D0h
                       out 64h, AL
                ; Wait for the controller to be ready with a byte of data
                       .dataWait2:
                                xor AX, AX
                                in AL, 64h
                                bt AX, 0
                                jnc .dataWait2
                ; Read the current port status from port 60h
                       xor AX, AX
                       in AL, 60h
                ; Is A20 enabled?
                       bt AX, 1
                ; Check the result.  If carry is on, A20 is on.
                       jc Enable_A20_success
                ; If the counter value in CX has not reached zero, we will retry
                       loop .startAttempt1
         ;method 2 for chipsets that don't support method 1
                mov CX, 5
                .startAttempt2:
                ; Wait for the keyboard to be ready for another command
                       .commandWait6:
                                xor AX, AX
                                in AL, 64h
                                bt AX, 1
                                jc .commandWait6
                ; Tell the controller we want to turn on A20
                       mov AL, 0DFh
                       out 64h, AL
                ; Again, we will attempt to read back the A20 status to ensure it was enabled.
                ; Wait for the controller to be ready for a command
                       .commandWait7:
                                xor AX, AX
                                in AL, 64h
                                bt AX, 1
                                jc .commandWait7
                ; Send the command D0h: read output port.
                       mov AL, 0D0h
                       out 64h, AL
                ; Wait for the controller to be ready with a byte of data
                       .dataWait3:
                                xor AX, AX
                                in AL, 64h
                                bt AX, 0
                                jnc .dataWait3
                ; Read the current port status from port 60h
                       xor AX, AX
                       in AL, 60h
                ; Is A20 enabled?
                       bt AX, 1
                       jc Enable_A20_success
                ; If the counter value in CX has not reached zero, we will retry
                       loop .startAttempt2
    ;unable to enable a20
        Enable_A20_fail:
                sti
                popa
                mov EAX, -1
                ret
    ;enabled a20
        Enable_A20_success:
                sti
                popa
                xor EAX, EAX
                ret

    
Post 01 Aug 2007, 19:55
View user's profile Send private message Reply with quote
Gizmo



Joined: 19 Jul 2007
Posts: 25
Gizmo 01 Aug 2007, 20:01
You should print out messages detailing the current operation so that when it stalls, the last message displayed may tell you where it is crashing.
Place a message and a hlt and retry the program. If it reaches it, move it down some more and try again- eventually you will reach the crash and then you will have a better idea whats crashing it.
Post 01 Aug 2007, 20:01
View user's profile Send private message Reply with quote
Dex4u



Joined: 08 Feb 2005
Posts: 1601
Location: web
Dex4u 02 Aug 2007, 01:47
Your welcome here some pointers, it true that not all A20 work on all PC and yes you can set A20 in realmode, also note bochs sets A20 by default, as do a lot of new PC Shocked .
As for unrealmode there example in the Dos fasm code or kelvar see here: http://flatassembler.net/examples/kelvar.zip

NOTE: When i want to use unrealmode many years ago, i found most example on the net i tried, just did not work.
Post 02 Aug 2007, 01:47
View user's profile Send private message Reply with quote
Display posts from previous:
Post new topic Reply to topic

Jump to:  


< Last Thread | Next Thread >
Forum Rules:
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Copyright © 1999-2024, Tomasz Grysztar. Also on GitHub, YouTube.

Website powered by rwasa.