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 <p16f684A.inc>

__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

This article has been dead for over six months. Start a new discussion instead.