JesperGravgaard Posted November 9, 2020 Share Posted November 9, 2020 Hi everyone, A new version of KickC has just been released. This release adds conio.h support for Atari XL/XE and improves conio.h on Commodore platforms. It fixes a lot of bugs and and adds a lot of new/improved ASM fragments. https://gitlab.com/camelot/kickc/-/releases The new version also compiles the awesome benchmarks created by @fenrock out-of-the-box. https://github.com/markjfisher/kickc-benchmarks A huge thanks to all of you who have tried out KickC and given feedback, suggestions and questions! 5 Quote Link to comment Share on other sites More sharing options...
funkheld Posted November 9, 2020 Share Posted November 9, 2020 Hi, Thank You. it's nice that it is being developed so quickly. greeting Quote Link to comment Share on other sites More sharing options...
funkheld Posted November 10, 2020 Share Posted November 10, 2020 (edited) have now tried the pm in kickc in graphics mode 7. you can move the pm with the q / w / e / s keys. the program xex is only 368 byte big in kickc. greeting #pragma target(atarixl) #pragma encoding(atascii) #pragma zp_reserve(0x00..0x7f) #include <atari-xl.h> #include <printf.h> #include <conio.h> #define ICCOM_OPEN_CHANNEL 0x03; #define ICCOM_PUT_BINARY_RECORD 0x0B; #define ICCOM_CLOSE 0x0C; #define ICCOM_DRAW_LINE 0x11; void * CIOV = 0xE456; struct ATARI_IOCB { char ICHID; char ICDNO; char ICCOM; char ICSTA; char* ICBA; char* ICPT; word ICBL; char ICAX1; char ICAX2; char ICAX3; char ICAX4; char ICAX5; char ICAX6; }; struct ATARI_IOCB * const IOCB0 = 0x340; struct ATARI_IOCB * const IOCB6 = 0x3A0; char *sDrive = "S:"; char * const ATACHR = 0x2FB; word i, pmgmem; char j, px0 , py0; char * ramto = 0x6a; char * colorpm0 = 704; char * colorpm1 = 705; char * colorpm2 = 706; char * colorpm3 = 707; char y,y1; const char pmdata[] = { 0,255,129,129,129,129,129,129,255,0}; void main() { graphics(7); color(1); for(y=0;y<10;y++) { y1=y1+10; plot(0, 0); drawTo(159,y1); } px0 = 100; py0 = 60; GTIA->GRACTL = 0; j = *ramto-24; ANTIC->PMBASE = j; pmgmem = j * 256; *SDMCTL = 46; GTIA->GRACTL = 3; GTIA->SIZEP0 = 0; *colorpm0 = 202; schiebe(); while (1) { *CH = 0xff; while(*CH == 0xff) ; char keyPressed = *CH; switch(keyPressed) { case $2f: { px0 =px0-1; schiebe(); }; break; case $2a: { px0 =px0+1; schiebe(); }; break; case $2e: { py0 =py0-1; schiebe(); }; break; case $3e: { py0 =py0+1; schiebe(); }; break; default: ; break; } } } void schiebe(){ char *pmadr = pmgmem + 512 + py0; for(y1=0;y1<10;y1++) { pmadr[y1] = pmdata[y1]; } GTIA->HPOSP0 = px0 ; } void closeChannel() { IOCB6->ICCOM = ICCOM_CLOSE; asm(clobbers "AXY") { ldx #$60 jmp CIOV }; } void graphics(char mode) { closeChannel(); IOCB6->ICCOM = ICCOM_OPEN_CHANNEL; IOCB6->ICAX1 = 0x0C; IOCB6->ICAX2 = mode; IOCB6->ICBA = sDrive; asm(clobbers "AXY") { ldx #$60 jmp CIOV }; } void position(word x, char y) { *COLCRS = x; *ROWCRS = y; } void plot(word x, char y) { position(x, y); IOCB6->ICCOM = ICCOM_PUT_BINARY_RECORD; IOCB6->ICBL = 0; asm(clobbers "AXY") { ldx #$60 lda ATACHR jmp CIOV }; } void drawTo(word x, char y) { position(x, y); IOCB6->ICCOM = ICCOM_DRAW_LINE; IOCB6->ICAX1 = 0x0C; IOCB6->ICAX2 = 0; asm(clobbers "AXY") { ldx #$60 jmp CIOV }; } void color(char c) { *ATACHR = c; } void waitkey() { while(!kbhit()) ; clrkb(); } Edited November 10, 2020 by funkheld 3 Quote Link to comment Share on other sites More sharing options...
ivop Posted November 10, 2020 Share Posted November 10, 2020 (edited) Practice makes perfect. Also checkout var-- and var++ (or --var and ++var, which used to be faster on some non-optimizing C compilers). You can also factor out schiebe() from all the case statements. Edit: good to see you use CIO. Edit2: your defines do not need a semicolon (;) at the end. They work now because IOCB6->ICCOM = ICCOM_OPEN_CHANNEL; turns into IOCB6->ICCOM = 0x03;; But consider this #define A 42; int main(int argc, char**argv) { int a2; a2 = A * 2; return 0; } where the a2 line turns into this a2 = 42; * 2; which is invalid. HTH HAND (google that ) Edited November 10, 2020 by ivop Quote Link to comment Share on other sites More sharing options...
fenrock Posted November 10, 2020 Share Posted November 10, 2020 (edited) 3 hours ago, ivop said: Edit2: your defines do not need a semicolon (;) at the end. This is my fault as I supplied most of this code in a quick example of opening graphics modes with CIO. But it's great to see PMs added to it. Thanks for pointing out an example of why not to use them, which explains why the line IOCB6->ICAX1 = 0x0C; // (IOCB_ICAX_READ | IOCB_ICAX_WRITE) doesn't work Edited November 10, 2020 by fenrock 2 Quote Link to comment Share on other sites More sharing options...
funkheld Posted November 11, 2020 Share Posted November 11, 2020 what effect does the "const" have? ------------------------------------ char * const ATACHR = 0x2FB; char * ATACHR = 0x2FB; ------------------------------------ greeting Quote Link to comment Share on other sites More sharing options...
ivop Posted November 11, 2020 Share Posted November 11, 2020 const after the * makes the pointer constant, which means you cannot assign to it, only dereference. Quote Link to comment Share on other sites More sharing options...
fenrock Posted November 11, 2020 Share Posted November 11, 2020 50 minutes ago, ivop said: const after the * makes the pointer constant, which means you cannot assign to it, only dereference. Which (I believe) in turn is used by the compiler to know that it doesn't have to assign this to a memory location, and can optimise its use. 1 Quote Link to comment Share on other sites More sharing options...
funkheld Posted November 11, 2020 Share Posted November 11, 2020 (edited) sometimes addresses from the system are constant and sometimes not, why? ------------------------------ char * const FILDAT = 0x2FD; char * ROWCRS = 0x54; word * COLCRS = 0x55; char * const ATACHR = 0x2FB; char * const CH = 0x2FC; void * CIOV = 0xE456; ----------------------------- greeting. Edited November 11, 2020 by funkheld 1 Quote Link to comment Share on other sites More sharing options...
zbyti Posted November 11, 2020 Share Posted November 11, 2020 (edited) Wrong bit order explanation in comment. kickc/include/atari-antic.h // Non-Maskable Interrupt (NMI) Enable // Enables Non-Maskable Interrupts. // 7 6 5 4 3 2 1 0 // 1 - - - - - - - RESET: Enable Reset key interrupt // - 1 - - - - - - VBI: Enable Vertical Blank Interrupt // - - 1 - - - - - DLI: Enable Display List Interrupt // The Operation System sets NMIEN to the default $40hex/64dec during the power up routines. // The NMI service routines first vector through $FFFAhex/65530dec which determines the cause and then transfers control to the interrupt service routine. // If NMIEN's DLI bit is set when ANTIC encounters a Display List instruction with the DLI modifier bit set, then ANTIC triggers the DLI on the last scan line of that Display List instruction mode line. // When NMIEN's VBI bit is set, ANTIC will signals a Vertical Blank Interrupt at the end of processing the JVB (Jump vertical blank) at the end of the Display List. // The OS jumps through VVBLKI ($0222hex/546dec) to begin the OS VBI Service Routine, and the OS VBI Routine exits with a jump through VVBLKD ($0224hex/548dec). // By default VVBLKI points to the OS jump vector SYSVBV ($E45Fhex/58463dec) to begin the Vertical Blank Interrupt, and VVBLKD points to the OS jump vector XITVBV ($E462hex/58466dec). char NMIEN; // Non-Maskable Interrupt (NMI) Status / Reset // Contains information about which NMI occured. // Any value written to NMIRES resets the bits in NMIST which indicate the reason for the most recent Non-Maskable Interrupt. // 7 6 5 4 3 2 1 0 // 1 - - - - - - - RESET: Reset key interrupt // - 1 - - - - - - VBI: Vertical Blank Interrupt // - - 1 - - - - - DLI: Display List Interrupt char NMIST; https://www.atariarchives.org/mapping/memorymap.php#54286 54286 D40E NMIEN (W) Non-maskable interrupt (NMI) enable. POKE with 192 to enable the Display List Interrupts. When BIT 7 is set to one, it means DL instruction interrupt; any display list instruction where BIT 7 equals one will cause this interrupt to be enabled at the start of the last video line displayed by that instruction. When BIT 6 equals one, it allows the Vertical Blank Interrupt and when BIT 5 equals one, it allows the RESET button interrupt. The RESET interrupt is never disabled by the OS. You should never press RESET during powerup since it will be acted upon. NMIEN is set to 64 ($40) by the OS IRQ code on powerup, enabling VBI's, but disabling DLI's. All NMI interrupts are vectored through 65530 ($FFFA) to the NMI service routine at 59316 ($E7B4) to determine their cause. Bit 7 6 5 4 3 2 1 0 Interrupt: DLI VBI RESET .... unused ..... 54287 D40F NMIRES (W) Reset for NMIST (below); clears the interrupt request register; resets all of the NMI status together. NMIST (R) NMI status; holds cause for the NMI interrupt in BITs 5, 6 and 7; corresponding to the same bits in NMIEN above. If a DLI is pending, a jump is made through the global RAM vector VDSLST (512; $200). The OS doesn't use DLI's, so 512 is initialized to point to an RTI instruction and must be changed by the user before a DLI is allowed. If the interrupt is not a DLI, then a test is made to see if the interrupt was caused by pressing RESET key and, if so, a jump is made to 58484 ($E474). If not a RESET interrupt, then the system assumes the interrupt was a VBLANK interrupt, and a jump is made through VVBLKI at 546 ($222), which normally points to the stage one VBLANK processor. From there it checks the flag at CRITIC (66; $42) and, if not from a critical section, jumps through VVBLKD at 548 ($224), which normally points to the VBLANK exit routine. On powerup, the VBLANK interrupts are enabled while the display list interrupts are disabled. See the end of the memory map for a description of the VBLANK procedures. For IRQ interrupts, see location 53744 ($D20E). Edited November 11, 2020 by zbyti memory map link 1 Quote Link to comment Share on other sites More sharing options...
ivop Posted November 11, 2020 Share Posted November 11, 2020 (edited) 1 hour ago, funkheld said: sometimes addresses from the system are constant and sometimes not, why? ------------------------------ char * const FILDAT = 0x2FD; char * ROWCRS = 0x54; word * COLCRS = 0x55; char * const ATACHR = 0x2FB; char * const CH = 0x2FC; void * CIOV = 0xE456; ----------------------------- greeting. That's just wrong. A pointer to fixed location should be constant. For example: *ROWCRS = 15; Should become: lda #15 sta $54 If the pointer is not constant, the compiler doesn't know, so it will first load the pointer in a zero page pair, and then use (zp),y: lda rowcrs_pointer sta zp lda rowcrs_pointer+1 sta zp+1 ldy #0 lda #15 sta (zp),y Or if the non-constant pointer is assigned/optimized to zero page location already, it becomes: ldy #0 lda #15 sta (rowcrs_pointer),y Edited November 11, 2020 by ivop 1 Quote Link to comment Share on other sites More sharing options...
funkheld Posted November 11, 2020 Share Posted November 11, 2020 hello thanks. greeting Quote Link to comment Share on other sites More sharing options...
fenrock Posted November 11, 2020 Share Posted November 11, 2020 (edited) 1 hour ago, ivop said: That's just wrong. A pointer to fixed location should be constant. it's entirely possible some of these are my mistakes as I contributed conio-atari.h to kickc and I was learning as I went along I think kickc recognises pointers to fixed addresses that don't change in the life of the program as constant anyway and automatically replaces them. Here are the current definitions: // Atari ZP registers // 1-byte cursor row char * ROWCRS = 0x54; // 2-byte cursor column word * COLCRS = 0x55; The following does actually compile down correctly in kickc #pragma target(atarixl) #include <atari-xl.h> #include <conio.h> void main() { *ROWCRS = 0; } // Atari ZP registers // 1-byte cursor row .label ROWCRS = $54 .segment Code main: { // /home/markf/dev/personal/atari/projects/kickc-io/./src/foo.c:6 lda #0 sta ROWCRS // /home/markf/dev/personal/atari/projects/kickc-io/./src/foo.c:7 rts } I've locally made them const and it still works. I'll talk to @JesperGravgaard and get them fixed with a PR. Edited November 11, 2020 by fenrock 1 Quote Link to comment Share on other sites More sharing options...
ivop Posted November 11, 2020 Share Posted November 11, 2020 (edited) But before you made it const, the generated code is not right, because the pointer can change. char * ROWCRS = 0x54; ROWCRS = 1234; *ROWCRS = 0; Edit: oh sorry, I see, you said it might detect that it doesn't change over the life time of the program. To what does this code compile then? Edited November 11, 2020 by ivop Quote Link to comment Share on other sites More sharing options...
fenrock Posted November 11, 2020 Share Posted November 11, 2020 (edited) 42 minutes ago, ivop said: But before you made it const, the generated code is not right, because the pointer can change. char * ROWCRS = 0x54; ROWCRS = 1234; *ROWCRS = 1; // i changed this deliberately I think we're agreeing When the variable is (incorrectly) not marked as const, yes you can change the pointer and the generated code changes to accommodate the new code. But kickc didn't need to declare it as a ZP variable to do the work, it optimized the code in place to "store 0 at 0x54". In your new case it generates: main: { // /home/markf/dev/personal/atari/projects/kickc-io/./src/foo.c:6 lda #0 sta $54 // /home/markf/dev/personal/atari/projects/kickc-io/./src/foo.c:8 lda #1 sta $4d2 // /home/markf/dev/personal/atari/projects/kickc-io/./src/foo.c:9 rts } However, if you mark it const, the compiler errors with: const variable may not be modified ROWCRS which is why I said I'd raise a PR to mark them constant, but it isn't strictly needed because as it says in the kickc manual: Quote The compiler is quite good at detecting constants automatically, so it is not strictly necessary to declare any constants Edited November 11, 2020 by fenrock expanded example 2 Quote Link to comment Share on other sites More sharing options...
ivop Posted November 11, 2020 Share Posted November 11, 2020 (edited) Oh, it's pretty cool that it detects that the pointer has changed and still does lda/sta! 37 minutes ago, fenrock said: but it isn't strictly needed because as it says in the kickc manual: The compiler is quite good at detecting constants automatically, so it is not strictly necessary to declare any constants True. But you might want to run the same code on different compilers (benchmarks), and not every compiler is as good at detecting constants I suppose. Some compilers need more guidance. And it is proper C Edited November 11, 2020 by ivop 1 Quote Link to comment Share on other sites More sharing options...
JesperGravgaard Posted November 11, 2020 Author Share Posted November 11, 2020 (edited) KickC tries to optimize the code as well as it can by analyzing the code thoroughly to identify variables that are actually constants. One of the methods is uses is converting the entire program to single static assignment form, which effectively changes the program (by introducing new variables) to ensure that each variable is only assigned once. This makes the compiler quite good at constant detection. However, it will compile faster and leave less room for mistakes if the programmer marks pointers as constant. Also @ivop has a point on creating portable code. Even with the best current compiler methods, nothing beats a programmer that has a thorough look at the code and rewrites it to achieve optimal performance. Edited November 11, 2020 by JesperGravgaard 2 Quote Link to comment Share on other sites More sharing options...
Blues76 Posted November 13, 2020 Share Posted November 13, 2020 @JesperGravgaard Thank you for working on this. I wanted to ask, for my own knowledge, what are the differences with cc65? One of the great features seems to be that KickC provides better optimizations, right? What other there are other more important features compared to cc65? I checked your reference manual (very nice by the way). Quote Link to comment Share on other sites More sharing options...
JesperGravgaard Posted November 13, 2020 Author Share Posted November 13, 2020 @Blues76 Here are some of the differences. The most significant difference is the modern compiler optimization techniques KickC uses to generate fast ASM from your C-code. A second difference is that KickC can generate very small binaries, since it only includes code for the functions that are actually used. This includes functions in libraries. If you include a library and only use a single function the compiler will only generate ASM for that one function. It even optimizes the library functions based on the parameters you pass, so if you only call the library function once or always pass the same parameters it will optimize the ASM based on the constant parameter. A third difference is that the generated ASM is very readable, so you can directly compare your C-program to the ASM it generates. As an example This C-code: https://gitlab.com/camelot/kickc/-/blob/master/src/test/kc/examples/atarixl/rasterbars.c compiles to this ASM-code: https://gitlab.com/camelot/kickc/-/blob/master/src/test/ref/examples/atarixl/rasterbars.asm A minor difference is the ability to initialize data structures using inline KickAssembler macro code. Kick Assembler has a great macro language, so this allows for very easy initialization of look-up tables. char SINTABLE[0x100] = kickasm {{ .fill $100, round(127.5+127.5*sin(2*PI*i/256)) }}; CC65 on the other hand is a lot more mature than KickC. It very rarely fails. It has better library support. There are more examples and information available online. 3 1 Quote Link to comment Share on other sites More sharing options...
Blues76 Posted November 13, 2020 Share Posted November 13, 2020 @JesperGravgaard Thank you for taking the time to give me this detailed answered and for giving us this alternative. Thanks! Quote Link to comment Share on other sites More sharing options...
funkheld Posted November 13, 2020 Share Posted November 13, 2020 I never got the (atarixl.cfg) right. there were always some errors with memory allocations. this kickc is ok. greeting Quote Link to comment Share on other sites More sharing options...
Gury Posted November 15, 2020 Share Posted November 15, 2020 JesperGravgaard, this is great new addition to the list of Atari programming languages and cross-compilers. Very neat, fast, optimized... And I have one "strange" question. Maybe I am not familiar with console syntax of Java system, but I have troubles with naming a filename to be compiled. Something like: kickc d:\atari\projects\tests\examples\testdata.c -a results in error: File not found d:\atari\projects\tests\examples\testdata.c All other, relative paths do work perfectly. For example: kickc ..\examples\atarixl\rasterbars.c -a works as expected. I posted here because I need a way of declaring path as described on top. Thank you for this new amazing tool! Gury 1 Quote Link to comment Share on other sites More sharing options...
fenrock Posted November 15, 2020 Share Posted November 15, 2020 (edited) Awesome! I've already forked it and sent a PR I'd like to also be able to create "init" blocks as separate segments. I suppose the easiest way to do that will be creating a pure data segment with just ".word $2e2,$2e3,<address>". I'll have a look at if this would be an easy thing to add to the plugin to allow an easier syntax. EDIT: Ooohhh, you've already add _RunAddr, I'll send a PR for "_InitAddr" to do a inter-segment load. Edited November 15, 2020 by fenrock _InitAddr 1 Quote Link to comment Share on other sites More sharing options...
JesperGravgaard Posted November 15, 2020 Author Share Posted November 15, 2020 (edited) 2 hours ago, Gury said: kickc d:\atari\projects\tests\examples\testdata.c -a results in error: File not found d:\atari\projects\tests\examples\testdata.c @Gury You have found a bug in the current version of KickC! The code loading source files looks through a list of search folders - but does not consider that the file name may be absolute. I have fixed it, and the fix will be in the next release. https://gitlab.com/camelot/kickc/-/issues/576 Thank you for finding the problem! Edited November 15, 2020 by JesperGravgaard Quote Link to comment Share on other sites More sharing options...
Gury Posted November 15, 2020 Share Posted November 15, 2020 That's ok, it's not a hurry. Keep up the good work! 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.