aboutsummaryrefslogtreecommitdiff
path: root/examples
diff options
context:
space:
mode:
authorspicyjpeg <thatspicyjpeg@gmail.com>2022-12-18 16:30:02 +0100
committerGitHub <noreply@github.com>2022-12-18 16:30:02 +0100
commitb12b716a9e54c4f1892795a136d6ffeb088ff001 (patch)
tree5888e4a77b207f24ccd9ad148d1c74e9c33400e9 /examples
parent77306e187ef1a7ad5d3508ae9acb38edc5b68255 (diff)
parenta3359c0e7d85bf4752cda3b00892ecd5ef691077 (diff)
downloadpsn00bsdk-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.c1
-rw-r--r--examples/cdrom/cdxa/main.c2
-rw-r--r--examples/demos/n00bdemo/main.c6
-rw-r--r--examples/io/pads/spi.c28
-rw-r--r--examples/io/system573/k573io.c126
-rw-r--r--examples/io/system573/k573io.h274
-rw-r--r--examples/io/system573/main.c80
-rw-r--r--examples/mdec/strvideo/main.c93
-rw-r--r--examples/sound/cdstream/main.c4
-rw-r--r--examples/sound/cdstream/stream.vagbin4646912 -> 4687872 bytes
-rw-r--r--examples/sound/spustream/interleave.py13
-rw-r--r--examples/sound/spustream/main.c51
-rw-r--r--examples/sound/spustream/stream.vagbin1140736 -> 1140736 bytes
-rw-r--r--examples/sound/vagsample/main.c4
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(&sector_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(&sector_header, sizeof(STR_Header) / 4);
+
+ if (sector_header.magic != 0x0160) {
str_ctx.frame_ready = -1;
return;
}
- STR_Header *header = &sector_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
index a6faf74..aab82ba 100644
--- a/examples/sound/cdstream/stream.vag
+++ b/examples/sound/cdstream/stream.vag
Binary files differ
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
index e1cb4f4..d75408b 100644
--- a/examples/sound/spustream/stream.vag
+++ b/examples/sound/spustream/stream.vag
Binary files differ
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);