Page 1 of 1

protected mode ?

Posted: Wed Apr 09, 2003 11:00 pm
by echristian
I have a boot strap that expects a bin program(not a com ). I also have 12 protected mode examples that are com programs. How can i convert one of these protected mode examples to a bin(compiling with nasm)..so i can use it with my boot loader?

this is boot.asm

;*************************************************************************
;the ultimate boot-strap loader
;to load a file from a DOS FAT12 floppy as the OS
;*************************************************************************
[BITS 16]
[ORG 0x0000]
jmp     START
    
     OEM_ID                db "QUASI-OS"
     BytesPerSector        dw 0x0200
     SectorsPerCluster     db 0x01
     ReservedSectors       dw 0x0001
     TotalFATs             db 0x02
     MaxRootEntries        dw 0x00E0
     TotalSectorsSmall     dw 0x0B40
     MediaDescriptor       db 0xF0
     SectorsPerFAT         dw 0x0009
     SectorsPerTrack       dw 0x0012
     NumHeads              dw 0x0002
     HiddenSectors         dd 0x00000000
     TotalSectorsLarge     dd 0x00000000
     DriveNumber           db 0x00
     Flags                 db 0x00
     Signature             db 0x29
     VolumeID              dd 0xFFFFFFFF
     VolumeLabel           db "QUASI  BOOT"
     SystemID              db "FAT12   "
    
     START:
     ; code located at 0000:7C00, adjust segment registers
          cli
          mov     ax, 0x07C0
          mov     ds, ax
          mov     es, ax
          mov     fs, ax
          mov     gs, ax
     ; create stack
          mov     ax, 0x0000
          mov     ss, ax
          mov     sp, 0xFFFF
          sti
     ; post message
          mov     si, msgLoading
          call    DisplayMessage
     LOAD_ROOT:
     ; compute size of root directory and store in ‘cx’
          xor     cx, cx
          xor     dx, dx
          mov     ax, 0x0020                          ; 32 byte directory entry
          mul     WORD [MaxRootEntries]               ; total size of directory
          div     WORD [BytesPerSector]               ; sectors used by directory
          xchg    ax, cx
     ; compute location of root directory and store in ‘ax’
          mov     al, BYTE [TotalFATs]                ; number of FATs
          mul     WORD [SectorsPerFAT]                ; sectors used by FATs
          add     ax, WORD [ReservedSectors]          ; adjust for bootsector
          mov     WORD [datasector], ax               ; base of root directory
          add     WORD [datasector], cx
     ; read root directory into memory (7C00:0200)
          mov     bx, 0x0200                          ; copy root dir above bootcode
          call    ReadSectors
     ; browse root directory for binary image
          mov     cx, WORD [MaxRootEntries]           ; load loop counter
          mov     di, 0x0200                          ; locate first root entry
     .LOOP:
          push    cx
          mov     cx, 0x000B                          ; eleven character name
          mov     si, ImageName                       ; image name to find
          push    di
     rep  cmpsb                                       ; test for entry match
          pop     di
          je      LOAD_FAT
          pop     cx
          add     di, 0x0020                          ; queue next directory entry
          loop    .LOOP
          jmp     FAILURE
     LOAD_FAT:
     ; save starting cluster of boot image
          mov     si, msgCRLF
          call    DisplayMessage
          mov     dx, WORD [di + 0x001A]
          mov     WORD [cluster], dx                  ; file’s first cluster
     ; compute size of FAT and store in ‘cx’
          xor     ax, ax
          mov     al, BYTE [TotalFATs]                ; number of FATs
          mul     WORD [SectorsPerFAT]                ; sectors used by FATs
          mov     cx, ax
     ; compute location of FAT and store in ‘ax’
          mov     ax, WORD [ReservedSectors]          ; adjust for bootsector
     ; read FAT into memory (7C00:0200)
          mov     bx, 0x0200                          ; copy FAT above bootcode
          call    ReadSectors
     ; read image file into memory (0100:0000)
          mov     si, msgCRLF
          call    DisplayMessage
          mov     ax, 0x0100                          ; destination of image CS
          mov     es, ax
          mov     bx, 0x0000                          ; destination for image IP
          push    bx
     LOAD_IMAGE:
          mov     ax, WORD [cluster]                  ; cluster to read
          pop     bx                                  ; buffer to read into
          call    ClusterLBA                          ; convert cluster to LBA
          xor     cx, cx
          mov     cl, BYTE [SectorsPerCluster]        ; sectors to read
          call    ReadSectors
          push    bx
     ; compute next cluster
          mov     ax, WORD [cluster]                  ; identify current cluster
          mov     cx, ax                              ; copy current cluster
          mov     dx, ax                              ; copy current cluster
          shr     dx, 0x0001                          ;
     ;divide by two
          add     cx, dx                              ; sum for (3/2)
          mov     bx, 0x0200                          ; location of FAT in memory
          add     bx, cx                              ; index into FAT
          mov     dx, WORD [bx]                       ; read two bytes from FAT
          test    ax, 0x0001
          jnz     .ODD_CLUSTER
     .EVEN_CLUSTER:
          and     dx, 0000111111111111b               ; take low twelve bits
         jmp     .DONE
     .ODD_CLUSTER:
          shr     dx, 0x0004                          ; take high twelve bits
     .DONE:
          mov     WORD [cluster], dx                  ; store new cluster
          cmp     dx, 0x0FF0                          ; test for end of file
          jb      LOAD_IMAGE
     DONE:
          mov     si, msgCRLF
          call    DisplayMessage
          push    WORD 0x0100
          push    WORD 0x0000
          retf
     FAILURE:
          mov     si, msgFailure
          call    DisplayMessage
          mov     ah, 0x00
          int     0x16                                ; await keypress
          int     0x19                                ; warm boot computer
    
     ;*************************************************************************
     ; PROCEDURE DisplayMessage
     ; display ASCIIZ string at ds:si via BIOS
     ;*************************************************************************
     DisplayMessage:
          lodsb                                       ; load next character
          or      al, al                              ; test for NUL character
          jz      .DONE
          mov     ah, 0x0E                            ; BIOS teletype
          mov     bh, 0x00                            ; display page 0
          mov     bl, 0x07                            ; text attribute
          int     0x10                                ; invoke BIOS
          jmp     DisplayMessage
     .DONE:
          ret
    
     ;*************************************************************************
     ; PROCEDURE ReadSectors
     ; reads ‘cx’ sectors from disk starting at ‘ax’ into
     ;memory location ‘es:bx’
     ;*************************************************************************
     ReadSectors:
     .MAIN
          mov     di, 0x0005                          ; five retries for error
     .SECTORLOOP
          push    ax
          push    bx
          push    cx
          call    LBACHS
          mov     ah, 0x02                            ; BIOS read sector
          mov     al, 0x01                            ; read one sector
          mov     ch, BYTE [absoluteTrack]            ; track
          mov     cl, BYTE [absoluteSector]           ; sector
          mov     dh, BYTE [absoluteHead]             ; head
          mov     dl, BYTE [DriveNumber]              ; drive
          int     0x13                                ; invoke BIOS
          jnc     .SUCCESS                            ; test for read error
          xor     ax, ax                              ; BIOS reset disk
          int     0x13                                ; invoke BIOS
          dec     di                                  ; decrement error counter
          pop     cx
          pop     bx
          pop     ax
          jnz     .SECTORLOOP                         ; attempt to read again
          int     0x18
     .SUCCESS
          mov     si, msgProgress
          call    DisplayMessage
          pop     cx
          pop     bx
          pop     ax
          add     bx, WORD [BytesPerSector]           ; queue next buffer
          inc     ax                                  ; queue next sector
          loop    .MAIN                               ; read next sector
          ret
    
     ;*************************************************************************
     ; PROCEDURE ClusterLBA
     ; convert FAT cluster into LBA addressing scheme
     ; LBA = (cluster - 2) * sectors per cluster
     ;*************************************************************************
     ClusterLBA:
          sub     ax, 0x0002                          ; zero base cluster number
          xor     cx, cx
          mov     cl, BYTE [SectorsPerCluster]        ; convert byte to word
          mul     cx
          add     ax, WORD [datasector]               ; base data sector
          ret
    
     ;*************************************************************************
     ; PROCEDURE LBACHS
     ; convert ‘ax’ LBA addressing scheme to CHS addressing scheme
     ; absolute sector = (logical sector / sectors per track) + 1
     ; absolute head   = (logical sector / sectors per track) MOD number of heads
     ; absolute track  = logical sector / (sectors per track * number of heads)
     ;*************************************************************************
     LBACHS:
          xor     dx, dx                              ; prepare dx:ax for operation
          div     WORD [SectorsPerTrack]              ; calculate
          inc     dl                                  ; adjust for sector 0
          mov     BYTE [absoluteSector], dl
          xor     dx, dx                              ; prepare dx:ax for operation
          div     WORD [NumHeads]                     ; calculate
          mov     BYTE [absoluteHead], dl
          mov     BYTE [absoluteTrack], al
          ret

     absoluteSector db 0x00
     absoluteHead   db 0x00
     absoluteTrack  db 0x00
    
     datasector  dw 0x0000
     cluster     dw 0x0000
     ImageName   db "KERNEL  BIN"
     msgLoading  db 0x0D, 0x0A, "Loading Boot Image ", 0x0D, 0x0A, 0x00
     msgCRLF     db 0x0D, 0x0A, 0x00
     msgProgress db ".", 0x00
     msgFailure  db 0x0D, 0x0A, "ERROR : Press Any Key to Reboot", 0x00
    
          TIMES 510-($-$$) DB 0
          DW 0xAA55
     ;*************************************************************************



;************************************************************
this is pm1.asm
; pm1.asm
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; pm1.asm - protected-mode demo code
; Christopher Giese <geezer[AT]execpc.com>
; http://www.execpc.com/~geezer/os
;
; Release date 10/7/98. Distribute freely. ABSOLUTELY NO WARRANTY.
; Assemble with NASM: nasm -o pm1.com pm1.asm
;
; Run this program in "raw" MS-DOS mode, not a Windows DOS box. Do not
; load EMM386 or other memory managers. Do not load HIMEM.SYS (see
; below). Clear the screen and hit Enter a few times before running it.
;
; What you should see:
; "012345                                           !"
; " still in real mode!"
; "  ES,DS still real mode!"
; "   Finally in protected mode!"
; "    ES,DS still protected mode!"
; "     back to BORING old real mode"
;
; Running the program puts a 4G limit in the segment descriptor
; cache for ES. This makes 32-bit addresses legal in real mode,
; when used with ES. Running the program a second time will add
; an 'R' to the top line.
;
; If you see the 'R' instead of the '!' the first time you run it,
; it is because a previous protected-mode program has munged the
; segment descriptor cache for ES. The most likely culprit is
; HIMEM.SYS, which is loaded silently and automatically by DOS 7.
;
; This code was tested with NASM version 0.93 and NASM version 0.97.
; Please let me know if you have problems with other versions.
;
; This code was tested on an Intel 486SX-based system and an Intel
; Pentium-based system. Please let me know of possible problems with
; other CPUs, especially the 386 or 386SX.
;
; Though I have tried to be as clear and accurate as possible, there
; may be errors. Constructive criticism is welcome.
;
; Demonstrates:
; - Basic 32-bit protected mode.
; - Linear (flat) memory.
; - Access to text-mode video memory.
; - Return to real mode.
; - "Unreal" mode (flat real, big real).
; - Effects of the segment descriptor cache.
; See other tutorials for:
; - Interrupts/exceptions
; - Virtual 8086 mode
; - CPU detection (>= 386)
; - Local Descriptor Tables (LDTs)
; - Running code in XMS
; - A20 gate
; - Multitasking
; - Task state segments (TSSes)
; - Privilege
; - Gates
; Sources:
;   INTEL 80386 PROGRAMMER'S REFERENCE MANUAL 1986
; http://www.execpc.com/~geezer/os/386intel.zip
;       ftp://ftp.cdrom.com/.20/demos/code/hard ... 6intel.zip
;       http://www.intercom.net/user/jeremyfo/S ... 6intel.zip
;   Robert Collins' "Intel Secrets" web site:
;       http://www.x86.org
;
[ORG 0x100]
[BITS 16]
;
;
; If you want to skip this commentary and go directly to the code,
; search for the label "start:"
;
; How to get into protected mode? First, you need a GLOBAL DESCRIPTOR
; TABLE (GDT). There is one at the end of this file, at address "gdt:"
; The GDT contains 8-byte DESCRIPTORS for each protected-mode segment.
; Each descriptor contains a 32-bit segment base address, a 20-bit segment
; limit, and 12 bits describing the segment type. The descriptors look
; like this:
;
;           MSB    bit 6   bit 5   bit 4   bit 3   bit 2   bit 1   LSB
;        +-------+-------+-------+-------+-------+-------+-------+-------+
;byte 0  | bit 7<---------------- segment limit------------------->bit 0 |
;        +-------+-------+-------+-------+-------+-------+-------+-------+
;
;        +-------+-------+-------+-------+-------+-------+-------+-------+
;byte 1  |bit 15<---------------- segment limit------------------->bit 8 |
;        +-------+-------+-------+-------+-------+-------+-------+-------+
;
;        +-------+-------+-------+-------+-------+-------+-------+-------+
;byte 2  | bit 7<---------------- segment base-------------------->bit 0 |
;        +-------+-------+-------+-------+-------+-------+-------+-------+
;
;        +-------+-------+-------+-------+-------+-------+-------+-------+
;byte 3  |bit 15<---------------- segment base-------------------->bit 8 |
;        +-------+-------+-------+-------+-------+-------+-------+-------+
;
;        +-------+-------+-------+-------+-------+-------+-------+-------+
;byte 4  |bit 23<---------------- segment base-------------------->bit 16|
;        +-------+-------+-------+-------+-------+-------+-------+-------+
;
;        +-------+-------+-------+-------+-------+-------+-------+-------+
;byte 5  |   P   |      DPL      | <----------- segment type ----------> |
;        +-------+-------+-------+-------+-------+-------+-------+-------+
;
; P is the Segment Present bit. It should always be 1.
;
; DPL is the DESCRIPTOR PRIVILEGE LEVEL. For simple code like this, these
; two bits should always be zeroes.
;
; Segment Type (again, for simple code like this) is hex 12 for data
; segments, hex 1A for code segments.
;
;        +-------+-------+-------+-------+-------+-------+-------+-------+
;byte 6  |   G   |   B   |   0   | avail | bit 19<-- seg limit--->bit 16 |
;        +-------+-------+-------+-------+-------+-------+-------+-------+
;
; G is the Limit Granularity. If zero, the segment limit is in bytes
; (0 to 1M, in 1-byte increments). If one, the segment limit is in 4K PAGES
; (0 to 4G, in 4K increments). For simple code, set this bit to 1, and
; set the segment limit to its highest value (FFFFF hex). You now have
; segments that are 4G in size! The Intel CPUs can address no more than
; 4G of memory, so this is like having no segments at all. No wonder
; protected mode is popular.
;
; B is the Big bit; also called the D (Default) bit. For code segments,
; all instructions will use 32-bit operands and addresses by default
; (BITS 32, in NASM syntax, USE32 in Microsoft syntax) if this bit is set.
; 16-bit protected mode is not very interesting, so set this bit to 1.
;
; None of these notes apply to the NULL descriptor. All of its bytes
; should be set to zero.
;
;        +-------+-------+-------+-------+-------+-------+-------+-------+
;byte 7  |bit 31<---------------- segment base------------------->bit 24 |
;        +-------+-------+-------+-------+-------+-------+-------+-------+
;
; Build a simple GDT with four descriptors: NULL (all zeroes), linear data
; (lets you use 32-bit addresses), code, and data/stack. (An extra
; descriptor or two is needed to return to real mode.) For simplicity,
; the limits of all descriptors (except NULL and the real-mode descriptors)
; are FFFFF hex, the largest possible limit.
;
; In a real-mode .COM file, CS=DS. To make addressing identical in real
; mode and protected mode, we set the base of the code and data descriptors
; to DS * 16. This number is computed at run-time; the other numbers in
; the GDT can be done at assemble-time.
;
start: xor ebx,ebx
mov bx,ds                       ; BX=segment
shl ebx,4                       ; BX="linear" address of segment base
mov eax,ebx
mov [gdt2 + 2],ax               ; set base address of 32-bit segments
mov [gdt3 + 2],ax
mov [gdt4 + 2],ax               ; set base address of 16-bit segments
mov [gdt5 + 2],ax
shr eax,16
mov [gdt2 + 4],al
mov [gdt3 + 4],al
mov [gdt4 + 4],al
mov [gdt5 + 4],al

mov [gdt2 + 7],ah
mov [gdt3 + 7],ah
mov [gdt4 + 7],ah
mov [gdt5 + 7],ah
;
; Now we have a valid GDT. The CPU has a 48-bit register (GDTR) which must
; contain the base address of the GDT, and its limit. We will put those
; values at the address "gdtr", then load the GDTR register from that
; address. The GDT limit is a fixed value:
;       number of descriptors * 8 - 1
; This code uses 5 descriptors (null, linear, code, data, real-mode),
; so the GDT limit is 39. (If you look at the code after "gdtr:", you
; will see that it can accommodate more or fewer descriptors
; "automatically".)
;
; What about the GDT base? This won't work:
;
;       lea eax,[gdt] ; This address is relative to
;       mov [gdtr + 2],eax ; the segment base.
;
; The address of the GDT base (the address to be loaded into the GDTR
; register) is a PHYSICAL address, one that is not translated by
; segmentation. So, we do the translation ourselves, by adding the
; 32-bit segment base address that we put in EBX:
;
        lea eax,[gdt + ebx]             ; EAX=PHYSICAL address of gdt
        mov [gdtr + 2],eax
;
; That's a bit confusing, but eventually you'll get it. (I have been
; hacking at protected mode for over a year, and it STILL confuses me.)
;
; This demo code neither demonstrates nor supports interrupts.
; Shut them off. (My thanks to John Fine for pointing out that INT 0Dh
; is also IRQ 5, and his suggestion that I move the 'cli' here.)
;
cli
;
; Before entering pmode, we will try to use 32-bit addressing while still
; in real mode. To do this, we have to take over the vector for INT 0Dh
; (the "pseudo GPF").
;
xor ax,ax
mov es,ax
mov edx,[es:0x0D * 4] ; INT 0Dh vector -> EDX
mov [es:0x0D * 4 + 2],cs
lea ax,[trap]
mov [es:0x0D * 4],ax
;
; Try using a 32-bit address in real mode. Depending on "where the CPU's
; been", this may or may not cause interrupt 0Dh.
;
mov ebx,0xB809A ; ES still 0
mov byte [es:ebx],'R' ; 'R' in upper right corner of screen
;
; Did it work? There's more info on 32-bit addressing in real mode below.
; Note: the second 'mov' above is 5 bytes long. Modify the 'trap' routine
; below if the 'mov' changes.
;
; If we want to return to real mode when we're done, we must save the
; contents of the CS register. To simplify the return to real mode,
; we also store the return-to-real-mode address, do_rm.
;
mov ax,cs
mov [RealModeCS],ax
lea ax,[do_rm]
mov [RealModeIP],ax
;
; To (literally) see this code in action, we need a way to access
; text-mode video memory. This memory is at real-mode address B800:0000
; (hex). Let's put the segment for this memory in ES:
;
mov ax,0xB800
mov es,ax
;
; Load the GDTR with the base address and limit of the GDT.
;
lgdt [gdtr]
;
; Set the PE [protected mode enable] bit in register CR0 to begin the
; switch to protected mode.
;
mov eax,cr0
or al,1
mov cr0,eax
;
; We are not yet in full protected mode! Section 10.3 of the INTEL
; 80386 PROGRAMMER'S REFERENCE MANUAL 1986 states:
;
;; Immediately after setting the PE flag, the initialization code must flush
;; the processor's instruction prefetch queue by executing a JMP instruction.
;; The 80386 fetches and decodes instructions and addresses before they are
;; used; however, after a change into protected mode, the prefetched
;; instruction information (which pertains to real-address mode) is no longer
;; valid. A JMP forces the processor to discard the invalid information.
;
; It isn't really necessary to do the JMP right away, as this implies.
; It simply means that protected mode doesn't "kick in" until the segment
; registers are reloaded. Above, we set the ES segment register to 0xB800.
; This is the real-mode segment of the text video memory. With the PE bit
; still set, let's copy a message from the real-mode data segment (DS) to
; the video memory (ES).
;
        lea si,[msg0]                   ; -> "still in real mode!"
        mov di,(80 * 1 + 2) * 2         ; row 1, column 2
        mov cx,38
        cld
        rep movsb
;
; The code above won't work in protected mode. It's there just to
; prove that setting the PE bit is not enough to enter protected mode.
;
; Now do a far jump. This reloads the CS register and flushes the
; real-mode instructions from the prefetch queue. CS is the segment
; register used for instruction fetches, so this is where the switch
; from 16-bit instructions (real-mode) to 32-bit instructions
; (protected-mode) takes place.
;
; But what goes into CS? In real mode, we use the segment address. In
; protected mode, we use a SELECTOR:
;
; MSB  b14  b13  b12  b11  b10   b9   b8   b7   b6   b5   b4   b3   b2   b1   LSB
;+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
;|                                index                           | L  |   RPL   |
;+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
;
; index is a 13-bit index into the GDT. It selects one of the possible 8192
; descriptors in the table. (Note: 8192 descriptors/table, 8 bytes/descriptor.
; The GDT is no larger than 64K.)
;
; If the L bit is set, this selector selects a descriptor from one of the
; LOCAL DESCRIPTOR TABLES (LDT), instead of the GDT. For simple code (like
; this code) or code that doesn't use LDTs, set this bit to 0.
;
; RPL is a 2-bit REQUESTOR PRIVILEGE LEVEL. For simple code: set these bits
; to zero.
;
; Here, we load the hex value 10 into CS. In binary, this is
; 0000 0000 0001 0000. The top thirteen bits are 0000000000010.
; This selector choses descriptor #2 in the GDT (the code segment
; descriptor).
;
jmp SYS_CODE_SEL:do_pm          ; jumps to do_pm
;
; Real-mode interrupt 0Dh handler:
;
trap: mov ax,0xB800
mov fs,ax
mov byte [fs:0x9C],'!'
pop ax ; point stacked IP beyond...
add ax,5 ; ...the offending instruction
push ax
iret
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;       Alert! Now in 32-bit protected mode.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
[BITS 32]
do_pm:
;
; Now we are in 32-bit protected mode -- or are we? Besides CS, all of the
; segment registers still contain real-mode values. Let's try using real-
; mode addressing to put another message on the screen.
;
; Here's a potential problem: addressing is still 16-bit real-mode (oops,
; I gave it away!), but instructions are 32-bit. The rep movsb used below
; will read from DS:ESI and write to ES:EDI. We can either make sure the
; top 16 bits of ESI and EDI are zeroed, or use an 16-bit address size
; override prefix with 'rep movsb'.
;
        xor edi,edi
        xor esi,esi

lea si,[msg1]                   ; -> "ES, DS still real mode!"
        mov di,(80 * 2 + 3) * 2         ; row 2, column 3
        mov ecx,46
        cld
        rep movsb
;
; ARRGH, it works :) Real-mode addressing hangs on.
;
; To enable full protected-mode addressing, we must put valid protected-
; mode selectors in the DS and SS registers:
;
mov ax,SYS_DATA_SEL
mov ds,ax
mov ss,ax
;
; Can we now print out a message to let the world know we've made it?
;
;       mov ax,0xB800
;       mov es,ax
;       mov byte [es:0],'@'
;
; This won't work (which is why it's commented out). Remember, the
; segment registers contain selectors when in protected mode. Looking
; at the GDT, we see four descriptors (and their associated selectors):
;
;       NULL                    0
;       linear data             8       =LINEAR_SEL
;       code                    10 hex  =SYS_CODE_SEL
;       data/stack              18 hex  =SYS_DATA_SEL
;
; Use of the NULL segment/selector/descriptor is forbidden. Trying to
; access video memory via the code segment/selector/descriptor might
; work, but it is a poor programming practice, and will almost certainly
; cause problems down the road. What about SYS_DATA_SEL?
;
;       mov ax,SYS_DATA_SEL
;       mov es,ax
;       mov byte [es:0],'@'
;
; As you may have guessed, that won't work either. It writes to the lowest
; byte of the data/stack segment. Above, we set the base of the data/stack
; segment descriptor to DS * 16. Since this is a .COM file, CS=DS, and
; we end up scribbling on memory 256 bytes before the address "start".
; None of these work, either:
;
;       mov ax,SYS_DATA_SEL
;       mov es,ax
;       mov byte [es:0xB800],'@'        ; writes unknown byte at offset
;                                       ; B800 hex of data segment
;
;       mov ax,SYS_DATA_SEL
;       mov es,ax
;       mov byte [es:0xB8000],'@'       ; writes unknown byte at offset
;                                       ; B8000 hex of data segment
;
; OK, I've beaten the point to death. The answer lies in LINEAR_SEL.
; In real mode, the address of the text-mode video memory is B800:0000
; As a "flat" ("linear"), 32-bit address, this would be 000B8000.
; Because the descriptor referred to by LINEAR_SEL has a base segment
; address of zero, we can simply use the linear address 000B8000 with
; LINEAR_SEL to get at the video memory:
;
mov ax,LINEAR_SEL
mov es,ax
;
; Here's a useful debug tip: once you have basic pmode running, with a
; linear selector like this, you can poke single bytes into video memory
; after each questionable piece of code, to see how far the code got:
;
; questionable PM code here
mov byte [es:dword 0xB8000],'0'

; more questionable PM code here
mov byte [es:dword 0xB8002],'1'

; still more questionable PM code here
mov byte [es:dword 0xB8004],'2'

; (you get the picture)
mov byte [es:dword 0xB8006],'3'
;
; Because we've chosen the base address of the data segment so that
; addressing works the same in real mode and protected mode, we can
; refer to an address like "msg2" without grief or confusion:
;
lea esi,[msg2]                  ; -> "Finally in protected mode!"
;
; (though we now use 32-bit registers ESI, EDI, and ECX, instead of 16-bit
; SI, DI, and CX).
;
; With LINEAR_SEL, the screen starts at address B8000 hex. We need to
; add that value to whatever address we compute for a given cursor
; location. Notes:
; - 32-bit addresses like this are ILLEGAL in real mode (but see below)
; - We could've set the base of LINEAR_SEL to B8000 hex (and possibly
;   called it VIDEO_SEL) to avoid this step. A segment with base 0,
;   however, lets us easily get at other "interesting" areas of memory
;   e.g. the ROMs.
;
mov edi,0xB8000 + (80 * 3 + 4) * 2      ; row 3, column 4
mov ecx,52
cld
rep movsb
;
; OK, enough of protected mode. Can we return to real-mode DOS without
; things crashing and burning? We'll try. Section 14.5 of the INTEL
; 80386 PROGRAMMER'S REFERENCE MANUAL 1986 states:
;
;;  2.  Transfer control to a segment that has a limit of 64K (FFFFH). This
;;      loads the CS register with the limit it needs to have in real mode.
;
; This is not enough! It implies that you could run with a 32-bit protected
; mode segment that is page-granular and has limit 16 (16 * 4K = 64K). The
; segment must also be a 16-bit segment (Default/Big bit set to zero).
; Essentially, we must switch (briefly) to 16-bit protected mode before
; going on to real mode. Fortunately, if the necessary descriptor is
; provided, that's easy to do:
;
jmp REAL_CODE_SEL:do_16
[BITS 16]
do_16:
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;       Now in 16-bit protected mode.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; Going back to real mode with the data and stack segments having a limit
; of 4G (0xFFFFF and page granular) can cause problems. Referring again to
; Section 14.5 of the INTEL 80386 PROGRAMMER'S REFERENCE MANUAL 1986:
;
;;  3.  Load segment registers SS, DS, ES, FS, and GS with a selector that
;;      points to a descriptor containing the following values, which are
;;      appropriate to real mode:
;;
;;      þ  Limit = 64K   (FFFFH)
;;      þ  Byte granular (G = 0)
;;      þ  Expand up     (E = 0)
;;      þ  Writable      (W = 1)
;;      þ  Present       (P = 1)
;;      þ  Base = any value
;
; So, we fix that here:
;
mov ax,REAL_DATA_SEL
mov ss,ax
mov ds,ax ; leave ES alone
;
; The rest of the procedure to return to real mode is somewhat like the
; the procedure to enter protected mode. Zero the PE bit in register CR0:
;
mov eax,cr0
and al,0xFE
mov cr0,eax
;
; Now put the real-mode CS value back into CS. There are relatively
; few instructions that change the CS register:
;       jmp (far)       retf            iret (probably some more)
;
; jmp (far) works:
;
jmp far [RealModeIP]
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;       Back in 16-bit real mode.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
[BITS 16]
;
; We've shut off the PE bit and put a real-mode value back into CS.
; Above, we saw how real-mode addressing hung on even after setting
; the PE bit and jumping to a 32-bit protected-mode code segment.
; Is the reverse true? Can we use 32-bit addressing while in real mode?
;
do_rm: mov byte [es:dword 0xB8008],'4'
;
; It looks like we can! This is called UNREAL MODE (or "flat real",
; "big real", etc.). Among other uses, unreal mode is useful for bootloaders,
; as you can use INT 13h to load a kernel from disk to conventional memory,
; then use unreal mode to copy it to extended memory.
;
; Each segment register in the x86 CPU has an associated 8-byte descriptor
; register, also in the CPU. This is called the SEGMENT DESCRIPTOR
; CACHE. Loading a selector into a segment register while in protected
; mode reloads the entire cache entry for that segment register. Loading
; a value into a segment register in real mode sets only the base address
; of the cache entry. The segment limit and flag bits are not changed.
;
; This means that we CAN reload the segment register after returning to
; real mode, and we'll still be able to use 32-bit addresses (as long as
; a suitably high segment limit was set while in protected mode).
;
xor ax,ax
mov es,ax
mov byte [es:dword 0xB800A],'5'
;
; Like protected mode in general, unreal mode is very subtle. This code
; fragment, for example, will not work:
;
lea esi,[msg3] ; -> "ES, DS still protected mode!"
mov edi,0xB8000 + (80 * 4 + 5) * 2      ; row 4, column 5
mov ecx,56
cld
;rep movsb
;
; The 'rep movsb' is commented out, because that's where the code fails.
; Though addressing is still 32-bit, the code itself is 16-bit. This means
; that 'rep movsb' will use the 16-bit SI, DI, and CX registers. Since
; the value in EDI is at least 0xB8000, the message won't be written to
; video memory, but to some other location.
;
; This behavior is easily fixed with an address size override prefix:
;
a32 ; same as 'db 0x67'
rep movsb
;
; Before returning to DOS, put real-mode compatible values in the
; segment registers:
;
mov ax,cs
mov ds,ax
mov ss,ax
mov ax,0xB800
mov es,ax
;
; Home again:
;
lea si,[msg4]                   ; -> "back to BORING old real mode"
mov di,(80 * 5 + 6) * 2         ; row 5, column 6
mov cx,56
cld
rep movsb
;
; Restore the INT 0D interrupt vector:
;
xor ax,ax
mov es,ax
mov [es:0x0D * 4],edx ; EDX -> INT 0x0D vector
;
; Protected mode is no more. It is now safe to re-enable interrupts.
;
sti
;
; Exit to DOS with errorlevel 0
;
mov ax,0x4C00
int 0x21
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; data
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
RealModeIP:
        dw 0

RealModeCS:
dw 0
;
; The alternating spaces are treated as attribute bytes by the video
; hardware. This makes the messages an eye-catching black on green.
;
msg0:   db "s t i l l   i n   r e a l   m o d e ! "
msg1:   db "E S ,   D S   s t i l l   r e a l   m o d e ! "
msg2:   db "F i n a l l y   i n   p r o t e c t e d   m o d e ! "
msg3: db "E S ,   D S   s t i l l   p r o t e c t e d   m o d e ! "
msg4:   db "b a c k   t o   B O R I N G   o l d   r e a l   m o d e "

gdtr: dw gdt_end - gdt - 1 ; GDT limit
dd gdt                  ; (GDT base gets set above)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; global descriptor table (GDT)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; null descriptor
gdt: dw 0 ; limit 15:0
dw 0 ; base 15:0
db 0 ; base 23:16
db 0 ; type
db 0 ; limit 19:16, flags
db 0 ; base 31:24

; linear data segment descriptor
LINEAR_SEL equ $-gdt
dw 0xFFFF ; limit 0xFFFFF
dw 0 ; base 0
db 0
db 0x92 ; present, ring 0, data, expand-up, writable
        db 0xCF                 ; page-granular, 32-bit
db 0

; code segment descriptor
SYS_CODE_SEL equ $-gdt
gdt2:   dw 0xFFFF               ; limit 0xFFFFF
dw 0 ; (base gets set above)
db 0
db 0x9A ; present, ring 0, code, non-conforming, readable
        db 0xCF                 ; page-granular, 32-bit
db 0

; data segment descriptor
SYS_DATA_SEL equ $-gdt
gdt3:   dw 0xFFFF               ; limit 0xFFFFF
dw 0 ; (base gets set above)
db 0
db 0x92 ; present, ring 0, data, expand-up, writable
        db 0xCF                 ; page-granular, 32-bit
db 0

; a code segment descriptor that is 'appropriate' for real mode
; (16-bit, byte-granular, limit=0xFFFF)
REAL_CODE_SEL equ $-gdt
gdt4:   dw 0xFFFF
dw 0 ; (base gets set above)
db 0
db 0x9A ; present, ring 0, code, non-conforming, readable
db 0 ; byte-granular, 16-bit
db 0

; a data segment descriptor that is 'appropriate' for real mode
; (16-bit, byte-granular, limit=0xFFFF)
REAL_DATA_SEL equ $-gdt
gdt5:   dw 0xFFFF
dw 0 ; (base gets set above)
db 0
db 0x92 ; present, ring 0, data, expand-up, writable
db 0 ; byte-granular, 16-bit
db 0

gdt_end:

RE:protected mode ?

Posted: Thu Apr 10, 2003 11:00 pm
by Anton
Why do you have to convert com programs to raw binary, but not make your boot loader load com programs,
Anton.

RE:protected mode ?

Posted: Thu Apr 10, 2003 11:00 pm
by echristian
I'll try to explain it better.

I am trying to have a boot loader that i downloaded load a program that switches to protected mode.I am new to protected mode programming and rusty at assembly language.

I downloaded some protected mode examples that were all com programs, but my boot loader will only except bin programs.

If you could show me how to modify my boot loader to load a com program or modify the protected mode example to be a bin program that would help me get started learning protected mode programming and brushing up on assembly?

I use the nasm assembler so it would help me if the code was nasm compatiable.
thanks

RE:protected mode ?

Posted: Sun Apr 13, 2003 11:00 pm
by Anton
Try to search for description of com headers and stuff. As far as i remember
1)a com program has not header :)
2)you need to load it in such a way, that it starts at offset 0x100.( the first byte of the program will be CS:0x100;
3)The stack starts from CS:0x100 too.(SS=CS SP=0x100)
I don't remember about other regs.
Anton.

RE:protected mode ?

Posted: Wed May 14, 2003 11:00 pm
by Eric
Just add ORG 0x0100 to the front of the protected mode code. Then compile it using nasmw pm1.asm -o kernel.bin. Put kernel.bin onto the floppy with the boot loader on it. It SHOULD work...

-EMS

RE:protected mode ?

Posted: Fri May 16, 2003 11:00 pm
by Adek336
I think, that if you do not change the bootstrap, you should add [ORG 0] not [ORG 0x100] to the pmode example. That makes it something more like a raw binary than a com binary.

Cheers,
Adrian,