2

I was messing around with some NASM code that does some math ops to a number and prints out the ASCII character corresponding to the answer. Here's the code:

[bits 16]
[org 0x7c00]

init:
mov ax, 1853
mov bl, 15
div bl ; some division
sub al, 2 ; a little subtraction
mov bx, 10 ; used to loop 10 times
subtract: ; subtracts 2 10 times from ax to test looping
sub ax, 2
sub bx, 1
cmp bx, 0
jne subtract

;the code below is stupid
mov bl, al
mov ax, 0
mov cx, 0
mov dx, 0
mov ah, 0x0e ; write character command
mov al, bl
mov bx, 0
int 0x10 ; BIOS video interrupt
;the code above is stupid

jmp $

times 510-($-$$) db 0
dw 0xaa55

When ran on a vm, it prints out "e" and works fine. However, when ran on a real computer booting a USB device, it becomes incredibly finnicky. I had to clear all the registers, even ones that shouldn't need to be cleared, and move my answer from one register to another to get it to work. I don't know why this was needed. If the code varies slightly, even something like not clearing cx and dx, nothing is printed. Why is this happening? Any help is appreciated.

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

1 Answers1

0

The standard int 10h requires only ax and bx to be set and it appears you have done this correctly. The calculations at the top leave bx set to zero and ax set to 0865H:

    mov ax, 1853      ; ax <- 073dH
    mov bl, 15        ; bx <- ??0fH
    div bl            ; ax <- 087bH
    sub al, 2         ; ax <- 0879H
    mov bx, 10        ; bx <- 000AH
subtract:             ; ten times decrement bx, sub 2 from ax
    sub ax, 2
    sub bx, 1
    cmp bx, 0
    jne subtract      ; ax <- 0865H, bx <- 0000H

You then set the registers as needed:

mov bl, al            ; bx <- 0065H
mov ax, 0             ; ax <- 0000H
mov cx, 0             ; cx <- 0000H
mov dx, 0             ; dx <- 0000H
mov ah, 0x0e          ; ax <- 0e00H
mov al, bl            ; ax <- 0e65H
mov bx, 0             ; bx <- 0000H

That final value of ax/bx should be okay to print e to the console:

ah = 0eH : teletype output command
al = 65H : The 'e' character.
bh = 00H : page zero.
bl = 00H : forground color (only for graphic modes).

The values of cx/dx should be irrelevant. There was a drop-in BIOS replacement that treated a value of cx = abcdH specially but it's incredibly unlikely to be an issue here.

Hence your actual code seems to be fine, with or without the "stupid" code. However, as other as have pointed out in comments, there is a known issue with booting from USB (if that is what you're doing) - it appears the USB boot process forces the first bit of the boot sector to hold certain disk property information (the BIOS parameter block, or BPB). That means you'll have to move your code out of the way so that it can run properly.

This answer provides all the gory details and, in terms of working around that, you could probably just shift your code to the end, something like:

    [bits 16]
    [org 0x7c00]
    jmp init
    times 510 - ($ - $$) - (last - init) db 0 ; leave as much space as possible.
init:
    jmp $ ; your actual code should go here.
last:
    dw 0xaa55

This way, you end up with enough space for USB booting to put the BPB (assuming your code isn't too long):

     1                    [bits 16]
     2                    [org 0x7c00]
     3 0000 E9F901        jmp init
     4 0003 00<rept>      times 510 - ($ - $$) - (last - init) db 0
     5                init:
     6 01FC EBFE          jmp $
     7                last:
     8 01FE 55AA          dw 0xaa55

Of course, you may want to consider just putting your own BPB in there, of the correct format. That would render unnecessary the trickiness of moving your code to the end of the boot sector.

paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
  • 3
    I'm pretty certain that when the OP finally responds to the comments that we will discover this is a USB FDD booting related issue. We have had a number of these kind of question in the past. https://stackoverflow.com/questions/47277702/custom-bootloader-booted-via-usb-drive-produces-incorrect-output-on-some-compute/47320115#47320115 . The BIOS doing USB FDD booting likely overwrote part of the OPs code with drive geometry and caused the resulting instructions to do something unexpected. The drive geometry is often blindly written into an area the BIOS thinks is the BIOS Parameter Block (BPB) – Michael Petch Nov 08 '20 at 01:42
  • That was it. Thanks a ton, Michael. – Ethan James Nov 08 '20 at 01:57
  • I was the upvote, and I'll mark this as a duplicate now – Michael Petch Nov 08 '20 at 02:03
  • Your last example fails with "error: non-constant argument supplied to TIMES". Instead you should do `jmp strict short init` \ `nop` \ `times 5Ah - ($ - $$) db 0` \ `init:` (5Ah is the offset [behind a FAT32 EBPB + BPBN](https://hg.ulukai.org/ecm/ldosboot/file/bfce2128643e/iniload.asm#l59)). – ecm Nov 08 '20 at 02:45
  • 1
    @ecm, I wasn't seeing that error but I *was* getting the wrong offset for the `aa55` marker since I didn't take into account the `jmp` instruction. The update has a more correct version and I've tested it with a real `nasm` to make sure. – paxdiablo Nov 08 '20 at 08:17
  • @paxdiablo: Okay, you're right, this works. Only problem is if your code grows too large so the padding does not cover the entire BPB area anymore then it will silently result in less padding, instead of failing. – ecm Nov 08 '20 at 09:42
  • 2
    @ecm: yes, which is why I suggested just putting a BPB in :-) – paxdiablo Nov 08 '20 at 17:00
  • @MichaelPetch: Curious. I put LILO on a USB floppy before. – Joshua Nov 08 '20 at 19:18