Jump to content


  • Posts

  • Joined

  • Last visited

  • Days Won


Everything posted by ZeroByte

  1. Can it do log2 of a 16bit into an 8.8 or 8.16 fixedpoint?
  2. Actually, there is a reason - I made my routine report results via a global struct containing the next-available memory location just like the Kernal's LOAD routine does. (only it's written into a struct and not passed back directly) Also, since all the values are right there in the routine's hot little hands, I decided to save the caller the effort of calculating offsets, etc., and the struct contains total bytes transferred and a success/fail boolean. (which I suppose could be the final status byte, come to think of it)
  3. I like cc65 for its linker. It's super easy to let the linker decide where everything goes in memory once you let go of using .org in your programs. Plus, it makes hybrid C/ASM projects very easy to do as well, since it can pass symbols between the two environments, etc. Once I got over how to accept __fastcall__ calls from C in the assembly side, and how to pass back return values, it was off to the races. Now I can easily code my way through C and when I hit a function that needs to be done in asm, I just declare it in C with a prototpe such as: unsigned int __fastcall__ times2 (char x); Then in assembly: .export _times2 _times2: ASL ; fastcall puts the rightmost parameter in .AX TAY LDA #0 ROL TAX ; put the high byte in X TYA ; restore the low byte into A RTS ; return to C
  4. I've uploaded a new version of my OPL music on X16 demo (VOPLDEMO) to the downloads section. Now, you can play _all_ the songs from the shareware version, and it loads each one into memory dynamically as you switch between them (using the left/right arrow keys)
  5. I left the original version in Try It Now, as that is still version R38, and this new version only runs on R39 and up. The new version requires that you run it from the SD image, so that's the only file in the ZIP other than the README. Run the emulator with -sdcard vopldemo_sd.img Do not use -prg or -run - these will not work. Once booted, load the program as you would on the real system: LOAD "VOPLDEMO.PRG" and then RUN Enjoy!
  6. Good catch - that is a bug indeed. Right now, I'm wrestling with why VIA IRQs aren't behaving as I expect.... will fix that line right now tho.
  7. Does the P command have a "seek end of file" cousin?
  8. As promised, here's the code that I'm using to harness MACPTR. It's incomplete in that it doesn't fall back on ACPTR (cbm_load() in this case) if MACPTR returns -1. I'm not happy with the return type being signed, and will probably fix this by either making it a signed log, or else just returning zero on failure and requiring that the calling code check status byte to know if it's zero because EOF or because of unsupported device. Most likely, though, this is a case that will never happen with files, as the OS supports block reads from disk - but if you pass it an LFN that's connected to the screen or keyboard or whatever, then that would fail.... extern int __fastcall__ macptr(unsigned char numbytes, void* buffer); int cx16_read(unsigned char lfn, void* buffer, unsigned int size) { int error = 0; char* buf = (char*)buffer; static unsigned int bytesread; static int tmp; /* if we can't change to the inputchannel #lfn then return an error */ if (_oserror = cbm_k_chkin(lfn)) return -1; bytesread = 0; printf("\n"); while (size > 0 && !cbm_k_readst()) { if (size>=512) tmp = macptr(0,buf); // let MACPTR read as much as it wants else if (size>=256) tmp = macptr(255,buf); // If size 256..512, unlimited MACPTR would read past desired stopping point. else tmp = macptr((size), buf); if (tmp == -1) return -1; bytesread += tmp; size -= tmp; // wrap the buffer pointer back into the bank window // if it advances above 0xbfff. Note that MACPTR did // wrap banks correctly, but our calculation must match. // also note that MACPTR incremented the active bank, // so there is no need to do RAM_BANK++ here. if (buf >= (char*)0xc000) buf -= 0x2000; buf += tmp; if (cbm_k_readst() & 0xBF) break; if (tmp == 0) break; } And the assembly is short: MACPTR := $FF44 .import popa .export _macptr ; the _ is important - C symbols all start with _ .proc _macptr: near phx tax jsr popa ply jsr MACPTR bcs macptr_unsupported macptr_supported: txa ; C wants int in .AX not .XY phy plx rts macptr_unsupported: ; return -1 on error ldx #$ff lda #$ff rts .endproc Note that the MACPTR function itself returning a signed int is fine, since MACPTR will return 512 bytes at most.
  9. No example code tonight, as it's late and I've just hit a good stopping point. So here is a screenshot of my Wolf3d music demo program, but running in a test mode playing with SEQ access. I post it here because the debug output gives a very good view of how MACPTR does its thing, and the screen includes a ticks counter benchmark to show load speeds. It loads 8K in about 12 frames' time (and would be faster if not having to stop and write all of this debug output to the screen on every pass)
  10. I just had some initial success using MACPTR in cc65. It’s TONS faster and already bank-aware. I’m going to play with it some more but I think it could be even faster if it really will blit in 512 bytes per pass. CHRIN has tons of overhead per byte. will post example code later.
  11. The saga continues: I fully expected this result, but let it be known that cbm_read() has no concept of banked RAM at this time. If you perform a read that runs off the end of a bank, then it will happily throw your data at the ROM region after running off the end of the bank window, and I presume then wrap back around to 0000 after passing FFFF Fortunately, my current experiment is going to involve streaming music from a data file on the fly (chip music, not PCM - no WAY to keep up with that!!!) and so it's not going to be an issue for me down the road with this particular program, but I thought folks should be made aware that while LOAD works in BASIC, and Kernal's LOAD routine works for HiRam, cbm_read() just runs off the cliff. (Goofy yell: Yaaaa-hoo-hoo-hoooey!) One of the items I'm going to experiment with is a cx16_macptr() frontend routine similar to the cx16_seek() routine above. Kernal MACPTR will be bank safe, and run much faster than cbm_read() {which doesn't know MACPTR exists - it's a CX16 exclusive! For those who don't know, MACPTR is the Kernal routine to perform a block read from disk as opposed to a byte-by-byte read using ACPTR. Also for those who don't know: VLOAD only uses ACPTR, which is hella-slow compared to MACPTR, so if your VLOADs are taking too much time, try LOAD-ing them into HiRam and then blitting them into VRAM yourself. You'll do that before VLOAD gets to the halfway marker with any "large" amounts of data.
  12. I have to say that working with the filesystem is one of the most challenging aspects of the Commander X16 that I've personally run across. There are tons of permutations of "Host FS vs SD image", emulator/ROM version, bugs, compiler/system library behavior, etc. One thing that's been updated since R39 is that the hyper load/save hooks have changed a bit, if my understanding is correct - and now instead of just intercepting LOAD and SAVE, the emulator(s) now intercept calls to MACPTR and ACPTR (or some other similar choke point) in order to allow more operations to work with the host FS. It appears that whatever the current methodology is, it doesn't catch everything, because I've recently started working with sequential access to files instead of just bulk loading them into (V)RAM/HIRAM. I've found that this doesn't work unless you're using an SD image. I find SD images to be very inconvenient to work with but hey, I guess that's just the nature of working with something like the X16 in pre-release state (life on the frontier - there's no Dominos and no running water). One thing I've run across is cc65's cbm.h library, which provides what it calls "BASIC-like" interfaces to the low-level kernal file handling routines: cbm_open(LFN, DEVICE, SA, filename) cbm_close(LFN) cbm_read(LFN, buffer* , num_bytes) cbm_write(LFN, buffer*, num_bytes) Where LFN = logical file number, DEVICE = 8 (for disk drive), and SA = that bizarre, multi-purpose byte that is essentially an "argument" for various low-level routines like LOAD or OPEN.... So here we are in cc65 space. The include file also provides three defines to use for the SA in cbm_open(): This leads me to believe that cbm_open() might be doing a little bit of spying on the SA value before calling the underlying Kernal routines. In BASIC, you can use any combo of LFN and SA as long as LFN >= 2 as 0 and 1 have special meaning. I'm going to dig a little deeper into the cc65 sources to see if it's intercepting and interpreting the SA and LFN or not. But the interesting thing I've discovered is that in order for sequential access to work properly, you must use the same value for LFN and SA - and furthermore, these must be 2, 6, or 10 in order for file seeking to work. (I notice that these result in bit 1 of the SA/LFN value being set - hmm) Oh, and seeking - stdio.h implements fopen() fseek() and fclose() - but fseek() won't build on X16 as there's no underlying code for it in cx16.lib - and there's no cbm_seek() function because CBM machines don't naturally support seek (well, without add-ons like SD2IEC or DOS extenders) so the library doesn't do it. To do a seek, I wrote my own function with some advice from @TomXP411 - if you open the command channel to device 8 and send a "P" command (position) while the file is open, Commander-DOS will do a seek. The P command epects binary values to follow it, so here's how to do it in cc65: int8_t cx16_fseek(uint8_t channel, uint32_t offset) { #define SETNAM 0xFFBD static struct cmd { char p; uint8_t lfn; uint32_t offset; } cmd; // open command channel to DOS and send P command. // P u8 u32 (no spaces) u8 is LFN to seek(), and u32 = offset. cmd.p='p'; cmd.lfn=channel; cmd.offset=offset; // can't call cbm_open because the P command is binary and may // contain zeros, which C would interpret as null terminator. // // Roll-your-own inline asm call to SETNAM: __asm__ ("lda #6"); __asm__ ("ldx #<%v",cmd); __asm__ ("ldy #>%v",cmd); __asm__ ("jsr %w",SETNAM); cbm_k_setlfs(15,8,15); cbm_k_open(); // this sends the CMD bytes.. cbm_k_close(15); // close the command channel return 0; // TODO: ERROR HANDLING!!!!! } Note that this code uses cbm_k_open() and not cbm_open() - the k version is a direct Kernal call, so it's not monkeying around with anything like cbm_open might be. Anyway, if anyone has any tips, insights, or whatever regarding file IO, I thought that would be a useful topic for people to be able to hit upon.
  13. Well, it was definitely making me feel a nice warm fuzzy wave of nostalgia looking back over the screenshots you'd posted. Good times.
  14. Discord user Natt Akuma built a VERA PSG+PCM module for FOSS chiptune tracker, Furnace. I have made an export function for Furnace to export VERA/YM2151 tunes into ZSM format which can be played back on Commander X16 using my Zsound library. Unfortunately, they're busy fixing bugs and finalizing features for the current rc target of Furnace, and likely won't merge my pull request until after that has been completed, but you can grab and compile a copy from my fork at https://github.com/ZeroByteOrg/furnace/tree/ZSMv1/ Here's a video of a VERA tune from the Furnace Discord server playing back on X16:
  15. During my recent project working with @Wavicle on making a test application for the YM2151, I got some good experience doing hybrid C/ASM. It turned out to be pretty easy to handle the __fastcall__ conventions, so I decided to throw together the C wrapper module for Zsound and see if it works or not. I've done the music player, and the "hello world" music player worked right away. That's how easy it is to get background music playing in cc65 now! I haven't merged this into the main branch, as there's still testing to be done, especially with the callback routine - just to make sure it doesn't crash stuff. Plus, the documentation needs to be updated, etc - but it's coming soon!
  16. I was wondering what it might take to make Zsound integrate with your project - I just got done with my first non-trivial hybrid C/asm project and was thinking I may go ahead and make C wrappers for the code, but it's for cc65. I think the shortest pathway to other compilers/assemblers would be to build a "player" that you load into memory somewhere and interact via a jmp table at the head of it. The thing I think might be the most problematic is the ZP space. Other than that, it stays in its lane and will eventually require 1 dedicated bank of working space (which you will signal at init() time)
  17. @svenvandevelde - I know I've asked before, but this is written in C, right? (although, if memory serves, not cc65)
  18. It really depends on the use case.
  19. MORSE6.PRG should be named MORSE6.BAS - it's a .bas file (plaintext BASIC) x16emu -bas MORSE6.PRG It doesn't have a trailing carriage-return either, so you'll need to hit ENTER on the last line before typing RUN
  20. To clarify - I'm not saying Ed's strategy doesn't work for several types of programs as well, but for games or things with lots of movement, you really need to do your VERA writing during vblank to avoid tearing and artifacting. If you're driving a menu system or other relatively-static UI, then yeah, you can do all the VERA writing during main execution and just use the vsync IRQ as a timer that bumps a flag once per frame so your program can stop and wait for that value to change in order to begin drawing the next update. I think the real thing to consider is that you probably need to decide whether you're doing your updates "on camera" or "off camera" and don't mix them.
  21. I have to disagree. VBLANK is exactly what this kind of thing is for, and waiting for the Kernal to finish polling the keyboard before you get another crack at doing anything is very risky of bleeding out into the visible area and getting tearing artifacts and such. The VBLANK IRQ can look for a signal value from the main loop that a frame is done, and only do no-op if that flag is not set. If the flag is set, then you know the program is sitting in an idle loop and you won't break anything by manipulating VERA. Main loop code should always assume dirty state for the data ports anyway, (i.e. always start by configuring the port address) and since you know IRQ won't manipulate the port unless you signaled "frame done", it's safe to have a long loop for loading into VRAM, etc. because you haven't written the "update frame" flag. I've seen the VBLANK IRQ take up to several characters' (not pixels, but characters) worth of visible raster time to complete and return to the main loop, depending on what the keyboard is doing. Yes, it takes precious cycles away to do all this pushing VERA onto the stack, but that's why you try to avoid things like using both ports, etc. Flappy Bird is written in C, and it doesn't even push VERA onto the stack during IRQ, and it writes the sprite shadows into VERA during VBLANK and it never crashes. I am not a super-coder or anything, and don't claim to know it all, and I'm sure there's all kinds of ways to get things done, but my understanding has been what I've just stated here.
  22. One thing to note: The above code backs up the currently-selected data port. If your code then proceeds to use the one that was not active, you'd be screwed. If you just want to back up and use a specific data port, then return to current state: ; save the current port selection lda $9f25 pha ; switch to the desired data port lda #desired_data_port_number sta $9f25 ; back up the state of that data port lda $9f20 pha lda $9f21 pha lda $9f22 pha ; Your code here ; Use ONLY the desired data port - be sure to leave $9f25 alone during this execution ; restore state of the used data port pla sta $9f22 pla sta $9f21 pla sta $9f20 ; return active port selection to previous state pla sta $9f25 If you want to back up and use both ports: ; backup currently-active data port lda $9f20 pha lda $9f21 pha lda $9f22 pha lda $9f25 pha ; switch to the other port.... eor #1 sta $9f25 ; ... and back it up as well lda $9f20 pha lda $9f21 pha lda $9f22 sta lda $9f25 pha ; your code goes here. Feel free to use both ports as required. ; restore both data port addresses and previous selected-port state pla sta $9f25 pla sta $9f22 pla sta $9f21 pla sta $9f20 pla sta $9f25 pla sta $9f22 pla sta $9f21 pla sta $9f20 Note that the order of the CTRL register in these two examples is different! The first example pushes CTRL first because this guarantees the final write of the restore will be to select the correct active port. It then switches to the desired data port (which may be the same one that was already active, but it takes more code to test than to just blindly swap to the same port) and backs up the address of the desired data port. When the restore begins, you'll still have the port you borrowed as the active one, which will be pointed back to wherever it was by the first 3 pops, and lastly, the selected port state is restored. The second example puts the CTRL register on the stack after the three address selection registers because that way, when it restores, it will first select the previously-not-selected port, restore it, then swap back to the previously-selected port and restore that, leaving it in place as the active port just as it was when your routine was called. Obviously, if you intend to use the DCSEL registers, you'd want to tuck those into your backup/restore logic. EDIT: This is incorrect. If you write to those registers, it's because you WANT those changed, so why would you back them up and restore them? It's completely fine to manipulate DCSEL during these two examples so long as in example 1, the ADDRSEL bit is correct before doing the restore.
  23. I think that's why he recommended snapshotting it to the stack first. I made a special wrapped version of the music playback routine in Zsound because it stomps all over the VERA ports too. I recommend against advancing the music during IRQ but if you must, here's a safe function wrapper that takes a few dozen extra cycles to accomplish.... have a nice day.
  24. I think it really would ultimately depend on how well the system(s) it was used in fared on the market. While the presence of such a chip does potentially boost a system's popularity, there are other factors weighing in on a system's overall success. If it were in something as ubiquitous as the Speccy or C64, then yeah - we'd be in a different situation now. After some time, I wonder whether the Japanese market would've embraced the SID functionality of such a chip - I see many VGMs from systems that had PSG+FM capabilities that totally forego the PSG side of things and exclusively use the FM. Perhaps the fancier waveforms might've enticed more composers to use these voices.
  25. For anyone having issues copy/pasting this into the emulator, here's the program as a .bas file. prelude.bas
  • Create New...

Important Information

Please review our Terms of Use