2

This is the working version of the code, but I have problems and questions about it

  • BIOS returns the floppy drive = 231 not 0 as I read floppy should be = 0 so when replacing dl with 0 in read: it fails to read the disk but when it replacing dl in reset: it resets successfully so which drive is reset while the floppy drive = 0
  • When I put pusha in print_string before the rest of the code and popa in print_done before ret after running the program it only prints bootmsg
  • When replacing 0x0F00 with 0x1000 and 0xF000 with 0x10000 it doesn't print Hello World but it prints S I don't know why
  • When replacing 0x0F00 with 0x1000 and 0xF000 with es:bx (while it must be equivalent to 0x10000) it doesn't Hello World nor S

Edit: I'm using qemu

program_intialization:
    org 0x7C00          ;load program at 0x7C00
    push dx             ;save drive number to stack appeared to be 231 not 0
    jmp program_start   ;go to program_start
print_string:
    cld                 ;clear direction flag to increase si after copying the value of the address pointed by it
    lodsb               ;copy [si] into al then increase si
    or al,al            ;check if al is null
    jz print_done       ;if null then go to print_done
    mov ah,0x0E         ;else copy 0x0E to ah which is the video output code
    int 0x10            ;then interrupt the program with 0x10 the video routine code which will print the character in al
    jmp print_string    ;and print the next character until null is found
    print_done:
        mov al,0x0A     ;copy the code of line feed into al
        mov ah,0x0E     ;copy 0x0E to ah which is the video output code
        int 0x10        ;interrupt the program with 0x10 the video routine code which will print the character in al (the line feed)
        mov al,0x0D     ;copy the code of carriage return into al
        mov ah,0x0E     ;copy 0x0E to ah which is the video output code
        int 0x10        ;interrupt the program with 0x10 the video routine code which will print the character in al (the carriage return)
        ret             ;return to the place the function is called from
program_start:
    xor ax,ax           ;intialize ax to 0
    mov ds,ax           ;intialize ds to 0
    mov es,ax           ;intialize es to 0
    mov si,bootmsg      ;copy bootmsg address to si
    call print_string   ;print bootmsg
    reset:
        mov si,rstmsg       ;copy reset adress to si
        call print_string   ;print reset
        mov ah,0            ;drive reset code
        pop dx              ;get drive number into dl
        push dx             ;save drive number again
        int 0x13            ;reset drive
        jc reset            ;if failed try again
        mov ax,0x0F00       ;copy 0x0F00 to ax
        mov es,ax           ;copy 0x0F00 to es
        xor bx,bx           ;make bx = 0x0000
        mov si,scs          ;if success
        call print_string   ;print scs
    read:
        mov si,rdmsg        ;copy reset adress to si
        call print_string   ;print reset
        pop dx              ;get the drive number into dl
        mov ah,0x02         ;copy disk read code to ah
        mov al,1            ;read 1 sector
        mov ch,0            ;on track 0
        mov cl,2            ;read sector number 2
        mov dh,0            ;head number 0
        int 0x13            ;read from disk to memory address es:bx (0xF000)
        jc read             ;if failed try again
        mov si,scs          ;if success
        call print_string   ;print scs           
end:
    mov si,0xF000           ;which is es:bx
    call print_string0      ;print Hello World read from memory 0xF000
    cli                     ;clear all interupts
    hlt                     ;halt the system
bootmsg db "Bootloader v0.1",0
rstmsg db "Trying to reset Floppy Disk",0
rdmsg db "Trying to read Floppy Disk",0
scs db "Operation Successful",0
times 510 - ($-$$) db 0     ;fill the rest of bootsector with 00
dw 0xAA55                   ;boot magic number
msg db "Hello World",0      ;add hello world to the next sector (512bytes) which will be read
Jason Aller
  • 3,541
  • 28
  • 38
  • 38

1 Answers1

3
org 0x7C00
push dx             ;save drive number to stack

You are using the stack without knowing where it is in memory and how much room it has for you to use which is important is relation to your pusha/popa issue.

; reset:
pop dx              ;get drive number into dl
push dx             ;save drive number again
; read:
pop dx              ;get the drive number into dl

In reset you keep the DriveCode on the stack. However in read you don't put the DriveCode back. When, on failure, read tries to repeat the operation, the DriveCode can no longer get popped off the stack and some random value will replace it!


When I put pusha in print_string before the rest of the code and popa in print_done before ret after running the program it only prints bootmsg

This is the code that you would have written:

print_string:
    PUSHA            <<< added
    cld
    lodsb
    or al,al
    jz print_done
    mov ah,0x0E
    int 0x10
    jmp print_string ;and print the next character until null is found
print_done:
    mov al,0x0A
    mov ah,0x0E
    int 0x10
    mov al,0x0D
    mov ah,0x0E
    int 0x10
    POPA             <<< added
    ret

What happens is that each time a character is printed, the code jumps back to the start at print_string where another set of registers is pushed to the stack (because the pusha instruction gets repeated). With your bootmsg text that has 15 characters, this will happen 16 times. When finally the terminating zero is found, the popa instruction will execute just once, leaving lots of unwanted bytes on the stack. The ret instruction has no meaningful address to return to and the program crashes.


When replacing 0x0F00 with 0x1000 and 0xF000 with 0x10000 it doesn't print Hello World but it prints S I don't know why

In code you went from:

mov ax,0x0F00
mov es,ax
xor bx,bx
...
mov si,0xF000           ;which is es:bx
call print_string0      ;print Hello World read from memory 0xF000

to:

mov ax,0x1000
mov es,ax
xor bx,bx
...
mov si,0x10000          ;which is es:bx
call print_string0      ;print Hello World read from memory 0x10000

You are correct that setting ES=0x1000 corresponds to linear address 0x10000 (65536). However an instruction like mov si, 0x10000 will not load that particular address in the SI register since it is too big to fit in a word-sized register (allowed values range from 0 to 65535). Most assemblers will not complain about this and just store a truncated value in the register, in casu 0. The "S" that you say it prints comes from the very first byte in memory.


When replacing 0x0F00 with 0x1000 and 0xF000 with es:bx (while it must be equivalent to 0x10000) it doesn't Hello World nor S

If you replaced mov si,0xF000 with push es push bx pop si pop ds, it would have worked fine since the print_string routine operates from DS:SI and you must use it that way.


BIOS returns the floppy drive = 231 not 0 as I read floppy should be = 0 so when replacing dl with 0 in read: it fails to read the disk but when it replacing dl in reset: it resets successfully so which drive is reset while the floppy drive = 0

Given the above errors and misconceptions, my best guess could be that you're not receiving drivecode 231 at all, but are perhaps looking in the wrong place.
I would definitely stay with the value that BIOS provided and not force something like DL=0.


This is an improved version of your code:

  • Stack beneath the bootsector
  • Drive number stored in memory (first byte of bootsector)
  • Subroutine out of the way of the execution path
  • Some codesize optimizations
        org  0x7C00

        cld                      ; Gets DriveCode later
        xor  ax, ax
        mov  ds, ax
        mov  ss, ax
        mov  sp, 0x7C00
        mov  [0x7C00], dl
        mov  si, bootmsg
        call print_string

    reset:
        mov  si, rstmsg
        call print_string
        mov  dl, [0x7C00]       ; DriveCode
        mov  ah, 0              ;drive reset code
        int  0x13               ;reset drive
        jc   reset              ;if failed try again
        mov  si, scs
        call print_string

    read:
        mov  si, rdmsg
        call print_string
        mov  ax, 0x0F00
        mov  es, ax
        xor  bx, bx
        mov  dh, 0              ;head number 0
        mov  dl, [0x7C00]       ;get the drive number into dl
        mov  cx, 0x0002         ;on track 0 read sector number 2
        mov  ax, 0x0201
        int  0x13               ;read from disk to memory address es:bx (0xF000)
        jc   read               ;if failed try again
        mov  si, scs
        call print_string

    end:
        mov  si, 0xF000         ;which is es:bx
        call print_string0      ;print Hello World read from memory 0xF000
        cli                     ;clear all interrupts
        hlt                     ;halt the system
        jmp  end

    print_string:
        pusha
        mov  bx, 7
    print_more:
        lodsb
        or   al, al
        jz   print_done
        mov  ah, 0x0E
        int  0x10
        jmp  print_more
    print_done:
        mov  ax, 0x0E0A
        int  0x10
        mov  al, 0x0E0D
        int  0x10
        popa
        ret

    bootmsg db "Bootloader v0.1",0
    rstmsg  db "Trying to reset Floppy Disk",0
    rdmsg   db "Trying to read Floppy Disk",0
    scs     db "Operation Successful",0
            times 510 - ($-$$) db 0     ;fill the rest of bootsector with 00
            dw 0xAA55                   ;boot magic number
    msg     db "Hello World",0          ;add hello world to the next sector

ps: Don't forget to remove the typo in call print_string0.

Sep Roland
  • 33,889
  • 7
  • 43
  • 76
  • Thank you very much , why added `mov sp,0x7C00` isn't this while overwrite the code when pushing to the stack , and why `mov [0x7C00],dl` which will overwrite adress 0x0000 , why removed `cld` before `lodsb` what if DF was set before calling `print_string` , can you explain this more "If you replaced mov si,0xF000 with `push es` `push bx` `pop si` `pop ds`, it would have worked fine since the `print_string` routine operates from `DS:SI` and you must use it that way." – Abdelrahman Yehia Apr 12 '22 at 05:38