News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

help with procedures and runtime errors

Started by musikgoat, April 02, 2007, 11:56:03 PM

Previous topic - Next topic

musikgoat

Hello all  (first post, be kind)

I have a homework assignment in my asm lang class that has the following requirements.   

To explain, I can correctly assemble and link the prog, but the current code will crash at runtime, and I'm not able to get the reason why, when I try to run windbg.

Can someone help me debug this program, as I've spent hours on it still getting to the same place.

p.s.  If there is any of my input that one needs, let me know.  I will add a zip of the .lst file and the .asm file.


; CS-301 - Programming Assignment 4
; Author:  Tim Potter
; Date Due: 4/3/07

; You need to write 3 procedures: GetPrimeNums, IsPrimeOrNot, and WritePrimeNums. 
; This program must calculate 30 prime numbers based off of user input of the starting number

.386
.MODEL FLAT

ExitProcess PROTO NEAR32 stdcall, dwExitCode:DWORD

GetStdHandle PROTO NEAR32 stdcall,
nStdHandle:DWORD

WriteFile PROTO NEAR32 stdcall,
hFile:DWORD, lpBuffer:NEAR32, nNumberOfCharsToWrite:DWORD,
lpNumberOfBytesWritten:NEAR32, lpOverlapped:NEAR32

CreateFileA PROTO NEAR32 stdcall,
lpFileName:NEAR32, access:DWORD, shareMode:DWORD,
lpSecurity:NEAR32, creation:DWORD, attributes:DWORD, copyHandle:DWORD

CloseHandle PROTO NEAR32 stdcall,
fHandle:DWORD

INCLUDE io.h

STD_OUTPUT EQU -11
GENERIC_WRITE EQU 40000000h
CREATE_ALWAYS EQU 2
cr EQU 0dh
lf EQU 0ah

.STACK 4096

.DATA

AR1 WORD 30 DUP (?)
AR2 WORD 30 DUP (?)
num WORD ?
oput BYTE 6 DUP (?)," ",cr,lf
strIn BYTE ?
fName BYTE "prg4out.txt",0
written DWORD ?
hStdOut DWORD ?
hFile DWORD ?

nextLn BYTE cr,lf
lineOut BYTE "Welcome to the Prime # Generator", cr,lf,lf,lf
BYTE "This program will generate the next 30",cr,lf
BYTE " prime numbers after the requested value",cr,lf,lf
prompt BYTE "Please enter a positive 4 digit integer: ",cr,lf,lf,lf
fileOut BYTE "Generated Prime Numbers after "
endOut BYTE "All numbers generated... check output file",cr,lf,lf
BYTE "Bye ...",cr,lf




.CODE
IsPrimeOrNot PROC NEAR32
; BX is coming with a number and this proc checks if this number is prime or not. If yes, BX returns unchanged, else BX returns 0.
; BE SURE TO RESTORE OTHER REGISTERS (pushad and popad)

pushad ; Saving register  and flag values from calling proc
pushfd

mov cx,2

tester:
mov ax,bx
cmp cx,ax ; Compare divisor and testing number, if equal, all divisors have been tested.
je prime
sub dx,dx
div cx
cmp dx,0 ; Is the remainder 0?
je notprime ; --return BX as 0
inc cx ; Next Divisor
jmp tester ; Loop


prime:
popfd ; Restoring register and flag values, leaving BX unchanged
popad
ret

notprime:
popfd ; Restoring register and flag values, then modifying BX to 0
popad
mov bx,0
ret
IsPrimeOrNot ENDP


GetPrimeNums PROC NEAR32
; SI is coming with a number and this proc has to generate 30 prime #'s that are larger than this. EDI is bringing the address of
; an array of 30 Words in which the prime numbers are to be stored.  For every potential #, this proc loads it into BX and calls
; IsPrimeOrNot to check if its prime or not.  If yes, # is stored in the array, else the next # is tried.
; BE SURE TO RESTORE ALL REGISTERS

pushad ; Saving register  and flag values from calling proc
pushfd

mov ecx,30 ; Loop 30 times to get prime #'s
mov edx,edi ; Store address of array in moving pointer

next:
mov bx,si ; Start testing with first potential number
inc si ; Prepare next testing number
call IsPrimeOrNot ; Test for prime
cmp bx,0 ; Was the potential number a prime?
je next ; No? - Try next potential number
mov [edx],bx ; Yes? - Put prime number in array
add edx,2 ; move pointer to next array element

loop next ; try next potential number and decrement ECX

popfd ; Restoring register and flag values
popad
ret ; Once all primes are found, return to caller
GetPrimeNums ENDP


WritePrimeNums PROC NEAR32
; EDI is pointing to an array of 30 Words and this proc has to write numbers from this array to a file, 5 to a line.
; BE SURE TO RESTORE ALL REGISTERS

pushad ; Saving register  and flag values from calling proc
pushfd

mov ecx,30 ; Output 30 numbers from array.
next:
mov ax,[edi] ; Store number in register
itoa oPut,ax ; convert prime to ASCII

push ecx

INVOKE WriteFile, ; Initial file output
hFile, ; file location
NEAR32 PTR oPut,
8, ; fileout length
NEAR32 PTR written,
0

pop ecx ; return counter value
        add     edi,4                                ; go to next array element
loop next

popfd
popad
ret

WritePrimeNums ENDP


_start:

INVOKE CreateFileA, ; open file
NEAR32 PTR fName, ; file name
GENERIC_WRITE, ; access
0, ; no sharing
0, ; no predefined security
CREATE_ALWAYS, ; open if file doesn't exist
0, ; no special attributes
0 ; no copied handle
mov hFile, eax ; handle for file


INVOKE GetStdHandle, ; Prepare output
STD_OUTPUT ;   --  to screen
mov hStdOut, eax

INVOKE WriteFile, ; Initial output
hStdOut, ; screen hardware location
NEAR32 PTR lineOut,
164, ; lineout and prompt
NEAR32 PTR written,
0

input strIn, 6 ; Read 4 digit # into strIn (6 ascii # max)
atoi strIn ; Convert ascii to doubleword
mov num,ax ; Store in num
mov si,num ; copy to tester value

lea EDI,AR1 ; Stores location of array1 to EDI

call GetPrimeNums

INVOKE WriteFile, ; Initial file output
hFile, ; file location
NEAR32 PTR fileOut,
30, ; fileout length
NEAR32 PTR written,
0

; INVOKE WriteFile, ; Initial file output
; hFile, ; file location
; NEAR32 PTR ax,
; 2, ; fileout length
; NEAR32 PTR written,
; 0


call WritePrimeNums


INVOKE WriteFile, ; Initial output
hStdOut, ; screen hardware location
NEAR32 PTR prompt,
45, ; prompt
NEAR32 PTR written,
0

input strIn, 6 ; Read 4 digit # into strIn (6 ascii # max)
atoi strIn ; Convert ascii to doubleword
mov num,ax ; Store in num
mov si,num ; copy to tester value

lea EDI,AR2 ; Stores location of array1 to EDI

call GetPrimeNums

INVOKE WriteFile, ; Initial file output
hFile, ; file location
NEAR32 PTR fileOut,
30, ; fileout length
NEAR32 PTR written,
0

; INVOKE WriteFile, ; Initial file output
; hFile, ; file location
; NEAR32 PTR ax,
; 2, ; fileout length
; NEAR32 PTR written,
; 0

call WritePrimeNums


closing:
INVOKE WriteFile, ; Initial output
hStdOut, ; screen hardware location
NEAR32 PTR endOut,
54, ; lineout and prompt
NEAR32 PTR written,
0

INVOKE CloseHandle, ; close output file
hFile

INVOKE ExitProcess, 0 ; exit with return code of 0

PUBLIC _start ; make entry point public

END ; end of source code







[attachment deleted by admin]

LimoDriver

I'd love to help, but I've just fruitlessly spent 15 minutes trying to build your code.

Please, next time you attach source files, make sure you include everything necessary for the build.
This include all headers and whatever batch scripts that you are running.

If you have it in some sort of a project/solution format for VS, or some other IDE, that would also be preferable.

Till next time.

musikgoat

So I use an include that carries some of the macros.   The problem is that it is the authors work.  If I properly credit his file, can I give that to you without breaking any rules? 

The Textbook is Introduction to 80x86 Assembly Language and Computer Architecture by Richard Detmer. 

it comes with MASM assembler:  ml.exe Macro Assembler ver 6.11,  i use that to assemble the code, then I use link.exe,  Incremental Linker ver 5.10.7303, which i link the assembled object to io.obj and kernel32.lib


Does this help at all?  Should I add the io.obj file to the package?  Do people freely have access to this software?

LimoDriver

I'm sorry but I'm not sure I can help you with your legal dillemas... it should be ok, although legaly looking it prolly isn't.

I can only try to debug your code if you decide to upload everything required for the proper build.

MichaelW

I do have the components necessary to build it, and io.h is just one of several missing.

musikgoat,

Your code is a mess. Have you even looked at what you posted? After removing the obvious duplications, your code seems to run OK, but it generates only 15 primes because it is skipping every second prime. For example, with an input of 0002 it generates:
2
5
11
17
23
31
41
47
59
67
73
83
97
103
109
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0

Another error I noticed, although is will not prevent the code from working:

strIn BYTE  ?
fName BYTE  "prg4out.txt",0
...
input strIn, 6 ; Read 4 digit # into strIn (6 ascii # max)

eschew obfuscation

musikgoat

Michael,

Your response helped me understand a few of the problems in my code.  I'm rough with this.

Quote from: MichaelW on April 04, 2007, 04:16:20 AM
Another error I noticed, although is will not prevent the code from working:

strIn BYTE  ?
fName BYTE  "prg4out.txt",0
...
input strIn, 6 ; Read 4 digit # into strIn (6 ascii # max)



I fixed that.   I've focused the problem down to a certain section.

WritePrimeNums PROC NEAR32
; EDI is pointing to an array of 30 Words and this proc has to write numbers from this array to a file, 5 to a line.
; BE SURE TO RESTORE ALL REGISTERS

pushad ; Saving register values from calling proc

sub ax,ax ; line counter

next:
mov cx,[edi] ; Store number in register
itoa oPut,cx ; convert prime to ASCII


INVOKE WriteFile, ; Initial file output
hFile, ; file location
NEAR32 PTR oPut,
6, ; fileout length
NEAR32 PTR written,
0


inc ax
push ax ; save counter

sub dx,dx
mov bx,5 ; set divisor to 5

div bx
cmp dx,0 ; does counter divide into 5?
je newline
newlineback:
pop ax
cmp ax,35
jge endprint
add edi,2 ; go to next array element
jmp next


newline:
INVOKE WriteFile, ; next line
hFile,
NEAR32 PTR nextLn,
2,
NEAR32 PTR written,
0

jmp newlineback

endprint:
popad
ret

WritePrimeNums ENDP




is there something wrong with the incrementing array element section above? 


and so that I know what to post in the future, what other components are needed. I'm sorry for the ignorance.


And I still cannot figure out why when I open windbg, and open the exe for debugging, it will start in a random place and then (in this situation it started at div bx, and went through to ret and just stopped. 

MichaelW

The code you posted, and that I tested, was adding 4 to EDI, which would be correct for an array of DWORDs, but not for an array of WORDs. Changing the value to 2 allows the procedure to display all of the primes in the array.

By your description the code is supposed to generate 30 primes larger than the input. If the input is a prime then only 29 larger primes are generated.

There is a problem with input values of 0 and 1 causing a divide by zero exception.

eschew obfuscation

musikgoat

Quote from: MichaelW on April 04, 2007, 05:14:39 PM
The code you posted, and that I tested, was adding 4 to EDI, which would be correct for an array of DWORDs, but not for an array of WORDs. Changing the value to 2 allows the procedure to display all of the primes in the array.

By your description the code is supposed to generate 30 primes larger than the input. If the input is a prime then only 29 larger primes are generated.

There is a problem with input values of 0 and 1 causing a divide by zero exception.



Thanks for those,  I'm tagging on my code again, tell me when you run this, if you can run it in a debugger.   I've used windbg, and the program now just silently finishes without any output on file or screen. 
When I assemble, i use the /Zi switch to add the .pdb file and when i link it i use the /debug switch.  Just like normal.   When I try to run the exe in windbg, the conditions that I described before happen.
it will start in a random place and then (in this situation it started at div bx, and went through to ret and just stopped.

[attachment deleted by admin]

MichaelW

I don't understand the "it will start in a random place". For me execution starts at the entry point, with the INVOKE CreateFileA statement. Is the entry point being specified on your link command line?

Your WritePrimeNums procedure is broken. Where the original was using ECX as a loop counter, preserving the value around the call to WriteFile, the new code is using AX and not preserving the value around the call. As a result, the procedure continues looping until it generates an access violation.

Using a mixture of 16-bit and 32-bit operands makes code harder to read, write, and understand, limits the addressing modes available for indirect memory operands, and actually makes the compiled code bigger. In a Win32 app most operands are generally either 32-bit or 8-bit.
eschew obfuscation

dsouza123


pushad ; Saving register  values from calling proc

mov cx,2

      cmp         bx,2
      jb          notprime   ;  jl  notprime  would be used if the input number could be negative

tester:
mov ax,bx


The code above deals with an input of 0 or 1. 

If you used 32 bit registers it would be better, by just specifying the lower 16 bits,
depending on the code something in the top 16 bits could change a calculation.
Also there could be some issues with the stack, usually items pushed on the stack are multiples of 32 bits.

musikgoat

Quote from: dsouza123 on April 04, 2007, 08:30:45 PM

pushad ; Saving register  values from calling proc

mov cx,2

      cmp         bx,2
      jb          notprime   ;  jl  notprime  would be used if the input number could be negative

tester:
mov ax,bx


The code above deals with an input of 0 or 1. 

If you used 32 bit registers it would be better, by just specifying the lower 16 bits,
depending on the code something in the top 16 bits could change a calculation.
Also there could be some issues with the stack, usually items pushed on the stack are multiples of 32 bits.

Thank You for that,  didn't think about using 0 or 1.

Quote from: MichaelW on April 04, 2007, 08:07:17 PM
I don't understand the "it will start in a random place". For me execution starts at the entry point, with the INVOKE CreateFileA statement. Is the entry point being specified on your link command line?

Your WritePrimeNums procedure is broken. Where the original was using ECX as a loop counter, preserving the value around the call to WriteFile, the new code is using AX and not preserving the value around the call. As a result, the procedure continues looping until it generates an access violation.

Using a mixture of 16-bit and 32-bit operands makes code harder to read, write, and understand, limits the addressing modes available for indirect memory operands, and actually makes the compiled code bigger. In a Win32 app most operands are generally either 32-bit or 8-bit.


Thank you for your help,  I fixed the outputting lines in the main section and it started working properly.   The only thing I can assume is that I was writing over my own values.

On to bit manipulation  :D

dsouza123

For an integer greater than 3 to be prime
a necessary condition is the number be either 1 mod 6 or 5 mod 6,
another necessary condition is the number is odd.

Together the two tests aren't sufficient to declare the number prime,
but they quickly eliminate alot of numbers.

      cmp         bx,2
      jb          notprime   ;  is 0 or 1

      cmp         bx,4       ;  is 2 or 3
      jb          prime

      mov         ax,bx      ;  check if odd
      and         ax,1
      jz          notprime   ;  even so not prime

      sub         dx,dx      ;  div bx by 6
      mov         ax,bx
      mov         cx,6
      div         cx

      cmp         dx,1       ;  remainder 1  so maybe
      je          maybe
      cmp         dx,5       ;  remainder 5  so maybe
      je          maybe

      jmp         notprime   ;  remainder 0, 2, 3, 4 so not prime

maybe:
      mov      cx,2

tester:
   mov      ax,bx

musikgoat

Quote from: dsouza123 on April 10, 2007, 05:04:29 PM
For an integer greater than 3 to be prime
a necessary condition is the number be either 1 mod 6 or 5 mod 6,
another necessary condition is the number is odd.

Together the two tests aren't sufficient to declare the number prime,
but they quickly eliminate alot of numbers.

      cmp         bx,2
      jb          notprime   ;  is 0 or 1

      cmp         bx,4       ;  is 2 or 3
      jb          prime

      mov         ax,bx      ;  check if odd
      and         ax,1
      jz          notprime   ;  even so not prime

      sub         dx,dx      ;  div bx by 6
      mov         ax,bx
      mov         cx,6
      div         cx

      cmp         dx,1       ;  remainder 1  so maybe
      je          maybe
      cmp         dx,5       ;  remainder 5  so maybe
      je          maybe

      jmp         notprime   ;  remainder 0, 2, 3, 4 so not prime

maybe:
      mov      cx,2

tester:
   mov      ax,bx

I like those methods of efficiency.   I think that the most efficient method  seems to be using the prime #'s already generated to test the potential #.   if the potential # divides into any lower prime #, then it is not prime,  I cant tell if that is more efficient or not though,  due to the fact that it has to grab these #'s from memory, where as, your method is all in registers.

Thanks again for your help.


musikgoat

To add to this thread.   Our next assignment is to use the stack instead of passing by registers.   I've gone over this multiple times.   I believe that my use of the stack is correct.   (I diagrammed out what the stack would look like)

I can assume its something small,  but any one see any trouble?     

I can assemble and link the prog,  but it crashes with Unhandled exception at 0x0040114a in prg5.exe: 0xC0000005: Access violation writing location 0x87d8ac2b.

This is visual studios disassembly of the exception.  The problem is located at the compare statement.

WritePrimeNums PROC NEAR32
004010F5  push        ebp 
; 2 parameters are coming in on stack - a word that has the handle of the output file, and an address
; (starting address of array of 30 words in which the prime #'s are stored).  This proc has to write numbers
; from this array to a file, 5 to a line.
; BE SURE TO RESTORE ALL REGISTERS

push ebp ; save EBP
mov ebp,esp ; establish stack frame
004010F6  mov         ebp,esp
pushad
004010F8  pushad           

mov eax,[ebp+12] ; store output file handle to hFile
004010F9  mov         eax,dword ptr [ebp+0Ch]
mov hFile,eax
004010FC  mov         dword ptr [hFile (4040AEh)],eax
mov edi,[ebp+8] ; store address of array in EDI
00401101  mov         edi,dword ptr [ebp+8]

mov ecx,30 ; element and line counter
00401104  mov         ecx,1Eh

next:
mov ax,[edi] ; Store number in register
00401109  mov         ax,word ptr [edi]
itoa oPut,ax ; convert prime to ASCII
0040110C  push        ebx 
0040110D  mov         bx,ax
00401110  push        bx   
00401112  lea         ebx,[oput (40407Ah)]
00401118  push        ebx 
00401119  call        @ILT+25(itoaproc) (40101Eh)
0040111E  pop         ebx 

push ecx
0040111F  push        ecx 

INVOKE WriteFile, ; Initial file output
00401120  push        0   
00401122  push        offset written (4040A6h)
00401127  push        6   
00401129  push        offset oput (40407Ah)
0040112E  push        dword ptr [hFile (4040AEh)]
00401134  call        WriteFile (401766h)
hFile, ; file location
NEAR32 PTR oPut,
6, ; fileout length
NEAR32 PTR written,
0


pop ecx ; restoring counter
00401139  pop         ecx 

mov ax,cx ; testing for line feed
0040113A  mov         ax,cx
sub dx,dx
0040113D  sub         dx,dx
mov bx,5 ; set divisor to 5
00401140  mov         bx,5

div bx
00401144  div         ax,bx
cmp dx,0 ; does counter divide into 5?
00401147  db          66h 
00401148  db          83h 
00401149  cli             
0040114A  add         byte ptr [edi+eax-7Dh],dh
0040114E  mov         dword ptr [edx],1DEBB7E2h



I will attach my source as usual,   any suggestions wise ones?

[attachment deleted by admin]

MichaelW

I'm not getting any exception. The program appears to run OK, displays Bye before it exits, and generates this output:

Generated Prime Numbers after      2
     3
     5     7    11    13    17
    19    23    29    31    37
    41    43    47    53    59
    61    67    71    73    79
    83    89    97   101   103
   107   109   113   127Generated Prime Numbers after      2
     3
     5     7    11    13    17
    19    23    29    31    37
    41    43    47    53    59
    61    67    71    73    79
    83    89    97   101   103
   107   109   113   127

Was it supposed to do something else?
eschew obfuscation