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:

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.