Last visit was: Sat Dec 21, 2024 2:24 pm
|
It is currently Sat Dec 21, 2024 2:24 pm
|
Porting the C64 compiler to target OPC5 (or OPC6)
Author |
Message |
BigEd
Joined: Wed Jan 09, 2013 6:54 pm Posts: 1808
|
I wonder if, for clarity, we could write Code: memory store - sto rdest, rsrc, imm - mem(rsrc + imm) <- rdest
as Code: memory store - sto rdata, raddress, imm - mem(raddress + imm) <- rdata (I'm not sure!)
|
Tue Aug 15, 2017 5:46 pm |
|
|
Revaldinho
Joined: Tue Apr 25, 2017 7:33 pm Posts: 32
|
Well, I still slightly prefer the original in the spec but only because in the context of the whole instruction table it's clear that very nearly all instructions perform the same effective data/address calculation. Something else which is also becoming clear though is that we could do with more documentation, maybe in a tutorial style, on top of the original one page spec. R
|
Tue Aug 15, 2017 6:02 pm |
|
|
BigEd
Joined: Wed Jan 09, 2013 6:54 pm Posts: 1808
|
Indeed! The oddity with store is that the LHS register is a source, which is different from all others. I know why that is, and it's not too much of a cognitive load, but if there's a way to make it less surprising that would be nice.
|
Tue Aug 15, 2017 6:04 pm |
|
|
robfinch
Joined: Sat Feb 02, 2013 9:40 am Posts: 2231 Location: Canada
|
The compiler was failing to initialize the temporaries properly after changing it to be two pass. It decided to load the address of the putchar() routine into a register because it was used so many times, but then didn't load the register with the putchar() address.
'C' doesn't have a way to represent an I/O address versus a memory address. If it did the compiler could output in / out instructions instead of ld / sto. I've been trying to think of an easy way to specify I/O in C64. There is the volatile keyword, but perhaps another keyword for I/O is in order. The current means of handling I/O in C is to use function calls that implement the I/O instructions. 'io' is too short of a keyword and likely used already by C programs. Although '__io' would be okay. (Double underscore names are reserved for the compiler). It's really for the address calculation when pointers are used. The '&' operator return the address of a variable.
Declaring something like: __io char *ptr; Would tell the compiler ptr points to I/O and not memory.
The other issue with I/O is fixing the I/O address associated with a variable. It would be nice to be able to specify something like: __io screenbuf[2000] at 0xa000
_________________Robert Finch http://www.finitron.ca
|
Tue Aug 15, 2017 7:43 pm |
|
|
hoglet
Joined: Tue Feb 10, 2015 7:07 am Posts: 52
|
Hi Rob, Thanks for the speedy update. That has fixed math16.c. But pi is still not working. The first error I spotted is with: Code: x = 10 * pi[i]+ q * i;
Code: 018f 10c6 fea9 mov r6,r12,-343 0191 0035 mov r5,r3 0192 0465 add r5,r6 0193 0051 mov r1,r5 # should be ld r1, r5 0194 1002 000a mov r2,r0,10 0196 190d 0332 jsr r13,r0,__mul 0198 0015 mov r5,r1 0199 17c1 fffa ld r1,r12,-6 019b 0032 mov r2,r3 019c 190d 0332 jsr r13,r0,__mul 019e 0016 mov r6,r1 019f 0051 mov r1,r5 01a0 0461 add r1,r6 01a1 0015 mov r5,r1 01a2 0054 mov r4,r5
Separately, there is an issue in math32.c with setting the MS word of a 32-bit variable. Code: void init() { asm { ORG 0x100 mov r15, r0, _main }; }
void putchar(char c) { asm __leafs { ld r1, r14, 1 jsr r13, r0, 0xffee }; }
void main() { long a,b,c,d,e; a = 123456L; b = 1234L; c = a * b; if (c == 0x9149880) { putchar('.'); } else { putchar('x'); } d = a / b; if (d == 100) { putchar('.'); } else { putchar('x'); } e = a % b; if (e == 56) { putchar('.'); } else { putchar('x'); } putchar(10); putchar(13); }
Code: 010a _main: 010a 28ed push r13,r14 010b 28ec push r12,r14 010c 00ec mov r12,r14 010d 0eee dec r14,14 010e 1005 e240 mov r5,r0,57920 0110 1006 0000 mov r6,r0,r0 # malformed, should be mov r6, r0, 1 0112 16c5 fffe sto r5,r12,-2 0114 16c6 ffff sto r6,r12,-1 0116 1005 04d2 mov r5,r0,1234 0118 1006 0000 mov r6,r0,r0 # malformed, should be mov r6, r0 011a 16c5 fffc sto r5,r12,-4 011c 16c6 fffd sto r6,r12,-3
Dave
|
Tue Aug 15, 2017 8:32 pm |
|
|
hoglet
Joined: Tue Feb 10, 2015 7:07 am Posts: 52
|
Hi Rob, I've just re-tested the latest compiler (git commit 73c5ce2): - Pi is working - Math16 is working - Math32 is failing at one of the 32-bit compares: Code: d = a / b; if (d == 100) { putchar('.'); } else { putchar('x'); }
Code: 0148 17c1 fffe ld r1,r12,-2 014a 17c2 ffff ld r2,r12,-1 014c 28e3 push r3,r14 014d 28e4 push r4,r14 014e 17c3 fffc ld r3,r12,-4 0150 17c4 fffd ld r4,r12,-3 0152 190d 0289 jsr r13,r0,__div32 0154 29e4 pop r4,r14 0155 29e3 pop r3,r14 0156 0015 mov r5,r1 0157 0026 mov r6,r2 0158 16c5 fff8 sto r5,r12,-8 015a 16c6 fff9 sto r6,r12,-7 015c 17c6 fff9 ld r6,r12,-7 015e 17c5 fff8 ld r5,r12,-8 0160 1004 0064 mov r4,r0,100 0162 0008 mov r8,r0 # r8 is never used 0163 0049 mov r9,r4 # r9 is never used 0164 0005 mov r5,r0 # ??? r5 is over-written here 0165 0204 or r4,r0 # ??? 0166 d005 ffff mi.mov r5,r0,-1 # ??? 0168 2a75 cmp r5,r7 # seems like r7 is incorrect 0169 2b36 cmpc r6,r3 # seems like r3 is incorrect 016a 6c7f nz.inc r15,math32_32-PC 016b 1005 002e mov r5,r0,46 016d 28e5 push r5,r14 016e 190d 0103 jsr r13,r0,_putchar 0170 0c1e inc r14,1 0171 0c6f inc r15,math32_33-PC 0172 math32_32: 0172 1005 0078 mov r5,r0,120 0174 28e5 push r5,r14 0175 190d 0103 jsr r13,r0,_putchar 0177 0c1e inc r14,1 0178 math32_33:
All the programs I'm testing with are here: https://github.com/hoglet67/opc/tree/c_ ... i-spigot-cDave
|
Wed Aug 16, 2017 8:17 am |
|
|
hoglet
Joined: Tue Feb 10, 2015 7:07 am Posts: 52
|
One more thing... if use long instead of int in the pi test program I get a core dump: Code: dmb@quadhog:~/atom/opc/examples/pi-spigot-c$ ./c64 pi.c Segmentation fault (core dumped)
dmb@quadhog:~/atom/opc/examples/pi-spigot-c$ gdb ./c64 core Core was generated by `./c64 pi.c'. Program terminated with signal SIGSEGV, Segmentation fault. #0 0x08078ab7 in copy_addr (ap=0x807b427 <ZeroMemory(void*, size_t)+32>) at Peepgen.cpp:53 53 if (ap->offset->nodetype==en_icon) { (gdb) bt #0 0x08078ab7 in copy_addr (ap=0x807b427 <ZeroMemory(void*, size_t)+32>) at Peepgen.cpp:53 #1 0x08078e35 in GenerateDiadic (op=7, len=0, ap1=0xa3c88bc, ap2=0x807b427 <ZeroMemory(void*, size_t)+32>) at Peepgen.cpp:144 #2 0x0804cb7b in GenStore (ap1=0x807b427 <ZeroMemory(void*, size_t)+32>, ap3=0xa3c875c, size=2) at CodeGenerator.cpp:448 #3 0x08052bfb in GenerateAssignModiv (node=0xa3c536c, flags=33807, size=2) at CodeGenerator.cpp:2122 #4 0x080551be in GenerateExpression (node=0xa3c536c, flags=33807, size=2) at CodeGenerator.cpp:2937 #5 0x0805b7a8 in Statement::Generate (this=0xa3c1f84) at GenerateStatement.cpp:742 #6 0x0805b62e in Statement::GenerateCompound (this=0xa3c1d44) at GenerateStatement.cpp:677 #7 0x0805b700 in Statement::Generate (this=0xa3c1d44) at GenerateStatement.cpp:713 #8 0x08059e5d in Statement::GenerateWhile (this=0xa3c1c84) at GenerateStatement.cpp:193 #9 0x0805b816 in Statement::Generate (this=0xa3c1c84) at GenerateStatement.cpp:761 #10 0x0805b62e in Statement::GenerateCompound (this=0xa3c1b04) at GenerateStatement.cpp:677 #11 0x0805b700 in Statement::Generate (this=0xa3c1b04) at GenerateStatement.cpp:713 #12 0x0806352f in GenerateFunction (sym=0x80a153c <compiler+42652>) at OPC6.cpp:560 #13 0x08076428 in ParseFunctionBody (sp=0x80a153c <compiler+42652>) at ParseFunction.cpp:478 #14 0x08075dbe in ParseFunction (sp=0x80a153c <compiler+42652>) at ParseFunction.cpp:340 #15 0x0806bfd5 in Declaration::declare (parent=0x0, table=0x8095180 <gsyms>, al=2, ilc=0, ztype=28) at ParseDeclarations.cpp:1391 #16 0x0806c590 in GlobalDeclaration::Parse (this=0xa3bda44) at ParseDeclarations.cpp:1533 #17 0x0805632f in Compiler::compile (this=0x8096ea0 <compiler>) at Compiler.cpp:104 #18 0x0804b773 in main (argc=1, argv=0xbf961128) at Cmain.cpp:83 (gdb) p ap $1 = (AMODE *) 0x807b427 <ZeroMemory(void*, size_t)+32> (gdb) p ap->offset $2 = (ENODE *) 0x875cf (gdb) p ap->offset->nodetype Cannot access memory at address 0x875cf (gdb)
|
Wed Aug 16, 2017 8:20 am |
|
|
robfinch
Joined: Sat Feb 02, 2013 9:40 am Posts: 2231 Location: Canada
|
Fixed the crash in the assign divide logic. Fixed the long compare. Redundant sign extension of immediate values was removed. For long math if a an 'L' is not specified after a constant number it may cause more code to be generated. (It loads the constant into a reg rather than using it directly sometimes).
Long math is still broken if no_regs is specified for the routine and the compiler uses memory variables. The compiler needs to be coded to access memory in an indirect fashion because a pointer to a value is stored in a memory location instead of in a register.
_________________Robert Finch http://www.finitron.ca
|
Thu Aug 17, 2017 2:50 am |
|
|
hoglet
Joined: Tue Feb 10, 2015 7:07 am Posts: 52
|
robfinch wrote: Fixed the crash in the assign divide logic. Fixed the long compare. Redundant sign extension of immediate values was removed. For long math if a an 'L' is not specified after a constant number it may cause more code to be generated. (It loads the constant into a reg rather than using it directly sometimes).
With these fixes, all three of my test programs are now working. Well done! - math16 - math32 - pi robfinch wrote: Long math is still broken if no_regs is specified for the routine and the compiler uses memory variables. The compiler needs to be coded to access memory in an indirect fashion because a pointer to a value is stored in a memory location instead of in a register. The only remaining issue I'm currently seeing is if I compile pi using "long" instead of "int" as the main type, I'm getting malformed assembler. I'm not sure if this is a manifestation of the same issue you mention above, or something else. Here's a smaller example of the same: Code: #define SCALE 10000L
void putchar(char c) { asm __leafs { ld r1, r14, 1 jsr r13, r0, 0xffee }; }
void bug21(long i) { long d = SCALE / 10L; while (d) { long c = (i / d); putchar(48 + c % 10); d /= 10L; } }
Code: _bug21: push r13,r14 push r12,r14 mov r12,r14 dec r14,8 mov r5,r0,1000 mov r6,r0 sto r5,r12,-2 sto r6,r12,-1 bug21_18: ld r6,r12,-1 ld r5,r12,-2 add r5,r0 z.mov r15,r0,bug21_19 ld r1,r12,2 ld r2,r12,-2 jsr r13,r0,__div mov r5,r1 sto r5,r12,-4 sto mov r5,r0,48 mov r6,r0 mov r7,r0,10 mov r3,r0 ld r1,r12,-4 mov r2,r7 jsr r13,r0,__mod mov r7,r1 mov r1,r5 add r1,r7 mov r5,r1 push r5,r14 jsr r13,r0,_putchar inc r14,1 ld r6,r12,-1 ld r5,r12,-2 mov r1,r5 mov r2,r6 push r3,r14 push r4,r14 mov r3,r0,10 mov r4,r0 jsr r13,r0,__div32 pop r4,r14 pop r3,r14 sto r1,r12,-2 sto r2,r12,-1 mov r15,r0,bug21_18 bug21_19: mov r14,r12 pop r12,r14 pop r13,r14 mov r15,r13
|
Thu Aug 17, 2017 9:05 am |
|
|
robfinch
Joined: Sat Feb 02, 2013 9:40 am Posts: 2231 Location: Canada
|
The size of the expression needed to be set correctly in a couple of places. Passing long arguments on the stack was broken. A function could receive the argument properly but a function call didn't place the arguments on the stack correctly. A long argument has to be passed on the stack, it cannot be passed properly in a register. The size of an immediate constant needed to be preserved through optimization.
Learning lots!
_________________Robert Finch http://www.finitron.ca
|
Thu Aug 17, 2017 10:50 am |
|
|
hoglet
Joined: Tue Feb 10, 2015 7:07 am Posts: 52
|
robfinch wrote: The size of the expression needed to be set correctly in a couple of places. Passing long arguments on the stack was broken. A function could receive the argument properly but a function call didn't place the arguments on the stack correctly. A long argument has to be passed on the stack, it cannot be passed properly in a register. The size of an immediate constant needed to be preserved through optimization.
Learning lots! Yeh, me too! It's fascinating to see this work evolve. Here's the next issue: 23. malformed code generated from expressions that mix 16 and 32 bit valuesCode: void bug23() { long a = 123; int q = 10; int i = 1; long x = 10 * a + q * i; }
Code: _bug23: push r13,r14 push r12,r14 mov r12,r14 dec r14,12 # not quite sure why the stack frame is 12 words, I make it 6 mov r5,r0,123 mov r6,r0 sto r5,r12,-2 sto r6,r12,-1 mov r5,r0,10 sto r5,r12,-3 mov r5,r0,1 sto r5,r12,-4 mov r5,r0,10 mov r6,r0 mov r1,r5 mov r2,r6 push r3,r14 push r4,r14 ld r3,r12,-2 ld r4,r12,-1 jsr r13,r0,__mul32 pop r4,r14 pop r3,r14 mov r5,r1 mov r6,r2 ld r1,r12,-3 ld r2 # malformed (should be mov r2, r0) push r3,r14 push r4,r14 ld r3,r12,-4 ld r4 # malformed (should be mov r4, r0) jsr r13,r0,__mul32 pop r4,r14 pop r3,r14 mov r4,r1 mov r8,r2 mov r7,r4 mov r3,r0 or r4,r0 # not sure what's going on here... mi.mov r3,r0,-1 mov r1,r5 mov r2,r6 add r1,r7 adc r2,r3 mov r5,r1 mov r6,r2 sto r5,r12,-6 sto r6,r12,-5 mov r14,r12 pop r12,r14 pop r13,r14
Dave
|
Thu Aug 17, 2017 11:55 am |
|
|
robfinch
Joined: Sat Feb 02, 2013 9:40 am Posts: 2231 Location: Canada
|
Not sure about this one. Should it perform q * I first as an int value, then convert it to a long ? the problem with that is if the value q * I overflows an int it won't be converted sensibly. Or should it convert q,i to longs first since the expression is a long result ? Code: void bug23() { long a = 123; int q = 10; int i = 1; long x = 10 * a + q * i; } I suppose the vars could be typecast to longs if desired.
_________________Robert Finch http://www.finitron.ca
|
Thu Aug 17, 2017 1:58 pm |
|
|
hoglet
Joined: Tue Feb 10, 2015 7:07 am Posts: 52
|
robfinch wrote: Not sure about this one. Should it perform q * I first as an int value, then convert it to a long ? the problem with that is if the value q * I overflows an int it won't be converted sensibly. Or should it convert q,i to longs first since the expression is a long result ?
This is definitely a minefield! When evaluating the multiply operator, as both the operands are ints, the multiplication should happen at int precision (and return an int) When evaluating the add operator, a long is being added to an int, so the int should be promoted to a long. I think this behaviour is part of what's called "usual arithmetic conversion" in the C standard. It's a little hard to test this in gcc, because long and int (on my system at least) are the same precision. Also, you can't test with smaller types, as these will be automatically promoted to an int. But here's an example that shows overflow can happen: Code: #include <stdio.h> void main() { long long a = 1000000000000L; int b = 1000000000; long long c = a + b * b; long long d = a + b * ((long long)b); printf("%lld %d %lld %lld\n", a, b, c, d); } $ ./test2 1000000000000 1000000000 998513381376 1000001000000000000
Dave
Last edited by hoglet on Thu Aug 17, 2017 4:50 pm, edited 1 time in total.
|
Thu Aug 17, 2017 3:14 pm |
|
|
BigEd
Joined: Wed Jan 09, 2013 6:54 pm Posts: 1808
|
Sometimes, getting the "wrong" answer is the compliant thing to do! (Which is to say, I think it's best to do what C is defined to do, not to try to improve on it.)
|
Thu Aug 17, 2017 4:15 pm |
|
|
hoglet
Joined: Tue Feb 10, 2015 7:07 am Posts: 52
|
BigEd wrote: Sometimes, getting the "wrong" answer is the compliant thing to do! (Which is to say, I think it's best to do what C is defined to do, not to try to improve on it.) There is a good discussion in the comments of this stackexchange thread: https://stackoverflow.com/questions/128 ... sion-rulesDave
|
Thu Aug 17, 2017 5:00 pm |
|
Who is online |
Users browsing this forum: claudebot and 0 guests |
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot post attachments in this forum
|
|