2

I'm creating custom mbr, something like mbr-lovenote and i can't create code that will copy 9th sector - (there is located original mbr) to 1st sector, i already tried take some code from mbr-lovenote and modify it, but i find out that code only load sector in memory and jump to it, but i have to copy it. I write my code, the code will be loaded from fist sector on PhysicalDrive0, but i don't know why it doesn't works.

;---create buffer
buffer db 512

;---read sector - 9th
mov ax, buffer              ;ES: BX must point to the buffer
mov es, ax                  ;
mov bx, buffer              ;
mov dl,0                    ;drive number
mov dh,0                    ;head number
mov ch,0                    ;track number
mov cl,9                    ;sector number 
mov al,1                    ;number of sectors to read
mov ah,2                    ;read function number
int 13h

;---write sector - 1th
mov ax, buffer              ;ES: BX must point to the buffer
mov es, ax                  ;
mov bx, buffer              ;
mov dl,0                    ;drive number
mov dh,0                    ;head number
mov ch,0                    ;track number
mov cl,1                    ;sector number
mov al,1                    ;number of sectors to write
mov ah,3                    ;write function number
int 13h

;---fake signature
times 510 - ($-$$) db 0
dw        0xaa55
Michael Petch
  • 46,082
  • 8
  • 107
  • 198
WobbyChip
  • 87
  • 8
  • 5
    `xor ax,ax` zeroes ax. So you put 0x0000 in ES and 0x0000 in DI. The address 0x0000:0x0000 (the destination of your memory move) is the interrupt vector table. Overwriting that will crash the system and likely end up in a reboot. – Michael Petch Jul 20 '18 at 05:52
  • 2
    Doesn't work how? What does happen when you single-step it in BOCHS's built-in debugger? This is not a [mcve]. – Peter Cordes Jul 20 '18 at 06:49
  • We need comments in English on SO. For most of people what might be interested in answering this question, there might as well not be any comments. BTW, is that Russian with Cyrillic characters? There is a Russian version of stack overflow, I think, if you'd rather ask there. – Peter Cordes Jul 28 '18 at 21:45

2 Answers2

2

You don't provide a minimal complete example and there are indications you may not know how real mode 20-bit segment:offset addressing works. Every memory location in real mode is made up of a 16-bit segment and a 16-bit offset. Both are combined to compute a 20-bit physical address with the formula: PhysicalAddress = (Segment<<4)+Offset. Shifting left by 4 is the same as multiplying by 16.

A bootloader is loaded at physical address 0x07c00 in memory. You have to choose an ORG and set the segments in your bootloader so that they reference physical address 0x07c00. More than one 20-bit segment:offset address can point to the same physical address. The 2 common ones for bootloaders are using an ORG 0x7c00 with segments set to 0x0000 ((0x0000<<4)+0x7c00=0x07c00), or usingORG 0x0000 and a segment of 0x07c0 ((0x07c0<<4)+0x0000=0x07c00)).

I have some general bootloader tips in this Stackoverflow answer. If you are potentially writing your bootloader to run on a USB drive in FDD mode you will also want to read my Stackoverflow answer about using a BIOS Data Area (BDA) representing a floppy.

This example is a simple bootloader with a BDA based on your code to copy sector 9 onto sector 1 (MBR) and then reboots with int 0x19. The code also uses the memory right after the bootloader in memory (@ 0x0000:0x7e00) for temporary storage to do the sector copy. I also provide a test bootloader that is placed into sector 9 that displays a message when it is running.

boot.asm:

org 0x7c00
bits 16

boot:
    jmp main
    TIMES 3-($-$$) DB 0x90   ; Support 2 or 3 byte encoded JMPs before BPB.

    ; Use a BIOS Parameter Block if you intend to use this on USB in FDD mode

    ; Dos 4.0 EBPB 1.44MB floppy
    OEMname:           db    "mkfs.fat"  ; mkfs.fat is what OEMname mkdosfs uses
    bytesPerSector:    dw    512
    sectPerCluster:    db    1
    reservedSectors:   dw    1
    numFAT:            db    2
    numRootDirEntries: dw    224
    numSectors:        dw    2880
    mediaType:         db    0xf0
    numFATsectors:     dw    9
    sectorsPerTrack:   dw    18
    numHeads:          dw    2
    numHiddenSectors:  dd    0
    numSectorsHuge:    dd    0
    driveNum:          db    0
    reserved:          db    0
    signature:         db    0x29
    volumeID:          dd    0x2d7e5a1a
    volumeLabel:       db    "NO NAME    "
    fileSysType:       db    "FAT12   "

main:
    ; BIOS passes our boot drive number in DL

    ; Setup segments
    xor ax, ax                  ; AX=0
    mov ds, ax                  ; DS=ES=0 because we use an org of 0x7c00
                                ;    segment<<4+offset = 0x0000<<4+0x7c00 = 0x07c00
    mov es, ax
    mov ss, ax
    mov sp, 0x7c00              ; SS:SP= 0x0000:0x7c00 stack just below bootloader

    ;---read sector - 9th
    mov bx, buffer              ; ES: BX point to buffer (ES set to zero previously)
;    mov dl,0                   ; use boot drive passed to bootloader by BIOS in DL
    mov dh,0                    ; head number
    mov ch,0                    ; track number
    mov cl,9                    ; sector number
    mov al,1                    ; number of sectors to read
    mov ah,2                    ; read function number
    int 13h

    ;---write sector - 1th

; The following commented lines aren't required. Int AH=13h/AH=2 only
; destroys AX and the following registers remain unchanged from the
; read disk BIOS call

;    mov bx, buffer              ; ES: BX must point to the buffer
;    mov dl,0                    ; use boot drive passed to bootloader by BIOS in DL
;    mov dh,0                    ; head number
;    mov ch,0                    ; track number

    mov cl,1                    ; sector number
    mov al,1                    ; number of sectors to write
    mov ah,3                    ; write function number
    int 13h


    mov si, message
    call print_string           ; Print a banner to the console

    int 19h                     ; Warm reboot, should run bootloader that was in sector 9

; Function: print_string
;           Display a string to the console on display page 0
;
; Inputs:   SI = Offset of address to print
; Clobbers: AX, BX, SI

print_string:               ; Routine: output string in SI to screen
    mov ah, 0eh             ; BIOS tty Print
    xor bx, bx              ; Set display page to 0 (BL)
    jmp .getch
.repeat:
    int 10h                 ; print character
.getch:
    lodsb                   ; Get character from string
    test al,al              ; Have we reached end of string?
    jnz .repeat             ;     if not process next character
.end:
    ret

message: db "Running original bootloader...", 0x0a, 0x0d, 0

times 510 - ($-$$) db 0
dw        0xaa55               ; Boot signature

; This buffer is right after the bootloader and will be at offset 0x7e00.
; 0x0000:0x7e00 is the memory location starting right after the 512 byte
; bootloader loaded into memory by the BIOS
buffer:

sector9.asm:

org 0x7c00
bits 16

boot:
    jmp main
    TIMES 3-($-$$) DB 0x90   ; Support 2 or 3 byte encoded JMPs before BPB.

    ; Use a BIOS Parameter Block if you intend to use this on USB in FDD mode

    ; Dos 4.0 EBPB 1.44MB floppy
    OEMname:           db    "mkfs.fat"  ; mkfs.fat is what OEMname mkdosfs uses
    bytesPerSector:    dw    512
    sectPerCluster:    db    1
    reservedSectors:   dw    1
    numFAT:            db    2
    numRootDirEntries: dw    224
    numSectors:        dw    2880
    mediaType:         db    0xf0
    numFATsectors:     dw    9
    sectorsPerTrack:   dw    18
    numHeads:          dw    2
    numHiddenSectors:  dd    0
    numSectorsHuge:    dd    0
    driveNum:          db    0
    reserved:          db    0
    signature:         db    0x29
    volumeID:          dd    0x2d7e5a1a
    volumeLabel:       db    "NO NAME    "
    fileSysType:       db    "FAT12   "

main:
    ; Setup segments
    xor ax, ax                  ; AX=0
    mov ds, ax                  ; DS=ES=0 because we use an org of 0x7c00
                                ;    segment<<4+offset = 0x0000<<4+0x7c00 = 0x07c00
    mov ss, ax
    mov sp, 0x7c00              ; SS:SP= 0x0000:0x7c00 stack just below bootloader

    mov si, message
    call print_string           ; Print a banner to the console

    cli
.endloop:                       ; Infinite loop to end bootloader
    hlt
    jmp .endloop

; Function: print_string
;           Display a string to the console on display page 0
;
; Inputs:   SI = Offset of address to print
; Clobbers: AX, BX, SI

print_string:               ; Routine: output string in SI to screen
    mov ah, 0eh             ; BIOS tty Print
    xor bx, bx              ; Set display page to 0 (BL)
    jmp .getch
.repeat:
    int 10h                 ; print character
.getch:
    lodsb                   ; Get character from string
    test al,al              ; Have we reached end of string?
    jnz .repeat             ;     if not process next character
.end:
    ret

message: db "Running sector 9 bootloader...", 0x0a, 0x0d, 0

times 510 - ($-$$) db 0
dw        0xaa55               ; boot signature

To build a 1.44MiB floppy image and place the primary bootloader at sector 1 and the secondary bootloader in sector 9 you could use commands like this if you are running on a system that has the dd command:

nasm -f bin boot.asm -o boot.bin
nasm -f bin sector9.asm -o sector9.bin

dd if=/dev/zero of=disk.img bs=1024 count=1440
dd if=boot.bin of=disk.img conv=notrunc seek=0
dd if=sector9.bin of=disk.img conv=notrunc seek=8

You can use QEMU to run this code using:

qemu-system-i386 -fda disk.img

If you run this in an emulator or virtual machine like QEMU it should display something like:

enter image description here

What has happened is that sector 9 was copied to sector 1 by the original bootloader in boot.bin and then the machine rebooted. Upon reboot it ran the bootloader code in sector9.bin that was copied from sector 9 to the MBR. It should print:

Running original bootloader...

And at a later time should print:

Running sector 9 bootloader...


Note: you will need to make sure that your disk is not write protected and that any BIOS you are using isn't using MBR security. MBR security prevent BIOS calls from overwriting the MBR (sector 1) on the boot drive.

Michael Petch
  • 46,082
  • 8
  • 107
  • 198
  • @WobbyChip : In theory if you could copy `boot.bin`to the first sector of a floppy it should work. It may work on a hard disk but the MBR may need to contain a partition table.Only way to know is to write `boot.bin` to the firsts sector and try. – Michael Petch Jul 29 '18 at 19:33
  • Hello @Michael Petch, I have one more question how can I copy sectors if I set `org 0h`? – WobbyChip Aug 18 '18 at 13:54
  • @WobbyChip If you mean using `org 0h` instead of `org 7c00h` then instead of setting ES and DS to 0 you would need to set them to `7c0h` – Michael Petch Aug 18 '18 at 13:58
  • Hmm, of all you say I understand nothing. – WobbyChip Aug 18 '18 at 14:03
  • @WobbyChip You probably don't understand how [20-bit segment offset addressing](https://thestarman.pcministry.com/asm/debug/Segments.html) works in real mode. Here is a copy of the code using `org 0h`: http://www.capp-sysware.com/misc/stackoverflow/51435724/org0/boot.asm . – Michael Petch Aug 18 '18 at 14:12
  • Hello @Michael Petch, sorry for abusing, can you please help me figure out why my code [CustomMBR.asm](https://github.com/WobbyChip/Assembly/blob/master/CustomMBR.asm) doesn't work as expected, because after I press CTRL+ALT+ESC, the code that copy sector fails and after reboot, nothing changes. – WobbyChip Aug 18 '18 at 15:48
  • @WobbyChip : Looking at your code you seem to be saving the bootdrive before setting the proper segment and you need to write it to the 0x8000 segment AFTER you read the sector into memory (or you just overwrite the saved value with what is on disk). You also clobber DL in `load_1` with the fixed HDD A (0x80). Use DL passed by the bootloader. I've made those changes here: http://www.capp-sysware.com/misc/stackoverflow/51435724/customMBR/boot.asm . I have placed `MDP` in the comments where I have made the modifications. Does that code work? – Michael Petch Aug 18 '18 at 16:39
  • Hmm, interesting but it doesn't change anything, it still fails copying sectors, maybe I forgot to say that this code is loaded from HDD. – WobbyChip Aug 18 '18 at 16:44
  • Yes, I creating new project and another one where I have original MBR in sector 9 I already did. And `mov cl,5 ; sector number - (9th)` was misctake it have to look like this `mov cl,5 ; sector number - (5th)` – WobbyChip Aug 18 '18 at 16:55
  • Yes and it fails on copying because after reboot I get my `CustomMBR.asm` executed and text displayed, I checked if my old bootloader is stored on sector 5 and yes it is. – WobbyChip Aug 18 '18 at 17:04
  • virtual emulator, VirtualBox, does it change something – WobbyChip Aug 18 '18 at 17:07
  • I don't use them, Virtualbox automatically creates them using settings 50GB and .vmdk format. For more information i tested `SectorCopy.asm` on this VirtualBox and it worked, so i think there is no problem in virtualbox. – WobbyChip Aug 18 '18 at 17:13
  • Just watch this video, I think it will give you more answers -> [link](https://yadi.sk/i/zpQh_zTn3aMHPo) – WobbyChip Aug 18 '18 at 17:22
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/178268/discussion-between-wobbychip-and-michael-petch). – WobbyChip Aug 18 '18 at 18:24
  • @WobbyChip I've made one last change in boot,asm that I missed before. Before copying the disk sectors you set _DS_ to 7c0h but you still need to access BOOT_DRIVE variable via DS segment of 0x8000. It is likely using the wrong drive number. I have placed an update `boot.asm` over top of my old version here: http://www.capp-sysware.com/misc/stackoverflow/51435724/customMBR/boot.asm . The change is not to set _DS_ to 7c0h, but only ES – Michael Petch Aug 18 '18 at 18:30
0

The OP (@WobbyChip) wrote this solution in an update to their question.


SectorCopy.asm -> Thanks to Michael Petch ---> Use this code to copy sectors.

org 0x7c00
bits 16

SectorCopy: 
    ; Setup segments
    xor ax, ax                  ; AX=0
    mov ds, ax                  ; DS=ES=0 because we use an org of 0x7c00 - Segment<<4+offset = 0x0000<<4+0x7c00 = 0x07c00
    mov es, ax
    mov ss, ax
    mov sp, 0x7c00              ; SS:SP= 0x0000:0x7c00 stack just below bootloader

    ;---read sector - 9th
    mov bx, buffer              ; ES: BX must point to the buffer
;   mov dl,0                    ; use boot drive passed to bootloader by BIOS in DL
    mov dh,0                    ; head number
    mov ch,0                    ; track number
    mov cl,9                    ; sector number - (9th)
    mov al,1                    ; number of sectors to read
    mov ah,2                    ; read function number
    int 13h

    ;---write sector - 1th
    mov bx, buffer              ; ES: BX must point to the buffer
;   mov dl,0                    ; use boot drive passed to bootloader by BIOS in DL
    mov dh,0                    ; head number
    mov ch,0                    ; track number
    mov cl,1                    ; sector number - (1th)
    mov al,1                    ; number of sectors to write
    mov ah,3                    ; write function number
    int 13h

times 510 - ($-$$) db 0
dw        0xaa55                ; Boot signature

buffer:
Michael Petch
  • 46,082
  • 8
  • 107
  • 198