dajacobs wrote:But to clarify, when you refer to the XMEGA initialization code when is that process accomplished?
There is some initialization code that is pulled in from the ZX Library when your application is linked. Consider this simple program:
Code: Select all
Dim adcVal as Integer
Sub Main()
adcVal = GetADC(A.0)
End Sub
When this application is compiled for xmega-based ZX with the compiler flags --list and --keep-files, the resulting .lss file contains these fragments:
Code: Select all
392: 0e 94 39 09 call 0x1272 ; 0x1272 <calibrateADC>
...
00001272 <calibrateADC>:
1272: 80 e2 ldi r24, 0x20 ; 32
1274: 0e 94 48 09 call 0x1290 ; 0x1290 <readCalibrationWord>
1278: e0 e0 ldi r30, 0x00 ; 0
127a: f2 e0 ldi r31, 0x02 ; 2
127c: 84 87 std Z+12, r24 ; 0x0c
127e: 95 87 std Z+13, r25 ; 0x0d
1280: 84 e2 ldi r24, 0x24 ; 36
1282: 0e 94 48 09 call 0x1290 ; 0x1290 <readCalibrationWord>
1286: e0 e4 ldi r30, 0x40 ; 64
1288: f2 e0 ldi r31, 0x02 ; 2
128a: 84 87 std Z+12, r24 ; 0x0c
128c: 95 87 std Z+13, r25 ; 0x0d
128e: 08 95 ret
00001290 <readCalibrationWord>:
1290: aa ec ldi r26, 0xCA ; 202
1292: b1 e0 ldi r27, 0x01 ; 1
1294: 92 e0 ldi r25, 0x02 ; 2
1296: 9c 93 st X, r25
1298: 90 e0 ldi r25, 0x00 ; 0
129a: fc 01 movw r30, r24
129c: 25 91 lpm r18, Z+
129e: 34 91 lpm r19, Z+
12a0: 1c 92 st X, r1
12a2: c9 01 movw r24, r18
12a4: 08 95 ret
dajacobs wrote:My intent was not to modify registers willy nilly
It wasn't my intent to be insulting but, rather, to make a point. I should have phrased it differently.
I was referring to the fact that the two #asm...#endasm sequences changed the processor general purpose registers (i.e. r0 through r31). There is a way to inform the back end compiler that an assembly language sequence modifies specific registers but even that mechanism doesn't allow for conveying register usage information from one inline assembly sequence to another. Generally, each inline assembly sequence must either stand alone or else convey intermediate results through C variables. This limitation, along with the very complex syntax of the gcc inline assembly construct, leads many to opt for writing C-callable assembly routines rather than using inline assembly. You'll see advice to do just this quite frequently on the avrfreaks.net forum.
Below is an example of a C-callable assembly language function to read a calibration word. Place this in a file called, for example, readCalWord.S (note that an upper case S extension *must* be used) and add that filename to your .pjt file.
Code: Select all
#include <avr/io.h>
#if !defined(NVM_CMD_READ_CALIB_ROW_gc)
#define NVM_CMD_READ_CALIB_ROW_gc 0x02
#endif
.section .text
/*
** readCalWord
*
* Read a 16-bit value from the production signature row.
*
* On entry: r24 has the index from which to read
*
* On exit: r25:24 has the 16-bit value
*
* C-callable: uint16_t readCalWord(uint8_t idx)
*
* Modifies: r22, r24, r25, r30, r31
*
*/
.global readCalWord
readCalWord:
#define sregSave r22
#define idx r24
#if defined(__AVR_XMEGA__)
// make r31:30 point to the desired word to read
mov r30, idx
clr r31
// disable interrupts
in sregSave, SREG
cli
// read the calibration row word
ldi r24, NVM_CMD_READ_CALIB_ROW_gc
sts NVM_CMD, r24
lpm r24, Z+
lpm r25, Z
// restore the NVM command register and the interrupt enable status
sts NVM_CMD, r1
out SREG, sregSave
#else
ldi r24, 0
ldi r25, 0
#endif
ret
#undef sregSave
#undef idx
.end
Then, you can call the assembly language function like this:
Code: Select all
Declare Function readCalWord(ByVal idx as Byte) as UnsignedInteger
Sub Main()
Debug.Print "&H"; CStrHex(readCalWord(&H20))
End Sub