3

I'm getting this error from:

loop    AdderLoop

error A2075: jump destination too far : by 25 byte(s)

This is an adder program.

I'm still learning pop and push so maybe I'm not doing something right, but it seems like my variable ARRAY_SIZE isn't being stored correctly?

I push the register where ARRAY_SIZE is stored at the beginning of the procedure and pop it back at the end of the procedure.

.386      ;identifies minimum CPU for this program

.MODEL flat,stdcall    ;flat - protected mode program
                       ;stdcall - enables calling of MS_windows programs

;allocate memory for stack
;(default stack size for 32 bit implementation is 1MB without .STACK directive 
;  - default works for most situations)

.STACK 4096            ;allocate 4096 bytes (1000h) for stack

;*******************MACROS********************************

;mPrtStr
;usage: mPrtStr nameOfString
;ie to display a 0 terminated string named message say:
;mPrtStr message

;Macro definition of mPrtStr. Wherever mPrtStr appears in the code
;it will  be replaced with 

mPrtStr  MACRO  arg1    ;arg1 is replaced by the name of string to be displayed
         push edx
         mov edx, offset arg1    ;address of str to display should be in dx
         call WriteString        ;display 0 terminated string
         pop edx
ENDM

;*************************PROTOTYPES*****************************

ExitProcess PROTO,
    dwExitCode:DWORD    ;from Win32 api not Irvine to exit to dos with exit code

ReadChar PROTO          ;Irvine code for getting a single char from keyboard
                        ;Character is stored in the al register.
                        ;Can be used to pause program execution until key is hit.

WriteDec PROTO          ;Irvine code to write number stored in eax
                        ;to console in decimal

WriteString PROTO       ;Irvine code to write null-terminated string to output
                        ;EDX points to string

WriteChar PROTO         ;write the character in al to the console

;************************  Constants  ***************************

    LF         equ     0Ah                   ; ASCII Line Feed

;************************DATA SEGMENT***************************

.data
    carryIn    byte 0,0,0,0,1,1,1,1
    inputA     byte 0,0,1,1,0,0,1,1
    inputB     byte 0,1,0,1,0,1,0,1
    ARRAY_SIZE equ $ - inputB  

    ;The '$' acts as a place maker where you are currently in memory
    ;which at the end of the carryInNum array.
    ;The ending address of the carryInNum array minus the beginning
    ;address equals the total bytes of the carryInNum array
    ;which is stored in the ARRAY_SIZE constant.
    ;NOTE: there can be no other variables between the 
    ;declation of the ARRAY_SIZE constant and the declaration
    ;of the array you are trying to calculate the size of.

    ;You can add LFs to the strings below for proper output line spacing
    ;but do not change anything between the quotes "do not change".

    ;I will be using a comparison program to compare your output to mine and
    ;the spacing must match exactly.

    endingMsg           byte "Hit any key to exit!",0

    ;Change my name to your name
    titleMsg            byte "Program 4 by ",LF,0

    testingAdderMsg     byte " Testing Adder",0

    inputA_Msg           byte "   Input A: ",0
    inputB_Msg           byte "   Input B: ",0
    carryin_Msg          byte "  Carry in: ",0
    sum                  byte "       Sum: ",0
    carryout             byte " Carry Out: ",0

    dashes              byte LF," ------------",LF,0

;************************CODE SEGMENT****************************

.code

main PROC

    mov ecx, ARRAY_SIZE         ;ecx = ARRAY_SIZE
    mov esi, 0                  ;esi = 0

    mPrtStr titleMsg            ;print "Program 4 by"

AdderLoop:
    mPrtStr inputA_Msg          ;print "   Input A: "
    movzx   eax, inputA[esi]    ;eax = inputA[esi]
    INVOKE  WriteDec            ;write decimal stored in eax
    mov     al, LF              ;al = ASCII Line Feed
    INVOKE  WriteChar           ;write the character stored in al

    mPrtStr inputB_Msg          ;print "   Input B: "
    movzx   eax, inputB[esi]    ;eax = inputB[esi]
    INVOKE  WriteDec            ;write decimal stored in eax
    mov     al, LF              ;al = ASCII Line Feed
    INVOKE  WriteChar           ;write the character stored in al

    mPrtStr carryin_Msg         ;print "  Carry in: "
    movzx   eax, carryIn[esi]   ;eax = carryIn[esi]
    INVOKE  WriteDec            ;write decimal stored in eax
    mov     al, LF              ;al = ASCII Line Feed
    INVOKE  WriteChar           ;write the character stored in al

    call    adder               ;call the adder procedure

    mPrtStr dashes              ;print " ------------"

    mPrtStr sum                 ;print "       Sum: "
    mov     al, LF              ;al = ASCII Line Feed
    INVOKE  WriteChar           ;write the character stored in al

    mPrtStr carryout            ;print " Carry Out: "
    mov     al, LF              ;al = ASCII Line Feed
    INVOKE  WriteChar           ;write the character stored in al

    add     esi, 4              ;add 4 to esi to go to next element in the arrays
    loop    AdderLoop           ;ARRAY_SIZE--, if ARRAY_SIZE = 0 then end, else loop

    mPrtStr endingMsg           ;print "Hit any key to exit!"
    call    ReadChar            ;Pause program execution while user inputs a non-displayed char
    INVOKE  ExitProcess,0       ;Exit to dos: like C++ exit(0)

main ENDP

;IMPORTANT: Do not delete the function comment block below.
;           Every function should have a similar comment block
;           specifying what the function is about including:
;           - Description
;           - Input (Entry)
;           - Output (Exit)
;           - Registers used (REGS)

;************** Adder – Simulate a full Adder circuit  
;  Adder will simulate a full Adder circuit that will add together 
;  3 input bits and output a sum bit and a carry bit
;
;    Each input and output represents one bit.
;
;  Note: do not access the arrays in main directly in the Adder function. 
;        The data must be passed into this function via the required registers below.
;
;       ENTRY - EAX = input bit A 
;               EBX = input bit B
;               ECX = Cin (carry in bit)
;       EXIT  - EAX = sum bit
;               ECX = carry out bit
;       REGS  - EAX, EBX, ECX, ESI
;
;       For the inputs in the input columns you should get the 
;       outputs in the output columns below:
;
;        input                  output
;     eax  ebx   ecx   =      eax     ecx
;      A  + B +  Cin   =      Sum     Cout
;      0  + 0 +   0    =       0        0
;      0  + 1 +   0    =       1        0
;      1  + 0 +   0    =       1        0
;      1  + 1 +   0    =       0        1
;      0  + 0 +   1    =       1        0
;      0  + 1 +   1    =       0        1
;      1  + 0 +   1    =       0        1
;      1  + 1 +   1    =       1        1
;
;   Note: the Adder function does not do any output. 
;         All the output is done in the main function.
;
;Do not change the name of the Adder function.
;
;See additional specifications for the Adder function on the 
;class web site.
;
;You should use AND, OR and XOR to simulate the full adder circuit.
;
;You should save any registers whose values change in this function 
;using push and restore them with pop.
;
;The saving of the registers should
;be done at the top of the function and the restoring should be done at
;the bottom of the function.
;
;Note: do not save any registers that return a value (ecx and eax).
;
;Each line of the Adder function must be commented and you must use the 
;usual indentation and formating like in the main function.
;
;Don't forget the "ret" instruction at the end of the function
;
;Do not delete this comment block. Every function should have 
;a comment block before it describing the function. FA17


Adder proc

    push ecx                ;store ARRAY_SIZE for later

    movzx eax, inputA[esi]  ;eax = inputA[esi]
    movzx ebx, inputB[esi]  ;ebx = inputB[esi]
    movzx ecx, carryIn[esi] ;ecx = carryIn[esi]

    push eax                ;store inputA[esi] for later
    xor eax, ebx            ;eax = inputA[esi] XOR inputB[esi]
    push eax                ;store inputA[esi] XOR inputB[esi] for later
    xor eax, ecx            ;eax = (inputA[esi] XOR inputB[esi]) XOR carryIn[esi] (this is the sum)

    pop eax                 ;pop inputA[esi] XOR inputB[esi] into eax
    and ecx, eax            ;eax = (inputA[esi] XOR inputB[esi]) AND carryIn[esi]
    pop eax                 ;pop inputA[esi] into eax
    and eax, ebx            ;eax = inputA[esi] AND inputB[esi]
    or ecx, eax             ;ecx = ((inputA[esi] XOR inputB[esi]) AND carryIn[esi]) OR (inputA[esi] AND inputB[esi]) (this is the carry out)

    pop ecx                 ;pop ARRAY_SIZE into ecx

    ret                     ;return

Adder endp

END main
Fifoernik
  • 9,779
  • 1
  • 21
  • 27
Justin
  • 170
  • 8
  • Which line is line 149? – fuz Sep 20 '17 at 10:01
  • My bad lol, added the line. – Justin Sep 20 '17 at 10:05
  • You should treat `loop` as one of those weird legacy instructions like [`xlatb`](http://felixcloutier.com/x86/XLAT:XLATB.html) or [`aad`](http://felixcloutier.com/x86/AAD.html) that are no longer worth using for anything, especially if you're still learning asm. Never heard of those? My point exactly. See https://stackoverflow.com/questions/35742570/why-is-the-loop-instruction-slow-couldnt-intel-have-implemented-it-efficiently for some historical background on why it's slow. (If `loop` was fast, it would have uses when optimizing, but as a beginner you lose nothing by ignoring it.) – Peter Cordes Sep 20 '17 at 11:53

1 Answers1

5

The problem is that loop can only jump by up to 128 bytes into either direction. It seems that your label is too far away for loop adderLoop to reach. Consider replacing loop with dec ecx and then jnz adderLoop. There is a variant of jnz that can jump much farther, solving this problem. Generally, I recommend to avoid using the loop instruction as it is slower than the dec ecx / jnz pair that can replace it.

fuz
  • 88,405
  • 25
  • 200
  • 352
  • Okay thanks it works now. I saw solutions online saying the same thing but didn't quite understand what the solution meant at first. I thought people were saying that the value you compare in loop can't be bigger than 128 bytes, not the the jump amount. – Justin Sep 20 '17 at 10:42
  • One question, you don't need to use cmp after dec? – Justin Sep 20 '17 at 10:57
  • 1
    @Justin the `dec` will set all flags except CF in the same way as `sub ?, 1` (CF is left intact), so if the zero value is of your interest, the ZF is already corresponding to the new value of `cx` after `dec cx` (`ZF = (cx == 0);`). – Ped7g Sep 20 '17 at 11:00
  • @Justin If `dec` decreases a value such that the result is zero, `ZF` is set. In fact, almost all arithmetic instructions (except for some esoteric ones like `adox`) set `ZF` this way. – fuz Sep 20 '17 at 11:48