Can ASM clobber a static varable in another file?

Go To Last Post
22 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Still new to ASM here but learning faster then my brain wants to allow.

 

I have another strange situation were ASM from file X corrupts a variable in file Y.

 

In file Y I have a static unsigned char _Prevent_Updates; Looking at the LST I can see the compiler puts it in r24.

lds r24,_Prevent_Updates

 

In file X I have the following ASM, it also uses r24 but it should be using it safely AFAIK. IF I return out of my app before this ASM runs, my  _Prevent_Updates is ok, if I run the ASM its set to 31. For fun I set it to 0 at the end of the ASM but that didn't help..

 

 

LST from ASM

 

 

138 00d0 8827      			clr r24
 139 00d2 28E0      		ldi r18,8
 140 00d4 1081      		ld  r17, Z
 141               	startBit:
 142 00d6 359B      		sbis 6, 5
 143 00d8 00C0      		rjmp startBit
 144 00da 00C0      		rjmp nextBit
 145               	nextByte:
 146 00dc 28E0      		ldi r18,8
 147 00de 1193      		st  Z+, r17
 148 00e0 1081      		ld  r17, Z
 149               	nextBit:
 150 00e2 00E8      	 	ldi r16, 0x80
 151               	waitlow:
 152 00e4 0A95      		dec r16
 153 00e6 01F0      		breq exit
 154 00e8 3599      		sbic 6, 5
 155 00ea 00C0      		rjmp waitlow
 156 00ec 0000      		nop
 157 00ee 0000      		nop
 158 00f0 0000      		nop
 159 00f2 0000      		nop
 160 00f4 0000      		nop
 161 00f6 0000      		nop
 162 00f8 0000      		nop
 163 00fa 0000      		nop
 164 00fc 0000      		nop
 165 00fe 0000      		nop
 166 0100 289A      		sbi 5, 0
 167 0102 2898      		cbi 5, 0
 168 0104 8894      		clc
 169 0106 3599      		sbic 6, 5
 170 0108 0894      		sec
 171 010a 111F      		rol r17
 172 010c 8395      		inc r24
 173 010e 00E8      		ldi r16, 0x80		

 174               	waithigh:
 175 0110 0A95      		dec r16
 176 0112 01F0      		breq exit
 177 0114 0000      		nop
 178 0116 0000      		nop
 179 0118 359B      		sbis 6, 5
 180 011a 00C0      		rjmp waithigh
 181 011c 2A95      	   dec    r18
 182 011e 01F4      	   brne nextBit
 183 0120 00C0      		rjmp nextByte
 184 0122 80E0      		ldi r24, 0x00
 185               	exit:	

 

 

 

 

 

This topic has a solution.
Last Edited: Sat. Feb 27, 2021 - 04:09 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Globber? The only "globber" I can find is a brand of scooter.

 

Ross McKenzie ValuSoft Melbourne Australia

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

With ASM, you can do anything - you are not restricted by any of these namby-pamby quiche-eating HLL notions of "scope" !

 

http://www.bernstein-plus-sons.c...

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
Last Edited: Thu. Feb 25, 2021 - 01:46 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Well the only way the code in one file can "damage" the contents of a variable elsewhere is when it actually makes memory writes. So out of the LST that would appear to be:

 138 00d0 8827      		clr r24
 139 00d2 28E0      		ldi r18,8
 140 00d4 1081      		ld  r17, Z
 141               	startBit:
 142 00d6 359B      		sbis 6, 5
 143 00d8 00C0      		rjmp startBit
 144 00da 00C0      		rjmp nextBit
 145               	nextByte:
 146 00dc 28E0      		ldi r18,8
 147 00de 1193      		st  Z+, r17
 148 00e0 1081      		ld  r17, Z
 149               	nextBit:
 150 00e2 00E8      	 	ldi r16, 0x80
 151               	waitlow:
 152 00e4 0A95      		dec r16
 153 00e6 01F0      		breq exit
 154 00e8 3599      		sbic 6, 5
 155 00ea 00C0      		rjmp waitlow
 156 00ec 0000      		nop
 157 00ee 0000      		nop
 158 00f0 0000      		nop
 159 00f2 0000      		nop
 160 00f4 0000      		nop
 161 00f6 0000      		nop
 162 00f8 0000      		nop
 163 00fa 0000      		nop
 164 00fc 0000      		nop
 165 00fe 0000      		nop
 166 0100 289A      		sbi 5, 0
 167 0102 2898      		cbi 5, 0
 168 0104 8894      		clc
 169 0106 3599      		sbic 6, 5
 170 0108 0894      		sec
 171 010a 111F      		rol r17
 172 010c 8395      		inc r24
 173 010e 00E8      		ldi r16, 0x80		

 174               	waithigh:
 175 0110 0A95      		dec r16
 176 0112 01F0      		breq exit
 177 0114 0000      		nop
 178 0116 0000      		nop
 179 0118 359B      		sbis 6, 5
 180 011a 00C0      		rjmp waithigh
 181 011c 2A95      	   dec    r18
 182 011e 01F4      	   brne nextBit
 183 0120 00C0      		rjmp nextByte
 184 0122 80E0      		ldi r24, 0x00
 185               	exit:

Dunno about anyone else but as far as I can see that is the only thing making a memory write from this sequence. So the next question is "what was in Z at the time?". In the quoted piece we don't see Z being set up so was there something before this assigning a pointer value to Z and, if so, where in memory was that pointing?

Last Edited: Thu. Feb 25, 2021 - 02:21 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1


valusoft wrote:

Globber? The only "globber" I can find is a brand of scooter.

 

I thought it was something different...

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Fixed my type-o

 

 

 

I have seen this before in ASM code.

        "    push r30                \n"    // save Z
        "    push r31                \n"    // save Z

later a pop..

 

 

Ia there some rules to know when this is needed with inline asm?

 

 

Yes, there was a pointer to another array just before this. Z was used to read from a local array pointer. Do I need to some how free up Z after/before  its used?

 

 0044 EF93      			push r30
  58 0046 FF93      		push r31
  59 0048 29E0      		ldi r18, 9
  60 004a 0191      		ld r16, z+
  61               	next_byte47:
  62 004c 2A95      		dec	r18
  63 004e 01F4      		brne skip47
  64 0050 0191      		ld r16, z+
  65 0052 28E0      		ldi r18, 8
  66               	skip47:
  67 0054 002B      		or r16,r16
  68 0056 02F0      		brmi make_a_147
  69 0058 00C0      		rjmp make_a_047
  70               	make_a_047:
  71 005a 000F      		lsl r16
  72 005c 3D9A      		sbi 7, 5
  73 005e 1BE0      	ldi r17, 11
  74 0060 00D0      	 rcall sb_dly47
  75 0062 0000      	nop
  76 0064 0000      	nop
  77 0066 3D98      		cbi 7, 5
  78 0068 0000      	nop
  79 006a 0000      	nop
  80 006c 1197      		sbiw	r26, 1
  81 006e 01F4      		brne next_byte47
  82 0070 00C0      		rjmp finished47
  83               	make_a_147:
  84 0072 000F      		lsl r16
  85 0074 3D9A      		sbi 7, 5
  86 0076 12E0      	ldi r17, 2
  87 0078 00D0      	 rcall sb_dly47
  88 007a 0000      	nop
  89 007c 0000      	nop
  90 007e 0000      	nop
  91 0080 3D98      		cbi 7, 5
  92 0082 16E0      	ldi r17, 6
  93 0084 00D0      	 rcall sb_dly47
  94 0086 0000      	nop
  95 0088 0000      	nop
  96 008a 0000      	nop
  97 008c 1197      		sbiw	r26, 1
  98 008e 01F4      		brne next_byte47
  99 0090 00C0      		rjmp finished47
 100               	sb_dly47:
 101 0092 1A95      		dec r17
 102 0094 01F4      		brne sb_dly47
 103 0096 0895      		ret
 104               	finished47:
 105 0098 0000      		nop
 106 009a 0000      	 	nop
 107 009c 0000      	 	nop
 108 009e FF91      	 	pop r31
 109 00a0 EF91      		pop r30
 110 00a2 3D9A      		sbi 7, 5
 111 00a4 00C0      		rjmp done47
 112 00a6 0FEF      		ldi r16, 0xff
 113               	stop_bit47:
 114 00a8 0A95      		dec r16
 115 00aa 01F0      		breq done47
 116 00ac 359B      		sbis 6, 5
 117 00ae 00C0      		rjmp stop_bit47
 118               	done47:

 

EDIT: I just tried using X for the former, and y for the later so that I'm not using the z pair, and different pairs in both cases. Though, seeing the same result.

Last Edited: Thu. Feb 25, 2021 - 05:32 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Is there some reason you don't just do this as .S ? It would actually be about 100 times easier!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I need to and want to learn how, I get how to call it from C and how to add an S in my MAKE but its all the set up in the S file itself that I do not really understand. If I'm wrong about the confusion, is it easy enough just to make an S file from what I have? I know someday I need to master this anyways.

Last Edited: Thu. Feb 25, 2021 - 05:38 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I need to have two sections of code

 

1

asm volatile(
	"	ldi r18, 9			\n" // byte counter
	"	ld r16, x+			\n" // move buffer to r16

"next_byte%=:					\n"
	"	dec	r18				\n"	
	"	brne skip%=			\n"// skip when we are not zero( Z flag and dec are not equal. )					\n"
	"	ld r16, x+			\n"// move buffer to r16
	"	ldi r18, 8			\n"// next byte

"skip%=:					\n"
	//"	andi r16, 0x80		\n" // skip if 7th but is set
	//	"	breq make_a_0%=		\n"
	"	or r16,r16			\n"
	"	brmi make_a_1%=		\n"
	"	rjmp make_a_0%=		\n"


"make_a_0%=:				\n"// 0 bit, low.
	"	lsl r16				\n"// shift right
	PULL_DATA
	DLY_LARGE_1ST
	RELEASE_DATA
	DLY_SHORT_2ND
	"	sbiw	%1, 1		\n"//check for done. 
	"	brne next_byte%=	\n"//next byte
	"	rjmp finished%=		\n"//then we are done
	
"make_a_1%=:				\n"// 1 bit, hi.
	"	lsl r16				\n"// shift right
	PULL_DATA
	DLY_SHORT_1ST
	RELEASE_DATA
	DLY_LARGE_2ND
	"	sbiw	%1, 1		\n"//check for done. 
	"	brne next_byte%=		\n"//next byte
	"	rjmp finished%=		\n"//then we are done

// delay sub (arg r17)
"sb_dly%=:				\n"
	"	dec r17				\n"
	"	brne sb_dly%=		\n"
	"	ret					\n"

"finished%=:\n"
	// going here is fast so we need to extend the last
	// delay by 500nS
	"	nop\n "
	"	nop\n "
	"	nop\n "
	"	nop\n "
	"	nop\n "
	PULL_DATA
	//"	nop\n "
	//"	nop\n "
	//DLY_SHORT_2ND //<-----this is the lo after the hi that is hi for too long...
	//RELEASE_DATA
	"	rjmp done%=		\n"
	//Make sure the controller polls high before we move on
	"	ldi r16, 0xff		\n" // setup a timeout
"stop_bit%=:			\n"
	"	dec r16				\n" // decrement timeout
	"	breq done%=		\n" // handle timeout condition
	"	sbis %3, 5			\n" // Read the port
	"	rjmp stop_bit%=	\n"
"done%=:\n"
	:
	: "I" (_SFR_IO_ADDR(GCN64_DATA_DDR)), // %0
	  "w" (bits),						// %1
	  "x" (data),					// %2
	  "I" (_SFR_IO_ADDR(GCN64_DATA_PIN)),	// %3
	  "I" (_SFR_IO_ADDR(PORTB))			// %4
	: "r16", "r17", "r18");

and 2

register  unsigned char count;
	 
		asm volatile(
		"	clr %0					\n" //clear counter
		"	ldi r18,8				\n" //set counter
		"	ld  r17, y 				\n" //load y to r17
"startBit:\n"
		"	sbis %2, 5				\n" // find the end of the start bit
		"	rjmp startBit			\n" // if its high, we are ready, skip this.
		"	rjmp nextBit	\n"
"nextByte:\n"
		"	ldi r18,8				\n" //set counter back to 8
		"	st  y+, r17 			\n" //load r17 to y and go to next element in array
		"	ld  r17, y 				\n" //load y to r17
"nextBit:\n"
		" 	ldi r16, 0x80			\n" ////54 us timeout, we really do not want to wait longer after, and any bits of 8us is not nintendo. Browlers need 34
"waitlow:\n"//start bit.
		"	dec r16					\n" //time out counter
		"	breq exit				\n" //exit when we are zero( Z  flag and dec are equal. )
//find the next falling edge to sync
		"	sbic %2, 5				\n" //check if low,then skip
		"	rjmp waitlow			\n" //wait while low.				
		"	nop						\n" //move 1us in to place. 
		"	nop						\n"
		"	nop						\n"
		"	nop						\n"
		"	nop						\n"
		"	nop						\n"
		"	nop						\n"
		"	nop						\n"
		"	nop						\n"
		"	nop						\n"
		//"	nop						\n"
		//"	nop						\n"
		//"	nop						\n"
		//"	nop						\n"
			SET_DBG
			CLR_DBG
		"	clc					\n" // clear carry before shifting

// check at this time for bit state
		"	sbic %2, 5				\n" //if state was low skip 
		"	sec   			  	    \n" //set Carry flag if true
		"	rol r17 				\n" //Rol flag on to r17
		"	inc %0					\n" //count bit.
		"	ldi r16, 0x80			\n"//54 us timeout, we really do not want to wait longer after, and any bits of 8us is not nintendo. Browlers need 34
"waithigh:\n"
//find the next rising edge to sync
		"	dec r16					\n"
		"	breq exit				\n" // exit when we are zero( Z flag and dec are equal. )
		"	nop						\n"
		"	nop						\n"
		"	sbis %2, 5				\n" //check for set
		"	rjmp waithigh			\n"	//go back not ready
        "   dec    r18              \n" //bit counter
        "   brne nextBit      		\n" //If Z  flag and dec are not equal. 
		"	rjmp nextByte			\n" //If Z  flag and dec are equal. 
		"	ldi r24, 0x00			\n"
"exit:	\n"
		: 	"=&r" (count)						// %0
		: 	"y" ((unsigned char volatile *)nBuffer),// %1
			"I" (_SFR_IO_ADDR(GCN64_DATA_PIN)),	// %2
			"I" (_SFR_IO_ADDR(PORTB))			// %3
		: 	"r16","r17","r18"
	);

 

 

Last Edited: Thu. Feb 25, 2021 - 05:40 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

its all the set up in the S file itself that I do not really understand.

Well there's no real difference apart from the overhead of (R)CALL and RET but you basically have the choice of:

// main.c

#include <avr/io.h>

int main(void) {
    asm volatile(
        "ldi r24, %1\n"
        "out %0, r24\n"
        :
        : "I" (_SFR_IO_ADDR(PORTB)),"M" (37)
        : "r24");
    while(1);
}

(and my head almost exploded trying to work out how to write that!). This generated:

0000007a <main>:
#include <avr/io.h>

int main(void) {
    asm volatile(
  7a:	85 e2       	ldi	r24, 0x25	; 37
  7c:	85 b9       	out	0x05, r24	; 5
  7e:	ff cf       	rjmp	.-2      	; 0x7e <main+0x4>

 

Or I could have done this as:

// main.c

#include <avr/io.h>

void setPortB(uint8_t val);

int main(void) {
    setPortB(37);
    while(1);
}
// asm.S
#define __SFR_OFFSET 0
#include <avr/io.h>

    .global setPortB

setPortB:
    out PORTB, R24
    ret

which together generate:

0000007a <setPortB>:
#include <avr/io.h>

    .global setPortB

setPortB:
    out PORTB, R24
  7a:	85 b9       	out	0x05, r24	; 5
    ret
  7c:	08 95       	ret

0000007e <main>:
#include <avr/io.h>

void setPortB(uint8_t val);

int main(void) {
    setPortB(37);
  7e:	85 e2       	ldi	r24, 0x25	; 37
  80:	fc df       	rcall	.-8      	; 0x7a <setPortB>
  82:	ff cf       	rjmp	.-2      	; 0x82 <main+0x4>

They both do the same thing. I know which one I'd prefer to write.

 

As I say the only "cost" here is the addition of an RCALL and a RET to get into/out of the Asm code.

 

To write code like this you do need to know the ABI:

 

https://gcc.gnu.org/wiki/avr-gcc

 

From that I knew that when I wrote:

    setPortB(37);

in the C code that when it arrived at:

setPortB:
    out PORTB, R24

I could be sure that the 37 value would already be in R24 as this is part of the calling convention, also known as the Application Binary Interface or just "ABI".

Last Edited: Thu. Feb 25, 2021 - 05:56 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So I just made an S files and move what I have over but I need to also work out the %1...4 stuff.  Passing in the arrays I'm not sure about either but it may come to me.

#include <avr/io.h>

#define __zero_reg__	r1


FUNCTION(nN64GC_Send, arrayData):
	ldi r18, 9			;byte counter
	ld r16, x+			;move buffer to r16

next_byte 				
	dec	r18				 
	brne skip			;skip when we are not zero( Z flag and dec are not equal. )		 
	ld r16, x+			; move buffer to r16
	ldi r18, 8			;next byte

"skip:					  
	or r16,r16			
	brmi make_a_1		
	rjmp make_a_0		


make_a_0				;0 bit, low.
	lsl r16				;hift right
	PULL_DATA
	DLY_LARGE_1ST
	RELEASE_DATA
	DLY_SHORT_2ND
	sbiw	%1, 1		;check for done. 
	brne next_byte%=	;next byte
	rjmp finished%=		;then we are done
	
make_a_1				;1 bit, hi.
	lsl r16				; shift right
	PULL_DATA
	DLY_SHORT_1ST
	RELEASE_DATA
	DLY_LARGE_2ND
	sbiw	%1, 1		;check for done. 
	brne next_byte%=	;//next byte
	rjmp finished		;then we are done

dly:		
	dec r17	
	brne dly
	ret		

"finished:
;going here is fast so we need to extend the last
;delay by 500nS
	nop
	nop
	nop
	nop
	nop
	PULL_DATA
	rjmp done
;Make sure the controller polls high before we move on
	ldi r16, 0xff		;setup a timeout
stop_bit: 
	dec r16				;decrement timeout
	breq done%=			;handle timeout condition
	sbis %3, 5			;Read the port
	rjmp stop_bit 
done:"
;	:
;	: "I" (_SFR_IO_ADDR(GCN64_DATA_DDR)), // %0
;	  "w" (bits),						// %1
;	  "x" (data),					// %2
;	  "I" (_SFR_IO_ADDR(GCN64_DATA_PIN)),	// %3
;	  "I" (_SFR_IO_ADDR(PORTB))			// %4
;	: "r16", "r17", "r18");	
	ret


FUNCTION(nN64GC_Receive, arrayData):
	clr %0			;clear counter
	ldi r18,8		;set counter
	ld  r17, y 		;load y to r17
                    
StartBit:	  
	sbis %2, 5		;find the end of the start bit
	rjmp startBit	;if its high, we are ready, skip this.
	rjmp nextBit	
                    
NextByte:  
	ldi r18,8		;set counter back to 8
	st  y+, r17 	;load r17 to y and go to next element in array
	ld  r17, y 		;load y to r17
                    
nextBit:   
	ldi r16, 0x80	;54 us timeout, we really do not want to wait longer after, and any bits of 8us is not nintendo. Browlers need 34

	waitlow:	;start bit.
	dec r16			;time out counter
	breq exit		;exit when we are zero( Z  flag and dec are equal. )
;find the next falling edge to sync
	sbic %2, 5		;check if low,then skip
	rjmp waitlow	;wait while low.				
	nop				;move 1us in to place. 
	nop				;
	nop				;
	nop				;
	nop				;
	nop				;
	nop				;
	nop				;
	nop				;
	nop				;
	nop				;
	nop				;
	nop				;
	nop				;
	clc				;clear carry before shifting

; check at this time for bit state
	sbic %2, 5		;if state was low skip 
	sec   			;set Carry flag if true
	rol r17 		;Rol flag on to r17
	inc %0			;count bit.
	ldi r16, 0x80	;54 us timeout, we really do not want to wait longer after, and any bits of 8us is not nintendo. Browlers need 34
waithigh:

;find the next rising edge to sync
	dec r16			;
	breq exit		; exit when we are zero( Z flag and dec are equal. )
	nop				;
	nop				;
	sbis %2, 5		; check for set
	rjmp waithigh	; go back not ready
	dec    r18      ; bit counter
	brne nextBit    ; If Z  flag and dec are not equal. 
	rjmp nextByte	; If Z  flag and dec are equal. 
	ldi r24, 0x00	;
	
exit:

;		: 	"=&r" (count)						// %0
;		: 	"y" ((unsigned char volatile *)nBuffer),// %1
;			"I" (_SFR_IO_ADDR(GCN64_DATA_PIN)),	// %2
;			"I" (_SFR_IO_ADDR(PORTB))			// %3
;		: 	"r16","r17","r18"

	ret

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Two ways to pass in arrays (or anything else for that matter).

 

1) if they are global C variables like:

uint8_t data[] = { 1, 2, 3, 4 };

int main(void)
etc.

then in the .S you can simply use:

    .extern data

entry:
    ldi r26, lo(data)
    ldi r27, hi(data)
    ld r24, X+

or something along those lines.

 

2) or you can pass the address

void asmFunc(uint8_t *);

int main(void) {
    uint8_t data[];

    asmFunc(data); // address will be passed in R25:R24
}
.global asmFunc

asmFunc:
    movw r28, r24
    ld r24, Y+

or similar. Again this uses the ABI. The first parameter if the function call is in R25:R24 so I just moved it to Y (R29:R28) so I could do the LD (could have been X or Z).

 

You DO need to worry about register preservation though (to adhere to the ABI). Some registers must be PUSH/POPd to maintain them.

Last Edited: Thu. Feb 25, 2021 - 06:20 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok so for my arrays I did this (func,parm,parm) is this right, I saw this from another example.?

 

FUNCTION(nN64GC_Receive, arrayDataIn):
    ldi    r26,lo8(arrayDataIn)
    ldi    r27,hi8(arrayDataIn)

 

and

 

FUNCTION(nN64GC_Send, arrayDataOut, numBytes):

    ldi    r26,lo8(numBytes)
    ldi    r27,hi8(numBytes)

Edit both are not global.

call for one
    count = nN64GC_Receive((unsigned char volatile *)nBuffer);

call for two

    nN64GC_Receive(data, n_bytes);

 

Not sure I understand how to tell nN64GC_Receive to return?

 

Last Edited: Thu. Feb 25, 2021 - 06:43 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok, I see what you mean about the parameters now.  I do not know what I was thinking. If the first parm is 25/24 what is next? Is it the next parm r22?

 

 nN64GC_Receive(data, bits);

 

#define yl  r28
#define yh  r29

nN64GC_Send:
    mov yl, r24;dataLo
    mov yh, r25;dataHI

 

but where would bits be, r22?

 

and in the case of count = nN64GC_Receive((unsigned char volatile *)nBuffer);

r22 is put in to count?

 

 

Well here was my best shot at it, the app does not run right, even if I remove the calls to the ASM. So I clearly botched it up. But I think I'm close here...

 

#include <avr/io.h>
#define _SET_DBG	sbi _SFR_IO_ADDR(PORTB), 0
#define _CLR_DBG	cbi _SFR_IO_ADDR(PORTB), 0
#define _PULL_DATA		sbi _SFR_IO_ADDR(DDRC), 5
#define _RELEASE_DATA	cbi _SFR_IO_ADDR(DDRC), 5
#define _DATAOUTPIN	_SFR_IO_ADDR(PINC)

;timings - ok with mini madcats, ok with wavebird, hori not tested.
#define DLY_SHORT_1ST	ldi r17, 2  rcall dly nop nop nop
#define DLY_LARGE_1ST	ldi r17, 11 rcall dly nop nop
#define DLY_SHORT_2ND	nop nop
#define DLY_LARGE_2ND	ldi r17, 6  rcall dly nop nop\ nop

#define xl  r26
#define xh  r27
#define yl  r28
#define yh  r29
#define zl  r30
#define zh  r31
;*****************************************
;data is put in to Y r24/r25 -> r28/29   *
;bits is in r22                          *
;*****************************************
nN64GC_Send:
	ldi r18, 9			;bit counter
	mov yl, r24;dataLo
	mov yh, r25;DataHi
	mov r26, r22; parameter in count
	ld r16, y+			;move buffer to r16

next_byte:
	dec	r18
	brne skip			;skip when we are not zero( Z flag and dec are not equal. )
	ld r16, y+			; move buffer to r16
	ldi r18, 8			;next byte

skip:
	or r16,r16
	brmi make_a_1
	rjmp make_a_0		

make_a_0:				;0 bit, low
	lsl r16				;hift right
	_PULL_DATA
						ldi r17, 11 ;timings
						rcall dly
						nop
						nop
	_RELEASE_DATA
						nop 		;timings
						nop
	sbiw r26, 1		;check for done.
	brne next_byte	;next byte
	rjmp finished	;then we are done

make_a_1:				;1 bit, hi.
	lsl r16				; shift right
	_PULL_DATA
						ldi r17, 2  ;timings
						rcall dly
						nop
						nop
						nop
	_RELEASE_DATA
						ldi r17, 6	;timings
						rcall dly
						nop
						nop
						nop
	sbiw r26, 1		;check for done.
	brne next_byte	;//next byte
	rjmp finished		;then we are done

dly:
	dec r17
	brne dly
	ret		

finished:
;going here is fast so we need to extend the last
;delay by 500nS
	nop
	nop
	nop
	nop
	nop
	_PULL_DATA
	rjmp done
;Make sure the controller polls high before we move on
	ldi r16, 0xff		;setup a timeout
stop_bit:
	dec r16				;decrement timeout
	breq done			;handle timeout condition
	sbis _DATAOUTPIN, 5			;Read the port
	rjmp stop_bit
done:
ret
;*******************************************
;nBuffer is put in to z r24/r25 -> r30/r31 *
; return is put in r22                     *
;*******************************************

nN64GC_Receive:
	mov zl, r24;nBufferLo
	mov zh, r25;nBufferHi
	push r19		;counter
	ldi r18,8		;set counter
	ld  r17, z 		;load y to r17

StartBit:
	sbis _DATAOUTPIN, 5		;find the end of the start bit
	rjmp StartBit	;if its high, we are ready, skip this.
	rjmp NextBit	

NextByte:
	ldi r18,8		;set counter back to 8
	st  z+, r17 	;load r17 to z and go to next element in array
	ld  r17, z 		;load z to r17

NextBit:
	ldi r16, 0x80	;54 us timeout, we really do not want to wait longer after, and any bits of 8us is not nintendo. Browlers need 34

	waitlow:	;start bit.
	dec r16			;time out counter
	breq exit		;exit when we are zero( Z  flag and dec are equal. )
;find the next falling edge to sync
	sbic _DATAOUTPIN, 5		;check if low,then skip
	rjmp waitlow	;wait while low.
	nop				;move 1us in to place.
	nop				;
	nop				;
	nop				;
	nop				;
	nop				;
	nop				;
	nop				;
	nop				;
	nop				;
	nop				;
	nop				;
	nop				;
	nop				;
	clc				;clear carry before shifting

; check at this time for bit state
	sbic _DATAOUTPIN, 5		;if state was low skip
	sec   			;set Carry flag if true
	rol r17 		;Rol flag on to r17
	inc r19			;count bit.
	ldi r16, 0x80	;54 us timeout, we really do not want to wait longer after, and any bits of 8us is not nintendo. Browlers need 34
waithigh:

;find the next rising edge to sync
	dec r16			;
	breq exit		; exit when we are zero( Z flag and dec are equal. )
	nop				;
	nop				;
	sbis _DATAOUTPIN, 5		; check for set
	rjmp waithigh	; go back not ready
	dec    r18      ; bit counter
	brne NextBit    ; If Z  flag and dec are not equal.
	rjmp NextByte	; If Z  flag and dec are equal.
	pop r19			

exit:

ret

 

 

 

Last Edited: Thu. Feb 25, 2021 - 08:55 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Read the ABI I linked to earlier. But, yeah, basically the parameters descend in pairs from 25:24, 23:22, 21:20 and so on. Return is in 25:24 (but read the doc)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

OH, sorry I didn't see a doc link? Most links on this page are just poking fun at me.

 

EDIT: found it in your post...

 

Is there a reason not to use R18–R27, R30, R31  ?  I mean if you do not have to worry about cleanup, and you only need one register pair, why even fool with the call-saved?

 

also after a lot of digging around the net I finally found why my app didn't work, and it was the part I was concerned about. I needed some information up top of the S file to make this all work. Not sure I have it %100 here but at least the app runs. I was missing this.

 

 .globl   N64GC_Send
 .globl   N64GC_Receive
 .type    N64GC_Send,%function
 .type    N64GC_Receive,%function

 

Now my app can call the functions. I guess you need to make function viable globally?

Last Edited: Fri. Feb 26, 2021 - 02:13 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

S_K_U_N_X wrote:
 .globl   N64GC_Send
 .globl   N64GC_Receive
 .type    N64GC_Send,%function
 .type    N64GC_Receive,%function
I assume that is a typo?

 .global   N64GC_Send
 .global   N64GC_Receive
 .type    N64GC_Send,%function
 .type    N64GC_Receive,%function

BTW if you ever wonder how to do stuff in Asm get the C compiler to show you. For example when I wrote:

clawson wrote:

void asmFunc(uint8_t *);

int main(void) {
    uint8_t data[];

    asmFunc(data); // address will be passed in R25:R24
}
.global asmFunc

asmFunc:
    movw r28, r24
    ld r24, Y+

I could test this in C:

__attribute__((noinline)) void asmFunc(uint8_t * data) {
    PORTB = data[3];
}

int main(void) {
    uint8_t data[] = { 1, 2, 3, 4 };
    asmFunc(data);
}

For which the .s file shows the compiler generated this asm for asmFunc():

	.text
.Ltext0:
	.cfi_sections	.debug_frame
	.section	.text.asmFunc,"ax",@progbits
.global	asmFunc
	.type	asmFunc, @function
asmFunc:
.LFB0:
	.file 1 "../Revisions/main-02.c"
	.loc 1 3 0
	.cfi_startproc
.LVL0:
/* prologue: function */
/* frame size = 0 */
/* stack size = 0 */
.L__stack_usage = 0
	.loc 1 4 0
	movw r30,r24	 ; , data
	ldd r24,Z+3	 ;  D.1618, MEM[(uint8_t *)data_2(D) + 3B]
.LVL1:
	out 0x5,r24	 ;  MEM[(volatile uint8_t *)37B], D.1618
	ret
	.cfi_endproc
.LFE0:
	.size	asmFunc, .-asmFunc

and this for main():

	.section	.text.main,"ax",@progbits
.global	main
	.type	main, @function
main:
.LFB1:
	.loc 1 7 0
	.cfi_startproc
	push r28	 ; 
.LCFI0:
	.cfi_def_cfa_offset 3
	.cfi_offset 28, -2
	push r29	 ; 
.LCFI1:
	.cfi_def_cfa_offset 4
	.cfi_offset 29, -3
	 ; SP -= 4	 ; 
	rcall .
	rcall .
.LCFI2:
	.cfi_def_cfa_offset 8
	in r28,__SP_L__	 ; 
	in r29,__SP_H__	 ; 
.LCFI3:
	.cfi_def_cfa_register 28
/* prologue: function */
/* frame size = 4 */
/* stack size = 6 */
.L__stack_usage = 6
	.loc 1 8 0
	ldi r24,lo8(1)	 ;  tmp43,
	std Y+1,r24	 ;  data, tmp43
	ldi r24,lo8(2)	 ;  tmp44,
	std Y+2,r24	 ;  data, tmp44
	ldi r24,lo8(3)	 ;  tmp45,
	std Y+3,r24	 ;  data, tmp45
	ldi r24,lo8(4)	 ;  tmp46,
	std Y+4,r24	 ;  data, tmp46
	.loc 1 9 0
	movw r24,r28	 ; ,
	adiw r24,1	 ; ,
	call asmFunc	 ; 
.LVL2:
	.loc 1 10 0
	ldi r24,0	 ; 
	ldi r25,0	 ; 
/* epilogue start */
	 ; SP += 4	 ; 
	pop __tmp_reg__
	pop __tmp_reg__
	pop __tmp_reg__
	pop __tmp_reg__
	pop r29	 ; 
	pop r28	 ; 
	ret
	.cfi_endproc
.LFE1:
	.size	main, .-main

A lot of that is simply sprinkling the info needed to debug things around the code so sometimes it can be easier to read the .s from the Release build:

	.file	"main-02.c"
__SP_H__ = 0x3e
__SP_L__ = 0x3d
__SREG__ = 0x3f
__tmp_reg__ = 0
__zero_reg__ = 1
	.text
.Ltext0:
	.cfi_sections	.debug_frame
	.section	.text.asmFunc,"ax",@progbits
.global	asmFunc
	.type	asmFunc, @function
asmFunc:
.LFB0:
	.file 1 "../Revisions/main-02.c"
	.loc 1 3 0
	.cfi_startproc
.LVL0:
/* prologue: function */
/* frame size = 0 */
/* stack size = 0 */
.L__stack_usage = 0
	.loc 1 4 0
	movw r30,r24
	ldd r24,Z+3
.LVL1:
	out 0x5,r24
	ret
	.cfi_endproc
.LFE0:
	.size	asmFunc, .-asmFunc
	.section	.rodata
.LC0:
	.byte	1
	.byte	2
	.byte	3
	.byte	4
	.section	.text.startup.main,"ax",@progbits
.global	main
	.type	main, @function
main:
.LFB1:
	.loc 1 7 0
	.cfi_startproc
	push r28
.LCFI0:
	.cfi_def_cfa_offset 3
	.cfi_offset 28, -2
	push r29
.LCFI1:
	.cfi_def_cfa_offset 4
	.cfi_offset 29, -3
	rcall .
	rcall .
.LCFI2:
	.cfi_def_cfa_offset 8
	in r28,__SP_L__
	in r29,__SP_H__
.LCFI3:
	.cfi_def_cfa_register 28
/* prologue: function */
/* frame size = 4 */
/* stack size = 6 */
.L__stack_usage = 6
	.loc 1 8 0
	lds r24,.LC0
	lds r25,.LC0+1
	lds r26,.LC0+2
	lds r27,.LC0+3
	std Y+1,r24
	std Y+2,r25
	std Y+3,r26
	std Y+4,r27
	.loc 1 9 0
	movw r24,r28
	adiw r24,1
	call asmFunc
.LVL2:
	.loc 1 10 0
	ldi r24,0
	ldi r25,0
/* epilogue start */
	pop __tmp_reg__
	pop __tmp_reg__
	pop __tmp_reg__
	pop __tmp_reg__
	pop r29
	pop r28
	ret
	.cfi_endproc
.LFE1:
	.size	main, .-main
	.text

Or perhaps even to switch off the debug info (set to -g2 under "Debugging" by default) all together:

	.file	"main-02.c"
__SP_H__ = 0x3e
__SP_L__ = 0x3d
__SREG__ = 0x3f
__tmp_reg__ = 0
__zero_reg__ = 1
	.section	.text.asmFunc,"ax",@progbits
.global	asmFunc
	.type	asmFunc, @function
asmFunc:
/* prologue: function */
/* frame size = 0 */
/* stack size = 0 */
.L__stack_usage = 0
	movw r30,r24
	ldd r24,Z+3
	out 0x5,r24
	ret
	.size	asmFunc, .-asmFunc
	.section	.rodata
.LC0:
	.byte	1
	.byte	2
	.byte	3
	.byte	4
	.section	.text.startup.main,"ax",@progbits
.global	main
	.type	main, @function
main:
	push r28
	push r29
	rcall .
	rcall .
	in r28,__SP_L__
	in r29,__SP_H__
/* prologue: function */
/* frame size = 4 */
/* stack size = 6 */
.L__stack_usage = 6
	lds r24,.LC0
	lds r25,.LC0+1
	lds r26,.LC0+2
	lds r27,.LC0+3
	std Y+1,r24
	std Y+2,r25
	std Y+3,r26
	std Y+4,r27
	movw r24,r28
	adiw r24,1
	call asmFunc
	ldi r24,0
	ldi r25,0
/* epilogue start */
	pop __tmp_reg__
	pop __tmp_reg__
	pop __tmp_reg__
	pop __tmp_reg__
	pop r29
	pop r28
	ret
	.size	main, .-main
	.ident	"GCC: (AVR_8_bit_GNU_Toolchain_3.6.2_1778) 5.4.0"
.global __do_copy_data

But, anyway, the point is the C compiler can teach you how to write the kind of Asm that the assembler likes to see.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It may be time you stumped up the cash for a real debugger capable of setting a Data Breakpoint. This will find the source of your Clobbering/Globbering problem in an instant.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

But this is so simple, just run it in the simulator

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

the ICE is on the way.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson I have to give you my profound thank you. I really have been waning to move this to a S file. Quite happy with it but by a shocking surprise the original issue came back just as I finished testing all the ASM and got it to work. 

 

EDIT: I can clearly see the data going in to my damaged variable is from uBuffer[0] as that is the exact value and it matches when changed.

Here is the exact line that does it

NextByte:  
    ldi r18,8        ;set counter back to 8
    st  z+, r17     ;go to next element in array
    ld  r17,0         ;clear

 

if I put a return just before that line, the damaged variable is not effected, but returning after that line it is. Is the ASM parameter or return of r24 causing my issue? It seems that whatever data is first  put on to r24, stays in my damaged variable after the asm. Maybe it is a code logic issue? The returned array is correct. The idea here is to take 8 bits, and then roll that on to the array and that seems to work (tested a few bytes worth). The function also return count, and it is always correct.

 

 

EDIT2: " Both spellings (‘.globl’ and ‘.global’) are accepted, for compatibility with other assemblers. " -- That's why it worked.

 

Figured I post my current progresses of that function..

N64GC_Receive:
	mov zl, r24		;nBufferLo - Call Used
	mov zh, r25		;nBufferHi - Call Used
	clr	r24			; free up for counter
	push r17		;array - Call saved
	push r16		;array - Call saved
	ldi r18,8		;set bit counter - Call Used
	ld  r17, 0 		;clear

StartBit:
	sbis _DATAOUTPIN, 5		;find the end of the start bit
	rjmp StartBit	;if its high, we are ready, skip this.
	rjmp NextBit	

NextByte:
	ldi r18,8		;set counter back to 8
	st  z+, r17 	;go to next element in array
	ld  r17, 0 	;clear
_SET_DBG
_CLR_DBG

NextBit:
	ldi r16, 0x80	;54 us timeout, we really do not want to wait longer after, and any bits of 8us is not nintendo. Browlers need 34

	waitlow:	;start bit.
	dec r16			;time out counter
	breq exit		;exit when we are zero( Z  flag and dec are equal. )
;find the next falling edge to sync
	sbic _DATAOUTPIN, 5		;check if low,then skip
	rjmp waitlow	;wait while low.
	nop				;move 1us in to place.
	nop				;
	nop				;
	nop				;
	nop				;
	nop				;
	nop				;
	nop				;
	nop				;
	nop				;
	nop				;
	nop				;
	nop				;
	nop				;
	clc				;clear carry before shifting

; check at this time for bit state
	sbic _DATAOUTPIN, 5		;if state was low skip
	sec   			;set Carry flag if true
	rol r17 		;Rol flag on to r17
	inc r24			;count bit.
	ldi r16, 0x80	;54 us timeout, we really do not want to wait longer after, and any bits of 8us is not nintendo. Browlers need 34
waithigh:

;find the next rising edge to sync
	dec r16			;
	breq exit		; exit when we are zero( Z flag and dec are equal. )
	nop				;
	nop				;
	sbis _DATAOUTPIN, 5		; check for set
	rjmp waithigh	; go back not ready
	dec    r18      ; bit counter
	brne NextBit    ; If Z  flag and dec are not equal.
	rjmp NextByte	; If Z  flag and dec are equal.
exit:
	pop r17
	pop r16
ret

 

-------------------------------------------------------------------------------------------------------------------------------

Wondering how this works in memory? -(slightly off topic but trying to figure this out in my head)

If r24 and r25 take in the array, does it hold the address to memory? That is the value of r24/r25 is nothing more then an address?

st  z+, r20 - This tells z to point to address + size and r20 is the value that pointer points to.
So what register holds the data then?

Ebc0:A000 <---- location of my array [0], that location is stored in r24/r25 ?

Ebc0:A001 <-----  after st  z+, r20, r24/r25 now points to this location in memory.

Ebc0:A002 <-----  after st  z+, r20, r24/r25 now points to this location in memory.

Ebc0:A003 <-----  after st  z+, r20, r24/r25 now points to this location in memory.

.

.

.

 

IF so then my damaged data must have been at memory location. Ebc0:A001 and some register knows that. So do I have a memory corruption here or a register issue?

 

 

 

 

 

Last Edited: Sat. Feb 27, 2021 - 12:42 AM
This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 

Well ok then, that's done, I'm not really sure I see how this works but I guess the rule if thumb is clear up your mess?

 

after  the ASM was done, and I used my array data, I just cleared it up.
memset(nBuffer, 0, 35);

and the problem was gone.

 

Last Edited: Sat. Feb 27, 2021 - 01:01 AM