Author Topic: PC-FX homebrew development.  (Read 17483 times)

nodtveidt

  • Guest
Re: PC-FX homebrew development.
« Reply #195 on: March 20, 2016, 07:35:22 PM »
Now, I've got the patience to keep-on-plugging-away it, but progress with GCC is slow-and-very-very-painful.
And this is why you're so awesome. :D

EDIT: I erased the rest of this post because I'm retarded.
« Last Edit: March 20, 2016, 08:33:43 PM by The Old Rover »

elmer

  • Hero Member
  • *****
  • Posts: 2153
Re: PC-FX homebrew development.
« Reply #196 on: March 21, 2016, 04:45:21 AM »
Now, I've got the patience to keep-on-plugging-away it, but progress with GCC is slow-and-very-very-painful.

And this is why you're so awesome. :D

Hahaha ... perhaps "stupid" is a better word!  ](*,)

Anyway, I think that I got the "stack-problem" in GCC 4.7.4 fixed late last-night. Now it needs some more testing to make sure that I wasn't hallucinating.

Then I still want to "break" it again and implement a different ABI, if I can.  :-s

The current ABI probably seemed-like-a-good-idea-at-the-time, but it breaks the possibility of in-game stack backtraces which are incredibly-helpful to decent debugging.

Luckily, NEC's idea of reserving the R2 and R5 registers for things that nobody-ever-used-in-practice gives us 2 free registers to use in a new ABI (if I can wrestle GCC into compliance).

Again ... very low-level technical stuff, but it's part of making the toolchain "usable" for any of the poor-suckers that decide to give-it-a-try.  :wink:

nodtveidt

  • Guest
Re: PC-FX homebrew development.
« Reply #197 on: March 21, 2016, 06:59:09 AM »
I'll leave the low-end stuff to you, heh. :lol: I'm more of the end-user type... and I can assure you that us poor suckers are going to be numerous once this is more refined. ;)

I did finally get ADPCM playback working, and how. It's not trivial to explain how it works, but I will probably write up a quick tutorial on it at some point, as well as other things I've managed to get working on the end-user side of the struggle. Not having access to a whole lot of proper, readable documentation has proven to be the largest hurdle, and trying to find any kind of example source code with google usually returns snippets that use existing libraries rather than going straight to the metal. My experiences with SCSI have been the worst offender on both counts.

elmer

  • Hero Member
  • *****
  • Posts: 2153
Re: PC-FX homebrew development.
« Reply #198 on: March 21, 2016, 03:43:21 PM »
Anyway, I think that I got the "stack-problem" in GCC 4.7.4 fixed late last-night. Now it needs some more testing to make sure that I wasn't hallucinating.

The good news ... "yes", that "stack problem" seems to be fixed!  :D

And with-a-few-more-changes, newlib is now actually linking into a test-app, and newlib's "strlen" is working.

With yet-more-changes, "sprintf" is actually compiled and linked into the test-app.

The bad news ... that's shown-up the next bug in the compiler, "varargs" are not working.  #-o

Oh well ... onto the next piece of nasty debugging.  ](*,)


I did finally get ADPCM playback working, and how. It's not trivial to explain how it works, but I will probably write up a quick tutorial on it at some point, as well as other things I've managed to get working on the end-user side of the struggle.

Great! Any documentation that you can do would be extremely helpful.

nodtveidt

  • Guest
Re: PC-FX homebrew development.
« Reply #199 on: March 21, 2016, 03:46:25 PM »
OK sooooo.... ADPCM. This turned out easier than I thought... I was just doing a few things wrong, which can happen when you code for so long without resting.

Code: [Select]
eris_king_set_kram_pages(0, 0, 0, 0);The last value is the ADPCM KRAM page. I leave it at 0. You can put it at 1, which means you have to set bit 31 when you load a sample into KRAM.

Code: [Select]
out8(0x120,1);Bits 0 and 1 set the sample rate. I set this to 1 for 16kHz. 0 is 32kHz, 2 is 8kHz and 3 is 4kHz. Bits 2 and 3 set linear interpolation for channels 0 and 1, respectively. Bits 4 and 5 reset channels 0 and 1 respectively. I left them all alone for now. Experiment at your own leisure.

Code: [Select]
out8(0x122,0x3F);
out8(0x124,0x3F);
out8(0x126,0x3F);
out8(0x128,0x3F);
Set the left/right volumes of channel 0 and 1 to max (range is 0x0 - 0x3F). liberis has a function for this but as you've noticed, I'm avoiding liberis functions for now and just using the ports directly... I know the ports work, not so much the functions. :)

Code: [Select]
out16(0x600,0x51);
out16(0x604,0);
out16(0x600,0x52);
out16(0x604,0);
Port 0x600, when written, is Register Select on the KING. For this, I'm first telling KING to select register 0x51, which is the channel control for ADPCM channel 0. Bit 0 is the buffer select (0 for sequential and 1 for ring), bit 1 enables end interrupt and bit 2 enables intermediate interrupt. I'm not 100% sure exactly how the interrupts work just yet so I leave them alone for now. Anyway, writing to 0x604 after selecting a register configures that register with the data you send it. Register 0x52 will configure ADPCM channel 1.

That's pretty much it for getting ADPCM set up. To actually play a sample, you have to do a few things.

Code: [Select]
out16(0x600,0x58);0x58 is the ADPCM channel 1 start address. This is going to tell KING where to get the ADPCM data from. ADPCM data is kept in normal KRAM. Using 0x5C instead of 0x58 does the same for channel 1.

Code: [Select]
out16(0x604,(0x2000/256));The KRAM address has to be divided by 256. I wrote this out longhand to demonstrate.

Code: [Select]
out16(0x600,0x59);Now I'm telling KING to set the end address. This is 0x59 for channel 0 and 0x5D for channel 1.

Code: [Select]
out16(0x604,(0x2000+10417));The end address is an absolute address, not a divided one. My ADPCM sample is 20834 bytes long, but since addressing is in words, I cut this value in half. I do believe that if your samples' addresses are outside of the range of 16 bits that you will also need to send the upper bits of the address to 0x606... I've never tested this but this seems logical.

Now that you've told KING the range of the sample, it's time to actually play it.

Code: [Select]
out16(0x600,0x50);0x50 is the register that controls playback for both channels. That also means that you will need to be mindful of samples already playing.

Code: [Select]
out16(0x604,5);Finally, play the sample. Bit 0 plays the sample configured for channel 0 and bit 1 plays the sample configured for channel 1. Bits 2 and 3 control the sampling rate and use the same scheme as earlier. Since I want 16kHz, I set bit 2 on and bit 3 off. So... bit 0 on, bit 1 off, bit 2 on, bit 3 off... value is 5. This plays the ADPCM sample in channel 0 at 16kHz.

If you need to stop a sample while it's playing, select register 0x50 and send a 0 to the bit of the channel you wish to stop. Writing just 0 to 0x604 will stop both channels. You can read 0x604 to see which channels are currently in use, and then just stop either of them if you want to. If you set the buffer type to Ring, the sample will playback infinitely until you tell it to stop in this manner... or so the docs state.

So that pretty much sums up how to use ADPCM on the PC-FX.

nodtveidt

  • Guest
Re: PC-FX homebrew development.
« Reply #200 on: March 22, 2016, 09:38:01 AM »
To continue on the ADPCM kick, I figure I should add in some more details.

So in my example, I give it a starting address of 0x2000. That's because I've loaded my ADPCM array data into KRAM at that point. Loading data into KRAM is easy...

Code: [Select]
eris_king_set_kram_write(0x2000, 1);
for(i = 0; i < sizeof(voxarray)-1; i++)
eris_king_kram_write(voxarray[i]);

I have a u16 array called voxarray that has all the ADPCM data in it. The two parameters of the first function call are the address and the auto-increment amount (1 word, in this case). Each time you write a word to KRAM, the pointer moves up 1 word, so you don't have to keep setting the address. Nifty and convenient methinks.

So... getting the ADPCM data into your source code... well, this is where having some coding knowledge worked out well for me. I coded a simple utility called any2arr which takes any file you give it and creates a header with the data of that file as a u16 array.

http://www.frozenutopia.com/pcfx/any2arr.7z

Making ADPCM files is also easy... just snag a copy of sox. To make the samples for Asteroid Challenge FX, I used sox like so:

Code: [Select]
sox -r 16000 boom.wav boom.vox
The -r 16000 tells it to use 16kHz, the .wav is obviously my source audio, and the .vox is the output file. sox knows to make an ADPCM file based on the .vox extension. So, just convert your file to a .vox with sox, run the .vox file through any2arr, and you've got your ADPCM data, ready to include into your program.

EDIT: Forgot to mention... since we're using words here, take the filesize of your .vox file and divide it in half to get the length. Take that divided value and add it to your starting address to get the ending address that you need. I think I did mention this briefly in the first post about this but it bears mentioning again, because reasons.
« Last Edit: March 22, 2016, 09:42:16 AM by The Old Rover »

elmer

  • Hero Member
  • *****
  • Posts: 2153
Re: PC-FX homebrew development.
« Reply #201 on: March 22, 2016, 10:18:17 AM »
So... getting the ADPCM data into your source code... well, this is where having some coding knowledge worked out well for me. I coded a simple utility called any2arr which takes any file you give it and creates a header with the data of that file as a u16 array.

Good stuff!  :)

But you're really going-out-of-your-way to avoid using "objcopy" or an assembly file with an "incbin", aren't you!  :wink:

http://www.linuxjournal.com/content/embedding-file-executable-aka-hello-world-version-5967

************

On my side of things ... GCC is now correctly passing the varargs to "sprintf" ... where it all just dies.  ](*,)

nodtveidt

  • Guest
Re: PC-FX homebrew development.
« Reply #202 on: March 22, 2016, 10:49:52 AM »
But you're really going-out-of-your-way to avoid using "objcopy" or an assembly file with an "incbin", aren't you!  :wink:
Honestly... I had no idea about that... heh :D I just go with what I know... remember, I'm not a hardcore coder like you are, I'm just a game developer who can code a little. :)

elmer

  • Hero Member
  • *****
  • Posts: 2153
Re: PC-FX homebrew development.
« Reply #203 on: March 22, 2016, 11:25:34 AM »
Honestly... I had no idea about that... heh :D I just go with what I know... remember, I'm not a hardcore coder like you are, I'm just a game developer who can code a little. :)

The point is ... you're finding problems, and you're solving them. That's brilliant!  8)

It's just that sometimes you're hitting problems that have already been solved, and this is one of those cases.  :wink:

objcopy -I binary -O elf32-v810 -B v810 filename.bin filename.o

nodtveidt

  • Guest
Re: PC-FX homebrew development.
« Reply #204 on: March 22, 2016, 12:33:53 PM »
Well, the one problem I still can't figure out is the SCSI bit. I'm experimenting with KING's SCSI ports now. I read 0x600 and 0x602 for now... 0x602 *should* contain the SCSI status, if I'm reading the daifukkat docs correctly. It's returning 1101100, which indicates Busy, Request, C/D, and I/O are all set to 1. I figured maybe I could use this to determine if the system knew when the audio track had finished playing. However, the status never changes. So I guess this is out.

elmer

  • Hero Member
  • *****
  • Posts: 2153
Re: PC-FX homebrew development.
« Reply #205 on: March 22, 2016, 12:44:40 PM »
It's returning 1101100, which indicates Busy, Request, C/D, and I/O are all set to 1. I figured maybe I could use this to determine if the system knew when the audio track had finished playing. However, the status never changes. So I guess this is out.

That sounds very-much-like what I was seeing in the Xanadu 2's custom CD-reading code.

I think that those bits are all just to do with controlling communication over the SCSI bus itself.

If you want to find out if the CD has finished playing a track, I suspect that you'll have to send it some kind of a "status query" command.

nodtveidt

  • Guest
Re: PC-FX homebrew development.
« Reply #206 on: March 22, 2016, 12:48:09 PM »
Here's what the SCSI-2 docs say, but I am not able to make heads or tails of this, honestly...

Quote
PLAY AUDIO commands with the immediate bit set in the audio control
mode return status as soon as the command has been validated (which may
involve a seek to the starting address). The playback operation
continues and may complete without notification to the initiator.
Error termination of audio operations shall be reported to the
initiator by returning immediate CHECK CONDITION status to the next
command (except for REQUEST SENSE and INQUIRY.)  The deferred error
sense data (see 8.2.14.2.) is used to indicate that the error is not
due to the current command.

The status of the play operation may be determined by issuing a REQUEST
SENSE command.  The sense key is set to NO SENSE and the audio status
(see 14.2.10) is reported in the additional sense code qualifier field.

1, I have no idea how to set the audio control mode. The docs are very, very dry and hard to follow. 2, I have no idea how to receive the results from an SCSI command.

elmer

  • Hero Member
  • *****
  • Posts: 2153
Re: PC-FX homebrew development.
« Reply #207 on: March 22, 2016, 01:05:21 PM »
Here's what the SCSI-2 docs say, but I am not able to make heads or tails of this, honestly...

Hahaha ... welcome to my world!  :wink:

Quote
PLAY AUDIO commands with the immediate bit set in the audio control...

Basically, the return-code from "PLAY AUDIO" just says whether the CD drive has validated and accepted the "PLAY AUDIO" command.

You've got to send the drive a "REQUEST SENSE" command and look at the return code to see if it is still playing.


Quote
I have no idea how to receive the results from an SCSI command.

That's going to be a problem.

You should see an example of reading the result of a "REQUEST SENSE" command somewhere in Alex's examples ... I can't imagine how he could have managed to read data from the CD without using it.

nodtveidt

  • Guest
Re: PC-FX homebrew development.
« Reply #208 on: March 22, 2016, 01:48:45 PM »
Unfortunately, I see no mention of it in his examples, and of course, his source is next to impossible for someone like me to read... but I tried at least. :)

http://www.staff.uni-mainz.de/tacke/scsi/SCSI2-08.html

^^^ has the "details" of REQUEST SENSE. This is where getting a response becomes dim... I have no idea what to read to get this response. It says it returns 18 bytes... where?

Okay so one other thing is that eris_low_scsi_command() returns an int... but does not actually say what the return value might actually be. From what limited amount I understand of our lovely assembler here, I assume that this is the entirety of the function:

Code: [Select]
_eris_low_scsi_command:
mov lp, r15
mov 0, r14
mov 1, r13
movea 0x84, r0, r12
mov 3, r11
mov r0, r10
out.h r11, 0x600[r0]
out.h r0, 0x604[r0]
out.h r0, 0x600[r0]
add 2, r11
out.h r12, 0x604[r0]

scsi_delay

out.h r13, 0x600[r0]
out.h r13, 0x604[r0]

scsi_delay

out.h r13, 0x600[r0]
out.h r11, 0x604[r0]

scsi_delay

1: jal _eris_low_scsi_get_phase
be 1b

scsi_delay

out.h r13, 0x600[r0]
out.h r0, 0x604[r0]

scsi_delay

1: jal _eris_low_scsi_get_phase
cmp 4, r10 # command
bne 1b

mov 3, r11
mov 2, r10
out.h r11, 0x600[r0]
out.h r10, 0x604[r0]

1: in.h 0x602[r0], r11
andi 0x20, r11, r11
be 1b

br 2f
1:
mov 1, r10
mov r0, r11
cmp r14, r7
bnh 3f
ld.b 0[r6], r11
add 1, r6
3:
add 1, r14
movea 0x11, r0, r12
out.h r0, 0x600[r0]
out.h r11, 0x604[r0]

out.h r10, 0x600[r0]
out.h r10, 0x604[r0]

out.h r10, 0x600[r0]
out.h r12, 0x604[r0]

3: in.h 0x602[r0], r11
andi 0x20, r11, r11
bne 3b

out.h r10, 0x600[r0]
out.h r10, 0x604[r0]

3: in.h 0x602[r0], r11
andi 0x20, r11, r11
be 3b

2: jal _eris_low_scsi_get_phase
cmp 4, r10 # command
be 1b

mov 1, r11
mov 3, r10
out.h r11, 0x600[r0]
out.h r0, 0x604[r0]
out.h r10, 0x600[r0]
out.h r0, 0x604[r0]

mov r14, r10
mov r15, lp
jmp [lp]

nodtveidt

  • Guest
Re: PC-FX homebrew development.
« Reply #209 on: March 22, 2016, 02:28:44 PM »
Using eris_low_scsi_status() freezes the system. Every. Single. Time. No matter what's going on... even if nothing's going on.

Sending another SCSI command freezes the system... even if I've also used eris_low_scsi_abort(). eris_low_scsi_abort() itself does not lock up the system, but using eris_low_scsi_command() a second time does.

At this point, my only guess is that something in the SCSI setup is misconfigured.

EDIT: Using eris_low_scsi_reset() allows me to use eris_low_scsi_command() a second time... but this is more than likely not the way it's supposed to work...
« Last Edit: March 22, 2016, 02:38:07 PM by The Old Rover »