maple: send a 'device request' command

On an emulator, the receive buffer is filled with the correct/expected
data for 'device status'.

I found this experiment useful:

- it revealed a bug in my register struct generator code (the
  maple_if-related registers were not at the correct offsets)

- it validates my understanding about endianness-swapping between the
  maple bus and the SH4
This commit is contained in:
Zack Buhman 2023-12-09 01:31:34 +08:00
parent 3f30c96dcd
commit f1a32d0719
11 changed files with 242 additions and 173 deletions

View File

@ -1 +1,28 @@
all: main.elf
include common.mk
MAIN_OBJ = \
start.o \
main.o \
load.o \
cache.o \
vga.o \
rgb.o \
holly/background.o \
holly/region_array.o \
holly/ta_fifo_polygon_converter.o \
holly/core.o \
maple.o \
scene.o \
macaw.data.o \
$(LIBGCC)
serial.elf: start.o serial_main.o load.o cache.o
$(LD) $(LDFLAGS) -T $(LIB)/alt.lds $^ -o $@
main.elf: $(MAIN_OBJ)
$(LD) $(LDFLAGS) -T $(LIB)/main.lds $^ -o $@
test.elf: $(MAIN_OBJ)
$(LD) $(LDFLAGS) -T $(LIB)/alt.lds $^ -o $@

View File

@ -7,13 +7,14 @@ AARCH = --isa=sh4 --little
AFLAGS = --fatal-warnings
CARCH = -m4-single-only -ml
CFLAGS += -falign-functions=4 -ffunction-sections -fdata-sections -fshort-enums -ffreestanding -nostdlib -Wno-error=narrowing
CFLAGS += -Wall -Werror -Wfatal-errors -Wno-error=unused-variable
CFLAGS += -falign-functions=4 -ffunction-sections -fdata-sections -fshort-enums -ffreestanding -nostdlib
CFLAGS += -Wall -Werror -Wfatal-errors
CFLAGS += -Wno-error=narrowing -Wno-error=unused-variable
CFLAGS += -mfsca -funsafe-math-optimizations
DEPFLAGS = -MMD -E
# --print-gc-sections
LDFLAGS = --gc-sections --no-warn-rwx-segment --print-memory-usage --entry=_start --orphan-handling=error
CXXFLAGS = -std=c++20 -fno-exceptions -fno-non-call-exceptions -fno-rtti -fno-threadsafe-statics
CXXFLAGS = -std=c++23 -fno-exceptions -fno-non-call-exceptions -fno-rtti -fno-threadsafe-statics
TARGET = sh4-none-elf-
CC = $(TARGET)gcc
@ -47,23 +48,6 @@ IP_OBJ = \
sg/sg_ini.o \
sg/aip.o
MAIN_OBJ = \
start.o \
main.o \
load.o \
cache.o \
vga.o \
rgb.o \
holly/background.o \
holly/region_array.o \
holly/ta_fifo_polygon_converter.o \
holly/core.o \
scene.o \
macaw.data.o \
$(LIBGCC)
all: main.cdi
%.bin.o: %.bin
$(BUILD_BINARY_O)
@ -87,15 +71,6 @@ all: main.cdi
%.o: %.cpp %.cpp.d
$(CXX) $(CARCH) $(CFLAGS) $(CXXFLAGS) $(OPT) $(DEBUG) -c $< -o $@
serial.elf: start.o serial_main.o load.o cache.o
$(LD) $(LDFLAGS) -T $(LIB)/alt.lds $^ -o $@
main.elf: $(MAIN_OBJ)
$(LD) $(LDFLAGS) -T $(LIB)/main.lds $^ -o $@
test.elf: $(MAIN_OBJ)
$(LD) $(LDFLAGS) -T $(LIB)/alt.lds $^ -o $@
%.bin: %.elf
$(OBJCOPY) -O binary $< $@

View File

@ -9,6 +9,8 @@
#include "holly/core_bits.h"
#include "holly/ta_fifo_polygon_converter.h"
#include "systembus.h"
#include "maple.h"
#include "maple_bits.h"
#include "holly/texture_memory_alloc.h"
@ -16,6 +18,7 @@
#include "load.h"
#include "vga.h"
#include "rgb.h"
#include "string.h"
#include "scene.h"
#include "macaw.h"
@ -64,6 +67,32 @@ uint32_t * align_32byte(uint32_t * mem)
return reinterpret_cast<uint32_t *>(((reinterpret_cast<uint32_t>(_scene) + 31) & ~31));
}
void serial_int(const uint32_t n)
{
char num_buf[9];
string::hex<char>(num_buf, 8, n);
num_buf[8] = 0;
serial_string("0x");
serial_string(num_buf);
serial_string("\n");
}
void maple_test()
{
uint32_t _command_buf[(32 + 32) / 4];
uint32_t _receive_address[(32 + 32) / 4];
uint32_t * command_buf = align_32byte(_command_buf);
uint32_t * receive_address = align_32byte(_receive_address);
serial_int(mdstar::table_address(reinterpret_cast<uint32_t>(command_buf)));
maple_init_host_command(command_buf, receive_address);
maple_dma_start(command_buf);
for (int i = 0; i < 32; i++) {
serial_int(receive_address[i]);
}
}
extern "C"
void main()
{
@ -82,6 +111,8 @@ void main()
v_sync_in();
maple_test();
volatile uint16_t * framebuffer = reinterpret_cast<volatile uint16_t *>(&texture_memory[0]);
for (int y = 0; y < 480; y++) {
for (int x = 0; x < 640; x++) {

View File

@ -1,11 +1,22 @@
#include <cstdint>
#include <bit>
#include "sh7091.h"
#include "sh7091_bits.h"
#include "systembus.h"
#include "systembus_bits.h"
#include "maple_bits.h"
#include "maple.h"
#define AP__PO__A (0b00 << 6)
#define AP__PO__B (0b01 << 6)
#define AP__PO__C (0b10 << 6)
#define AP__PO__D (0b11 << 6)
#define AP__DE__DEVICE = (1 << 5)
#define AP__DE__EXPANSION_DEVICE = (0 << 5)
#define AP__DE__PORT = (0 << 5)
#define AP__DE__DEVICE (1 << 5)
#define AP__DE__EXPANSION_DEVICE (0 << 5)
#define AP__DE__PORT (0 << 5)
#define AP__LM(reg) ((reg) & 0b11111)
@ -26,9 +37,9 @@ struct maple_host_command {
uint32_t protocol_data[N];
};
void maple_host_command(uint32_t * buf, uint32_t * receive_address)
void maple_init_host_command(uint32_t * buf, uint32_t * receive_address)
{
auto command = reinterpet_cast<maple_host_command<1> *>(buf);
auto command = reinterpret_cast<maple_host_command<1> *>(buf);
command->host_instruction = HOST_INSTRUCTION__END_FLAG
| HOST_INSTRUCTION__PORT_SELECT__A
@ -40,10 +51,11 @@ void maple_host_command(uint32_t * buf, uint32_t * receive_address)
uint32_t destination_ap = AP__DE__DEVICE | AP__PO__A;
uint32_t source_ap = AP__PO__A;
uint32_t data_size = 0;
command->protocol_data[0] = (command_code << 24)
// maple bus is big endian
command->protocol_data[0] = std::byteswap( (command_code << 24)
| (destination_ap << 16)
| (source_ap << 8)
| (data_size << 0);
| (data_size << 0));
}
void maple_dma_start(uint32_t * command_buf)
@ -56,28 +68,28 @@ void maple_dma_start(uint32_t * command_buf)
system.ISTNRM = ISTNRM__END_OF_DMA_MAPLE_DMA;
// disable maple-DMA
system.MDEN = mden::dma_enable::abort;
maple_if.MDEN = mden::dma_enable::abort;
volatile uint32_t _dummy = system.MDST;
volatile uint32_t _dummy = maple_if.MDST;
(void)_dummy;
// 20nsec * 0xc350 = 1ms
constexpr uint32_t one_msec = 0xc350;
system.MSYS = msys::time_out_counter(one_msec)
maple_if.MSYS = msys::time_out_counter(one_msec)
| msys::sending_rate::_2M;
system.MDTSEL = mdtsel::trigger_select::software_initiation;
maple_if.MDTSEL = mdtsel::trigger_select::software_initiation;
/* top address: the first/lowest address
bottom address: the last/highest address */
system.MDAPRO = mdapro::security_code
maple_if.MDAPRO = mdapro::security_code
| mdapro::top_address(0x00)
| mdapro::bottom_address(0x7f);
system.MDSTAR = mdstar::table_address(command_buf);
maple_if.MDSTAR = mdstar::table_address(reinterpret_cast<uint32_t>(command_buf));
system.MDEN = mden::dma_enable::enable;
system.MDST = mdst::start_status::start;
maple_if.MDEN = mden::dma_enable::enable;
maple_if.MDST = mdst::start_status::start;
// wait for completion
while ((system.ISTNRM & ISTNRM__END_OF_DMA_MAPLE_DMA) == 0);

80
maple.h
View File

@ -1,78 +1,6 @@
#pragma once
#include <cstdint>
#include "float_uint32.h"
namespace mdstar {
constexpr uint32_t table_address(uint32_t num) { return (num & 0xfffffe0) << 0; }
}
namespace mdtsel {
namespace trigger_select {
constexpr uint32_t software_initiation = 0 << 0;
constexpr uint32_t v_blank_initiation = 1 << 0;
}
}
namespace mden {
namespace dma_enable {
constexpr uint32_t abort = 0 << 0;
constexpr uint32_t enable = 1 << 0;
constexpr uint32_t status(uint32_t reg) { return (reg >> 0) & 0x1; }
}
}
namespace mdst {
namespace start_status {
constexpr uint32_t status(uint32_t reg) { return (reg >> 0) & 0x1; }
constexpr uint32_t start = 1 << 0;
}
}
namespace msys {
constexpr uint32_t time_out_counter(uint32_t num) { return (num & 0xffff) << 16; }
constexpr uint32_t single_hard_trigger = 1 << 12;
namespace sending_rate {
constexpr uint32_t _2M = 0 << 8;
constexpr uint32_t _1M = 1 << 8;
}
constexpr uint32_t delay_time(uint32_t num) { return (num & 0xf) << 0; }
}
namespace mst {
constexpr uint32_t move_status(uint32_t reg) { return (reg >> 31) & 0x1; }
constexpr uint32_t internal_frame_monitor(uint32_t reg) { return (reg >> 24) & 0x7; }
constexpr uint32_t internal_state_monitor(uint32_t reg) { return (reg >> 16) & 0x3f; }
constexpr uint32_t line_monitor(uint32_t reg) { return (reg >> 0) & 0xff; }
}
namespace mshtcl {
constexpr uint32_t hard_dma_clear = 1 << 0;
}
namespace mdapro {
constexpr uint32_t security_code = 0x6155 << 16;
constexpr uint32_t top_address(uint32_t num) { return (num & 0x7f) << 8; }
constexpr uint32_t bottom_address(uint32_t num) { return (num & 0x7f) << 0; }
}
namespace mmsel {
namespace msb_selection {
constexpr uint32_t bit7 = 0 << 0;
constexpr uint32_t bit31 = 1 << 0;
}
}
namespace mtxdad {
constexpr uint32_t txd_address_counter(uint32_t reg) { return (reg >> 0) & 0x1fffffff; }
}
namespace mrxdad {
constexpr uint32_t rxd_address_counter(uint32_t reg) { return (reg >> 0) & 0x1fffffff; }
}
namespace mrxdbd {
constexpr uint32_t rxd_base_address(uint32_t reg) { return (reg >> 0) & 0x1fffffff; }
}
void maple_init_host_command(uint32_t * buf, uint32_t * receive_address);
void maple_dma_start(uint32_t * command_buf);

78
maple_bits.h Normal file
View File

@ -0,0 +1,78 @@
#include <cstdint>
#include "holly/float_uint32.h"
namespace mdstar {
constexpr uint32_t table_address(uint32_t num) { return (num & 0xfffffe0) << 0; }
}
namespace mdtsel {
namespace trigger_select {
constexpr uint32_t software_initiation = 0 << 0;
constexpr uint32_t v_blank_initiation = 1 << 0;
}
}
namespace mden {
namespace dma_enable {
constexpr uint32_t abort = 0 << 0;
constexpr uint32_t enable = 1 << 0;
constexpr uint32_t status(uint32_t reg) { return (reg >> 0) & 0x1; }
}
}
namespace mdst {
namespace start_status {
constexpr uint32_t status(uint32_t reg) { return (reg >> 0) & 0x1; }
constexpr uint32_t start = 1 << 0;
}
}
namespace msys {
constexpr uint32_t time_out_counter(uint32_t num) { return (num & 0xffff) << 16; }
constexpr uint32_t single_hard_trigger = 1 << 12;
namespace sending_rate {
constexpr uint32_t _2M = 0 << 8;
constexpr uint32_t _1M = 1 << 8;
}
constexpr uint32_t delay_time(uint32_t num) { return (num & 0xf) << 0; }
}
namespace mst {
constexpr uint32_t move_status(uint32_t reg) { return (reg >> 31) & 0x1; }
constexpr uint32_t internal_frame_monitor(uint32_t reg) { return (reg >> 24) & 0x7; }
constexpr uint32_t internal_state_monitor(uint32_t reg) { return (reg >> 16) & 0x3f; }
constexpr uint32_t line_monitor(uint32_t reg) { return (reg >> 0) & 0xff; }
}
namespace mshtcl {
constexpr uint32_t hard_dma_clear = 1 << 0;
}
namespace mdapro {
constexpr uint32_t security_code = 0x6155 << 16;
constexpr uint32_t top_address(uint32_t num) { return (num & 0x7f) << 8; }
constexpr uint32_t bottom_address(uint32_t num) { return (num & 0x7f) << 0; }
}
namespace mmsel {
namespace msb_selection {
constexpr uint32_t bit7 = 0 << 0;
constexpr uint32_t bit31 = 1 << 0;
}
}
namespace mtxdad {
constexpr uint32_t txd_address_counter(uint32_t reg) { return (reg >> 0) & 0x1fffffff; }
}
namespace mrxdad {
constexpr uint32_t rxd_address_counter(uint32_t reg) { return (reg >> 0) & 0x1fffffff; }
}
namespace mrxdbd {
constexpr uint32_t rxd_base_address(uint32_t reg) { return (reg >> 0) & 0x1fffffff; }
}

View File

@ -4,7 +4,7 @@ def should_autonewline(line):
return (
"static_assert" not in line
and "extern" not in line
and line.split()[1] != '=' # hacky; meh
and (len(line.split()) < 2 or line.split()[1] != '=') # hacky; meh
)
def _render(out, lines):

View File

@ -51,8 +51,7 @@ def new_writer():
if last_block is not None:
yield "};"
for address, name in stack:
relative_address = address - first_address
yield f"static_assert((offsetof (struct {last_block.lower()}_reg, {name})) == {hex(relative_address)});"
yield f"static_assert((offsetof (struct {last_block.lower()}_reg, {name})) == {hex(address)});"
yield ""
stack = []
@ -76,8 +75,8 @@ def new_writer():
if block != last_block:
yield from terminate()
first_address = address
last_address = address
first_address = 0 # hmm...
last_address = 0
size_total = 0
reserved_num = 0
yield f"struct {block.lower()}_reg {{"

View File

@ -14,7 +14,7 @@ def blocks(rows):
blocks.append(block)
for block in blocks:
yield f'extern struct {block.lower()}_reg {block} __asm("{block}");'
yield f'extern struct {block.lower()}_reg {block.lower()} __asm("{block.lower()}");'
input_file = sys.argv[1]
rows = read_input(input_file)

21
string.h Normal file
View File

@ -0,0 +1,21 @@
#pragma once
#include <cstdint>
namespace string {
template <typename T>
inline void hex(T * c, uint32_t len, uint32_t n)
{
while (len > 0) {
uint32_t nib = n & 0xf;
n = n >> 4;
if (nib > 9) {
nib += (97 - 10);
} else {
nib += (48 - 0);
}
c[--len] = nib;
}
}
}

View File

@ -88,81 +88,83 @@ static_assert((offsetof (struct system_reg, G2DTNRM)) == 0x150);
static_assert((offsetof (struct system_reg, G2DTEXT)) == 0x154);
struct maple_if_reg {
reg8 _pad0[4];
reg32 MDSTAR; /* Maple-DMA command table address */
reg8 _pad0[8];
reg8 _pad1[8];
reg32 MDTSEL; /* Maple-DMA trigger select */
reg32 MDEN; /* Maple-DMA enable */
reg32 MDST; /* Maple-DMA start */
reg8 _pad1[100];
reg8 _pad2[100];
reg32 MSYS; /* Maple system control */
reg32 MST; /* Maple status */
reg32 MSHTCL; /* Maple-DMA hard trigger clear */
reg32 MDAPRO; /* Maple-DMA address range */
reg8 _pad2[88];
reg8 _pad3[88];
reg32 MMSEL; /* Maple MSP selection */
reg8 _pad3[8];
reg8 _pad4[8];
reg32 MTXDAD; /* Maple TXD address counter */
reg32 MRXDAD; /* Maple RXD address counter */
reg32 MRXDBD; /* Maple RXD address base */
};
static_assert((offsetof (struct maple_if_reg, MDSTAR)) == 0x0);
static_assert((offsetof (struct maple_if_reg, MDTSEL)) == 0xc);
static_assert((offsetof (struct maple_if_reg, MDEN)) == 0x10);
static_assert((offsetof (struct maple_if_reg, MDST)) == 0x14);
static_assert((offsetof (struct maple_if_reg, MSYS)) == 0x7c);
static_assert((offsetof (struct maple_if_reg, MST)) == 0x80);
static_assert((offsetof (struct maple_if_reg, MSHTCL)) == 0x84);
static_assert((offsetof (struct maple_if_reg, MDAPRO)) == 0x88);
static_assert((offsetof (struct maple_if_reg, MMSEL)) == 0xe4);
static_assert((offsetof (struct maple_if_reg, MTXDAD)) == 0xf0);
static_assert((offsetof (struct maple_if_reg, MRXDAD)) == 0xf4);
static_assert((offsetof (struct maple_if_reg, MRXDBD)) == 0xf8);
static_assert((offsetof (struct maple_if_reg, MDSTAR)) == 0x4);
static_assert((offsetof (struct maple_if_reg, MDTSEL)) == 0x10);
static_assert((offsetof (struct maple_if_reg, MDEN)) == 0x14);
static_assert((offsetof (struct maple_if_reg, MDST)) == 0x18);
static_assert((offsetof (struct maple_if_reg, MSYS)) == 0x80);
static_assert((offsetof (struct maple_if_reg, MST)) == 0x84);
static_assert((offsetof (struct maple_if_reg, MSHTCL)) == 0x88);
static_assert((offsetof (struct maple_if_reg, MDAPRO)) == 0x8c);
static_assert((offsetof (struct maple_if_reg, MMSEL)) == 0xe8);
static_assert((offsetof (struct maple_if_reg, MTXDAD)) == 0xf4);
static_assert((offsetof (struct maple_if_reg, MRXDAD)) == 0xf8);
static_assert((offsetof (struct maple_if_reg, MRXDBD)) == 0xfc);
struct g1_if_reg {
reg8 _pad0[4];
reg32 GDSTAR; /* GD-DMA start address */
reg32 GDLEN; /* GD-DMA length */
reg32 GDDIR; /* GD-DMA direction */
reg8 _pad0[4];
reg8 _pad1[4];
reg32 GDEN; /* GD-DMA enable */
reg32 GDST; /* GD-DMA start */
reg8 _pad1[100];
reg8 _pad2[100];
reg32 G1RRC; /* System ROM read access timing */
reg32 G1RWC; /* System ROM write access timing */
reg32 G1FRC; /* Flash ROM read access timing */
reg32 G1FWC; /* Flash ROM write access timing */
reg32 G1CRC; /* GD PIO read access timing */
reg32 G1CWC; /* GD PIO write access timing */
reg8 _pad2[8];
reg8 _pad3[8];
reg32 G1GDRC; /* GD-DMA read access timing */
reg32 G1GDWC; /* GD-DMA write access timing */
reg8 _pad3[8];
reg8 _pad4[8];
reg32 G1SYSM; /* System mode */
reg32 G1CRDYC; /* G1IORDY signal control */
reg32 GDAPRO; /* GD-DMA address range */
reg8 _pad4[56];
reg8 _pad5[56];
reg32 GDSTARD; /* GD-DMA address count (on Root Bus) */
reg32 GDLEND; /* GD-DMA transfer counter */
};
static_assert((offsetof (struct g1_if_reg, GDSTAR)) == 0x0);
static_assert((offsetof (struct g1_if_reg, GDLEN)) == 0x4);
static_assert((offsetof (struct g1_if_reg, GDDIR)) == 0x8);
static_assert((offsetof (struct g1_if_reg, GDEN)) == 0x10);
static_assert((offsetof (struct g1_if_reg, GDST)) == 0x14);
static_assert((offsetof (struct g1_if_reg, G1RRC)) == 0x7c);
static_assert((offsetof (struct g1_if_reg, G1RWC)) == 0x80);
static_assert((offsetof (struct g1_if_reg, G1FRC)) == 0x84);
static_assert((offsetof (struct g1_if_reg, G1FWC)) == 0x88);
static_assert((offsetof (struct g1_if_reg, G1CRC)) == 0x8c);
static_assert((offsetof (struct g1_if_reg, G1CWC)) == 0x90);
static_assert((offsetof (struct g1_if_reg, G1GDRC)) == 0x9c);
static_assert((offsetof (struct g1_if_reg, G1GDWC)) == 0xa0);
static_assert((offsetof (struct g1_if_reg, G1SYSM)) == 0xac);
static_assert((offsetof (struct g1_if_reg, G1CRDYC)) == 0xb0);
static_assert((offsetof (struct g1_if_reg, GDAPRO)) == 0xb4);
static_assert((offsetof (struct g1_if_reg, GDSTARD)) == 0xf0);
static_assert((offsetof (struct g1_if_reg, GDLEND)) == 0xf4);
static_assert((offsetof (struct g1_if_reg, GDSTAR)) == 0x4);
static_assert((offsetof (struct g1_if_reg, GDLEN)) == 0x8);
static_assert((offsetof (struct g1_if_reg, GDDIR)) == 0xc);
static_assert((offsetof (struct g1_if_reg, GDEN)) == 0x14);
static_assert((offsetof (struct g1_if_reg, GDST)) == 0x18);
static_assert((offsetof (struct g1_if_reg, G1RRC)) == 0x80);
static_assert((offsetof (struct g1_if_reg, G1RWC)) == 0x84);
static_assert((offsetof (struct g1_if_reg, G1FRC)) == 0x88);
static_assert((offsetof (struct g1_if_reg, G1FWC)) == 0x8c);
static_assert((offsetof (struct g1_if_reg, G1CRC)) == 0x90);
static_assert((offsetof (struct g1_if_reg, G1CWC)) == 0x94);
static_assert((offsetof (struct g1_if_reg, G1GDRC)) == 0xa0);
static_assert((offsetof (struct g1_if_reg, G1GDWC)) == 0xa4);
static_assert((offsetof (struct g1_if_reg, G1SYSM)) == 0xb0);
static_assert((offsetof (struct g1_if_reg, G1CRDYC)) == 0xb4);
static_assert((offsetof (struct g1_if_reg, GDAPRO)) == 0xb8);
static_assert((offsetof (struct g1_if_reg, GDSTARD)) == 0xf4);
static_assert((offsetof (struct g1_if_reg, GDLEND)) == 0xf8);
struct g2_if_reg {
reg32 ADSTAG; /* ACIA:G2-DMA G2 start address */
@ -302,11 +304,7 @@ static_assert((offsetof (struct pvr_if_reg, PDSTARD)) == 0xf4);
static_assert((offsetof (struct pvr_if_reg, PDLEND)) == 0xf8);
extern struct system_reg system __asm("system");
extern struct maple_if_reg maple_if __asm("maple_if");
extern struct g1_if_reg g1_if __asm("g1_if");
extern struct g2_if_reg g2_if __asm("g2_if");
extern struct pvr_if_reg pvr_if __asm("pvr_if");