0

Performing a division by 10, I get a "floating point exception". when stepping through the code in GDB, I find that ax = 23 and bx = 10 when dividing.

.686                                    ;
                                        ;
BSS SEGMENT                             ;
    BUF DB 0                            ;
BSS ENDS                                ;
                                        ;
DATA SEGMENT                            ;
    ASSUME DS:BSS                       ;
                                        ;
PUBLIC _start                           ;
_start PROC                             ;
    MOV cx, 230                         ;
    CALL prN                            ;
                                        ;
...
_start ENDP                             ;
                                        ;
;---------------------------------------;
; prNum                                 ;
; TOFIX: numbers are printed backwards  ;
;---------------------------------------;
; CX = number to print                  ;
;---------------------------------------;
prN PROC                                ;
    MOV ax, cx                          ; prepare first loop
    MOV bx, 10                          ; !! BX = 10
@@loop:                                 ;
    ;- PROBLEMATIC LINE vvvvvvvvvvvvvvvv;
    DIV bx                              ; Get rightmost decimal digit
    ;- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^;
    ADD dl, '0'                         ; convert to ASCII-numeral
    MOV BYTE PTR [BUF], dl              ;
    PUSHA                               ; Not ideal, this is a playground
    ;- print byte buffer                ;
...
    ;--                                 ;
    POPA                                ;
    OR ax, ax                           ; Is cx = 0?
    JNZ @@loop                          ; NO: loop for the next decimal part
    RET                                 ; YES: leave loop
prN ENDP                                ;
DATA ENDS
END

Example runthrough of Loop (as I imagine it):

MOV ax, cx ; ax = 230
MOV bx, 10 ; bx = 10
           ;
(@@loop)   ;
DIX bx     ; ax = 23 - dx = 0
;- print   ; STDOUT = 0
OR ax, ax  ; ZF = 0
JNZ @@loop ; continue looping with AX = 23

DIV bx     ; AX = 2 - DX = 3 --> THIS is where the SIGFPE occurs
;- print   ; STDOUT = 03
OR ax, ax  ; ZF = 0
JNZ @@loop ; continue looping with AX = 2
           ;
DIX bx     ; AX = 0 - DX = 2
;- print   ; STOUD = 032 (230 but backwards)
OR ax, ax  ; ZF = 1
JNZ @@loop ; not looping
RET        ; all done, we are returning

Since I am dividing 23 by 10, I expect this operation to be problem-free. In particular:

  • No division by zero
  • No overflow
  • No underflow

I expect

AX = 2
DX = 3
BX = 10
Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Maximilian Wittmer
  • 173
  • 1
  • 3
  • 14
  • Just curious, but how are you using [masm] on an OS that has a [sigfpe]? (Linux, MacOS, *BSD, etc. But not Windows, it doesn't have POSIX signals.) Are you maybe using JWASM to build MASM syntax into Linux executables? Or are you using Windows or DOS, but mis-tagged [sigfpe] instead of [floating-point-exception] or something? I forget if any non-POSIX OSes report integer arithmetic exceptions like `#DE` as floating-point exceptions – Peter Cordes Jan 13 '23 at 06:57
  • 1
    Also curious why you're using 16-bit registers for a Unix-like OS; the mainstream ones all use 32 or 64-bit mode so the normal thing is 32-bit integer registers; using 16-bit registers needs larger machine code (prefixes) and is slower. And BTW, `or ax,ax` is an obsolete idiom that comes from 8080. `test ax,ax` does the same thing but is more efficient. [Test whether a register is zero with CMP reg,0 vs OR reg,reg?](https://stackoverflow.com/a/33724806) – Peter Cordes Jan 13 '23 at 06:59
  • Oh I see UASM in the title, so it is derived from JWASM. Yeah, the [masm] tag is appropriate I guess, JWASM is essentially just MASM syntax, although there is a [jwasm] tag that we should use. – Peter Cordes Jan 13 '23 at 07:08
  • This code is almost a straight copy from my bootloader - it acted crazy and I wanted to dissect it in a clean environment. – Maximilian Wittmer Jan 13 '23 at 11:36
  • 1
    Ok that makes sense, yeah assembling 16-bit code for 32-bit mode usually Just Works after changing pointers to 32-bit. Except stuff like `push 123` defaulting to a dword push instead of word. Bochs has a built-in debugger that lets you single-step a legacy BIOS MBR bootloader (or presumably a modern EFI bootloader), but playing around with stuff in a user-space process is easier. – Peter Cordes Jan 13 '23 at 11:47
  • I just checked test - https://c9x.me/x86/html/file_module_x86_id_315.html It does NOT do the same as `or ax, ax`, it performs an `and ax, ax` which is NOT the behaviour I want, I'd need to `test ax, 0FFFFH`, which encodes in 3 bytes instead of 2. Also, I doubt that there is a speed difference, the stackoverflow-page you linked for this is not relevant (CMP vs OR vs TEST) – Maximilian Wittmer Jan 13 '23 at 12:35
  • 1
    They differ if used with different registers, but `ax` = `ax & ax` = `ax | ax`; look at the truth-table for AND and OR when both values are the same if you're not convinced. They both set FLAGS according to the value in AX, the same way `cmp ax,0` does. Read the linked Q&A again, it explains why there's a speed difference: extra latency in the critical path for later uses of `ax` on modern out-of-order exec CPUs. – Peter Cordes Jan 13 '23 at 12:44
  • You can also use my bootable 86-DOS debugger, install it into a disk image then load your program from within the debugger and trace or examine it that way. Downloads and manual at https://pushbx.org/ecm/web/#projects-ldebug – ecm Jan 13 '23 at 16:05

1 Answers1

2

Oversight on my part. The 16-bit DIV operation takes in DX:AX.
With DX being 0x0030 in the first pass, the number I was dividing seemed not to be valid anymore:

DX = 0x0030 (48 in decimal)
AX = 0x0017 (23 in decimal)

0x00300017 divided by 10 is bigger than 0xFFFF.

if(Source == 0) Exception(DE); //divide error

if(OperandSize == 8) { //word/byte operation
  Temporary = AX / Source;
  if(Temporary > 0xFF) Exception(DE); //divide error
  else {
      AL = Temporary;
      AH = AX % Source;
  }
}
else if(OperandSize == 16) { //doubleword/word operation
  Temporary = DX:AX / Source;
  if(Temporary > 0xFFFF) Exception(DE); //divide error
  else {
      AX = Temporary;
      DX = DX:AX % Source;
  }
}
else { //quadword/doubleword operation
  Temporary = EDX:EAX / Source;
  if(Temporary > 0xFFFFFFFF) Exception(DE); //divide error
  else {
      EAX = Temporary;
      EDX = EDX:EAX % Source;
  }
}

https://c9x.me/x86/html/file_module_x86_id_72.html

Sep Roland
  • 33,889
  • 7
  • 43
  • 76
Maximilian Wittmer
  • 173
  • 1
  • 3
  • 14