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:
protected mode ?
RE:protected mode ?
Why do you have to convert com programs to raw binary, but not make your boot loader load com programs,
Anton.
Anton.
RE:protected mode ?
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
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 ?
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.
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 ?
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
-EMS
RE:protected mode ?
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,
Cheers,
Adrian,