westfw:
Here's the cookbook I referenced:
Inline Assembler Cookbook
You want your code to look like:
int main(void) {
asm (
"init: \n" // Initialize routine.
// tbl address is already in z
" movw r24, %[tbl] \n"
:
: [tbl] "z" (tbl)
:
);
}
The "z" modifier all cause the compiler to put the designated thing in the "z" register for you.
Thank you everyone for your help!
First, the 'z' modifier is the key if you want to let the compiler load the 'Z' register for you. Second, in gcc's inline asm writing '%[tbl]' to get the address of the table is incorrect.
Here is a small example. The first '#if'd' block demonstrates letting the compiler load the Z register. The second demonstrates loading it from a pointer. Following is the objdump showing the asm output for both methods.
#include "Arduino.h"
//
//sintbl[]
// First 8 bytes of a 256 byte sine table
//
const uint8_t sintbl[8] PROGMEM = {
0x80,0x83,0x86,0x89,0x8C,0x8F,0x92,0x95
};
//
//main()
// Load first byte of sin table into r24, increment Z. NOP's exist to
// help find in objdump.
//
int main(void) {
#if 1
asm (
"compiler_lod_z: \n"
" NOP \n"
" LPM r24, Z+ \n"
" NOP \n"
:
: [tbl] "z" (sintbl)
:
);
#else
asm (
"assembler_lod_z: \n"
" NOP \n"
" LDI r30, lo8(sintbl) \n"
" LDI r31, hi8(sintbl) \n"
" LPM r24, Z+ \n"
" NOP \n"
:
:
:
);
#endif
}
For both examples the table is stored in address 0x0068:
00000068 <__trampolines_end>:
68: 80 83 st Z, r24
6a: 86 89 ldd r24, Z+22 ; 0x16
6c: 8c 8f std Y+28, r24 ; 0x1c
6e: 92 95 swap r25
First example (r30,31 are the 'Z' register):
00000088 <main>:
88: e8 e6 ldi r30, 0x68 ; 104
8a: f0 e0 ldi r31, 0x00 ; 0
0000008c <compiler_lod_z>:
8c: 00 00 nop
8e: 85 91 lpm r24, Z+
90: 00 00 nop
Second example (r30,31 are the 'Z' register):
00000088 <main>:
88: 00 00 nop
8a: e8 e6 ldi r30, 0x68 ; 104
8c: f0 e0 ldi r31, 0x00 ; 0
8e: 85 91 lpm r24, Z+
90: 00 00 nop
I'm not sure if this behavior will persist but now at least I know what I should be looking for. 
Again, thank you for all your help!
Appreciatively,
-Richard