Hi!
I'm new to assembly coding and I have an assignment in Intel/AMD where I'm supposed to code a file that consists of different subroutines and another file that produces the code to take care of. But first, as a start, I think I have to start with small pieces of code. The first file, intellab.s:
.data
UTBUFFERT: .skip 128
INBUFFERT: .skip 128
.text
puttext: pushl %ebp
movl %esp, %ebp
movl UTBUFFERT, %eax
popl %ebx
igenUtBuffert: movb (%ebx), %al
cmpb $0, %al
je puttextLoopEnd
incl %ebx
incl %eax
jmp igenUtBuffert
puttextLoopEnd: pushl UTBUFFERT
popl %ebp
ret
outimage: pushl %ebp
movl %esp, %ebp
popl %eax
call printf
popl %ebp
ret
and the second file, MprovEgen.s:
.data
Head: .asciz "Start av testprogram.Skriv in 2 tal!"
.text
.global main
main:
pushl $Head
call puttext
call outimage
call exit
But when i compile this
anders@anders-VirtualBox:~$ gcc -o intellab intellab.s MprovEgen.s
I get the error message:
/tmp/ccZ5m2tf.o: In function `main':
(.text+0x6): undefined reference to `puttext'
/tmp/ccZ5m2tf.o: In function `main':
(.text+0xb): undefined reference to `outimage'
collect2: ld returned 1 exit status
Does anyone understand this, what is wrong here?
Regards, Anders
Try including the file containing subroutines in your main file.. Not sure about syntax exactly, but its like
include file.inc
where file.inc contains your subroutines, etc
Thank you for answering,
I solved the problem by coding
globl puttext, outimage
in the intellab file.
But now I get another problem.
When I try to run the program I get an error message:
Segmentatoin fault
What is this?
//Anders
Thank you for answering, I solved the problem by coding globl puttext, outimage
in the intellab file.
But now I get another problem.
When I try to run the program I get an error message: Segmentatoin fault
What is this?
//Anders
The reason your program is SegFaulting is because your stack gets messed up. First you push "Head" (MprovEgen.s:7) and then you push EBP (intellab.s:9). When you then pop a value to EBX, you do not pop "Head", as it appears you think you do, but instead you pop the topmost value of the stack, which is the original value of EBP.
When you've fixed the abovem, the thing that will most likely cause a SegFault, is on lines 21-23. By pushing a value and then directly popping it, nothing has really changed on stack, and the original value of EBP remains on top. What should really be on top of the stack there is the return address.
Right now however, the return address appears to be on top of the stack by then, so the SegFault is probably not occuring there. The more likely place for the SegFault top happen is in your "outimage" procedure. Once again the stack gets messed up, and this time the return address wont be on top of the stack.
Some tips:
* Instead of popping arguments from stack, use the base pointer to get their values. To get the address of the argument "Head" in "puttext", do:
movl -8(%ebp),%ebx * Don't set up a new stack frame for every new procedure, especially if they take arguments
* Though it is possible to return arguments on the stack, it is not as easy as just pushing the value and returning. Also, I believe it's very rare to do so, and you should as often as possible put return values in registers or global variables.
Hello!
Thanks a lot for answering. But I don't think I understand it all. I have changed the code and it now looks like this, intellabb.s:
.data
OUTBUFFERT: .skip 128
.text
.globl puttext, outimage
puttext: pushl %ebp
movl %esp, %ebp
movl OUTBUFFERT, %eax
movl -8(%ebp), %ebx
againOutBuffert:
movb (%ebx), %al
cmpb $0, %al
je puttextLoopEnd
incb %bl
incb %al
jmp againOutBuffert
puttextLoopEnd:
movl %ebp, %esp
popl %ebp
ret
outimage:
pushl %eax
call printf The testing file, MprovEgen.s
.data
Head: .asciz "Start of the testing program.Put in 2 numbers!\n"
.text
.global main
main:
pushl $Head /*Head is pushed on to the stack*/
call puttext /*This piece of text is now put in the OUTBUFFERT*/
call outimage /*Prints out whats in the OUTBUFFERT*/
call exit
But I get the same problem - segmentation fault.
Two questions:
- why -8 on this piece of code;
movl -8(%ebp), %ebx
shouldn't it be 0?
- you're saying that I shouldn't "set up a new stack frame for every new procedure, especially if they take arguments". But how shall I do this then? Since I call outimage don't I have to have a new stack frame , then when leaving the stack frame I end the stack with ret?
// Anders
Your code looks a lot better now, but there are still a few problems that might cause a Segmentation Fault. The first problem is embarrassingly mine... :$
The code for accessing a parameter should not be -8(%ebp), but should instead be 8(%ebp) (if you've set up a new stack frame for the procedure, which you have). Since the stack grows downwards in memory, and since %ebp points to the top of the stack, accessing 8(%ebp), means we're accessing the 3rd last thing of the stack, which happens to be the last argument that was passed before the procedure was called. The 2nd last thing on stack is the address that we're supposed to return to when we're done, and the very last thing on stack, at address (%ebp), is the contents of "old" (%ebp) which was pushed at the top of the procedure (that "pushl %ebp" in "puttext"). If we know this (keep in mind that the new stack frame is below the address in %ebp), it should be easy to see that:
0(%ebp) ; Original contents of %ebp
4(%ebp) ; Return address
8(%ebp) ; Argument 1
12(%ebp) ; Argument 2 ...
(All arguments are expected to be 4 bytes in size)
I hope that answered your first question.
As for your second question, I'm not quite sure you understand what a stack frame is. A stack frame is like a special area on the stack, set up for a procedure. The stack frame is used to store local variables and arguments that are passed to other procedures. To create a stack frame, you first first push the base pointer (%ebp) on the stack (so that the callers stack frame can be restored later) and then you copy the address of the bottom of the callers stack frame (%esp) to %ebp. To allocate space on the new stack frame, the number of bytes required are then subtracted from %esp.
To restore the callers stack frame (which is done right before returning), you simply pop %ebp and copy the value of %ebp to %esp (in that order).
The "Don't set up a new stack frame, especially if they take arguments" is wrong by me... :$ Actually, if you take arguments or use local variables in the procedure, set up a new stack frame, otherwise don't bother.
The place where the segfault most likely occurs now, is in your "outimage" procedure. First of all, it appears %eax contains some "random" value. Also, you forgot a "ret" instruction after "call printf". Another problem might however appear here, since "cdecl" (the C calling convention) says that the caller should clean up the stack. It's fixed with a simple "sub 4,%esp" after the call.
Sorry about this long post! If something remains unclear, just ask again ;)
Thanks alot for answering. I post the code as I've programmed it till now. Sorry that it has taken me so long to come back here. I had some other courses to do. With puttext I'm supposed to (in this case)put (read it byte for byte) whats in Head in a buffer. And with outimage I'm supposed to print out the string thats in the buffer.
.data
Show: .asciz "%s\n"
UTBUFFERT: .skip 64
Head: .asciz "Start"
.text
.globl main
main:
pushl $Head
call puttext /*0 4 8 12 16 20 24*/
call outimage /* 0 4 8 12 16*/
call exit
puttext: popl %ebx
movl UTBUFFERT, %ecx
next: movb (%ebx), %al
cmpb $0, %al
je puttextLoopEnd
movb %al, (%ecx)
incl %ecx
incl %ebx
jmp next
puttextLoopEnd:
pushl %ecx /* 0 4 8 12 16 20*/
ret
outimage:
pushl %ebp /* 0 4 8 12*/
movl %esp,%ebp
movl 8(%ebp), %eax
pushl %eax /* 0 4 8*/
pushl Show /* 0 4*/
call printf /* 0*/
popl %eax
popl %ebp
ret
But I still get segmentation fault. The numbers I've put to the right is me counting the bytes on the stack, i e the position on the stack. The outcome from this peace of program should be Start. But I get segmentation fault. One thing that I'm still a little confused about is to clean up the stack. Shouldn't I do add $8, (%esp) after call printf. ?
Then another thing. Could someone recommend a debugger for this assembly code? I guess it will be easier to find the segmentation fault problem if I use a debugger.
// Anders
Once again I spot a few errors:
* On line 15 your not really popping '$Head'. Instead, your popping the address of line 11. Set up another stack frame at the start of 'puttext' and destroy it right before you return. Then you can replace 'popl %ebp' with 'movl 8(%ebp)'.
2. On line 27 you push %ecx right before returning, which means you return to whatever the contents of '%ecx' are!
These are just a few errors I see immediately, but there might be more. I definiately recommend you look into debugging some on your own, since it's an essential part of the programming experience. It seems you're on linux, so I'd recommend either Evan's Debugger or, if you want to use debugging symbols, some GDB-based debugger (such as Nemiver for GNOME or Kdbg for KDE).
Shouldn't I do add $8, (%esp) after call printf
Yes you should, since you're passing two arguments to 'printf'. Right now, your stack gets real messy!
When looking at your code, I get the feeling you don't quite understand how 'call' and 'ret' works yet. It's important to remember that these instructions use the stack to store and retrieve addresses. Whenever you 'CALL', the address of the next instruction that should be executed is pushed on the stack, something like this:
pushl $next_instruction
jmp some_function
next_instruction:
...
The 'ret' instruction on the other hand, pops and address from the stack and jumps to that address.
This does not apply to the various jump instructions (JMP, JE, JNZ etc.)! They're just plain jumps without any stack involved!
Good luck in your debugging!
Thanks alot for helping me out here. I've actually solved this part of the problem now, so the subroutines puttext and outimage works fine now. But the whole project contains more subprograms and right now I'm struggling with trying to convert a string to an integer. If you are interested in helping out I have a new thread "convert string to integer"
Anders
hello..i have this coding...when i burn into pic it does not work. not output...i want someone help me to solve this problem...i'm new for assembly language.. i have get the correct coding because i have presentation for next week...help me please...i begging u..
;************************************************************************
; Digital Thermometer *
; *
;************************************************************************
list p=16f84A
#include
__CONFIG _CP_OFF & _DATA_CP_OFF & _LVP_OFF & _BOREN_OFF & _MCLRE_OFF & _WDT_OFF & _PWRTE_ON & _INTOSC_OSC_NOCLKOUT
errorlevel 0,-302
CBLOCK 0x20
DS_DAT
DS_SIGN
DUMMY0
DUMMY1
DUMMY2
DUMMY3
DUMMY4
SEND
COUNT
NUM1
NUM2
NUM3
DATA1
HALF
endc
#define DAT_PIN PORTB,0 ; 74595 DATA pin
#define LATCH PORTB,4 ; 74595 LATCH pin
#define CLK PORTB,5 ; 74595 CLK pin
#Define DQ PORTA,0
org 0x000
goto Start
Table_ addwf PCL,f
dt 0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f
Start clrf PORTA ; Set port A for digital I/O
movlw 0x07
movwf CMCON
bsf STATUS,RP0
movlw b'00000110' ; Set port B for displaying via LED 7 segments
movwf PORTB
bcf STATUS,RP0
Main call RD_Temp ; Read data from DS1820
clrf HALF ; A Half digit
bcf STATUS,C
rrf DS_DAT,w ; Use 7-bit upper
rlf HALF,f ; Store a half degree
movwf DATA1 ; Data for convert to decimal
call Convert ; Convert data to decimal
call Delay3 ; Display data delay
goto Main
;**********************************************************************
; Display On 7 Segment *
;**********************************************************************
Delay3 clrf DUMMY4 ; Display data delay
DelayLp call Disp
decfsz DUMMY4,f
goto DelayLp
return
Delay2 clrf DUMMY3 ; Delay for scanning LED 7 segments
decfsz DUMMY3,f
goto $-1
return
Disp movf NUM1,w ; Load data to DSP1
call Table_ ; Convert
btfsc HALF,0 ; Show a half degree on dot-point LED
addlw 0x80
call Send_Dat ; Send data to LED 7 segments
bcf PORTB,6 ; ON DSP1 Display
call Delay2
bsf PORTB,6
movf NUM2,w ; Load data to DSP2
call Table_ ; Convert
call Send_Dat ; Send data to LED 7 segments
bcf PORTB,7 ; On DSP2
call Delay2
bsf PORTB,7 ; Off DSP2
return
Send_Dat movwf SEND ; Load data
movlw 0x08 ; Loop 8 times for sending 8-bit data
movwf COUNT
Chk_Bit bcf DAT_PIN ;
btfsc SEND,7 ; Check bit 7 "0" or "1"
bsf DAT_PIN ; Send bit 7 to shift register
Clock bsf CLK ; Clock pulse
bcf CLK
Rotate rlf SEND,f ; Rotate bit for sending next bit
decfsz COUNT,f ; Test loop 8 times ?
goto Chk_Bit ; If no, send bit again
Latch bsf LATCH ; Latch data for displaying
bcf LATCH
return
;**********************************************************************
; Macro for DS1820 *
;**********************************************************************
DQLOW macro
bcf DQ ; DQ bit ready
bsf STATUS,RP0
bcf DQ ; Set DQ to output
bcf STATUS,RP0
endm
DQHIZ macro
bsf STATUS,RP0
bsf DQ ; Set DQ to input
bcf STATUS,RP0
endm
PAUSE macro DELAY ; Generate delay time
movlw DELAY
movwf DUMMY0
call Delay5
endm
;**********************************************************************
; DS1820 1-Wire bus *
;**********************************************************************
RD_Temp call DS_Rx ; Check chip status
addlw 0x01
btfss STATUS,Z ; Z set = ready
return ; W is not zero = not ready
Get_Temp call DS_Reset
movlw 0xcc ; Skip ROM
call DS_Tx
movlw 0xbe ; Read scratch pad RAM of DS1820
call DS_Tx
call DS_Rx ; Read 8-bit data
movwf DS_DAT
call DS_Reset ; Restart
movlw 0xcc ; Skip ROM
call DS_Tx
movlw 0x44 ; Start conversion
call DS_Tx
Delay5 nop ; Delay for PAUSE macro
nop
decfsz DUMMY0,f
goto Delay5
return
DS_Reset DQLOW
PAUSE 0x77 ; 600 microcsecond delay
DQHIZ
PAUSE 0x0c ; Wait for response pulse (67 microsecond)
nop
nop
movf PORTA,w ; Read response
andlw 0x01 ; Use RA0 only
movwf DUMMY1
PAUSE 0x3b ; 300 microcsecond delay
movf DUMMY1,w ; Response in W
return
DS_Tx movwf DUMMY2 ; Transmission data
movlw 0x08 ; Prepare 8-bit counter for sending data
movwf DUMMY1 ; Define loop counter
Tx_Loop DQLOW ; Macro of DQ pin to low, This is start bit
PAUSE 0x01 ; 10 microsecond delay
rrf DUMMY2,f ; Rotate data to Carry flag
btfsc STATUS,C ; Test Carry flag
bsf DQ ; If Carry flag = "1" , set DQ to high
PAUSE 0x0d ; 70 microsecond delay
DQHIZ
nop
decfsz DUMMY1,f ; 8 times ?
goto Tx_Loop ; No, send again
return
DS_Rx movlw 0x08 ; Recieve 8-bit data
movwf DUMMY1
Rx_Loop DQLOW ; Macro of DQ pin to low, this is start bit
PAUSE 0x01 ; 10 microsecond delay
DQHIZ ; Back to high for receiving
nop
nop
movf PORTA,w ; Read data
andlw 0x01 ; Get data bit 0 only
addlw 0xff ; Move data bit 0 to Carry flag with addition method
rrf DUMMY2,f ; Move data bit 0 to DUMMY bit 7
PAUSE 0x0b ; 60 microsecond delay
decfsz DUMMY1,f ; Loop 8 times
goto Rx_Loop ; Read again
movf DUMMY2,w ; Save data to W register
return
;**********************************************************************
; Convert Hex to Decimal (3 Digit) *
;**********************************************************************
Convert clrf NUM2 ; Clear register of 10's unit
Check movlw 0x0A ;
subwf DATA1,w ; Subtract with 10 until lower 10
btfss STATUS,C ; Check subtraction result lower 10 ?
goto Less1 ; If yes then return
incf NUM2,f ; If no, increase 10's unit value
movlw 0x0A ;
subwf DATA1,f ; Subtract with 10 until lower 10 and check result again
goto Check
Less1 movf DATA1,w ;
movwf NUM1 ; Send data to 1's unit display
clrf NUM3 ; Check hundred unit
Check2 movlw 0x0A
subwf NUM2,w ;
btfss STATUS,C ; 10's unit over 10 ?
return ; If no then return
incf NUM3,f
movlw 0x0A ; If yes, subtract again and check until the result is lower 10
subwf NUM2,f
goto Check2
;**********************************************************************
end