Linted markdown
parent
98315f9a72
commit
6760f6d5de
|
@ -1,5 +1,4 @@
|
|||
## Introduction
|
||||
---
|
||||
|
||||
- There are four known versions of this uCode: the accelerator version, the DMA version, the light version and the SMS version.
|
||||
- The accelerator version uses the DSP's accelerator to read sound data, whereas the DMA version reads sound data by DMA transfers.
|
||||
|
@ -8,14 +7,11 @@
|
|||
- The SMS version uses a different communication protocol which requires three sync mails every frame instead of two every 16 PBs. Super Mario Sunshine uses the SMS version (hence its name).
|
||||
|
||||
## Details
|
||||
---
|
||||
|
||||
### Initialization Mails
|
||||
---
|
||||
|
||||
These are all the mails that the game sends to the DSP upon initialization:
|
||||
|
||||
|
||||
#### Command 0xE (AKA DsetDMABaseAddr)
|
||||
|
||||
```
|
||||
|
@ -39,7 +35,6 @@ Note that this command is available only in DMA version.
|
|||
```
|
||||
|
||||
### Continuous Mails
|
||||
---
|
||||
|
||||
These are all the mails that the game continuously sends to the DSP.
|
||||
|
||||
|
@ -59,7 +54,6 @@ The Legend of Zelda: Wind Waker keeps sending these forever, alternating between
|
|||
|
||||
|
||||
### Synchronization Mails
|
||||
---
|
||||
|
||||
```
|
||||
00000000
|
||||
|
@ -76,19 +70,18 @@ The Legend of Zelda: Wind Waker keeps sending these forever, alternating between
|
|||
```
|
||||
|
||||
### Ending Mails
|
||||
---
|
||||
|
||||
`CDD10003`
|
||||
Sent by the CPU after DsyncFrame command has completed (and sent 0xDCD10005 to the CPU).
|
||||
|
||||
|
||||
### Notes
|
||||
---
|
||||
|
||||
The first command sent is 0xE. In the real Zelda ucode, this command is dummy. In the ucode used by SMG, it isn't the case.
|
||||
Here is one of the differences between these two ucodes.
|
||||
|
||||
For each command:
|
||||
|
||||
- The CPU sends one mail whose high part is zero and whose low part represents the number of mails that will follow (non-light versions only).
|
||||
- The CPU sends one mail whose bits 24 to 27 represent the command.
|
||||
- The CPU sends the remaining mails which are the parameters for the command
|
||||
|
@ -98,13 +91,12 @@ After studying the ucode a bit, I noticed that these mails are tied by pairs.
|
|||
A mail that has its low part empty means that another mail will follow, with a number in its high part.
|
||||
The number can probably vary from 0 to 15, though here it's only varying from 0 to 3.
|
||||
When receiving these mails, the ucode seems to store some values from the first mail into memory. These values are used only by command 0x2 (SyncFrame).
|
||||
The purpose of that whole system is probably to keep mixing synchronous with the game's main loop.
|
||||
The purpose of that whole system is probably to keep mixing synchronous with the game's main loop.
|
||||
|
||||
|
||||
## How the Zelda ucode interprets mails
|
||||
---
|
||||
|
||||
The mails are interpreted by the exception 7 handler.
|
||||
The mails are interpreted by the exception 7 handler.
|
||||
This means that exception 7 is probably triggered when a new mail was received (by DSP).
|
||||
|
||||
The first mail received determines how the following ones will be interpreted:
|
||||
|
@ -112,37 +104,33 @@ The first mail received determines how the following ones will be interpreted:
|
|||
|
||||
|
||||
### If G is zero
|
||||
---
|
||||
|
||||
The whole mail is likely zero. The following mail will be formatted as follows:
|
||||
`0x000HIIII`
|
||||
|
||||
The following operation will then be executed:
|
||||
|
||||
```
|
||||
MEM[0x034E] = ((H+1) << 4);
|
||||
MEM[0x04FC+H] = I;
|
||||
```
|
||||
|
||||
|
||||
### If G is non-zero
|
||||
---
|
||||
|
||||
G determines how much mails will follow.
|
||||
The first following mail defines the command number, the next ones define additional parameters depending on the command.
|
||||
|
||||
Then the mails are stored to memory and will be processed by the message loop.
|
||||
|
||||
|
||||
## How to play sound using the Zelda ucode (non-light versions)
|
||||
---
|
||||
|
||||
**First of all, keep in mind that the Zelda uCode is under Nintendo's copyright.**
|
||||
We don't recommend releasing apps that would use that uCode. However you're free to play around with it to see how powerful it is.
|
||||
|
||||
### Initializing the ucode
|
||||
---
|
||||
|
||||
If you're using the DMA version, send a DsetDMABaseAddr command, by sending the following mails to the DSP:
|
||||
|
||||
```
|
||||
00000002
|
||||
8E000000
|
||||
|
@ -156,6 +144,7 @@ When reading sound data from main memory, the base address G will be added to th
|
|||
For no fancy stuff, we'd recommend setting the DMA base address to the beginning of the ARAM.
|
||||
|
||||
Finally, you need to send a DsetupTable command:
|
||||
|
||||
```
|
||||
00000005
|
||||
8100GGGG
|
||||
|
@ -166,6 +155,7 @@ KKKKKKKK
|
|||
```
|
||||
|
||||
where:
|
||||
|
||||
- G is the number of voices
|
||||
- H is the address of the voice parameter blocks (PBs). See below for more info about them.
|
||||
- I is the address of a 1280-byte data table. Its purpose isn't known yet.
|
||||
|
@ -174,9 +164,9 @@ where:
|
|||
|
||||
|
||||
### Playing sound
|
||||
---
|
||||
|
||||
The DsyncFrame command is responsible for mixing, so if you want to hear any sound, you need to send DsyncFrame commands regularly:
|
||||
|
||||
```
|
||||
00000005
|
||||
82GGHHHH
|
||||
|
@ -187,6 +177,7 @@ JJJJJJJJ
|
|||
```
|
||||
|
||||
where:
|
||||
|
||||
- G is the number of frames
|
||||
- H is some unknown number
|
||||
- I is the address of right output buffers
|
||||
|
@ -195,6 +186,7 @@ where:
|
|||
For each frame to render, there is a right buffer of 80 samples, and a left buffer of same capacity.
|
||||
|
||||
After you sent a DsyncFrame, you need, for each frame, to send sync mails:
|
||||
|
||||
```
|
||||
00000000
|
||||
000GHHHH
|
||||
|
@ -209,11 +201,13 @@ Then it sends two mails: `0xDCD10004` and `0xF355FFXX`, to indicate the current
|
|||
Then it processes the next frame.
|
||||
|
||||
When the uCode finished processing all the frames, it sends a `0xDCD10005` mail and triggers an interrupt. Then you must send a last mail:
|
||||
|
||||
```
|
||||
CDD1000G
|
||||
```
|
||||
|
||||
where G is the task to execute, as follows:
|
||||
|
||||
- 0: Halt
|
||||
- 1: Dump
|
||||
- 2: Do something and halt
|
||||
|
@ -229,7 +223,6 @@ Phew. Now that your DsyncFrame successfully completed, you have to send another
|
|||
|
||||
|
||||
## The parameter blocks
|
||||
---
|
||||
|
||||
Like the well-known AX ucode, the Zelda ucode uses one parameter block (PB) per voice. (The number of voices is set by DsetupTable command).
|
||||
It also uses 4 much smaller PBs which seem to be used for something like reverb...
|
||||
|
@ -290,12 +283,14 @@ Here is a table of what has been figured out inside these PBs (some parts of it
|
|||
| 0x90 | 48 | Padding |
|
||||
|
||||
Notes:
|
||||
|
||||
- The last 64 words (128 bytes) are read-only. The ucode reads them from memory but never writes them back.
|
||||
- The read offset and start offset are exprimed in bytes. They're offsets in ARAM, except in SMG ucode where they're offsets in main memory (minus DMA base set by command 0xE).
|
||||
- The lengths and loop start positions are exprimed in samples.
|
||||
- When processing blank voices the ucode fills the temporary buffer with the value at offset 0x33 in PB.
|
||||
|
||||
List of available sound formats:
|
||||
|
||||
- 0x0000: Square wave
|
||||
- 0x0001: Saw wave
|
||||
- 0x0002: Square+saw?
|
||||
|
@ -315,7 +310,6 @@ List of available sound formats:
|
|||
|
||||
|
||||
## Unknown Registers?
|
||||
---
|
||||
|
||||
The Zelda ucode accesses a memory region at 0xFF8X. At glance you may think it's accessing unknown DSP registers, but this is not the case.
|
||||
This region is always accessed by lrs/srs opcodes, while CR is set to 0x0004.
|
||||
|
@ -327,4 +321,4 @@ This explanation makes sense. The ucode doesn't read from 0xFF8X but from 0x048X
|
|||
It also accesses various other addresses using lrs/srs, while CR is set to 0x0004 too.
|
||||
The ucode is actually using these opcodes to access the current voice PB (which is stored at 0x0400).
|
||||
This is meant as an optimization of space, and possibly speed as well, since lrs/srs opcodes occupy only one word, while opcodes reading from an absolute address (such as lr/sr) occupy two words.
|
||||
It might be possible that lrs/srs are faster, as well.
|
||||
It might be possible that lrs/srs are faster, as well.
|
Loading…
Reference in New Issue