lucien2 Posted August 1, 2011 Share Posted August 1, 2011 I'm trying to make some low level library for GCC. When the TI starts up, the SCAN routine (>000E) reads only uppercase. Then when I load Editor/Assembler, it reads lowercase too. What do I need to do to have lowercase with KSCAN without loading E/A? Some CRU stuff with Alphalock? Here's KSCAN and GPLLNK for GCC (sorry, no comments). The only problem is this lowercase thing. Assembly part gplws equ 0x83E0 gr4 equ gplws+8 gr6 equ gplws+12 stkpnt equ 0x8373 ldgadd equ 0x60 xtab27 equ 0x200E getstk equ 0x166C def memcpy memcpy L1 movb *r2+,*r1+ dec r3 jne L1 b *r11 def kscan kscan lwpi gplws bl @>E lwpi >8300 b *r11 def gpllnk glnkws equ 0x2000 gpllnk data glnkws,glink1 rtnad data xmlrtn gxmlad data 0x176C,0x50 glink1 li r0,gpllnk li r2,>200e li r3,5 gplln0 mov *r0+,*r2+ dec r3 jne gplln0 mov *r11,@gr4 mov @2(R13),@gr6 mov @xtab27,r12 mov r9,@xtab27 lwpi gplws bl *r4 mov @gxmlad,@0x8302(r4) inct @stkpnt b @ldgadd xmlrtn mov @getstk,r4 bl *r4 lwpi glnkws mov r12,@xtab27 rtwp C part #define VDP_READ_DATA_REG (*(volatile char*)0x8800) #define VDP_WRITE_DATA_REG (*(volatile char*)0x8C00) #define VDP_ADDRESS_REG (*(volatile char*)0x8C02) #define VDP_READ_FLAG 0x00 #define VDP_WRITE_FLAG 0x40 #define VDP_REG_FLAG 0x80 #define SEED (*(int*)0x83C0) #define STATUS (*(char*)0x837C) #define KEY_UNIT (*(char*)0x8374) #define KEY (*(char*)0x8375) #define JOYST_X (*(char*)0x8376) #define JOYST_Y (*(char*)0x8377) #define FAC (*(volatile int*)0x834A) void vdp_copy_from_sys(int index, char* src, int size) { char* end = src + size; VDP_ADDRESS_REG = index; VDP_ADDRESS_REG = (char)(index >> | VDP_WRITE_FLAG; while(src < end) VDP_WRITE_DATA_REG = *src++; } void vdp_write_reg(int i,char c) { VDP_ADDRESS_REG=c; VDP_ADDRESS_REG=i|VDP_REG_FLAG; } void interrupts() { asm("limi 2"); asm("limi 0"); } char random_byte(char c) { SEED=SEED*0x6FE5+0x7AB9; int i=((unsigned)SEED>>+(SEED<<; return (unsigned)i%c; } char vdp_read_byte(int index) { VDP_ADDRESS_REG = index; VDP_ADDRESS_REG = (char)(index >> | VDP_READ_FLAG; asm("nop"); return VDP_READ_DATA_REG; } int key_scan(char* key) { KEY_UNIT=0; asm("bl @kscan"); *key=KEY; char c=STATUS&0x20; return c; } void gpl_link(int addr) { asm( "mov %0,r1\n\t" "blwp @gpllnk" : :"r"(addr) :"r1" ); } void main() { int i=0; char c; FAC=0x900; gpl_link(0x18); FAC=0xB00; gpl_link(0x4A); while(1) { interrupts(); if(key_scan(&c)) { vdp_copy_from_sys(i,&c,1); /* c=random_byte(5); */ i++; if(i>=32*24)i=0; gpl_link(0x34); } } } Quote Link to comment Share on other sites More sharing options...
Willsy Posted August 1, 2011 Share Posted August 1, 2011 The keyboard type is set by writing to the byte >8374. TurboForth writes the value >05 into >8374 to enable a 'full' keyboard scan in both upper and lower case. More info here: http://nouspikel.group.shef.ac.uk//ti99/tutor1.htm#KSCAN Quote Link to comment Share on other sites More sharing options...
lucien2 Posted August 1, 2011 Author Share Posted August 1, 2011 The keyboard type is set by writing to the byte >8374. TurboForth writes the value >05 into >8374 to enable a 'full' keyboard scan in both upper and lower case. More info here: http://nouspikel.group.shef.ac.uk//ti99/tutor1.htm#KSCAN Thanks, it works! int key_scan(char* key) { KEY_UNIT=5; asm("bl @kscan"); *key=KEY; char c=STATUS&0x20; return c; } Quote Link to comment Share on other sites More sharing options...
sometimes99er Posted August 1, 2011 Share Posted August 1, 2011 char c=STATUS&0x20; return c; Can you do this instead ? If not, why not ? If yes, will it be that bit smaller in size and more speedy ? I guess I could try it out, but I haven't followed the GCC stuff. Sorry. return STATUS&0x20; Quote Link to comment Share on other sites More sharing options...
lucien2 Posted August 1, 2011 Author Share Posted August 1, 2011 Can you do this instead ? If not, why not ? If yes, will it be that bit smaller in size and more speedy ? I guess I could try it out, but I haven't followed the GCC stuff. Sorry. return STATUS&0x20; That's what I did at first: #define STATUS (*(char*)0x837C) int key_scan(char* key) { KEY_UNIT=5; asm("bl @kscan"); *key=KEY; return STATUS&0x20; } def key_scan key_scan li r2, >500 movb r2, @-31884 * Begin inline assembler code * 47 "main.c" 1 bl @kscan * End of inline assembler code movb @-31883, *r1 movb @-31876, r1 andi r1, >20 b *r11 With a "char" local variable: #define STATUS (*(char*)0x837C) int key_scan(char* key) { KEY_UNIT=5; asm("bl @kscan"); *key=KEY; char c=STATUS&0x20; return c; } def key_scan key_scan li r2, >500 movb r2, @-31884 * Begin inline assembler code * 47 "main.c" 1 bl @kscan * End of inline assembler code movb @-31883, *r1 movb @-31876, r1 andi r1, >2000 sra r1, 8 b *r11 I'm not expert enough in C to say that it's wrong. Quote Link to comment Share on other sites More sharing options...
sometimes99er Posted August 1, 2011 Share Posted August 1, 2011 (edited) Okay, clearly two different results. So you can't do the short version, for now or for good. Maybe it's down to why 9900 put the byte in the most significant rather than the other one. Is there a good reason for them (TI designers) choosing one over the other ? Edited August 1, 2011 by sometimes99er Quote Link to comment Share on other sites More sharing options...
Willsy Posted August 1, 2011 Share Posted August 1, 2011 That looks correct, it would return an int. It may not be what you intended, but I think the compiler got it right...? Quote Link to comment Share on other sites More sharing options...
Tursi Posted August 2, 2011 Share Posted August 2, 2011 No, the first block is wrong, since it's masking the low byte of the word but put the data in the high byte. It's perfectly legal C syntax to do return STATUS&0x20, GCC seems to have gotten confused about the data size. It knew it was a char when it did the MOVB, but then treated it as an integer for the masking. What's missing is it didn't expand from char to int inbetween (or, in fact, before returning, so the return value would be multiplied by 256). It looks like a type conversion bug. I wonder what the code would be if it had been return STATUS&(char)0x20; ? I guess either way though, it missed the conversion from char to int. That's not a show stopper, and it's thrilling to see how much works, but it is a gotcha. Quote Link to comment Share on other sites More sharing options...
insomnia Posted August 2, 2011 Share Posted August 2, 2011 (edited) Well, that's funky. A few things you should know: By using inline assembly, to call "kscan" you are preventing GCC from knowing that it has to retain the "key" pointer stored in R1 and return pointer in R11. If "kscan" were to use these registers, the values stored there would be destroyed, and the C code would behave weirdly. That can be fixed by doing something like this: extern void kscan(); int key_scan(char* key) { KEY_UNIT=5; kscan(); *key=KEY; char c=STATUS&0x20; return c; } That results in this assembly: def key_scan key_scan ai r10, >FFFC mov r11, *r10 mov r9, @2(r10) mov r1, r9 li r1, >500 movb r1, @-31884 bl @kscan movb @-31883, *r9 movb @-31876, r1 andi r1, >2000 sra r1, 8 mov *r10+, r11 mov *r10+, r9 b *r11 ref kscan The different results you are seeing in the return values are due to GCC removing a sign extension on the read value of the STATUS macro. During the register allocation step, GCC is trying to be helpful and removing what it thinks is an unnecessary operation, since it believes that byte quantities are already stored in the least significant byte of the register. I missed this when I was fixing things for the weird byte format. By using the temporary variable, you are forcing the change in data types. I need to go take a hard look at the compiler and find a fix for this. (The decimal addresses are also annoying and unhelpful. That increment in "mov *r10+, r9" should probably be removed too. Looks like I'll be busy for a while.) By the way, I think TI chose to store bytes this way to allow the comparison logic to work for word and byte values. This would also remove the need for sign extend and zero extend logic. It also opens up the possibility of storing data in the otherwise unused low byte. I don't have anything to back up this reasoning, but it seems to make sense. In any event, no matter why this decision was made, we're stuck with it now. Edited August 2, 2011 by insomnia Quote Link to comment Share on other sites More sharing options...
Willsy Posted August 2, 2011 Share Posted August 2, 2011 I missed this when I was fixing things for the weird byte format. Weird format? WEIRD FORMAT? It might be in your world, mate, but it's perfectly normal for us! By the way, I think TI chose to store bytes this way to allow the comparison logic to work for word and byte values. This would also remove the need for sign extend and zero extend logic. It also opens up the possibility of storing data in the otherwise unused low byte. I don't have anything to back up this reasoning, but it seems to make sense. In any event, no matter why this decision was made, we're stuck with it now. Well, big endian is actually very common. The TMS9900 may have been one of the first/the first bits of silicon to be big endian, but IIRC all the mini's of the day were big endian. The entire 68K range is big endian etc... Don't forget the TMS9900 was an attempt to consolidate an entire CPU board into a single chip, so the big-endian constraint was already established. Anyway, sorry to hijack... I'm getting away from the GCC compiler... I'll shut up now! Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.