0

This is a bizarre situation I'm in. I've tried writing this code to find prime numbers by trial division, in 8086 Assembly. It doesn't work, but even stranger is that when I was trying to debug it, I noticed that making the code any longer would cause the CPU to crash. Even adding a NOP! I'm at a loss for why that is.

The code:

isPrime:
    ;input: AX = the number you wish to test (u16)
    ;output:
    ;   carry clear = number is prime
    ;   carry set = number is not prime
    push ax
    push bx
    push cx
    push dx
        mov cx,ax
        inc cx  
        ;if the "loops" will fallthru carry will be clear already
        loop $+5
            ; clc
            jmp wasPrime    ;if AX == 0 return CARRY CLEAR
        loop $+5
            ; clc
            jmp wasPrime    ;else if AX == 1 return CARRY CLEAR
        loop $+5
            ; clc
            jmp wasPrime    ;else if AX == 2 return CARRY CLEAR
            
        ;else
        mov bx,3
        mov cx,ax
        
loop_isPrime:
        mov ax,cx
        cmp ax,bx       ;if we got so far that they're equal, it was prime
        je wasPrime     ;equal implies no carry
        
        div bx          ;remainder is in dx, quotient in ax. bx unchanged
        
        test dx,dx
        jz notPrime     ;if divides w/o remainder, it wasn't prime(we won't get to equality this way)
        inc bx          ;next bx
        jmp loop_isPrime
        
notPrime:
        nop             ;somehow adding anything here (even a nop) makes the program softlock!
        stc             ;not prime - set carry
wasPrime:
    pop dx
    pop cx
    pop bx
    pop ax
    ret
puppydrum64
  • 1,598
  • 2
  • 15
  • 1
    The `loop $+5` doesn't look right. When I assemble this, the `jmp wasPrime` are short jumps, 2 bytes each, and the `loop` itself is another 2 bytes, so `$+5` puts you in the middle of the next `loop` instruction, which could cause all sorts of unpredictable behavior. Unless you are sure for some reason that your assembler is (unnecessarily) using near jumps? – Nate Eldredge Dec 10 '21 at 03:49
  • 2
    Anyway, if you insist on this odd `loop` hack, why not use labels like a normal programmer? But the `loop` stuff seems much more complicated and less efficient than a simple `cmp ax, 2` and `jbe wasPrime`, even if you do have to include code to clear the carry. (Or if you insist, you could `mov dx, 2`, `cmp dx, ax`, `jae wasPrime` in which case the carry will be clear whenever the jump is taken. Or `cmp ax, 3`, `cmc`, `jnc wasPrime`. ) – Nate Eldredge Dec 10 '21 at 03:54
  • 1
    Oh, also, remember that `div bx` takes the 32-bit dividend in `dx:ax`. You don't seem to be initializing `dx` so you are dividing some potentially large garbage number, which may give you the wrong answer, or else overflow causing an exception. Most likely you want `xor dx, dx` prior to `div bx`. – Nate Eldredge Dec 10 '21 at 03:59
  • (To nitpick, mathematically 0 and 1 are not prime, but it's your program so up to you to define what happens in that case.) – Nate Eldredge Dec 10 '21 at 04:02
  • What do you see when you single-step the code containing the NOP? What loop is it stuck in? Are you sure it's not just different input values leading to `div` producing a non-zero DX that makes future remainders non-zero, i.e. a side effect of missing `xor dx,dx`? I'd guess that's most likely. – Peter Cordes Dec 10 '21 at 04:43
  • I didn't use labels because I don't know how to do local labels and I didn't want to "pollute the namespace" so to speak. Also this is DOSBox and I don't have a debugger. I think I see what the problem is now, I forgot that ```div bx``` was 32-bit. That seemed to fix everything. As for short jumps, yes, I did a hexdump of the bytecode and confirmed that the jumps are indeed short when I type ```jmp``` – puppydrum64 Dec 10 '21 at 05:17
  • 1
    I believe Dosbox has a debugger built in, see https://www.vogons.org/viewtopic.php?t=3944. If all else fails you can also use classic debug.com, like the [FreeDOS version](https://www.ibiblio.org/pub/micro/pc-stuff/freedos/files/distributions/1.2/repos/pkg-html/debug.html). – Nate Eldredge Dec 10 '21 at 05:31
  • 1
    If they really are short jumps (opcode EB) then `$+5` is wrong, it should be `$+4`. But my advice is to just go ahead and use labels. Knowing the size of your instructions and computing addresses is what the assembler is for; let it do its job. By the time you write a program large enough for namespace pollution to be a factor, you'll have figured out local labels. (Though realistically, very few people these days would be writing a program that large in pure assembly anyway.) – Nate Eldredge Dec 10 '21 at 05:34
  • Trying to learn asm without a debugger is a waste of your own and everyone else's time. There are DOS debuggers, or you could test this function in 32-bit code under a normal OS instead of messing around with obsolete DOS stuff. – Peter Cordes Dec 10 '21 at 13:02
  • @NateEldredge I've looked everywhere in the documentation and can't find anything on how to use local labels for jumps. They exist for memory storage but I see nothing with regards to jumps. Not even a single example. This is UASM by the way – puppydrum64 Dec 12 '21 at 05:48

0 Answers0