2
votes

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

2
can you show the code you use for dt.asm? Or how do you know that the problem is in loading your sector rather than in the sector itself?mihi
Knowing what is in dt.asm would be useful. If you are in fact using 0x5678:0x1234 as the load address and you do a FAR JMp to that address - then whatever you are doing in dt.asm will require an ORG directive of 0x1234 (org 0x1234 at the top of dt.asm) and you will have to set DS and ES to 0x5678.Michael Petch
@ Michael Petch my first line of code in dt.asm : ORG 0x1234:0x5678 or it is supposedly be org 579b4 ?paradox
thank you! followed instructions you gave me and I got my desired string displayed on screen correctly! I may have further questions regarding listening to key stroke, may I contact you from your email address?paradox
@MichaelPetch BTW, can you explain: if jump out to the first 512byte floppy disk space, (bootloader space), dos will not preset to the current environment and int 20h will not exist. I'm suggesting that in fact DOS is involved when I'm using my own MBR alone with calling interrupt 20h? Is it still a bootstrap process since DOS is involved?paradox

2 Answers

4
votes

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:

  • a 3-byte near jump
  • a 2-byte short jump followed by a 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!

3
votes

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:

  1. 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.

  2. 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. Use STD/CLD to set it to the direction you wish (CLD=forward/STD=backwards). In this case the code assumes forward movement so one should use CLD. More on this can be found in an instruction set reference

  3. When 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

Other Observations

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.