The new forums will be named Coin Return (based on the most recent vote)! You can check on the status and timeline of the transition to the new forums here.
The Guiding Principles and New Rules document is now in effect.

More assembly help (Macros!)

clsCorwinclsCorwin Registered User regular
edited April 2007 in Help / Advice Forum
Ok, so I need to finish this assignment tonight before my final, and I can't figure out where my error is.

This is 16-bit x86 assembly. It assembles fines, but during runtime I get divide overflow error, then crash.

Its just supposed to take input for 2 numbers, then find the greatest common denominator between the two.
.MODEL  SMALL

.STACK  100h

.DATA
	crlf		db	13, 10, '$'
	count		db	0
	printSize	db	1
	prompt		db	'Please enter a number: ','$'
	prompt2		db	'Do you want to compute more GCDs? (Y/N)', '$'
	removeBits	db	0Fh
	mult2		db	10
	ASCII1		db	5 DUP(?)
	ASCII2		db	5 DUP(?)
	storage		db	5 DUP(?)

	addBits		dw	0030h
	mult		dw	1
	binaryNum1	dw	0
	binaryNum2	dw	0
	greatestCommon	dw	?

.CODE

MAIN PROC
	mov	ax, @data
	mov	ds, ax
	
	call 	GCD
	mov	ax, 4c00h				;exit with error code 0
	int	21h
MAIN ENDP

GETNUMBER PROC
	mov	si, 0					;set pointer to 0
	mov	count, 0				;reset count

	WHILE1:
		mov	ah, 01h
		int 	21h				;get character input

		cmp	al, 0dh				;test for end of input
		je	CONTINUE			;if end (linefeed) goto CONTINUE
		mov	[bx+si], al			;move the digit to the offset of the address in bx
		inc	count
		inc	si
		jmp	WHILE1

	CONTINUE:
		mov	mult, 1
		mov	cx, 0
		dec count
		mov cl, count
		mov	si, cx

	LOOP1:
		mov ax, 0
		mov dx, 0
		mov	al, [bx+si]
		AND	al, removeBits		;remove ASCII bits
		mul	mult				;multiply by multiplier
		
		add	`dx, ax
		
		mov	ax, mult
		mul	mult2				;multiply multiplier by 10
		mov	mult, ax
		dec	si
	LOOP	LOOP1

	ret
GETNUMBER ENDP

PRINTNUMBER PROC
	cmp	ax, 10000				;compare GCD to 10000
	jb	NOT5					;if smaller, jump to NOT5
	mov	printSize, 5			;else size = 5

	NOT5:
		cmp	ax, 1000			;compare GCD to 1000
		jb	NOT4				;if smaller, jump to NOT4
		mov	printSize, 4		;else size = 4

	NOT4:
		cmp	ax, 100				;compare GCD to 100
		jb	NOT3				;if smaller, jump to NOT3
		mov	printSize, 3		;else size = 3

	NOT3:
		cmp	ax, 10				;compare GCD to 10
		jb	CONTINUE1			;if smaller, jump to CONTINUE1 (size will be 1)
		mov	printSize, 2		;else size = 2

	CONTINUE1:
		mov cx, 0
		mov cl, printSize
		mov	si, cx				;set index to size
		mov	storage+si, '$'		;move $ to end of string being built
		dec	si					;decrement index

	WHILE3:
		cmp	ax, 10				;compare GCD to 10
		jb	CONTINUE3			;if less than 10, continue
		div	mult2				;divide GCD by 10, remain in dx, quo in ax
		OR	dx, addBits			;append ASCII bits
		mov	storage+si, dl		;move the ASCII remainder to storage array
		dec	si					;decrement index
		jmp	WHILE3				;loop

	CONTINUE3:
		OR	ax, addBits			;append ASCII bits to final digit
		mov	storage+si, al		;mov final digit to storage array
		LEA	dx, storage			;load address of storage into dx

		mov	ah, 09h				;set function code
		int	21h					;call interrupt to display the string

	ret
PRINTNUMBER ENDP

GCD PROC
	lea dx, prompt
	mov ah, 09h
	int 21h						;display prompt

	lea	bx, ASCII1				;load ASCII1 to bx
	lea	ax, binaryNum1			;load binNum1 to dx so GETNUMBER can work with them
	call	GETNUMBER

	lea dx, crlf
	mov ah, 09h
	int 21h
	
	lea dx, prompt
	int 21h						;display prompt

	lea	bx, ASCII2				;load ASCII2 to bx
	lea	dx, binaryNum2			;load binNum2 to dx so GETNUMBER can work with them
	call	GETNUMBER

	lea dx, crlf
	mov ah, 09h
	int 21h

	WHILE2:
		mov	ax, binaryNum1
		cmp	ax, binaryNum2
		je	DISPLAY1			;if num1 and num2 are equal, display the gcd
		cmp	ax, binaryNum2
		ja	GREATER1			;if num1 > num2, jump to GREATER1

		mov	ax, binaryNum2		;subtract num1 from num2
		sub	ax, binaryNum1
		mov	binaryNum2, ax		;put val in ax back into num2
		mov	greatestCommon, ax	;set value of gcd
		jmp	WHILE2

	GREATER1:
		mov	ax, binaryNum1		;subtract num2 from num1
		sub	ax, binaryNum2
		mov	binaryNum1, ax		;put val in ax back into num1
		mov	greatestCommon, ax	;set value of gcd
		jmp	WHILE2

	DISPLAY1:
		mov	ax, greatestCommon	;move gcd into ax, so PRINTNUMBER can work with it
		call 	PRINTNUMBER

GCD ENDP

END MAIN

clsCorwin on

Posts

  • clsCorwinclsCorwin Registered User regular
    edited April 2007
    Note: I had this working, and then I lost most of my hard drive. For the life of me, I can't recall what differences I ahd between this and the working version.

    clsCorwin on
  • ecco the dolphinecco the dolphin Registered User regular
    edited April 2007
    Man, it was back in the days when I had MASM 6.xx all set up with the full debugger and everything.

    Now, I'm reduced to using debug.exe =/

    Anyway, one thing that I've found is that you don't have the "ret" at the end of your GCD procedure. Looking at the assembly output, my version of the assembler doesn't put one in automatically.

    That won't solve the logic error, but it will get past the apparent crash. I think. Well, it worked on my machine. Hard to tell with assembly.

    EDIT 1: It looks like you call GETNUMBER having it save the number somewhere.

    lea bx, ASCII1 ;load ASCII1 to bx
    lea ax, binaryNum1 ;load binNum1 to dx so GETNUMBER can work with them
    call GETNUMBER

    lea bx, ASCII2 ;load ASCII2 to bx
    lea dx, binaryNum2 ;load binNum2 to dx so GETNUMBER can work with them
    call GETNUMBER

    So bx is the string buffer, and ax (or dx?) is the address to save it to. Either way, two things:

    1.) Typo - lea ax, or lea dx? Your comments seem to indicate dx, so lea ax, binaryNum1 is wrong?
    2.) I'm not seeing where in GETNUMBER you use the value of dx passed in to save the number.

    edit 2: You've got an off by one error in GETNUMBER

    You go:
    mov cx, 0
    dec count
    mov cl, count

    And then run the loop. However, loop decrements first, and only loops if the value is non-zero. So say you had two characters typed in ("95"), then count == 2. You decremented count, so it now equals 1. So you go into your loop, and it hits the

    LOOP LOOP1

    and decrements cx. cx is now equal to 0, so it stops looping - only after processing one character.

    Another bug:

    You use dx as your result, but at the start of every loop, you set it to 0. In C, this might be better written as:

    int nDX;

    while(someCondition)
    {
    nDX = 0;

    ...

    nDX += somethingOrRather;
    }

    while what you really want is:

    int nDX;

    nDX = 0;
    while(someCondition)
    {

    ...

    nDX += somethingOrRather;
    }

    EDIT 3: Your PRINTNUMBER has quite a few bugs... tell you what, I'll just post my version with a few comments below.

    By the way, the divide overflow was caused by not clearing out the dx register prior to using the div instruction.
    .MODEL  SMALL
    .386
    .STACK  100h
    
    .DATA
            crlf                db        13, 10, '$'
            count                db        0
            printSize        db        1
            prompt                db        'Please enter a number: ','$'
            prompt2                db        'Do you want to compute more GCDs? (Y/N)', '$'
            removeBits        db        0Fh
    ; EECC: Introduced mult3 because mult2 has to be byte sized for GETNUMBER, but PRINTNUMBER wants dx:ax results
    ; which mean a word sized operand is required.
    	mult3			dw	10
            mult2                db        10
            ASCII1                db        5 DUP(?)
            ASCII2                db        5 DUP(?)
            storage                db        5 DUP(?)
    
            addBits                dw        0030h
    ; EECC: Made byte size so that the mul mult instruction would use al * mult = ah:al and not overwrite dx in GETNUMBER
            mult                db        1
            binaryNum1        dw        0
            binaryNum2        dw        0
            greatestCommon        dw        ?
    
    .CODE
    
    
    
    MAIN PROC
            mov        ax, @data
            mov        ds, ax
           
            call        GCD
            mov        ax, 4c00h                                ;exit with error code 0
            int        21h
    MAIN ENDP
    
    GETNUMBER PROC
            mov        si, 0                                        ;set pointer to 0
            mov        count, 0                                ;reset count
    
    ; EECC: Save the address of the byte to save to on to the stack
    	push dx
    
            WHILE1:
                    mov        ah, 01h
                    int        21h                                ;get character input
    
                    cmp        al, 0dh                                ;test for end of input
                    je        CONTINUE                        ;if end (linefeed) goto CONTINUE
                    mov        [bx+si], al                        ;move the digit to the offset of the address in bx
                    inc        count
                    inc        si
                    jmp        WHILE1
    
            CONTINUE:
                    mov        mult, 1
                    mov        cx, 0
    ; EECC: cx loop counter loops the same number of times as the value in cx, not cx+1
    ;                dec count
                    mov cl, count
                    mov        si, cx
    ; EECC: Added so to compensate for removal of dec count (string manipulation is 0 offset)
    		dec si
    
    ; EECC: Moved out of the loop to reset the result accumulator
                    mov dx, 0
            LOOP1:
                    mov ax, 0
                    mov        al, [bx+si]
                    AND        al, removeBits                ;remove ASCII bits
                    mul        mult                                ;multiply by multiplier
                   
                    add        dx, ax
                   
                    mov        al, mult
                    mul        mult2                                ;multiply multiplier by 10
                    mov        mult, al
                    dec        si
            LOOP        LOOP1
    
    ; EECC: Cheap hack to save the result somewhere
    	pop bx
    	mov [bx], dx
    
            ret
    GETNUMBER ENDP
    
    PRINTNUMBER PROC
    ; EECC: Initialise printSize explicitly (although it is initialised implicitly above)
    	mov	printSize, 1
    
            cmp        ax, 10000                                ;compare GCD to 10000
            jb        NOT5                                        ;if smaller, jump to NOT5
            mov        printSize, 5                        ;else size = 5
    
    ; EECC: Added otherwise if ax > 10000 then printSize would get set to ... 2, I think.
    	jmp CONTINUE1
    
            NOT5:
                    cmp        ax, 1000                        ;compare GCD to 1000
                    jb        NOT4                                ;if smaller, jump to NOT4
                    mov        printSize, 4                ;else size = 4
    
    ; EECC: Added otherwise if ax > 1000 then printSize would get set to 2
    	jmp CONTINUE1
    
            NOT4:
                    cmp        ax, 100                                ;compare GCD to 100
                    jb        NOT3                                ;if smaller, jump to NOT3
                    mov        printSize, 3                ;else size = 3
    
    ; EECC: Added otherwise if ax > 100 then printSize would get set to 2
    	jmp CONTINUE1
    
            NOT3:
                    cmp        ax, 10                                ;compare GCD to 10
                    jb        CONTINUE1                        ;if smaller, jump to CONTINUE1 (size will be 1)
                    mov        printSize, 2                ;else size = 2
    
            CONTINUE1:
                    mov cx, 0
                    mov cl, printSize
                    mov        si, cx                                ;set index to size
                    mov        [storage+si], '$'                ;move $ to end of string being built
                    dec        si                                        ;decrement index
    
            WHILE3:
                    cmp        ax, 10                                ;compare GCD to 10
                    jb        CONTINUE3                        ;if less than 10, continue
    
    ; EECC: If using word length division, then it's dx:ax/argument, so in this case, we need to make
    ; sure that dx is zero
    		xor	dx, dx
    
                    div        mult3                                ;divide GCD by 10, remain in dx, quo in ax
                    OR        dx, addBits                        ;append ASCII bits
                    mov        [storage+si], dl                ;move the ASCII remainder to storage array
                    dec        si                                        ;decrement index
                    jmp        WHILE3                                ;loop
    
            CONTINUE3:
                    OR        ax, addBits                        ;append ASCII bits to final digit
                    mov        [storage+si], al                ;mov final digit to storage array
                    LEA        dx, storage                        ;load address of storage into dx
    
                    mov        ah, 09h                                ;set function code
                    int        21h                                        ;call interrupt to display the string
    
            ret
    PRINTNUMBER ENDP
    
    GCD PROC
            lea dx, prompt
            mov ah, 09h
            int 21h                                                ;display prompt
    
            lea        bx, ASCII1                                ;load ASCII1 to bx
            lea        dx, binaryNum1                        ;load binNum1 to dx so GETNUMBER can work with them
            call        GETNUMBER
    
            lea dx, crlf
            mov ah, 09h
            int 21h
           
            lea dx, prompt
            int 21h                                                ;display prompt
    
            lea        bx, ASCII2                                ;load ASCII2 to bx
            lea        dx, binaryNum2                        ;load binNum2 to dx so GETNUMBER can work with them
            call        GETNUMBER
    
            lea dx, crlf
            mov ah, 09h
            int 21h
    
            WHILE2:
                    mov        ax, binaryNum1
                    cmp        ax, binaryNum2
                    je        DISPLAY1                        ;if num1 and num2 are equal, display the gcd
                    cmp        ax, binaryNum2
                    ja        GREATER1                        ;if num1 > num2, jump to GREATER1
    
                    mov        ax, binaryNum2                ;subtract num1 from num2
                    sub        ax, binaryNum1
                    mov        binaryNum2, ax                ;put val in ax back into num2
                    mov        greatestCommon, ax        ;set value of gcd
                    jmp        WHILE2
    
            GREATER1:
                    mov        ax, binaryNum1                ;subtract num2 from num1
                    sub        ax, binaryNum2
                    mov        binaryNum1, ax                ;put val in ax back into num1
                    mov        greatestCommon, ax        ;set value of gcd
                    jmp        WHILE2
    
            DISPLAY1:
                    mov        ax, greatestCommon        ;move gcd into ax, so PRINTNUMBER can work with it
                    call        PRINTNUMBER
    
    ; EECC: Don't forget to return from the function!
    	ret
    GCD ENDP
    
    END MAIN
    

    ecco the dolphin on
    Penny Arcade Developers at PADev.net.
  • clsCorwinclsCorwin Registered User regular
    edited April 2007
    Thanks eecc.

    New question for you, regarding macros:
    INPUT MACRO addr, prompt
    	lea	bx, addr		;load addr into bx
    	mov	cx, 2			;set counter to 2
    	mov	si, 0			;set pointer to 0
    	
    	lea	dx, prompt
    	mov	ah, 09h
    	int	21h			;display prompt
    	
    INLOOP:
    	mov	ah, 01h
    	int	21h			;get char from keyboard
    	mov	bx + si, al		;put char into bx
    	inc	si
    	loop	INLOOP
    	
    ENDM
    

    Thats my macro code, and it seems fine to me. I get these errors upon assemble...
    **Error** lab8.asm(56) INPUT(12) Too many registers in expression
    **Error** lab8.asm(57) INPUT(9) Symbol already defined elsewhere: INLOOP
    **Error** lab8.asm(57) INPUT(12) Too many registers in expression

    Also, googling just gives me a bunch of .ru sites, which... yea...

    clsCorwin on
  • clsCorwinclsCorwin Registered User regular
    edited April 2007
    The rest of the code for this project is here...
    lab8.asm
    ;Programmer's Name: 	Lucas Laws
    ;Course: 		CMPSC 241
    ;Due Date:		April 28, 2007
    ;Problem Description	Macros
    ;
    ;Data Needed		
    ;
    ;Algorithm Description (Pseudocode)
    ;	Main
    ;Step 1 - get input (base num and exp num)
    ;Step 2 - convert numbers from ASCII to binary
    ;Step 3 - compute base raised to the exp power
    ;Step 4 - convert answer to ASCII
    ;Step 5 - print answer
    ;
    ;	GETSIZE
    ;Step 1- compare numbers to 10000, 1000, 100, and 10 to determine the number of digits
    ;Step 2- put answer into bx
    ;
    ;	GETNUM
    ;Step 1 - remove ASCII bits from base
    ;Step 2 - remove ASCII bits from exp
    ;
    ;	POWER
    ;Step 1 - set counter to exp
    ;Step 1 - multiply answer by base
    ;Step 1 - loop exp times
    
    INCLUDE input
    
    .MODEL  SMALL
    
    .STACK  100h
    
    .DATA
    	basePrompt	db	'Please enter a number for the base: $'
    	expPrompt	db	'Please enter a number to raise the base to: $'
    	base		db	?
    	exp		db	?
    	binBase		db	?
    	binExp		db	?
    	baseSize	db	?
    	expSize		db	?
    	answerSize	db	?
    	answer		dw	1
    	divider		db	10
    	ASCIIanswer	dw	?
    	cheat		db	'$'
    
    .CODE
    	EXTRN GETNUMBER:far, PUTNUMBER:far, POWER:far
    
    MAIN PROC
    	mov	ax, @data
    	mov	ds, ax
    	
    	INPUT	base, basePrompt	;get base number
    	INPUT	exp, expPrompt		;get exponent
    	call	GETNUMBER		;convert numbers to binary
    	
    	mov	ax, 0
    	mov	al, binBase
    	call	GETSIZE			;get number of digits in base
    	;mov	baseSize, bl		;set base size
    		
    	mov	ax, 0
    	mov	al, binExp
    	call	GETSIZE			;get number of digits in exp
    	;mov	expSize, bx		;set exp size	
    	
    	call	POWER			;raise base to exp power
    	
    	mov	ax, answer
    	call	GETSIZE			;get number of digits in answer
    	mov	bx, 0
    	mov	answerSize, bl		;set answer size
    	
    	call	PUTNUMBER		;convert answer to ascii for printing
    	call	PRINT			;print answer
    	
    	mov	ax, 4c00h
    	int	21h			;terminate program
    	
    MAIN ENDP
    
    GETSIZE PROC
    	cmp	ax, 10000		;compare num to 10000
    	jb	NOT5			;if smaller, jump to NOT5
    	mov	bx, 5			;else size = 5
    
    	NOT5:
    	cmp	ax, 1000		;compare num to 1000
    	jb	NOT4			;if smaller, jump to NOT4
    	mov	bx, 4			;else size = 4
    
    	NOT4:
    	cmp	ax, 100			;compare num to 100
    	jb	NOT3			;if smaller, jump to NOT3
    	mov	bx, 3			;else size = 3
    
    	NOT3:
    	cmp	ax, 10			;compare num to 10
    	jb	CONTINUE1		;if smaller, size = 1
    	mov	bx, 2			;else size = 2
    	
    	CONTINUE1:
    	mov	bx, 1			;size = 1
    	
    	ret
    GETSIZE ENDP
    
    PRINT PROC
    	mov	ax, ASCIIanswer
    	mov	ah, 09h
    	int	21h			;print answer
    
    PRINT ENDP
    
    END MAIN
    

    exponent.asm
    .model small
    
    EXTRN base:byte, exp:byte, answer:word
    
    PUBLIC POWER
    
    .code
    
    POWER PROC far
    	mov	cx, 0
    	mov	cl, exp			;set loop counter
    	
    	EXPLOOP:
    	mov	ax, answer
    	mov	bx, 0
    	mov	bl, base		
    	mul	bx			;multiply answer (ax) by base (bx)
    	LOOP	EXPLOOP
    
    	ret
    POWER ENDP
    END
    

    getnum.asm
    ;convert to binary
    
    .model small
    
    EXTRN base:byte, exp:byte, binBase:byte, binExp:byte
    
    PUBLIC GETNUMBER
    
    .code
    GETNUMBER PROC far
    	mov	ah, base
    	mov	binBase, ah
    	and	binBase, 03h		;strip ASCII bits
    	
    	mov	ah, exp
    	mov	binExp, ah
    	and	binExp, 03h		;strip ASCII bits
    	ret	
    GETNUMBER ENDP
    END
    
    
    

    putnum.asm
    ;convert to ASCII
    
    .model small
    
    EXTRN answer:word, ASCIIanswer:word, answerSize:byte, divider:byte
    
    PUBLIC PUTNUMBER
    
    .code
    
    PUTNUMBER PROC far
    	mov	cx, 0
    	mov	cl, answerSize
    	mov	si, cx
    	dec	si
    	mov	ax, answer
    	
    ;AGAIN1:
    ;	cmp	ax, cx
    ;	jb	FINISH
    ;	xor	dx, dx
    ;	div	cx
    ;	or	dl, 30h
    ;;	mov	[si], dl
    ;	dec	si
    ;	jmp	AGAIN1
    	
    AGAIN1:
    	cmp        ax, 10                                ;compare answer to 10
    	jb         FINISH                                ;if less than 10, continue
    	xor        dx, dx				 ;zero out dx reg
    	div        divider                               ;divide answer by 10, remainder in dx, quo in ax
    	OR         dx, 0030h                             ;append ASCII bits
    	mov        [ASCIIanswer+si], dx                  ;move the ASCII remainder to storage array
    	dec        si                                    ;decrement index
    	jmp        AGAIN1                                ;loop
    		
    FINISH:
    	or	al, 30h
    	mov	[ASCIIanswer+si], ax
    	
    	ret
    	
    PUTNUMBER ENDP
    END
    

    clsCorwin on
Sign In or Register to comment.