-1

My computer halts when i write my software onto MBR. My software is for hook INT 13h and execute some code when INT 13h is executed. I already try with too many ISRs for INT 13h but unsuccessfully. Here is my example source:

cli
cs
mov si,[4Ch]
cs
mov di,[4Eh]
cs
mov [180h],cx
cs
mov [182h],dx
mov si,0h
mov di,2000h
cs
mov [4Ch],si
cs
mov [4Eh],di
sti
mov si,2000h
mov di,0
mov ds,di
mov es,si
mov si,7C6DH
mov di,0
mov cx,codelastbyte-payload
cld     
repz
movsb

mov ax,201h
push cs
pop es
mov bx,7c00h
mov cx,7
mov dx,80h
int 60h
jmp 0:7C00h

payload:           ;NEW INT13H
sti
INT 60H
iret

codelastbyte:

What is wrong?

Michael Petch
  • 46,082
  • 8
  • 107
  • 198
  • 1
    I think there is more to this code than you are showing? Is there code before what you have posted here? (there is no boot signature 0xaa55 so I'm thinking this has been trimmed down). But at a mininum `mov [180h],cx` I believe should be `mov [180h],si` and `mov [182h],dx` should be `mov [182h],di`. You have `mov si,7C6DH`.Can'y you use `mov si,payload`? – Michael Petch Feb 09 '18 at 18:40

1 Answers1

3

You don't provide any context, and your code doesn't appear to be complete. One glaring error appears to be in these line:

mov si,[4Ch]
cs
mov di,[4Eh]
cs
mov [180h],cx
cs
mov [182h],dx

It appears you really meant the last 4 lines to be SI and DI:

cs
mov [180h],si
cs
mov [182h],di

If chain loading you can't load a new bootloader on top of an area where the code is already executing. You will likely get unusual behavior. You will need to copy the bootloader elsewhere; continue executing code in that new location; and then load the new boot loader into memory at physical address 0x07C00.

For readability it is preferable to place the segment override (CS in this case) inside the memory operand. This code:

cs
mov [180h],si

Can be written as:

mov [cs:180h],si

Complete Implementation in FASM

The code example below:

  • Sets the stack pointer so we know our disk read won't clobber it
  • Saves the boot drive number that is passed in via DL by the BIOS. It is preferable not to hard code the boot drive number if we don't have to.
  • Copies the bootloader code and data to 0x0060:0x0000 so that we don't clobber it when chainloading the new boot sector to 0x07c0:0x0000. Memory location 0x0060:0x0000 is above the interrupt table, the BIOS data area and reserved memory.
  • Continue code execution in the 0x0060 segment using a FAR JMP
  • Copy the new Int 13h interrupt handler to 0x2000:0x0000
  • Install the new Int 13h interrupt handler
  • Reads sector 7 of the boot disk over top of the original at physical address 0x07c00
  • FAR JMP to the newly loaded bootloader at 0x0000:0x7c00
  • Pad the assembly file out to the size of a 1.44MiB floppy

The FASM code that achieves this:

RESIDENT_SEG equ 0x2000
BOOT_SEG     equ 0x07c0
BOOTCOPY_SEG equ 0x0060
STACK_OFFSET equ 0x7c00

INT13_OFFSET equ 0x13 * 4

org 0x0000
boot_start:
    ; Set up our own stack just below bootloader @ 0x0000:0x7c00
    xor ax, ax
    mov ss, ax
    mov sp, STACK_OFFSET

    ; Copy the bootloader code and data from 0x07C0:0x0000 to 0x0060:0x0000
    mov ax, BOOT_SEG
    mov ds, ax
    mov [boot_drive], dl
    mov ax, BOOTCOPY_SEG
    mov es, ax
    cld
    xor si, si
    mov di, si
    mov cx, SIZEOF_BOOT+SIZEOF_PAYLOAD
    rep movsb

    ; Transfer control to copy of bootloader relocated to segment 0x0060
    jmp BOOTCOPY_SEG:bootcopy_cont
bootcopy_cont:

    ; Set DS = ES= 0x0060
    push es
    pop  ds

    ; ES = 0 to access IVT that begins at 0x0000:0x0000
    xor  ax, ax
    mov  es, ax

    ; Place the original FAR PTR of Int 13 into BX:AX
    cli
    mov ax, [es:INT13_OFFSET]
    mov bx, [es:INT13_OFFSET+2]

    ; Install our new int 0x13 handler
    mov si, int13
    mov di, RESIDENT_SEG
    mov [es:INT13_OFFSET], si
    mov [es:INT13_OFFSET+2], di

    ; Copy the interrupt handler to 0x2000:0x0000
    mov es, di
    mov si, payload_boot_offset
    xor di, di
    mov cx, SIZEOF_PAYLOAD
    rep movsb

    ; Now that interrupt and interrupt data (payload) is copied to 0x2000 segment
    ; save the orig_int13 to the interrupt data area. AX and BX were set earlier
    mov [es:orig_int13], ax
    mov [es:orig_int13+2], bx

    sti

    ; Read sector 7(CHS=0,0,7) to 0x07c0:0x0000 (phys address 0x07c00)
    mov ax, 201h
    mov bp, BOOT_SEG
    mov es, bp
    xor bx, bx
    mov cx, 7
    xor dx, dx
    mov dl, [boot_drive]
    call do_orig_int13

    ; Jump to newly loaded bootloader
    jmp 0:0x7C00

    ; Function : do_orig_int13
    ; Return   : none
    ; Clobbers : Same as int 0x13
    ;
    ; Description:
    ; Manually call the original Int 0x13 handler. Int instruction pushes
    ; the flags and then calls through the interrupt vector table. We
    ; Do that same process. This avoids having to use Int 0x60. The
    ; Old interrupt vector was saved to RESIDENT_SEG(0x2000):orig_int13
    ; earlier.
do_orig_int13:
    push ds
    push bp
    mov bp, RESIDENT_SEG
    mov ds, bp
    pop bp
    pushf
    call far [ds:orig_int13]
    pop ds
    ret

; Boot data
boot_drive: db 0x00

SIZEOF_BOOT = $-$$

payload_boot_offset:
org 0x0000
int13:           ;NEW INT 0x13

    ; Print MDP to upper left corner of screen in magenta on white
    push ax
    push es
    mov ax, 0xb800
    mov es, ax
    mov word [es:0x0000], (0x75 shl 8) or 'M'
    mov word [es:0x0002], (0x75 shl 8) or 'D'
    mov word [es:0x0004], (0x75 shl 8) or 'P'
    pop es
    pop ax

    ; Do original disk interrupt routine. We jump to it
    ; instead of using the INT instruction because
    ; The Int 0x13 original routine sets the flags and we
    ; want them to be returned to whoever called us.
    jmp far [cs:orig_int13]

    ; The original interrupt handler will do the IRET for us
    ; Will never return to this point

; Insert data used by the interrupt handler here
orig_int13: dd 0x0000          ; Copy of the original Int 13h handler

SIZEOF_PAYLOAD = $-$$

;Pad first sector with 0, add boot signature to last 2 bytes
times 510-(SIZEOF_BOOT+SIZEOF_PAYLOAD) db 0
dw 0xaa55

; Pad out 5 more sectors with 0
times 512 * 5 db 0x00

; Sector 7 test code
org 0x7c00
sector7:
    ; Set DS = 0x0000
    xor ax, ax
    mov ds, ax

    ; Call Int 0x13 to reset disk. Our interrupt handler should also display MDP
    ; in upper left corner of the screen. AX set to zero above. Int 0x13/AH=0 is reset.
    ; DL set by the code that loaded this bootloader just like the BIOS would have done.
    int 0x13

    cli

; Infinite loop to end the code
.endloop:
    hlt
    jmp .endloop

; Pad out remainder of sector 7
times 512-($-$$) db 0x00

; Pad out remaining bytes to create a 1.44MiB floppy image
times (2880-7)*512 db 0x00

You can assemble this into a floppy disk image with:

fasm bootload.asm disk.img

Run it with:

qemu-system-i386 -fda disk.img

The output would look similar to:

enter image description here


Notes:

  • FASM and NASM's ORG directive work differently.
  • The code no longer saves the original Int 0x13 interrupt vector to Int 0x60. We call the old interrupt manually by doing a PUSHF and a FAR CALL via the address stored to memory.
  • The code modifies Int 0x13 so that it displays MDP in the upper left of screen in magenta on white and then passes control to the original handler.
  • The code in sector 7 (CHS=0,0,7) uses Int 0x13/AH=2 to reset the boot drive. This code is used for test purposes but would be the actual bootloader you wish to chain load.
  • This code assumes we are chain loading a different boot sector (@ CHS=0,0,7) from the same disk that was booted from.
Michael Petch
  • 46,082
  • 8
  • 107
  • 198