diff options
| author | spicyjpeg <thatspicyjpeg@gmail.com> | 2022-12-18 16:30:02 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-12-18 16:30:02 +0100 |
| commit | b12b716a9e54c4f1892795a136d6ffeb088ff001 (patch) | |
| tree | 5888e4a77b207f24ccd9ad148d1c74e9c33400e9 /examples | |
| parent | 77306e187ef1a7ad5d3508ae9acb38edc5b68255 (diff) | |
| parent | a3359c0e7d85bf4752cda3b00892ecd5ef691077 (diff) | |
| download | psn00bsdk-b12b716a9e54c4f1892795a136d6ffeb088ff001.tar.gz | |
Merge pull request #68 from spicyjpeg/bugfix
Bugfixes, psxcd rewrite, psxgpu and CMake tweaks (v0.22)
Diffstat (limited to 'examples')
| -rw-r--r-- | examples/cdrom/cdbrowse/main.c | 1 | ||||
| -rw-r--r-- | examples/cdrom/cdxa/main.c | 2 | ||||
| -rw-r--r-- | examples/demos/n00bdemo/main.c | 6 | ||||
| -rw-r--r-- | examples/io/pads/spi.c | 28 | ||||
| -rw-r--r-- | examples/io/system573/k573io.c | 126 | ||||
| -rw-r--r-- | examples/io/system573/k573io.h | 274 | ||||
| -rw-r--r-- | examples/io/system573/main.c | 80 | ||||
| -rw-r--r-- | examples/mdec/strvideo/main.c | 93 | ||||
| -rw-r--r-- | examples/sound/cdstream/main.c | 4 | ||||
| -rw-r--r-- | examples/sound/cdstream/stream.vag | bin | 4646912 -> 4687872 bytes | |||
| -rw-r--r-- | examples/sound/spustream/interleave.py | 13 | ||||
| -rw-r--r-- | examples/sound/spustream/main.c | 51 | ||||
| -rw-r--r-- | examples/sound/spustream/stream.vag | bin | 1140736 -> 1140736 bytes | |||
| -rw-r--r-- | examples/sound/vagsample/main.c | 4 |
14 files changed, 277 insertions, 405 deletions
diff --git a/examples/cdrom/cdbrowse/main.c b/examples/cdrom/cdbrowse/main.c index f614f1d..c554f64 100644 --- a/examples/cdrom/cdbrowse/main.c +++ b/examples/cdrom/cdbrowse/main.c @@ -55,6 +55,7 @@ */ #include <stdint.h> +#include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> diff --git a/examples/cdrom/cdxa/main.c b/examples/cdrom/cdxa/main.c index 93cf01a..94e6d45 100644 --- a/examples/cdrom/cdxa/main.c +++ b/examples/cdrom/cdxa/main.c @@ -189,7 +189,7 @@ char padbuff[2][34]; char xa_sector_buff[2048]; /* Callback for detecting end of channel (hooked by CdReadyCallback) */ -void xa_callback(int intr, unsigned char *result) +void xa_callback(CdlIntrResult intr, unsigned char *result) { SECTOR_HEAD *sec; diff --git a/examples/demos/n00bdemo/main.c b/examples/demos/n00bdemo/main.c index 069b549..6d0be3c 100644 --- a/examples/demos/n00bdemo/main.c +++ b/examples/demos/n00bdemo/main.c @@ -76,7 +76,7 @@ volatile int timer_counter = 0, frame_counter = 0, frame_rate = 0; void sort_overlay(int showlotl); void lightdemo(); -#define K573_WATCHDOG *((volatile unsigned short *) 0x1f5c0000) +#define K573_WATCHDOG *((volatile unsigned short *) 0xbf5c0000) #define K573_EXP1_CFG 0x24173f47 void timerTick() { @@ -101,8 +101,8 @@ void timerSetup() { EnterCriticalSection(); #ifdef SYSTEM_573_SUPPORT - EXP1_ADDR = 0x1f000000; - EXP1_DELAY_SIZE = K573_EXP1_CFG; + BUS_EXP1_ADDR = 0x1f000000; + BUS_EXP1_CFG = K573_EXP1_CFG; #endif TIMER_CTRL(2) = 0x0258; // CLK/8 input, IRQ on reload diff --git a/examples/io/pads/spi.c b/examples/io/pads/spi.c index 292e682..234bdba 100644 --- a/examples/io/pads/spi.c +++ b/examples/io/pads/spi.c @@ -93,8 +93,8 @@ static void _spi_next_req(void) { static void _spi_poll_irq_handler(void) { // Fetch the last response byte, which wasn't followed by a pulse on /ACK, // from the RX FIFO. - if (JOY_STAT & 0x0002) - _context.rx_buff[_context.rx_len - 1] = (uint8_t) JOY_TXRX; + if (SIO_STAT(0) & 0x0002) + _context.rx_buff[_context.rx_len - 1] = (uint8_t) SIO_DATA(0); if (_context.callback) _context.callback(_context.port, _context.rx_buff, _context.rx_len); @@ -109,49 +109,49 @@ static void _spi_poll_irq_handler(void) { // enabling the /ACK IRQ. In order to communicate with controllers, /CS has // to be driven low again for about 20 us before sending the first byte. // TODO: these delays can be probably tweaked for better performance - JOY_CTRL = 0x0010; + SIO_CTRL(0) = 0x0010; for (uint32_t i = 0; i < 1000; i++) __asm__ volatile(""); - JOY_CTRL = 0x1003 | (_context.port << 13); + SIO_CTRL(0) = 0x1003 | (_context.port << 13); for (uint32_t i = 0; i < 2000; i++) __asm__ volatile(""); // Send the first byte indicating which device to address. If the matching // device is connected, it will reply by triggering the /ACK IRQ. - JOY_TXRX = _context.tx_buff[0]; + SIO_DATA(0) = _context.tx_buff[0]; } static void _spi_ack_irq_handler(void) { // Wait until /ACK is pulled up by the controller before sending the next // byte. According to nocash docs, this has to be done before resetting the // IRQ. - while (JOY_STAT & 0x0080) + while (SIO_STAT(0) & 0x0080) __asm__ volatile(""); // Keep /CS pulled low and acknowledge the IRQ (bit 4) to ensure it can be // triggered again. - JOY_CTRL = 0x1013 | (_context.port << 13); + SIO_CTRL(0) = 0x1013 | (_context.port << 13); if (!_context.rx_len) { // We just sent the first address byte. Obviously the response we // received was read from an open bus, so the SPI port's internal FIFO // must be flushed (by performing dummy reads) to ensure we are only // going to read valid data from now on. - JOY_TXRX; + SIO_DATA(0); } else if (_context.rx_len <= SPI_BUFF_LEN) { // If this is not the first byte, put it in the RX buffer. - _context.rx_buff[_context.rx_len - 1] = (uint8_t) JOY_TXRX; + _context.rx_buff[_context.rx_len - 1] = (uint8_t) SIO_DATA(0); } // Send the next byte, or a null byte if there is no more data to send and // we're just reading a response. _context.rx_len++; if (_context.rx_len < _context.tx_len) - JOY_TXRX = (uint32_t) _context.tx_buff[_context.rx_len]; + SIO_DATA(0) = (uint32_t) _context.tx_buff[_context.rx_len]; else - JOY_TXRX = 0x00; + SIO_DATA(0) = 0x00; } /* Public API */ @@ -198,9 +198,9 @@ void SPI_Init(SPI_Callback callback) { InterruptCallback(7, &_spi_ack_irq_handler); ExitCriticalSection(); - JOY_CTRL = 0x0040; // Reset all registers - JOY_MODE = 0x000d; // 1x multiplier, 8 data bits, no parity - JOY_BAUD = 0x0088; // 250000 bps + SIO_CTRL(0) = 0x0040; // Reset all registers + SIO_MODE(0) = 0x000d; // 1x multiplier, 8 data bits, no parity + SIO_BAUD(0) = 0x0088; // 250000 bps SPI_SetPollRate(250); _current_req = 0; diff --git a/examples/io/system573/k573io.c b/examples/io/system573/k573io.c index 53c109f..69cc871 100644 --- a/examples/io/system573/k573io.c +++ b/examples/io/system573/k573io.c @@ -1,12 +1,6 @@ /* * PSn00bSDK Konami System 573 example (I/O driver) * (C) 2022 spicyjpeg - MPL licensed - * - * Note that this is far from being a complete driver. It currently lacks: - * - ATAPI driver - * - Flash erasing/writing APIs - * - JVS bus APIs - * - Functions for accessing the digital I/O board's MP3 decoder */ #include <stdint.h> @@ -15,115 +9,47 @@ #include "k573io.h" -K573_IOBoardType _board_type = IO_TYPE_ANALOG; - -/* I/O board light control */ - -static void _k573_set_lights_analog(uint32_t lights) { - uint32_t bits = 0xffffffff; - - bits ^= (lights & 0x01010101) << 7; // Lamp n*8+0 -> bit n*8+7 - bits ^= (lights & 0x02020202) << 5; // Lamp n*8+1 -> bit n*8+6 - bits ^= (lights & 0x04040404) >> 1; // Lamp n*8+2 -> bit n*8+1 - bits ^= (lights & 0x08080808) >> 3; // Lamp n*8+3 -> bit n*8+0 - bits ^= (lights & 0x10101010) << 1; // Lamp n*8+4 -> bit n*8+5 - bits ^= (lights & 0x20202020) >> 1; // Lamp n*8+5 -> bit n*8+4 - bits ^= (lights & 0x40404040) >> 3; // Lamp n*8+6 -> bit n*8+3 - bits ^= (lights & 0x80808080) >> 5; // Lamp n*8+7 -> bit n*8+2 - - K573_IO_BOARD[ANALOG_IO_REG_LIGHTS0] = (bits) & 0xff; - K573_IO_BOARD[ANALOG_IO_REG_LIGHTS1] = (bits >> 8) & 0xff; - K573_IO_BOARD[ANALOG_IO_REG_LIGHTS2] = (bits >> 16) & 0xff; - K573_IO_BOARD[ANALOG_IO_REG_LIGHTS3] = (bits >> 24) & 0xff; -} - -// This function controls light outputs on digital I/O boards (i.e. the ones -// that also include MP3 playback hardware, used by most Bemani games). -// TODO: test this on real hardware -- it might not work if lights are handled -// by the board's FPGA, which requires a binary blob... -static void _k573_set_lights_digital(uint32_t lights) { - uint32_t bits = 0xffffffff; - - bits ^= (lights & 0x11111111); // Lamp n*4+0 -> bit n*4+0 - bits ^= (lights & 0x22222222) << 1; // Lamp n*4+1 -> bit n*4+2 - bits ^= (lights & 0x44444444) << 1; // Lamp n*4+2 -> bit n*4+3 - bits ^= (lights & 0x88888888) >> 2; // Lamp n*4+3 -> bit n*4+1 - - K573_IO_BOARD[DIGITAL_IO_REG_LIGHTS0] = ((bits) & 0xf) << 12; - K573_IO_BOARD[DIGITAL_IO_REG_LIGHTS1] = ((bits >> 4) & 0xf) << 12; - K573_IO_BOARD[DIGITAL_IO_REG_LIGHTS2] = ((bits >> 8) & 0xf) << 12; - K573_IO_BOARD[DIGITAL_IO_REG_LIGHTS3] = ((bits >> 12) & 0xf) << 12; - K573_IO_BOARD[DIGITAL_IO_REG_LIGHTS4] = ((bits >> 16) & 0xf) << 12; - K573_IO_BOARD[DIGITAL_IO_REG_LIGHTS5] = ((bits >> 20) & 0xf) << 12; - //K573_IO_BOARD[DIGITAL_IO_REG_LIGHTS6] = ((bits >> 24) & 0xf) << 12; - K573_IO_BOARD[DIGITAL_IO_REG_LIGHTS7] = ((bits >> 28) & 0xf) << 12; -} - -static const void (*_k573_set_lights[])(uint32_t) = { - &_k573_set_lights_analog, - &_k573_set_lights_digital -}; - -/* Public API */ - uint32_t K573_GetJAMMAInputs(void) { uint32_t inputs; - inputs = K573_IO_CHIP[IO_REG_IN2]; - inputs |= ((K573_IO_CHIP[IO_REG_IN3_LOW] >> 8) & 0x0f) << 16; - inputs |= ((K573_IO_CHIP[IO_REG_IN3_HIGH] >> 8) & 0x0f) << 20; - inputs |= ((K573_IO_CHIP[IO_REG_IN1_HIGH] >> 8) & 0x1f) << 24; - inputs |= (K573_IO_CHIP[IO_REG_IN1_LOW] & 0x07) << 29; + inputs = K573_JAMMA_IN; + inputs |= ((K573_EXT_IN_P1 >> 8) & 0x0f) << 16; + inputs |= ((K573_EXT_IN_P2 >> 8) & 0x0f) << 20; + inputs |= ((K573_MISC_IN >> 8) & 0x1f) << 24; + inputs |= (K573_DIP_CART_IN & 0x07) << 29; return ~inputs; } -void K573_SetLights(uint32_t lights) { - if (_board_type > IO_TYPE_DIGITAL) - return; - - _k573_set_lights[_board_type](lights); -} +void K573_SetAnalogLights(uint32_t lights) { + uint32_t bits = 0xffffffff; -void K573_SetBoardType(K573_IOBoardType type) { - _k573_set_lights[_board_type](0); - _board_type = type; + bits ^= (lights & 0x00010101) << 7; // Bit 0 -> bit 7 + bits ^= (lights & 0x00020202) >> 1; // Bit 1 -> bit 0 + bits ^= (lights & 0x00040404) << 4; // Bit 2 -> bit 6 + bits ^= (lights & 0x00080808) >> 2; // Bit 3 -> bit 1 + bits ^= (lights & 0x00101010) << 1; // Bit 4 -> bit 5 + bits ^= (lights & 0x00202020) >> 3; // Bit 5 -> bit 2 + bits ^= (lights & 0x00404040) >> 2; // Bit 6 -> bit 4 + bits ^= (lights & 0x00808080) >> 4; // Bit 7 -> bit 3 + bits ^= (lights & 0x0f000000) >> 24; + + K573_IO_BOARD[ANALOG_IO_REG_LIGHTS_A] = (bits) & 0xff; + K573_IO_BOARD[ANALOG_IO_REG_LIGHTS_B] = (bits >> 8) & 0xff; + K573_IO_BOARD[ANALOG_IO_REG_LIGHTS_C] = (bits >> 16) & 0xff; + K573_IO_BOARD[ANALOG_IO_REG_LIGHTS_D] = (bits >> 24) & 0xff; } -/*void K573_DDRStageCommand(uint32_t value, uint32_t length) { - uint32_t last_bit = 0; - uint32_t mask = 1; - - for (uint32_t i = 0; i < length; i++) { - uint32_t bit = DDR_LIGHT_P1_MUX_DATA | DDR_LIGHT_P2_MUX_DATA; - if (value & mask) - bit = 0; - - K573_SetLights(last_bit | DDR_LIGHT_P1_MUX_CLK | DDR_LIGHT_P2_MUX_CLK); - _k573_delay_hblanks(20); - K573_SetLights(bit | DDR_LIGHT_P1_MUX_CLK | DDR_LIGHT_P2_MUX_CLK); - _k573_delay_hblanks(20); - K573_SetLights(bit); - _k573_delay_hblanks(20); - - last_bit = bit; - mask <<= 1; - } - - K573_SetLights(0); -}*/ - -//K573_DDRStageCommand(0x000c90, 13); -//K573_DDRStageCommand(0x000001, 22); - void K573_Init(void) { - EXP1_ADDR = 0x1f000000; - EXP1_DELAY_SIZE = 0x24173f47; // 573 BIOS uses this value + BUS_EXP1_ADDR = 0x1f000000; + BUS_EXP1_CFG = 0x24173f47; // 573 BIOS uses this value // Bit 6 of this register controls the audio DAC and must be set, otherwise // no sound will be output. Most of the other bits are data clocks/strobes // and should be pulled high when not in use. - K573_IO_CHIP[IO_REG_OUT0] = 0x01e7; + K573_MISC_OUT = + MISC_OUT_ADC_MOSI | MISC_OUT_ADC_CS | MISC_OUT_ADC_SCK | + MISC_OUT_CDDA_ENABLE | MISC_OUT_SPU_ENABLE | MISC_OUT_MCU_CLOCK; K573_RESET_WATCHDOG(); } diff --git a/examples/io/system573/k573io.h b/examples/io/system573/k573io.h index 8655237..552a93c 100644 --- a/examples/io/system573/k573io.h +++ b/examples/io/system573/k573io.h @@ -1,57 +1,49 @@ /* * PSn00bSDK Konami System 573 example (I/O driver) * (C) 2022 spicyjpeg - MPL licensed + * + * Reference: https://psx-spx.consoledev.net/konamisystem573/#register-map */ #ifndef __K573IO_H #define __K573IO_H #include <stdint.h> +#include <hwregs_c.h> /* Register definitions */ -#define K573_BANK_SWITCH *((volatile uint16_t *) 0xbf500000) -#define K573_IDE_RESET *((volatile uint16_t *) 0xbf560000) -#define K573_WATCHDOG *((volatile uint16_t *) 0xbf5c0000) -#define K573_EXT_OUT *((volatile uint16_t *) 0xbf600000) -#define K573_JVS_INPUT *((volatile uint16_t *) 0xbf680000) -#define K573_SECURITY_OUT *((volatile uint16_t *) 0xbf6a0000) - -#define K573_FLASH ((volatile uint16_t *) 0xbf000000) -#define K573_IO_CHIP ((volatile uint16_t *) 0xbf400000) -#define K573_IDE_CS0 ((volatile uint16_t *) 0xbf480000) -#define K573_IDE_CS1 ((volatile uint16_t *) 0xbf4c0000) -#define K573_RTC ((volatile uint16_t *) 0xbf620000) -#define K573_IO_BOARD ((volatile uint16_t *) 0xbf640000) - -typedef enum _K573_IOChipRegister { - IO_REG_OUT0 = 0x0, - IO_REG_IN0 = 0x0, - IO_REG_IN1_LOW = 0x2, - IO_REG_IN1_HIGH = 0x3, - IO_REG_IN2 = 0x4, - IO_REG_IN3_LOW = 0x6, - IO_REG_IN3_HIGH = 0x7 -} K573_IOChipRegister; +#define K573_MISC_OUT _MMIO16(EXP1BASE | 0x400000) +#define K573_DIP_CART_IN _MMIO16(EXP1BASE | 0x400004) +#define K573_MISC_IN _MMIO16(EXP1BASE | 0x400006) +#define K573_JAMMA_IN _MMIO16(EXP1BASE | 0x400008) +#define K573_JVS_RX_DATA _MMIO16(EXP1BASE | 0x40000a) +#define K573_EXT_IN_P1 _MMIO16(EXP1BASE | 0x40000c) +#define K573_EXT_IN_P2 _MMIO16(EXP1BASE | 0x40000e) + +#define K573_BANK_SEL _MMIO16(EXP1BASE | 0x500000) +#define K573_JVS_RESET _MMIO16(EXP1BASE | 0x520000) +#define K573_IDE_RESET _MMIO16(EXP1BASE | 0x560000) +#define K573_WATCHDOG _MMIO16(EXP1BASE | 0x5c0000) +#define K573_EXT_OUT _MMIO16(EXP1BASE | 0x600000) +#define K573_JVS_TX_DATA _MMIO16(EXP1BASE | 0x680000) +#define K573_CART_OUT _MMIO16(EXP1BASE | 0x6a0000) + +#define K573_FLASH _ADDR16(EXP1BASE | 0x000000) +#define K573_IDE_CS0 _ADDR16(EXP1BASE | 0x480000) +#define K573_IDE_CS1 _ADDR16(EXP1BASE | 0x4c0000) +#define K573_RTC _ADDR16(EXP1BASE | 0x620000) +#define K573_IO_BOARD _ADDR16(EXP1BASE | 0x640000) typedef enum _K573_IOBoardRegister { - ANALOG_IO_REG_LIGHTS0 = 0x40, - ANALOG_IO_REG_LIGHTS1 = 0x44, - ANALOG_IO_REG_LIGHTS2 = 0x48, - ANALOG_IO_REG_LIGHTS3 = 0x4c, + ANALOG_IO_REG_LIGHTS_A = 0x40, + ANALOG_IO_REG_LIGHTS_B = 0x44, + ANALOG_IO_REG_LIGHTS_C = 0x48, + ANALOG_IO_REG_LIGHTS_D = 0x4c, - // The digital I/O board has a lot more registers than these, but there - // seems to be no DIGITAL_IO_LIGHTS6 register. WTF - DIGITAL_IO_REG_LIGHTS1 = 0x70, - DIGITAL_IO_REG_LIGHTS0 = 0x71, - DIGITAL_IO_REG_LIGHTS3 = 0x72, - DIGITAL_IO_REG_LIGHTS7 = 0x73, - DIGITAL_IO_REG_DS2401 = 0x77, DIGITAL_IO_REG_FPGA_STATUS = 0x7b, - DIGITAL_IO_REG_FPGA_UPLOAD = 0x7c, - DIGITAL_IO_REG_LIGHTS4 = 0x7d, - DIGITAL_IO_REG_LIGHTS5 = 0x7e, - DIGITAL_IO_REG_LIGHTS2 = 0x7f, + DIGITAL_IO_REG_FPGA_PROGRAM = 0x7c, + DIGITAL_IO_REG_FPGA_DATA = 0x7d, FISHBAIT_IO_REG_UNKNOWN = 0x08, FISHBAIT_IO_REG_MOTOR = 0x40, @@ -74,10 +66,22 @@ typedef enum _K573_RTCRegister { RTC_REG_YEAR = 0x1fff } K573_RTCRegister; -/* Inputs and lights bitfields */ - -typedef enum _K573_JAMMAInputs { - // IO_REG_IN2 bits 0-15 +/* Bitfields */ + +typedef enum _K573_MiscOutFlag { + MISC_OUT_ADC_MOSI = 1 << 0, + MISC_OUT_ADC_CS = 1 << 1, + MISC_OUT_ADC_SCK = 1 << 2, + MISC_OUT_COIN_COUNTER1 = 1 << 3, + MISC_OUT_COIN_COUNTER2 = 1 << 4, + MISC_OUT_AMP_ENABLE = 1 << 5, + MISC_OUT_CDDA_ENABLE = 1 << 6, + MISC_OUT_SPU_ENABLE = 1 << 7, + MISC_OUT_MCU_CLOCK = 1 << 8 +} K573_MiscOutFlag; + +typedef enum _K573_JAMMAInput { + // K573_JAMMA_IN bits 0-15 JAMMA_P2_LEFT = 1 << 0, JAMMA_P2_RIGHT = 1 << 1, JAMMA_P2_UP = 1 << 2, @@ -95,108 +99,114 @@ typedef enum _K573_JAMMAInputs { JAMMA_P1_BUTTON3 = 1 << 14, JAMMA_P1_START = 1 << 15, - // IO_REG_IN3_LOW bits 8-11 + // K573_EXT_IN_P1 bits 8-11 JAMMA_P1_BUTTON4 = 1 << 16, JAMMA_P1_BUTTON5 = 1 << 17, JAMMA_TEST = 1 << 18, JAMMA_P1_BUTTON6 = 1 << 19, - // IO_REG_IN3_HIGH bits 8-11 + // K573_EXT_IN_P2 bits 8-11 JAMMA_P2_BUTTON4 = 1 << 20, JAMMA_P2_BUTTON5 = 1 << 21, JAMMA_UNKNOWN = 1 << 22, JAMMA_P2_BUTTON6 = 1 << 23, - // IO_REG_IN1_HIGH bits 8-12 + // K573_MISC_IN bits 8-12 JAMMA_COIN1 = 1 << 24, JAMMA_COIN2 = 1 << 25, JAMMA_PCMCIA1 = 1 << 26, JAMMA_PCMCIA2 = 1 << 27, JAMMA_SERVICE = 1 << 28, - // IO_REG_IN1_LOW bits 0-2 + // K573_DIP_CART_IN bits 0-2 JAMMA_DIP1 = 1 << 29, JAMMA_DIP2 = 1 << 30, JAMMA_DIP3 = 1 << 31 -} K573_JAMMAInputs; +} K573_JAMMAInput; typedef enum _K573_Light { + LIGHT_A0 = 1 << 0, + LIGHT_A1 = 1 << 1, + LIGHT_A2 = 1 << 2, + LIGHT_A3 = 1 << 3, + LIGHT_A4 = 1 << 4, + LIGHT_A5 = 1 << 5, + LIGHT_A6 = 1 << 6, + LIGHT_A7 = 1 << 7, + LIGHT_B0 = 1 << 8, + LIGHT_B1 = 1 << 9, + LIGHT_B2 = 1 << 10, + LIGHT_B3 = 1 << 11, + LIGHT_B4 = 1 << 12, + LIGHT_B5 = 1 << 13, + LIGHT_B6 = 1 << 14, + LIGHT_B7 = 1 << 15, + LIGHT_C0 = 1 << 16, + LIGHT_C1 = 1 << 17, + LIGHT_C2 = 1 << 18, + LIGHT_C3 = 1 << 19, + LIGHT_C4 = 1 << 20, + LIGHT_C5 = 1 << 21, + LIGHT_C6 = 1 << 22, + LIGHT_C7 = 1 << 23, + LIGHT_D0 = 1 << 24, + LIGHT_D1 = 1 << 25, + LIGHT_D2 = 1 << 26, + LIGHT_D3 = 1 << 27, + // Dance Dance Revolution (2-player) - DDR_LIGHT_P1_UP = 1 << 0, - DDR_LIGHT_P1_LEFT = 1 << 1, - DDR_LIGHT_P1_RIGHT = 1 << 2, - DDR_LIGHT_P1_DOWN = 1 << 3, - DDR_LIGHT_P1_MUX_DATA = 1 << 4, // Used for stage commands - DDR_LIGHT_P1_MUX_CLK = 1 << 7, // Used for stage commands - DDR_LIGHT_P2_UP = 1 << 8, - DDR_LIGHT_P2_LEFT = 1 << 9, - DDR_LIGHT_P2_RIGHT = 1 << 10, - DDR_LIGHT_P2_DOWN = 1 << 11, - DDR_LIGHT_P2_MUX_DATA = 1 << 12, // Used for stage commands - DDR_LIGHT_P2_MUX_CLK = 1 << 15, // Used for stage commands - DDR_LIGHT_P1_BUTTONS = 1 << 17, - DDR_LIGHT_P2_BUTTONS = 1 << 18, - DDR_LIGHT_MARQUEE_BR = 1 << 20, - DDR_LIGHT_MARQUEE_BL = 1 << 21, - DDR_LIGHT_MARQUEE_TL = 1 << 22, - DDR_LIGHT_MARQUEE_TR = 1 << 23, - DDR_LIGHT_SPEAKER_DIGITAL = 1 << 28, // Speaker neon on digital I/O boards - DDR_LIGHT_SPEARKER_ANALOG = 1 << 30, // Speaker neon on analog I/O boards + LIGHT_DDR_P1_UP = 1 << 0, + LIGHT_DDR_P1_DOWN = 1 << 1, + LIGHT_DDR_P1_LEFT = 1 << 2, + LIGHT_DDR_P1_RIGHT = 1 << 3, + LIGHT_DDR_P1_IO_DATA = 1 << 4, + LIGHT_DDR_P1_IO_CLK = 1 << 5, + LIGHT_DDR_P2_UP = 1 << 8, + LIGHT_DDR_P2_DOWN = 1 << 9, + LIGHT_DDR_P2_LEFT = 1 << 10, + LIGHT_DDR_P2_RIGHT = 1 << 11, + LIGHT_DDR_P2_IO_DATA = 1 << 12, + LIGHT_DDR_P2_IO_CLK = 1 << 13, + LIGHT_DDR_P1_BUTTONS = 1 << 18, + LIGHT_DDR_P2_BUTTONS = 1 << 19, + LIGHT_DDR_MARQUEE_BR = 1 << 20, + LIGHT_DDR_MARQUEE_TR = 1 << 21, + LIGHT_DDR_MARQUEE_BL = 1 << 22, + LIGHT_DDR_MARQUEE_TL = 1 << 23, + LIGHT_DDR_SPEAKER = 1 << 24, // Dance Dance Revolution Solo - DDRSOLO_LIGHT_EXTRA4 = 1 << 8, - DDRSOLO_LIGHT_EXTRA2 = 1 << 9, - DDRSOLO_LIGHT_EXTRA1 = 1 << 10, - DDRSOLO_LIGHT_EXTRA3 = 1 << 11, - DDRSOLO_LIGHT_SPEAKER = 1 << 16, - DDRSOLO_LIGHT_BUTTONS = 1 << 20, - DDRSOLO_LIGHT_BODY_CENTER = 1 << 21, - DDRSOLO_LIGHT_BODY_RIGHT = 1 << 22, - DDRSOLO_LIGHT_BODY_LEFT = 1 << 23, + LIGHT_SOLO_SPEAKER = 1 << 16, + LIGHT_SOLO_BUTTONS = 1 << 20, + LIGHT_SOLO_BODY_LEFT = 1 << 21, + LIGHT_SOLO_BODY_CENTER = 1 << 22, + LIGHT_SOLO_BODY_RIGHT = 1 << 23, // DrumMania 1st Mix - DM_LIGHT_HIHAT = 1 << 16, - DM_LIGHT_HIGH_TOM = 1 << 17, - DM_LIGHT_LOW_TOM = 1 << 18, - DM_LIGHT_SNARE = 1 << 19, - DM_LIGHT_CYMBAL = 1 << 20, - DM_LIGHT_START_BUTTON = 1 << 21, - DM_LIGHT_SELECT_BUTTON = 1 << 22, - DM_LIGHT_NEON_BOTTOM = 1 << 27, - DM_LIGHT_SPOT = 1 << 30, - DM_LIGHT_NEON_TOP = 1 << 31, + LIGHT_DM_HIHAT = 1 << 16, + LIGHT_DM_SNARE = 1 << 17, + LIGHT_DM_HIGH_TOM = 1 << 18, + LIGHT_DM_LOW_TOM = 1 << 19, + LIGHT_DM_CYMBAL = 1 << 20, + LIGHT_DM_START_BUTTON = 1 << 22, + LIGHT_DM_SELECT_BUTTON = 1 << 23, + LIGHT_DM_SPOT = 1 << 24, + LIGHT_DM_NEON_TOP = 1 << 25, + LIGHT_DM_NEON_BOTTOM = 1 << 27, // DrumMania 2nd Mix and later - DM2_LIGHT_HIHAT = 1 << 0, - DM2_LIGHT_HIGH_TOM = 1 << 1, - DM2_LIGHT_LOW_TOM = 1 << 2, - DM2_LIGHT_SNARE = 1 << 3, - DM2_LIGHT_SPOT = 1 << 8, - DM2_LIGHT_NEON_TOP = 1 << 9, - DM2_LIGHT_NEON_BOTTOM = 1 << 11, - DM2_LIGHT_CYMBAL = 1 << 12, - DM2_LIGHT_START_BUTTON = 1 << 13, - DM2_LIGHT_SELECT_BUTTON = 1 << 14 + LIGHT_DM2_HIHAT = 1 << 0, + LIGHT_DM2_SNARE = 1 << 1, + LIGHT_DM2_HIGH_TOM = 1 << 2, + LIGHT_DM2_LOW_TOM = 1 << 3, + LIGHT_DM2_SPOT = 1 << 8, + LIGHT_DM2_NEON_BOTTOM = 1 << 9, + LIGHT_DM2_NEON_TOP = 1 << 10, + LIGHT_DM2_CYMBAL = 1 << 12, + LIGHT_DM2_START_BUTTON = 1 << 14, + LIGHT_DM2_SELECT_BUTTON = 1 << 15 } K573_Light; -/* System information structures */ - -typedef enum _K573_IOBoardType { - IO_TYPE_ANALOG = 0, // Light control board (early Bemani) - IO_TYPE_DIGITAL = 1, // Light control + MP3 playback board (late Bemani) - IO_TYPE_FISHBAIT = 2, // Fishing reel controls interface (Fisherman's Bait) - IO_TYPE_GUNMANIA = 3, // Gun control board (Gun Mania) - IO_TYPE_KARAOKE = 4, // Karaoke I/O + video mux board (DDR Karaoke Mix) - IO_TYPE_SERIAL = 5 // Serial port (debug?) board (Great Bishi Bashi Champ) - // TODO: does PunchMania have its own board? -} K573_IOBoardType; - -typedef enum _K573_DDRStageType { - DDR_TYPE_NONE = 0, - DDR_TYPE_2PLAYER = 1, - DDR_TYPE_SOLO = 2 -} K573_DDRStageType; - /* Public API */ #define K573_RESET_WATCHDOG() { \ @@ -208,43 +218,27 @@ extern "C" { #endif /** - * @brief Returns a bitfield containing the state of all JAMMA inputs and DIP + * @brief Returns the state of JAMMA inputs and DIP switches. + * + * @details Returns a bitfield containing the state of all JAMMA inputs and DIP * switches. All bits are inverted as they represent the actual signal levels * on the JAMMA pins (i.e. normally pulled up by resistors, shorted to ground * when a button is pressed). * - * @return Inverted logical OR of K573_JAMMAInputs flags + * @return Binary OR of K573_JAMMAInputs flags */ uint32_t K573_GetJAMMAInputs(void); /** - * @brief Sets the 32 light outputs provided by the the analog and digital I/O - * boards to match the provided bitfield. K573_SetBoardType(IO_TYPE_ANALOG) or - * K573_SetBoardType(IO_TYPE_DIGITAL) must be called beforehand to set the I/O - * board type. - * - * @param lights Non-inverted logical OR of K573_Light flags - */ -void K573_SetLights(uint32_t lights); - -/** - * @brief Sets the installed I/O board type. Currently only IO_TYPE_ANALOG and - * IO_TYPE_DIGITAL are supported. + * @brief Sets light outputs on the analog I/O board. * - * @param type - */ -void K573_SetBoardType(K573_IOBoardType type); - -/** - * @brief Sends a command to the multiplexer PCB embedded into DDR stage units - * (if the system is a DDR cabinet) by bitbanging it through the light outputs. - * K573_SetBoardType(IO_TYPE_ANALOG) or K573_SetBoardType(IO_TYPE_DIGITAL) must - * be called beforehand to set the I/O board type. + * @details Sets the 32 light outputs provided by the analog I/O board (if + * installed) to match the provided bitfield. No other I/O boards are currently + * supported. * - * @param value - * @param length Number of bits to send (1-32) + * @param lights Binary OR of K573_Light flags */ -//void K573_DDRStageCommand(uint32_t value, uint32_t length); +void K573_SetAnalogLights(uint32_t lights); /** * @brief Initializes the expansion port registers to enable System 573 I/O diff --git a/examples/io/system573/main.c b/examples/io/system573/main.c index 39ddb64..2878508 100644 --- a/examples/io/system573/main.c +++ b/examples/io/system573/main.c @@ -66,12 +66,7 @@ #include "k573io.h" -const char *const IO_BOARD_TYPES[] = { - "ANALOG", - "DIGITAL" -}; - -#define btoi(x) ((((x) >> 4) & 0xf) * 10 + ((x) & 0xf)) +#define _btoi(x) ((((x) >> 4) & 0xf) * 10 + ((x) & 0xf)) /* Display/GPU context utilities */ @@ -137,30 +132,18 @@ void display(RenderContext *ctx) { static RenderContext ctx; -#define SHOW_STATUS(...) { FntPrint(-1, __VA_ARGS__); FntFlush(-1); display(&ctx); } -#define SHOW_ERROR(...) { SHOW_STATUS(__VA_ARGS__); while (1) __asm__("nop"); } - int main(int argc, const char* argv[]) { init_context(&ctx); K573_Init(); const char *const version = (const char *const) GetSystemInfo(0x02); - //if (strncmp(version, "Konami OS", 9)) - //SHOW_ERROR("ERROR: NOT RUNNING ON A SYSTEM 573!\n\n[%s]\n", version); uint32_t counter = 0; uint32_t inputs = K573_GetJAMMAInputs(); uint32_t last_inputs = 0xff; uint32_t current_light = 0; - // DIP switch 1 is used to determine if an analog or digital I/O board is - // installed. - K573_IOBoardType io_type = (inputs & JAMMA_DIP1) - ? IO_TYPE_ANALOG - : IO_TYPE_DIGITAL; - - K573_SetBoardType(io_type); - K573_SetLights(1); + K573_SetAnalogLights(1); while (1) { inputs = K573_GetJAMMAInputs(); @@ -168,16 +151,14 @@ int main(int argc, const char* argv[]) { FntPrint(-1, "COUNTER=%d\n", counter++); FntPrint(-1, "\nJAMMA INPUTS:\n"); - FntPrint(-1, " IN2 =%016@\n", inputs & 0xffff); - FntPrint(-1, " IN3_L=%04@\n", (inputs >> 16) & 0x0f); - FntPrint(-1, " IN3_H=%04@\n", (inputs >> 20) & 0x0f); - FntPrint(-1, " IN1_H=%05@\n", (inputs >> 24) & 0x1f); + FntPrint(-1, " MAIN=%016@\n", inputs & 0xffff); + FntPrint(-1, " EXT1=%04@\n", (inputs >> 16) & 0x0f); + FntPrint(-1, " EXT2=%04@\n", (inputs >> 20) & 0x0f); + FntPrint(-1, " COIN=%05@\n", (inputs >> 24) & 0x1f); - FntPrint(-1, "\nCABINET LIGHTS:\n"); - FntPrint(-1, " BOARD=%s\n", IO_BOARD_TYPES[io_type]); - FntPrint(-1, " LIGHT=%d\n", current_light); - FntPrint(-1, "\n [DIP SW1] CHANGE BOARD TYPE\n"); - FntPrint(-1, "\n [TEST SW] CHANGE ACTIVE LIGHT\n"); + FntPrint(-1, "\nANALOG IO LIGHT TEST:\n"); + FntPrint(-1, " LAMP=%d\n", current_light); + FntPrint(-1, " PRESS [TEST] TO CHANGE LAMP\n"); // Request the current date/time from the RTC and display it. K573_RTC[RTC_REG_CTRL] |= 0x40; @@ -185,18 +166,17 @@ int main(int argc, const char* argv[]) { FntPrint( -1, " %02d-%02d-%02d %02d:%02d:%02d\n", - btoi(K573_RTC[RTC_REG_YEAR]), - btoi(K573_RTC[RTC_REG_MONTH]), - btoi(K573_RTC[RTC_REG_DAY_OF_MONTH] & 0x3f), - btoi(K573_RTC[RTC_REG_HOURS]), - btoi(K573_RTC[RTC_REG_MINUTES]), - btoi(K573_RTC[RTC_REG_SECONDS] & 0x7f) + _btoi(K573_RTC[RTC_REG_YEAR]), + _btoi(K573_RTC[RTC_REG_MONTH]), + _btoi(K573_RTC[RTC_REG_DAY_OF_MONTH] & 0x3f), + _btoi(K573_RTC[RTC_REG_HOURS]), + _btoi(K573_RTC[RTC_REG_MINUTES]), + _btoi(K573_RTC[RTC_REG_SECONDS] & 0x7f) ); FntPrint(-1, "\nSYSTEM:\n"); FntPrint(-1, " KERNEL=%s\n", version); FntPrint(-1, " DIP SW=%03@\n", inputs >> 29); - FntPrint(-1, " PCMCIA=%02@\n", (inputs >> 26) & 0x3); FntFlush(-1); display(&ctx); @@ -208,26 +188,16 @@ int main(int argc, const char* argv[]) { // Change the currently active light if the test button on the 573's // front panel is pressed. DDR non-light outputs are skipped. if (!(last_inputs & JAMMA_TEST) && (inputs & JAMMA_TEST)) { - current_light++; - if ( - (current_light == 4) || // DDR_LIGHT_P1_MUX_DATA - (current_light == 7) || // DDR_LIGHT_P1_MUX_CLK - (current_light == 12) || // DDR_LIGHT_P2_MUX_DATA - (current_light == 15) // DDR_LIGHT_P2_MUX_CLK - ) current_light++; - - current_light %= 32; - K573_SetLights(1 << current_light); - } - - // if DIP switch 1 is toggled, change the I/O board type. - if ((last_inputs & JAMMA_DIP1) != (inputs & JAMMA_DIP1)) { - io_type = (inputs & JAMMA_DIP1) - ? IO_TYPE_ANALOG - : IO_TYPE_DIGITAL; - - K573_SetBoardType(io_type); - K573_SetLights(1 << current_light); + do { + current_light = (current_light + 1) % 28; + } while ( + (current_light == 4) || // LIGHT_DDR_P1_IO_DATA + (current_light == 5) || // LIGHT_DDR_P1_IO_CLK + (current_light == 12) || // LIGHT_DDR_P2_IO_DATA + (current_light == 13) // LIGHT_DDR_P2_IO_CLK + ); + + K573_SetAnalogLights(1 << current_light); } last_inputs = inputs; diff --git a/examples/mdec/strvideo/main.c b/examples/mdec/strvideo/main.c index 842bbb8..28d39b2 100644 --- a/examples/mdec/strvideo/main.c +++ b/examples/mdec/strvideo/main.c @@ -31,10 +31,8 @@ * pipeline must also run in lockstep with each other to prevent frame * corruption, hence several functions and flag variables are used to stall the * main loop until a frame is available for decoding and the MDEC is ready. - * Playback is stopped when a sector with the end-of-file flag set in the XA - * subheader (added at the end of the file by most .STR encoders) is - * encountered; in order to access the subheader, this example requests 2340 - * bytes of data for each sector (rather than the usual 2048) from the drive. + * Playback is stopped once the .STR header is no longer present in sectors + * read. * * Note that PSn00bSDK's bitstream decoding API only supports version 1 and 2 * bitstreams currently, so make sure your .STR files are encoded as v2 and not @@ -42,6 +40,7 @@ */ #include <stdint.h> +#include <stdio.h> #include <string.h> #include <psxetc.h> #include <psxapi.h> @@ -85,15 +84,15 @@ void init_context(RenderContext *ctx) { SetDefDispEnv(&(db->disp), 0, 0, SCREEN_XRES, SCREEN_YRES); SetDefDrawEnv(&(db->draw), 0, SCREEN_YRES, SCREEN_XRES, SCREEN_YRES); setRGB0(&(db->draw), BGCOLOR_R, BGCOLOR_G, BGCOLOR_B); - db->draw.isbg = 1; - db->draw.dtd = 1; + db->draw.isbg = 1; + db->draw.dtd = 1; db = &(ctx->db[1]); SetDefDispEnv(&(db->disp), 0, SCREEN_YRES, SCREEN_XRES, SCREEN_YRES); SetDefDrawEnv(&(db->draw), 0, 0, SCREEN_XRES, SCREEN_YRES); setRGB0(&(db->draw), BGCOLOR_R, BGCOLOR_G, BGCOLOR_B); - db->draw.isbg = 1; - db->draw.dtd = 1; + db->draw.isbg = 1; + db->draw.dtd = 1; PutDrawEnv(&(db->draw)); //PutDispEnv(&(db->disp)); @@ -145,22 +144,6 @@ typedef struct { uint32_t _reserved; } STR_Header; -// https://problemkaputt.de/psx-spx.htm#cdromxasubheaderfilechannelinterleave -typedef struct { - uint8_t file, channel; - uint8_t submode, coding_info; -} XA_Header; - -// https://problemkaputt.de/psx-spx.htm#cdromsectorencoding -typedef struct { - CdlLOC pos; - XA_Header xa_header[2]; - STR_Header str_header; - uint8_t data[2016]; - uint32_t edc; - uint8_t ecc[276]; -} STR_Sector; - typedef struct { uint16_t width, height; uint32_t bs_data[0x2000]; // Bitstream data read from the disc @@ -186,57 +169,52 @@ StreamContext str_ctx; // read from the CD. Due to DMA limitations it can't be allocated on the stack // (especially not in the interrupt callbacks' stack, whose size is very // limited). -STR_Sector sector_buffer; +STR_Header sector_header; void cd_sector_handler(void) { - // Fetch the .STR header of the sector that has been read and check if the - // end-of-file bit is set in the XA header. - CdGetSector(§or_buffer, sizeof(STR_Sector) / 4); - - if ( - (sector_buffer.xa_header[0].submode & (1 << 7)) || - (sector_buffer.xa_header[1].submode & (1 << 7)) - ) { - CdControlF(CdlPause, 0); + StreamBuffer *frame = &str_ctx.frames[str_ctx.cur_frame]; + + // Fetch the .STR header of the sector that has been read and make sure it + // is valid. If not, assume the file has ended and set frame_ready as a + // signal for the main loop to stop playback. + CdGetSector(§or_header, sizeof(STR_Header) / 4); + + if (sector_header.magic != 0x0160) { str_ctx.frame_ready = -1; return; } - STR_Header *header = §or_buffer.str_header; - StreamBuffer *frame = &str_ctx.frames[str_ctx.cur_frame]; - // Ignore any non-MDEC sectors that might be present in the stream. - if (header->type != 0x8001) + if (sector_header.type != 0x8001) return; // If this sector is actually part of a new frame, validate the sectors // that have been read so far and flip the bitstream data buffers. - if (header->frame_id != str_ctx.frame_id) { + if (sector_header.frame_id != str_ctx.frame_id) { // Do not set the ready flag if any sector has been missed. if (str_ctx.sector_count) str_ctx.dropped_frames++; else str_ctx.frame_ready = 1; - str_ctx.frame_id = header->frame_id; - str_ctx.sector_count = header->sector_count; + str_ctx.frame_id = sector_header.frame_id; + str_ctx.sector_count = sector_header.sector_count; str_ctx.cur_frame ^= 1; frame = &str_ctx.frames[str_ctx.cur_frame]; // Initialize the next frame. Dimensions must be rounded up to the // nearest multiple of 16 as the MDEC operates on 16x16 pixel blocks. - frame->width = (header->width + 15) & 0xfff0; - frame->height = (header->height + 15) & 0xfff0; + frame->width = (sector_header.width + 15) & 0xfff0; + frame->height = (sector_header.height + 15) & 0xfff0; } // Append the payload contained in this sector to the current buffer. - memcpy( - &(frame->bs_data[2016 / 4 * header->sector_id]), - sector_buffer.data, - 2016 - ); str_ctx.sector_count--; + CdGetSector( + &(frame->bs_data[2016 / 4 * sector_header.sector_id]), + 2016 / 4 + ); } void mdec_dma_handler(void) { @@ -262,7 +240,7 @@ void mdec_dma_handler(void) { ); } -void cd_event_handler(int event, uint8_t *payload) { +void cd_event_handler(CdlIntrResult event, uint8_t *payload) { // Ignore all events other than a sector being ready. if (event != CdlDataReady) return; @@ -292,9 +270,8 @@ void init_stream(void) { DecDCTvlcSize(0x8000); DecDCTvlcCopyTable((DECDCTTAB *) 0x1f800000); - str_ctx.dropped_frames = 0; - str_ctx.cur_frame = 0; - str_ctx.cur_slice = 0; + str_ctx.cur_frame = 0; + str_ctx.cur_slice = 0; } StreamBuffer *get_next_frame(void) { @@ -310,14 +287,15 @@ StreamBuffer *get_next_frame(void) { void start_stream(CdlFILE *file) { str_ctx.frame_id = -1; + str_ctx.dropped_frames = 0; str_ctx.sector_pending = 0; str_ctx.frame_ready = 0; CdSync(0, 0); - // Configure the CD drive to read 2340-byte sectors at 2x speed and to - // play any XA-ADPCM sectors that might be interleaved with the video data. - uint8_t mode = CdlModeSize | CdlModeRT | CdlModeSpeed; + // Configure the CD drive to read at 2x speed and to play any XA-ADPCM + // sectors that might be interleaved with the video data. + uint8_t mode = CdlModeRT | CdlModeSpeed; CdControl(CdlSetmode, (const uint8_t *) &mode, 0); // Start reading in real-time mode (i.e. without retrying in case of read @@ -369,6 +347,9 @@ int main(int argc, const char* argv[]) { // ended, restart playback from the beginning. StreamBuffer *frame = get_next_frame(); if (!frame) { + printf("End of file, looping...\n"); + CdControlB(CdlPause, 0, 0); + start_stream(&file); continue; } @@ -423,7 +404,7 @@ int main(int argc, const char* argv[]) { int x_offset = (fb_clip->w - frame->width) / 2; int y_offset = (fb_clip->h - frame->height) / 2; - str_ctx.slice_pos.x = VRAM_X_COORD(fb_clip->x + x_offset); + str_ctx.slice_pos.x = fb_clip->x + VRAM_X_COORD(x_offset); str_ctx.slice_pos.y = fb_clip->y + y_offset; str_ctx.slice_pos.w = BLOCK_SIZE; str_ctx.slice_pos.h = frame->height; diff --git a/examples/sound/cdstream/main.c b/examples/sound/cdstream/main.c index 636ef10..324abb2 100644 --- a/examples/sound/cdstream/main.c +++ b/examples/sound/cdstream/main.c @@ -308,8 +308,8 @@ void start_stream(void) { for (int i = 0; i < NUM_CHANNELS; i++) { SPU_CH_ADDR(i) = getSPUAddr(str_ctx.spu_addr + str_ctx.buffer_size * i); SPU_CH_FREQ(i) = getSPUSampleRate(str_ctx.sample_rate); - SPU_CH_ADSR1(i) = 0x80ff; - SPU_CH_ADSR2(i) = 0x1fee; + SPU_CH_ADSR1(i) = 0x00ff; + SPU_CH_ADSR2(i) = 0x0000; } // Unmute the channels and route them for stereo output. You'll want to diff --git a/examples/sound/cdstream/stream.vag b/examples/sound/cdstream/stream.vag Binary files differindex a6faf74..aab82ba 100644 --- a/examples/sound/cdstream/stream.vag +++ b/examples/sound/cdstream/stream.vag diff --git a/examples/sound/spustream/interleave.py b/examples/sound/spustream/interleave.py index 4e68974..4f4f20f 100644 --- a/examples/sound/spustream/interleave.py +++ b/examples/sound/spustream/interleave.py @@ -8,7 +8,7 @@ from struct import Struct from itertools import zip_longest from argparse import ArgumentParser, FileType -VAG_HEADER = Struct("> 4s I 4s 2I 12x 16s") +VAG_HEADER = Struct("> 4s 4I 10x H 16s") VAG_MAGIC = b"VAGp" VAGI_MAGIC = b"VAGi" VAG_VERSION = 0x20 @@ -17,6 +17,9 @@ CHUNK_ALIGN = 0x800 ## Helpers +def swap_endian(value, size): + return int.from_bytes(value.to_bytes(size, "big"), "little") + def align(data, size): chunks = (len(data) + size - 1) // size @@ -40,7 +43,7 @@ class VAGReader: magic, _, _, self.size, self.sample_rate, - self.name + _, _ ) = VAG_HEADER.unpack(header) if magic == VAGI_MAGIC: @@ -116,9 +119,6 @@ def main(): size = input_files[0].size sample_rate = input_files[0].sample_rate - if (not args.raw) and (len(input_files) != 2): - warn(RuntimeWarning("interleaved .VAG only supports stereo (2 input files)")) - for vag in input_files[1:]: if vag.size != size: warn(RuntimeWarning(f"{vag.file.name} has a different file size")) @@ -135,9 +135,10 @@ def main(): header = VAG_HEADER.pack( VAGI_MAGIC, VAG_VERSION, - args.buffer_size.to_bytes(4, "little"), + swap_endian(args.buffer_size, 4), size, sample_rate, + swap_endian(len(input_files), 2), os.path.basename(_file.name).encode()[0:16] ) diff --git a/examples/sound/spustream/main.c b/examples/sound/spustream/main.c index d240433..6bf2c71 100644 --- a/examples/sound/spustream/main.c +++ b/examples/sound/spustream/main.c @@ -54,8 +54,6 @@ extern const uint8_t stream_data[]; -#define NUM_CHANNELS 2 - /* Display/GPU context utilities */ #define SCREEN_XRES 320 @@ -124,7 +122,8 @@ typedef struct { uint32_t interleave; // Little-endian, size of each channel buffer uint32_t size; // Big-endian, in bytes uint32_t sample_rate; // Big-endian, in Hertz - uint32_t _reserved[3]; + uint16_t _reserved[5]; + uint16_t channels; // Little-endian, if 0 the file is mono char name[16]; } VAG_Header; @@ -147,17 +146,12 @@ typedef struct { #define DUMMY_BLOCK_ADDR 0x1000 #define BUFFER_START_ADDR 0x1010 -typedef enum { - STATE_IDLE, - STATE_BUFFERING -} StreamState; - typedef struct { const uint8_t *data; - int buffer_size, num_chunks, sample_rate; + int buffer_size, num_chunks, sample_rate, channels; volatile int next_chunk, spu_addr; - volatile int8_t db_active, state; + volatile int8_t db_active, buffering; } StreamContext; static StreamContext str_ctx; @@ -166,13 +160,13 @@ void spu_irq_handler(void) { // Acknowledge the interrupt to ensure it can be triggered again. The only // way to do this is actually to disable the interrupt entirely; we'll // enable it again once the chunk is ready. - SPU_CTRL &= 0xffbf; + SPU_CTRL &= ~(1 << 6); - int chunk_size = str_ctx.buffer_size * NUM_CHANNELS; + int chunk_size = str_ctx.buffer_size * str_ctx.channels; int chunk = (str_ctx.next_chunk + 1) % (uint32_t) str_ctx.num_chunks; str_ctx.db_active ^= 1; - str_ctx.state = STATE_BUFFERING; + str_ctx.buffering = 1; str_ctx.next_chunk = chunk; // Configure to SPU to trigger an IRQ once the chunk that is going to be @@ -184,7 +178,7 @@ void spu_irq_handler(void) { str_ctx.spu_addr = addr; SPU_IRQ_ADDR = getSPUAddr(addr); - for (int i = 0; i < NUM_CHANNELS; i++) + for (int i = 0; i < str_ctx.channels; i++) SPU_CH_LOOP_ADDR(i) = getSPUAddr(addr + str_ctx.buffer_size * i); // Start uploading the next chunk to the SPU. @@ -194,9 +188,9 @@ void spu_irq_handler(void) { void spu_dma_handler(void) { // Re-enable the SPU IRQ once the new chunk has been fully uploaded. - SPU_CTRL |= 0x0040; + SPU_CTRL |= 1 << 6; - str_ctx.state = STATE_IDLE; + str_ctx.buffering = 0; } /* Helper functions */ @@ -228,6 +222,7 @@ void init_stream(const VAG_Header *vag) { str_ctx.buffer_size = buf_size; str_ctx.num_chunks = (SWAP_ENDIAN(vag->size) + buf_size - 1) / buf_size; str_ctx.sample_rate = SWAP_ENDIAN(vag->sample_rate); + str_ctx.channels = vag->channels ? vag->channels : 1; str_ctx.db_active = 1; str_ctx.next_chunk = -1; @@ -235,18 +230,22 @@ void init_stream(const VAG_Header *vag) { // Ensure at least one chunk is in SPU RAM by invoking the IRQ handler // manually and blocking until the chunk has loaded. spu_irq_handler(); - while (str_ctx.state != STATE_IDLE) + while (str_ctx.buffering) __asm__ volatile(""); } void start_stream(void) { - int bits = 0x00ffffff >> (24 - NUM_CHANNELS); + int bits = 0x00ffffff >> (24 - str_ctx.channels); + + // Disable the IRQ as we're going to call spu_irq_handler() manually (due + // to finicky SPU timings). + SPU_CTRL &= ~(1 << 6); - for (int i = 0; i < NUM_CHANNELS; i++) { + for (int i = 0; i < str_ctx.channels; i++) { SPU_CH_ADDR(i) = getSPUAddr(str_ctx.spu_addr + str_ctx.buffer_size * i); SPU_CH_FREQ(i) = getSPUSampleRate(str_ctx.sample_rate); - SPU_CH_ADSR1(i) = 0x80ff; - SPU_CH_ADSR2(i) = 0x1fee; + SPU_CH_ADSR1(i) = 0x00ff; + SPU_CH_ADSR2(i) = 0x0000; } // Unmute the channels and route them for stereo output. You'll want to @@ -257,19 +256,19 @@ void start_stream(void) { SPU_CH_VOL_L(1) = 0x0000; SPU_CH_VOL_R(1) = 0x3fff; - spu_irq_handler(); SpuSetKey(1, bits); + spu_irq_handler(); } // This is basically a variant of reset_spu_channels() that only resets the // channels used to play the stream, to (again) prevent them from triggering // the SPU IRQ while the stream is paused. void stop_stream(void) { - int bits = 0x00ffffff >> (24 - NUM_CHANNELS); + int bits = 0x00ffffff >> (24 - str_ctx.channels); SpuSetKey(0, bits); - for (int i = 0; i < NUM_CHANNELS; i++) + for (int i = 0; i < str_ctx.channels; i++) SPU_CH_ADDR(i) = getSPUAddr(DUMMY_BLOCK_ADDR); SpuSetKey(1, bits); @@ -300,7 +299,7 @@ int main(int argc, const char* argv[]) { while (1) { FntPrint(-1, "PLAYING SPU STREAM\n\n"); FntPrint(-1, "BUFFER: %d\n", str_ctx.db_active); - FntPrint(-1, "STATUS: %s\n\n", str_ctx.state ? "BUFFERING" : "IDLE"); + FntPrint(-1, "STATUS: %s\n\n", str_ctx.buffering ? "BUFFERING" : "IDLE"); FntPrint(-1, "POSITION: %d/%d\n", str_ctx.next_chunk, str_ctx.num_chunks); FntPrint(-1, "SMP RATE: %5d HZ\n\n", (sample_rate * 44100) >> 12); @@ -350,7 +349,7 @@ int main(int argc, const char* argv[]) { // Only set the sample rate registers if necessary. if (pad->btn != 0xffff) { - for (int i = 0; i < NUM_CHANNELS; i++) + for (int i = 0; i < str_ctx.channels; i++) SPU_CH_FREQ(i) = sample_rate; } diff --git a/examples/sound/spustream/stream.vag b/examples/sound/spustream/stream.vag Binary files differindex e1cb4f4..d75408b 100644 --- a/examples/sound/spustream/stream.vag +++ b/examples/sound/spustream/stream.vag diff --git a/examples/sound/vagsample/main.c b/examples/sound/vagsample/main.c index 6a60c19..dffa4cc 100644 --- a/examples/sound/vagsample/main.c +++ b/examples/sound/vagsample/main.c @@ -149,8 +149,8 @@ void play_sample(int addr, int sample_rate) { // dummy values that disable the ADSR envelope entirely). SPU_CH_VOL_L(ch) = 0x3fff; SPU_CH_VOL_R(ch) = 0x3fff; - SPU_CH_ADSR1(ch) = 0x80ff; - SPU_CH_ADSR2(ch) = 0x1fee; + SPU_CH_ADSR1(ch) = 0x00ff; + SPU_CH_ADSR2(ch) = 0x0000; // Start the channel. SpuSetKey(1, 1 << ch); |
