lunoka
Gun-headed
Diving into retrodev
Posts: 55
Homebrew skills: art, music
Fave PCE Shooter: Burning angels
Fave PCE Platformer: Ninja Spirit
Fave PCE Game Overall: Valis 3
Fave PCE RPG: Neutopia
|
Post by lunoka on Jul 9, 2023 14:16:48 GMT
Hello/
Working on a simple sound driver, I've embedded a binary file as my module with #incbin directive. I assume the first parameter is a far pointer and I can actually play a simple song. But I use farpeekb() to read the file and it points to the name parameter from #incbin only. How can I stock or pass this pointer to one of my methods? And what would be its signature type? I've tried "farptr" without success.
Thank you for your time o/
|
|
|
Post by elmer on Jul 9, 2023 15:27:24 GMT
How can I stock or pass this pointer to one of my methods? I hope that someone has a better answer for you, but AFAIK this is one of the things that you really need to use __fastcall for, because HuC itself has no concept of how to handle or use far pointers, they're only implemented so that you can pass far pointers to assembly-language library routines. You should probably read these threads ... Getting data in assembly (HuC/ASM)Beginner Fastcall AdviceThen you can use something like this ... __fastcall __nop getDataPtr(unsigned char far *my_data_label<__bl:__ax>);And then do getDataPtr( my_music_data ); to put the full 24-bit ptr into the _ax and _bl variables (HuC versions of the names). BTW ... you really, really, really don't want to be using HuC's farpeekb() to read data from a file like this, because it will be horrendously slow. You can do it as a proof-of-concept for your algorithm, but you will be much better off if you recode your player in assembly language once you're happy that you've got the algorithm correct.
|
|
lunoka
Gun-headed
Diving into retrodev
Posts: 55
Homebrew skills: art, music
Fave PCE Shooter: Burning angels
Fave PCE Platformer: Ninja Spirit
Fave PCE Game Overall: Valis 3
Fave PCE RPG: Neutopia
|
Post by lunoka on Jul 13, 2023 20:39:51 GMT
Thanks for your answer. When I add the prototype you gave me in my main.c, I have an error : ;error: main.c(23) ;__fastcall __nop getDataPtr(unsigned char far *my_data_label<__bl:__ax>); ; ^ ;****** missing open paren ****** ;error: main.c(23)
I assume I'm doing something wrong :/
|
|
|
Post by elmer on Jul 14, 2023 15:53:16 GMT
I assume I'm doing something wrong :/ Probably! But, since you've not posted any source code, or even-better, an actual buildable example of what's not working for you ... there not much help that I can provide.
|
|
|
Post by turboxray on Jul 14, 2023 17:59:13 GMT
Thanks for your answer. When I add the prototype you gave me in my main.c, I have an error : ;error: main.c(23) ;__fastcall __nop getDataPtr(unsigned char far *my_data_label<__bl:__ax>); ; ^ ;****** missing open paren ****** ;error: main.c(23) I assume I'm doing something wrong :/ What version of HuC are you using?
|
|
lunoka
Gun-headed
Diving into retrodev
Posts: 55
Homebrew skills: art, music
Fave PCE Shooter: Burning angels
Fave PCE Platformer: Ninja Spirit
Fave PCE Game Overall: Valis 3
Fave PCE RPG: Neutopia
|
Post by lunoka on Jul 14, 2023 18:03:54 GMT
From the repo in the new organization made by D.Shadoff
|
|
|
Post by turboxray on Jul 16, 2023 19:15:56 GMT
Thanks for your answer. When I add the prototype you gave me in my main.c, I have an error : ;error: main.c(23) ;__fastcall __nop getDataPtr(unsigned char far *my_data_label<__bl:__ax>); ; ^ ;****** missing open paren ****** ;error: main.c(23) I assume I'm doing something wrong :/ you need a return type for the proto. Since that proto is an "inline", hence the nop, it doesn't actually call a function.. but still needs a return type. void __fastcall __nop getDataPtr(unsigned char far *my_data_label<__bl:__ax>); The problem is, this only gets you so far. You can't really just pass the captured pointer (bank and logical address) to use directly in another C function. This tends to be for ASM under the hood. There is no data type that can hold a far pointer either in HuC (needs a minimum 3 bytes). You could do something like a pointer struct (one char and one int), and have an array of them. Or you could just have an INT array, and treat every two entries as a "pair data type" to hold the far pointer. I tend to do this when I want HuC, or just C logic, handling organization and calling methods with far pointers... but that's always paired with fastcall protos that are just ASM functions (zero/nada/nein C code). I have a far ptr routine that looks like this: void __fastcall getFarPointer( char far *obj<__fbank:__fptr>, unsigned int bank_p<__ax>, unsigned int addr_p<__bx> ); And I call it like this: getFarPointer(pcm1, &(pcmPointers.bank[pcmPointers.idx]), &(pcmPointers.addr[pcmPointers.idx]) ); It doesn't really matter how you setup the last two arguments, as long as it's an address to some two locations so it gets stored there. This is what the ASM backend looks like for that call.. ;//......................................................... _getFarPointer.3
lda __fbank sta [__ax] cly lda __fptr sta [__bx],y iny lda __fptr+1 sta [__bx],y rts Just for the sake of completion, and to see how the data structure is passed into my function, here's what it looks like: typedef struct { char idx; char bank[20]; int addr[20]; } PcmPointers; PcmPointers pcmPointers; Honestly, the tricky part in ALL of this - is that the asm code needs to live in a static-mapped location because of the way it's called. This is kind of a pain in the arse (even if you use spring boards to call the far version of it). There is a workaround for this by setting up the fastcall proto to be an asm macro. void __fastcall __macro getFarPointer( char far *obj<__fbank:__fptr>, unsigned int bank_p<__ax>, unsigned int addr_p<__bx> ); And then you can place the asm macro define anywhere you want, since it's not a function call thus it's not code that needs to live somewhere. (it's generated on the fly) ;//......................................................... _getFarPointer.3 macro
lda __fbank sta [__ax] cly lda __fptr sta [__bx],y iny lda __fptr+1 sta [__bx],y .endm With this approach, you can now make a C wrapper function that has #asm inside of it and not have to worry about static mapped asm functions or spring boards. I'm not sure if this made this whole thing more clear, or less clear hahah. It is what it is tho.
|
|
lunoka
Gun-headed
Diving into retrodev
Posts: 55
Homebrew skills: art, music
Fave PCE Shooter: Burning angels
Fave PCE Platformer: Ninja Spirit
Fave PCE Game Overall: Valis 3
Fave PCE RPG: Neutopia
|
Post by lunoka on Jul 17, 2023 13:14:22 GMT
thank you turboxray for your explanation. I still struggle with the all thing haha but at least I think I understand what a far pointer is. I know it would be best for me to invest in asm but it's on my free time and I wish to keep portability on my projects and C language is for me a fair option. Sorry I think I didn't explain quite well what I wanted to achieve, especially saying "my methods". In the end it's still to work with standard HuC methods. Actually working on my own sound driver as a proof of concept. My module files are around 5KB. So of course I use #INCBIN to add it to the rom but to avoid loading the all file in memory, I was trying to read chunks of the file each frame ( 12 bytes, 2 bytes per channel ) first with farpeek(), but Elmer told me it was very slow so I switched to farmemget() ( maybe very slow too haha but at least it's easier to read a longer buffer ). So basically: #incbin(file1, "file2.mus") #incbin(file2, "file2.mus") main()... for(; { farmemget(buffer, file1 + seek, 12); play(buffer); seek = seek +12; if seek >= file1_size seek = 0; ... } So my question about the far pointer was to find a way to give file1 or file2 or anything else back to function farmemget() at demand ( title screen, loading level etc. ). Considering your solution, does it mean I can do something like this to get a pointer on my file : getFarPointer(file1, &(pcmPointers.bank[pcmPointers.idx]), &(pcmPointers.addr[pcmPointers.idx]) ); and then give it back through the address field to farmemget() in my example ? farmemget(buffer, pcmPointers.addr[0] + seek, 12 ); May I assume that pcmPointers.addr[0] represents an address in the 8KB space of a data bank, which means I could check myself if my file is overlaping 2 banks ? Because this is the case actually and my player crashes if the music is overlaping ( when adding several #incbin() in a row and playing the last one for example). And I assume I would need another asm function to increment the active data bank for farmemget() when I check an overlap. I hope to be not so far from the all thing ^^; EDIT: I tried to add this function, got a pointer on the ID bank and an address but doesn't seem to work with farmemget() . This function really needs the pseudo variable representing the resource in order to work so I'm stuck.
|
|
|
Post by elmer on Jul 18, 2023 15:21:23 GMT
So of course I use #INCBIN to add it to the rom but to avoid loading the all file in memory, I was trying to read chunks of the file each frame ( 12 bytes, 2 bytes per channel ) first with farpeek(), but Elmer told me it was very slow so I switched to farmemget() ( maybe very slow too haha but at least it's easier to read a longer buffer ). So basically: #incbin(file1, "file2.mus") #incbin(file2, "file2.mus") main()... for(; { farmemget(buffer, file1 + seek, 12); play(buffer); seek = seek +12; if seek >= file1_size seek = 0; ... } So my question about the far pointer was to find a way to give file1 or file2 or anything else back to function farmemget() at demand ( title screen, loading level etc. ). Ah, some actual code ... that helps a lot, thanks! I've taken a quick look at farmemget(), and it certainly *looks* like it is supposed to work in exactly the way that you want it to. If it's not working for you, then I'll have to see why it is breaking ... that won't be until the weekend. Another way to access the #incbin data in HuC is to read the file memory as an array, so databyte = file1[seek]; should work ... but it will be terribly slow. Your idea to use farmemget() is much better, but from what you say, currently broken. EDIT: I tried to add this function, got a pointer on the ID bank and an address but doesn't seem to work with farmemget() . This function really needs the pseudo variable representing the resource in order to work so I'm stuck. Nope, turboxray 's advice and technique is good, but it won't work with farmemget(), which expects the raw name/address of the resource in order to properly activate its internal "far pointer" functionality. This stuff is easy to accomplish in assembly-language, but you're definitely hitting one of the areas that the HuC compiler itself doesn't handle very well.
|
|
|
Post by turboxray on Jul 18, 2023 16:14:34 GMT
So my question about the far pointer was to find a way to give file1 or file2 or anything else back to function farmemget() at demand ( title screen, loading level etc. ). Considering your solution, does it mean I can do something like this to get a pointer on my file : getFarPointer(file1, &(pcmPointers.bank[pcmPointers.idx]), &(pcmPointers.addr[pcmPointers.idx]) ); and then give it back through the address field to farmemget() in my example ? farmemget(buffer, pcmPointers.addr[0] + seek, 12 ); Yeah, unfortunately that won't work. The function gets you the pointer, but you can't directly use it with existing HuC Lib functions that expect a label as an argument. I had planned on adding a special internal function that would return (populate __fbank and __fptr or whatever) and populate the label (full address) argument. I.e you could use the function in place of a label for any existing internal lib funcs. I didn't add it yet. An intermediate workaround would be to add another (alt) entry point to all the HuC functions that takes a char,int combo instead of a label. The latter is easier, but it's not clean. The first is hacky too, but it's cleaner. I think for this situation we could just write you a new farmemget() as a C function, using the getFarPointer vars, and just stick some ASM inside the C. I'm actually working on a sound (VGM) sfx streamer for HuC side right now. I'll be working on it this weekend, so I can whip something up real quick for you. So just something like farmemget(src_bank, src_addr, num_bytes, dest_buffer)?
|
|
lunoka
Gun-headed
Diving into retrodev
Posts: 55
Homebrew skills: art, music
Fave PCE Shooter: Burning angels
Fave PCE Platformer: Ninja Spirit
Fave PCE Game Overall: Valis 3
Fave PCE RPG: Neutopia
|
Post by lunoka on Jul 18, 2023 17:58:55 GMT
Thank you for your time! I'm going to post a simple test sample just to reproduce the issue. That would be perfectly fine to me. Will I have to manage for overlaping bank files with this method or is it possible to check with Elmer if he gets time to investigate on the possible overlaping bank issue ? Cheers.
|
|
lunoka
Gun-headed
Diving into retrodev
Posts: 55
Homebrew skills: art, music
Fave PCE Shooter: Burning angels
Fave PCE Platformer: Ninja Spirit
Fave PCE Game Overall: Valis 3
Fave PCE RPG: Neutopia
|
Post by lunoka on Jul 18, 2023 18:44:44 GMT
And here is the test sample. I've duplicated the binary file for ease of use, so both binary files are 6717 byte long.
The loop works perfectly fine with the first file. It crashes at around 1KB on the second file. That seems to match with the PCEAS info log since the second file is split on bank 3 and 4.
By the way, I added srand() function in the beginning of main block otherwise the application crashes at first use of farmemget(), any init issue under the hood?
Attachments:TestFarMemGet.zip (126.34 KB)
|
|
|
Post by turboxray on Jul 25, 2023 18:54:32 GMT
Had power outage and took down my PC over the weekend. I'm back up and running now, and will have a quick example you can use/modify/playwith this weekend.
|
|
lunoka
Gun-headed
Diving into retrodev
Posts: 55
Homebrew skills: art, music
Fave PCE Shooter: Burning angels
Fave PCE Platformer: Ninja Spirit
Fave PCE Game Overall: Valis 3
Fave PCE RPG: Neutopia
|
Post by lunoka on Jul 26, 2023 7:02:47 GMT
thank you
|
|
lunoka
Gun-headed
Diving into retrodev
Posts: 55
Homebrew skills: art, music
Fave PCE Shooter: Burning angels
Fave PCE Platformer: Ninja Spirit
Fave PCE Game Overall: Valis 3
Fave PCE RPG: Neutopia
|
Post by lunoka on Aug 2, 2023 6:01:36 GMT
I've run more tests on farmemget() issues on overlaping banks. It will work only with 1 byte size step. farpeekw() works without any problem, and so does farpeekb() or indexing the resource file with [] notation but will give less performance.
I also may have found a temporary solution on how managing my song files. I've made a Python script that will concatenate all my files in one archive and write a resource.h file with all songs offsets. After that, it's just a matter of indexes.
Using farpeekw(), I can overpass the crashing issue with farmemget(), though I will lose some perfs ( around 1.5X slower ) on the reading and some code space.
Attachments:TestFarMemGet.zip (2.45 KB)
|
|