Reputation: 258
my code for master boot record:
;bit16 ; 16bit by default
org 0x7c00
jmp short start
nop
bsOEM db "OS423 v.0.1" ; OEM String
start:
;;cls
mov ah,06h ;Function 06h (scroll screen)
mov al,0 ;Scroll all lines
mov bh,0x0f ;Attribute (lightgreen on blue)
mov ch,0 ;Upper left row is zero
mov cl,0 ;Upper left column is zero
mov dh,24 ;Lower left row is 24
mov dl,79 ;Lower left column is 79
int 10h ;BIOS Interrupt 10h (video services)
;;print welcome msg
mov ah,13h ;Function 13h (display string), XT machine only
mov al,1 ;Write mode is zero: cursor stay after last char
mov bh,0 ;Use video page of zero
mov bl,0x0f ;Attribute (lightgreen on blue)
mov cx,mlen ;Character string length
mov dh,0 ;Position on row 0
mov dl,0 ;And column 0
lea bp,[msg] ;Load the offset address of string into BP, es:bp
;Same as mov bp, msg
int 10h
;;load sector into memory & 5678h:1234h
mov bx, 0x5678 ;segmented address
mov es, bx ;move segemented address to es
mov bx,0x1234 ;base address to bx
mov ah, 02 ;function read sectors
mov al, 01 ;# of sectors to load
mov ch, 00 ;track to read
mov cl, 02 ;sector to read
mov dh, 00 ;head to read
mov dl, 00 ;drive number
int 0x13 ;call interrupt 13
jmp 0x5678:0x1234 ;jump to memory address
int 20
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;variables;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
msg: db 'Welcome to Pradox OS 0.1! Authored by Jiansong he', 10, 13, '$'
mlen equ $-msg
padding times 510-($-$$) db 0 ;to make MBR 512 bytes
bootSig db 0x55, 0xaa ;signature (optional)
my terminal command for nasm compiling and puting the binary files into my .img floppy disk:
line1: define a floppy dick named boot.img, block size 512 byte, in total of 2880 byte
line2: use nasm compiler, compile mbr.asm file into binary format, store it at file named mbr.bin(which is my master boot record)
line3: compile dt.asm into binary at dt.bin
line4: put mbr.bin's content into boot.img, block size 512, put total 1 block
line5: put dt.bin's content into boot.img, block size 512, at physical sector #2(logical sector #1)
dd if=/dev/zero of=boot.img bs=512 count=2880
nasm -f bin mbr.asm -o mbr.bin
nasm -f bin dt.asm -o dt.bin
dd if=mbr.bin of=boot.img bs=512 count=1 conv=notrunc
dd if=dt.bin of=boot.img bs=512 seek=1 count=1 conv=notrunc
code in dt.asm:
[BITS 16] ;Set code generation to 16 bit mode
ORG 0x5647:0x1234 ;set addressing to begin at 579b4H
startdt:
;call cls ;call routine to clear screen
;call dspmsg ;call routine to display message
call date
call cvtmo
call cvtday
call cvtcent
call cvtyear
call dspdate
call time
call cvthrs
call cvtmin
call cvtsec
call dsptime
int 20h ;halt operation (VERY IMPORTANT!!!)
cls:
mov ah,06h ;function 06h (Scroll Screen)
mov al,0 ;scroll all lines
mov bh,0x0f ;Attribute (bright white on blue)
mov ch,0 ;Upper left row is zero
mov cl,0 ;Upper left column is zero
mov dh,24 ;Lower left row is 24
mov dl,79 ;Lower left column is 79
int 10H ;BIOS Interrupt 10h (video services)
ret
dspmsg:
mov ah,13h ;function 13h (Display String)
mov al,1 ;Write mode is zero
mov bh,0 ;Use video page of zero
mov bl,0x0a ;Attribute (bright white on bright blue)
mov cx,mlen2 ;Character length
mov dh,0 ;position on row 0
mov dl,0 ;and column 0
lea bp,[welcom] ;load the offset address of string into BP
int 10H
ret
welcom: db 'jiansong Hes first Operating System :D',10,13,'$'
mlen2 equ $-welcom;
date:
;Get date from the system
mov ah,04h ;function 04h (get RTC date)
int 1Ah ;BIOS Interrupt 1Ah (Read Real Time Clock)
ret
;CH - Century
;CL - Year
;DH - Month
;DL - Day
cvtmo:
;Converts the system date from BCD to ASCII
mov bh,dh ;copy contents of month (dh) to bh
shr bh,1
shr bh,1
shr bh,1
shr bh,1
add bh,30h ;add 30h to convert to ascii
mov [dtfld],bh
mov bh,dh
and bh,0fh
add bh,30h
mov [dtfld + 1],bh
ret
cvtday:
mov bh,dl ;copy contents of day (dl) to bh
shr bh,1
shr bh,1
shr bh,1
shr bh,1
add bh,30h ;add 30h to convert to ascii
mov [dtfld + 3],bh
mov bh,dl
and bh,0fh
add bh,30h
mov [dtfld + 4],bh
ret
cvtcent:
mov bh,ch ;copy contents of century (ch) to bh
shr bh,1
shr bh,1
shr bh,1
shr bh,1
add bh,30h ;add 30h to convert to ascii
mov [dtfld + 6],bh
mov bh,ch
and bh,0fh
add bh,30h
mov [dtfld + 7],bh
ret
cvtyear:
mov bh,cl ;copy contents of year (cl) to bh
shr bh,1
shr bh,1
shr bh,1
shr bh,1
add bh,30h ;add 30h to convert to ascii
mov [dtfld + 8],bh
mov bh,cl
and bh,0fh
add bh,30h
mov [dtfld + 9],bh
ret
dtfld: db '00/00/0000'
dspdate:
;Display the system date
mov ah,13h ;function 13h (Display String)
mov al,0 ;Write mode is zero
mov bh,0 ;Use video page of zero
mov bl,0x0f ;Attribute
mov cx,10 ;Character string is 10 long
mov dh,4 ;position on row 4
mov dl,0 ;and column 28
push ds ;put ds register on stack
pop es ;pop it into es register
lea bp,[dtfld] ;load the offset address of string into BP
int 10H
ret
time:
;Get time from the system
mov ah,02h
int 1Ah
ret
;CH - Hours
;CL - Minutes
;DH - Seconds
cvthrs:
;Converts the system time from BCD to ASCII
mov bh,ch ;copy contents of hours (ch) to bh
shr bh,1
shr bh,1
shr bh,1
shr bh,1
add bh,30h ;add 30h to convert to ascii
mov [tmfld],bh
mov bh,ch
and bh,0fh
add bh,30h
mov [tmfld + 1],bh
ret
cvtmin:
mov bh,cl ;copy contents of minutes (cl) to bh
shr bh,1
shr bh,1
shr bh,1
shr bh,1
add bh,30h ;add 30h to convert to ascii
mov [tmfld + 3],bh
mov bh,cl
and bh,0fh
add bh,30h
mov [tmfld + 4],bh
ret
cvtsec:
mov bh,dh ;copy contents of seconds (dh) to bh
shr bh,1
shr bh,1
shr bh,1
shr bh,1
add bh,30h ;add 30h to convert to ascii
mov [tmfld + 6],bh
mov bh,dh
and bh,0fh
add bh,30h
mov [tmfld + 7],bh
ret
tmfld: db '00:00:00'
dsptime:
;Display the system time
mov ah,13h ;function 13h (Display String)
mov al,0 ;Write mode is zero
mov bh,0 ;Use video page of zero
mov bl,0x0f;Attribute
mov cx,8 ;Character string is 8 long
mov dh,5 ;position on row 5
mov dl,0;and column 0
push ds ;put ds register on stack
pop es ;pop it into es register
lea bp,[tmfld] ;load the offset address of string into BP
int 10H
ret
int 20H
my testing environment is dosbox, I can successfully display the welcome message on screen but failed to load another sector into memory starting at 0x5647:0x1234 thanks
Upvotes: 2
Views: 604
Reputation: 47553
Since there are a number of issues not covered in the other answer, I'll provide a new answer.
I recommend looking at my General Bootloader Tips in this other Stackoverflow answer. In particular the first few tips apply here:
When the BIOS jumps to your code you can't rely on CS,DS,ES,SS,SP registers having valid or expected values. They should be set up appropriately when your bootloader starts. You can only be guaranteed that your bootloader will be loaded and run from physical address 0x00007c00 and that the boot drive number is loaded into the DL register.
The direction flag used by
lodsb
,movsb
etc could be either set or cleared. If the direction flag is set improperly SI/DI registers may be adjusted in the wrong direction. UseSTD
/CLD
to set it to the direction you wish (CLD=forward/STD=backwards). In this case the code assumes forward movement so one should useCLD
. More on this can be found in an instruction set referenceWhen jumping to a kernel it is generally a good idea to FAR JMP to it so that it properly sets CS:IP to expected values. This can avoid problems with kernel code that may do absolute near JMPs and CALLs within the same segment.
In your code your bootloader should set up a stack pointer in SS:SP. Placing it just below the bootloader is reasonable at 0x0000:0x7c00. You should save DL register before destroying it because it contains the boot drive number. You could push it on the stack at the start and restore it when you set up the disk related routines accessed int 13h
. You shouldn't assume that ES or DS (DS in particular) are set to the value of 0. Since you use an ORG of 0x7c00 the segments need 0x0000. (0x0000<<4)+0x7c00 = physical address 0x07c00.
To resolve these issues you could add these lines after the start
label:
start:
mov [bootdrv],dl;Save the boot drive passed in via DL to the bootloader
xor ax,ax ;Set ES and DS to zero since we use ORG 0x7c00
mov es,ax
mov ds,ax
mov ss,ax ;Set SS:SP to 0x0000:0x7c00 below bootloader
mov sp,0x7c00
cld ;Set direction flag forward
You'll need to add a bootdrv
variable after you have msg
bootdrv: db 0
Before you use the int 13h
disk read feature you can now use the value in bootdrv
and place it into DL before issuing the interrupt call. This line should be replaced:
mov dl, 00 ;drive number
With:
mov dl,[bootdrv];Get the boot drive saved at start of bootloader
You have int 20
in your bootloader after the jmp 0x5678:0x1234
. I believe you meant int 20h. The JMP will never return so placing code after it will do nothing. However, int 20h
is a DOS interrupt that is only available after MS-DOS system is loaded from disk into memory. You have no MS-DOS on your disk of course. In a bootloader (bare metal) environment just don't use DOS interrupts at all.
In dt.asm
you have some issues. You FAR JMP to the newly loaded code at 0x5678:0x1234. In doing so CS was set to 0x5678. You'll need to manually set ES and DS, You can do this by copying the value in CS to DS and ES. You also need to set the appropriate ORG. In this case the origin point is 0x1234 from the beginning of the segment (0x5678) so you must use org 0x1234
. The top of dt.asm
could be modified to look like:
BITS 16 ;Set code generation to 16 bit mode
ORG 0x1234 ;set origin point to 0x1234
mov ax, cs ;copy CS to DS and ES
;alternatively could have used mov ax, 0x5678
mov ds, ax
mov es, ax
startdt:
The issue with int 20h
discussed previously is an issue in dt.asm
. Remove all occurrences of it. Instead you can put the processor in an infinite loop. The last of your code in dt.asm
that is executed is after call dsptime
returns. After that CALL you can put an infinite loop with something like:
jmp $
A more preferable infinite loop that can take less processing power is to turn off the interrupts using CLI, use a HLT instruction and then for safe measure if HLT returns JMP back and do HLT again (Could happen if there is an NMI Non-Maskable Interrupt). HLT waits until the next interrupt occurs. This is what you'll often see instead:
cli
endloop:
hlt
jmp endloop
It appears in the first version of the code you posted the parameters to INT 13h/AH=2 disk read function were incorrect. Sector numbers start at 1 and heads and cylinders are zero based. The best source of interrupt information is Ralph Brown's Interrupt List which covers both BIOS and MS-DOS interrupts. If you need information on the parameters of the interrupts it is an excellent reference.
I'd recommend BOCHS for debugging bootloaders. It has a command line debugger that understands real mode addressing and can be used to watch instructions as they are executed,set breakpoints, display registers, examine memory etc.
Upvotes: 3
Reputation: 39166
Simple enough.
You can't just start the bootsector with the OEM string!
You have to jump over this text to the real start point.
Either use:
nop
instruction.mov ch, 01 ;track to read mov cl, 02 ;sector to read mov dh, 01 ;head to read
If you want to read the 2nd sector of the drive, you will want to specify Cylinder=0, Head=0, and Sector=2
mov ch, 0 ;track to read
mov cl, 2 ;sector to read
mov dh, 0 ;head to read
Your boot message will perhaps no get displayed because you didn't setup the ES
segment register. Given the org 0x7C00
you need to set ES=0. You mustn't trust BIOS to do this for you.
You might want to remove the int 20
instruction from the program. It can't help you.
In stead of using mov dl, 0
to specify the disk to load from, you would better use the contents of the DL
register that you got when your bootsector was first called by BIOS. An emulator like DOSBOX might use some specific value here!
Upvotes: 4