Jump to content

Greg King

  • Posts

  • Joined

  • Last visited

  • Days Won


Everything posted by Greg King

  1. "-t" sets the CPU. Therefore, if you want a different one, then you must put "--cpu" on the right side of "-t".
  2. The LLVM linker works that way.
  3. You can go further. If several of your functions use the same data or variables, in a way that doesn't conflict with each other, then you can put that data/variables into their own source files (those files might not have any code in them). The idea is that your program won't have two copies of them even if that program is using two functions that need them -- it's less bloat.
  4. Their philosophies are different. stdio is stream-oriented, while conio is screen-oriented. A stdio program is expected just to print and print and print and... It cares little about the height of a screen. A conio program is expected to stay on a single screen. It reuses that screen's real estate. It overwrites old text with new text. That's why conio has ...xy() positioning functions. It's why there are cclear... functions. And, it's why carriage return and line feed are separate characters. We can use carriage return to type and retype on the same line.
  5. "(_printf.o)" is the common formatter function that everything uses. "(printf.o)" has the printf() function. But, it calls "(vfprintf.o)", which calls "(fwrite.o)", which calls "(write.o)".
  6. You should use printf(); in an example about stdio code.
  7. We use https://github.com/cc65/cc65/blob/master/targettest/dir-test.c to test our standard directory functions. (Ignore the comment at the top of the file! It describes the CBM library implementation, not the test program.)
  8. Yes, only the load and save functions work on the emulator's filesystem. Standard I/O and POSIX I/O work on the SD filesystem as the standards say that they should. However, subdirectories don't exist in CBM DOS. Therefore, those functions might not work in CMNDR DOS as you would expect.
  9. Currently, the increment/decrement must be or'ed into the address expression that's given to vpeek() and vpoke(). No one has taken the time to document a lot of the functions that are available in cc65's libraries. (Usually, someone finds a function in a header, learns that it's very useful to him, notices that it's not in the doc, then submits a patch to "funcref.sgml".) Yep. But, it isn't complete. For example, there are no constants for configuring sprites. "cx16.inc" (for Assembly programming) has much more of that stuff in it.
  10. #include <cx16.h> ... VERA.control = 0; VERA.address = address; VERA.address_hi = 1; VERA.data0 = the_byte; It compiles to the same Assembly code. But, it looks more C-like, less BASIC-like. cc65 also has vpeek() and vpoke() functions. They directly touch VRAM and internal registers.
  11. Those functions were designed for the mouse sprite. Its shape data is in the Kernal ROM. sprite_set_image is used to copy it into VRAM. sprite_set_position is used to move the mouse pointer during vertical blanking.
  12. Run the emulator. Use BASIC's MON command to start the monitor. Give the command "m fff0 ffff". You'll see it.
  13. This thread actually is about the mainboard version, not the Kernal version. This is the easiest way to test it, and switch to the Kernal bank: ; Assume BASIC ROM bank 4 at this point. stz $01 lda $FFF8 ; BASIC has zero, Kernal has nonzero bne mb2 stz $9F60 mb2: "stz $01" changes that location, but does nothing else, on mainboard #1. It changes to Kernal's bank on other mainboard versions. $FFF8 is a part of Kernal's "mist" signature. But, it's zeroes in the BASIC bank.
  14. The language in the description doesn't make it clear whether or not the LFO can do both modulations at the same time. Maybe, the F bit controls the output Function of the LFO (either it changes the frequency of another oscillator, or it changes the volume of that oscillator, but not both).
  15. That comment is about KickC. As I wrote earlier, cc65 works very differently.
  16. No. cc65's interrupt handling is much less efficient. It goes solely through the interrupt vector "daisy chain". Several independent handlers can be connected at the same time (if a handler is linked into a program, then it's connected automatically). Therefore, there's an extra layer that runs each handler, in turn. If you want to run C code in a handler, then another layer must be used to save and restore all of the housekeeping data that the compiler's runtime needs. Normal interrupt handling is slow -- not suitable for some video effects! If you want to stuff a lot of data into video RAM during the vertical blanking period, then your code must start as soon as possible, and run as fast as possible. Your program would need to intercept the interrupts manually. The handler should be written in Assembly code.
  17. There are two register lists: pseudo-registers (used by X16's Kernal) imaginary registers (used by LLVM-MOS) Be careful how much you overlap them! Imaginary register "__rs0" is used as the permanent soft-stack pointer.
  18. You cancel a VERA interrupt by writing the appropriate one-bit to the register: // end interrupt *VERA_ISR = VERA_LINE;
  19. I, also, don't understand why CMake doesn't recognize that they have different paths. However, I worked around it by renaming the new header -- output -- file to "cx16_header". (It needs to be renamed in both a CMakeLists.txt file and the link.ld script.)
  20. As Stephen wrote, cc65's R39 library is "waiting in the wings". You can see it at https://github.com/greg-king5/cc65/tree/commander-r39.
  21. In order to do what you want, your program needs two things: It needs to know when the vertical blanking is happenning. It needs to keep control of the computer. Your current code has number 1; but, number 1 can't work because it doesn't have number 2 (Kernal takes control and cancels number 1). Your program can get number 2 by temporarily preventing the CPU from seeing interrupts. The usual way in C code is to use a "SEI()" macro. Then, your code waits for the VSYNC flag. After it sees that flag, your code updates the menu. Then, it allows the CPU to notice interrupts again [a CLI() macro]. After that, your code gets player input, then possibly repeats the loop. [Notice that VSYNC() is done at the beginning of the loop, not at the end.] #define VSYNC() while (!(ISR & 1)) do { SEI(); VSYNC(); // Redraw the new menu. // And update the positions of "carots" and highlights. CLI(); // Handle input from the player. } while (!finished); How your code gets the SEI() and CLI() macros depends on which C compiler you're using (your code looks like KickC).
  22. He's right. I had assumed that Kernal checks for the vertical sync interrupt -- but, it doesn't!! Therefore, our programs are forced to do it. Your handler must have some additional code. __interrupt void myInterrupt(void) { if (*VERA_ISR & VERA_LINE) { vbcounter++; *VERA_IRQLINE_L = 100; *VERA_ISR = VERA_LINE; } else if (*VERA_ISR & VERA_VSYNC) { asm { jmp (default_irq_vector) } } // Other interrupts are ignored. } Note that the __interrupt qualifier makes this function return by jumping to R38 Kernal's $E049, which pulls the registers and does RTI.
  23. Your program does too much! Instead, it should follow these rules: Ignore interrupts that aren't important to the program. Your test program cares about only the raster line. Let Kernal handle the other interrupt. Don't constantly re-enable the interrupt. Set it once, at the beginning (it will stay enabled). After you handle the interrupt, clear only that interrupt! Always exit through the saved vector. Let Kernal do its things. #pragma target(cx16) #include <cx16.h> #include <6502.h> #include <string.h> #include <stdlib.h> #include <conio.h> #include <veralib.h> volatile char vbcounter=0; volatile void *default_irq_vector; __interrupt void myInterrupt(void) { if (*VERA_ISR & VERA_LINE) { vbcounter++; *VERA_IRQLINE_L = 100; *VERA_ISR = VERA_LINE; } asm { jmp (default_irq_vector) } } void initMain(void) { volatile unsigned char ll = 0, mm=0; default_irq_vector = *KERNEL_IRQ; SEI(); *KERNEL_IRQ = &myInterrupt; CLI(); *VERA_IRQLINE_L = 4; *VERA_IEN |= VERA_LINE; vpoke( 0,0, 0 ); // set "@" left top screen vpoke( 0,1, 0x12 ); // set "R" next to it while (1) { unsigned char c = kbhit(); if (c == 32) { vpoke( 0,0, ++mm ); // increment left top screen vpoke( 0,1, 0x12 ); // set "R" next to it } vpoke( 0,2, vbcounter ); vpoke( 0,3, 0x12 ); // set "R" next to it vpoke( 0,4, ll++ ); vpoke( 0,5, 0x12 ); // set "R" next to it } } void main(void) { initMain(); }
  24. set_irq() is slower than Assembly, but it should be faster than waitvsync(). waitvsync() doesn't run until KERNAL has finished running its interrupt handler. That handler reads the mouse, the keyboard, and the joystick controllers. And, it "bumps" the jiffy timer, and blinks the text cursor.
  25. The "dummy variable" isn't a dummy -- it's the KERNAL interrupt vector. It must be described this way: // 0x314 as a variable #define IRQvector (*(void (*)(void))IRQVECTORLOC) However, read this: https://github.com/cc65/wiki/wiki/Interrupt-handlers-in-C The set_irq() method is safe, but it's slow. Your C function won't start as soon after the interrupt as you want it to start -- especially for VERA code! Writing your handler in Assembly is a better plan. Another consideration is the 16-bit API. If you use the pseudo-registers r0 to r15 in your C code and in your handler, then you must preserve their values during your handler's run. P.S., You don't need the "&" address operator -- the name of the function is its address.
  • Create New...

Important Information

Please review our Terms of Use