Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
b9b8bf6
2 instructions saved for de / sega framedetect
PastorL69 Jun 2, 2026
9b9ef7e
exec wait blocking for pixel info, frame pio was also being started t…
PastorL69 Jun 2, 2026
851ef78
correct wpc again
PastorL69 Jun 2, 2026
9b75b76
use pio encode out
PastorL69 Jun 2, 2026
f474d55
rgular exec
PastorL69 Jun 2, 2026
6ad1d9c
use y instead
PastorL69 Jun 2, 2026
6a7cfbf
make it blocking with the put
PastorL69 Jun 2, 2026
e2b8b8a
different approach
PastorL69 Jun 2, 2026
545add6
handle irq's first
PastorL69 Jun 2, 2026
c8734b9
no more wait blocking
PastorL69 Jun 2, 2026
d2b8508
try 1k pixels
PastorL69 Jun 2, 2026
e8c58be
initialize osr with null
PastorL69 Jun 2, 2026
feaa9ca
we cant have a fifo join
PastorL69 Jun 2, 2026
55a7988
spike check
PastorL69 Jun 2, 2026
3a635e5
add back spike / sleic split as it wasn't the issue
PastorL69 Jun 2, 2026
9cd357b
Spike additional optimizations
PastorL69 Jun 2, 2026
4e70bc5
RCLK no longer used with spike framedetect
PastorL69 Jun 2, 2026
ccbfe32
rclk skip needs to be included..
PastorL69 Jun 2, 2026
e54e5fe
redo spike frame detect
PastorL69 Jun 2, 2026
9d090d2
Spike new framedetect comments
PastorL69 Jun 2, 2026
88ded27
Sleic optimized too
PastorL69 Jun 2, 2026
66a7451
Alvin G framedetect optimized
PastorL69 Jun 2, 2026
4dbbf8c
homepin reader optimized
PastorL69 Jun 2, 2026
f5441b6
Capcom optimization
PastorL69 Jun 2, 2026
ca5bace
should be mov
PastorL69 Jun 2, 2026
28f6b70
capcom comment
PastorL69 Jun 3, 2026
3395733
add comment to framedetect generic
PastorL69 Jun 3, 2026
2b7da27
remove white space
PastorL69 Jun 3, 2026
874473a
enable lock in again for capcom HD, decrease amount of checks
PastorL69 Jun 3, 2026
d72ed9e
give romstar its own dmd_type, it needs to not use the capcom algorithm
PastorL69 Jun 3, 2026
d317e02
try to remove endian conversion through bswap
PastorL69 Jun 3, 2026
1a23092
shift righ true
PastorL69 Jun 3, 2026
dcd346c
bring back the loop
PastorL69 Jun 3, 2026
2a9497e
experimental
PastorL69 Jun 3, 2026
0caf659
quicker byteswap
PastorL69 Jun 3, 2026
56484d1
remove bswap dma setting
PastorL69 Jun 3, 2026
cfddff0
bswapper
PastorL69 Jun 3, 2026
ee06b3f
another attempt at reversing byte order
PastorL69 Jun 3, 2026
f623f8a
set ot 32
PastorL69 Jun 3, 2026
db3cd79
configure bswap agian
PastorL69 Jun 3, 2026
7216a88
use builtin bswap func
PastorL69 Jun 4, 2026
9f77794
slight optimization
PastorL69 Jun 4, 2026
756087a
revert to original loop
PastorL69 Jun 4, 2026
81a2c5e
correct dotloop back to original. shift right false
PastorL69 Jun 4, 2026
6ff6350
slightly cleaner
PastorL69 Jun 4, 2026
ac583d2
white space
PastorL69 Jun 4, 2026
686a39c
PinMAME correct, revert hook LUT to 0, 2 and 3 as main shades.
PastorL69 Jun 5, 2026
43920b6
should be == now because the previous statement was changed
PastorL69 Jun 6, 2026
1d5cbf8
Dotmation support
PastorL69 Jun 10, 2026
6645d8a
dotmation gets its own dotloop
PastorL69 Jun 10, 2026
d16d04b
dotclk as jmp pin
PastorL69 Jun 10, 2026
3643d33
lower delay
PastorL69 Jun 10, 2026
43ec875
it needs to not do another in null. it already knows that its low
PastorL69 Jun 10, 2026
e5473bc
try without for now
PastorL69 Jun 10, 2026
70094d6
check if exact replica of old pio works in new pio
PastorL69 Jun 10, 2026
6c9f725
make dotclk pin as jmp pin global
PastorL69 Jun 10, 2026
32b1adc
try with this first
PastorL69 Jun 10, 2026
e6d32cf
dotmation gets its own frame detect
PastorL69 Jun 10, 2026
c31cb9e
add dotmation to interface
PastorL69 Jun 10, 2026
385da10
add dotmation
PastorL69 Jun 10, 2026
a880236
homepin frame detect for dotmation
PastorL69 Jun 10, 2026
09308fd
correct frame detect now...
PastorL69 Jun 10, 2026
89ad01a
check if statement possible
PastorL69 Jun 10, 2026
382807d
change to capcom framedetect with 64 rows
PastorL69 Jun 10, 2026
d57321f
dotmation sal file
PastorL69 Jun 10, 2026
c008e52
can be removed
PastorL69 Jun 10, 2026
b28b722
remove dotmation frame detect
PastorL69 Jun 10, 2026
d5ba3bb
parse dmd type to setup of dotloop program
PastorL69 Jun 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ Since not any consumer (especially the Raspberry Pi) can act as an SPI slave thi
* Homepin -> 128x32
* Spinball -> 128x32
* Sleic/Petaco -> 128x32
* Dotmation -> 192x64

## Reading data

Expand Down
Binary file added logicanalyzer/WPC/Dotmation 192x64.sal
Binary file not shown.
25 changes: 11 additions & 14 deletions src/dmd_interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,35 +14,32 @@
#include "pio/dmd_framedetect_sleic.pio.h"
#include "pio/dmd_framedetect_spike.pio.h"

// Init the DMD reader (dots) PIO program, common for all DMD types.
// Init the DMD reader (dotloop) PIO program
void dmd_reader_program_init(float dmd_clkdiv, PIO pio, uint sm, uint offset, pio_sm_config c,
uint in_base_pin) {
uint dmd_type, uint in_base_pin) {
sm_config_set_in_pins(&c, in_base_pin);

if (in_base_pin != SDATA_X16) {
// We only send, so disable the TX FIFO to make the RX FIFO deeper.
// Joining is not possible with data east x16
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_RX);

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This had to be donne to make use of the pio_sm_put function. Remember that the isr is still 4 x 32 bits deep. So we have nothing to worry about.

} else {
// -- Data East 128x16 case --
// We need to set DOTCLK as the jump pin
if (dmd_type == DMD_DOTMATION || dmd_type == DMD_DE_X16_V1 ||
dmd_type == DMD_DE_X16_V2) {
// set DOTCLK as the jmp pin
sm_config_set_jmp_pin(&c, DOTCLK);
// Make sure we run the sm with a 125MHz clk
sm_config_set_clkdiv(&c, dmd_clkdiv);
}

if (in_base_pin == SDATA_X16) {
pio_gpio_init(pio, SDATA_X16); // Extra data line for Data East X16
pio_gpio_init(pio, SDATA_X16_PADDING); // used as a padding 0 bit

pio_sm_set_consecutive_pindirs(pio, sm, SDATA_X16, 1, false);
pio_sm_set_consecutive_pindirs(pio, sm, SDATA_X16_PADDING, 1, false);

// Make sure we run this sm with a 125MHz clk
sm_config_set_clkdiv(&c, dmd_clkdiv);
}

// Connect these GPIOs to this PIO block
pio_gpio_init(pio, SDATA);
pio_gpio_init(pio, DOTCLK);

// Set the pin direction at the PIO, handle pins seprately to support alphaDMD
// as well
// Set the pin direction at the PIO
pio_sm_set_consecutive_pindirs(pio, sm, SDATA, 1, false);
pio_sm_set_consecutive_pindirs(pio, sm, DOTCLK, 1, false);

Expand Down
97 changes: 68 additions & 29 deletions src/dmdreader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,11 @@ DmdType detect_dmd() {
(rclk < 4000) && (rdata > 115) && (rdata < 130)) {
return DMD_WPC;

// DOTMATION: DOTCLK: 1900000 | RCLK: 9950 | RDATA: 155
} else if ((dotclk > 1800000) && (dotclk < 2000000) && (rclk > 9700) &&
(rclk < 10200) && (rdata > 140) && (rdata < 170)) {
return DMD_DOTMATION;

// Data East X16 V1: DOTCLK: 121000 or 60544 | RCLK: 3905 | RDATA: 120
} else if ((dotclk > 55000) && (dotclk < 125000) && (rclk > 3880) &&
(rclk < 3930) && (rdata > 110) && (rdata < 125)) {
Expand Down Expand Up @@ -383,13 +388,18 @@ DmdType detect_dmd() {
(rclk < 4850) && (rdata > 135) && (rdata < 155)) {
return DMD_SLEIC;

// ROMSTAR -> DOTCLK: 4000000 | RCLK: 15625 | RDATA: 245
} else if ((dotclk > 3900000) && (dotclk < 4100000) && (rclk > 15300) &&
(rclk < 15900) && (rdata > 230) && (rdata < 260)) {
return DMD_ROMSTAR;

// Capcom -> DOTCLK: 4168000 | RCLK: 16280 | RDATA: 510
} else if ((dotclk > 4000000) && (dotclk < 4300000) && (rclk > 16000) &&
(rclk < 16500) && (rdata > 490) && (rdata < 530)) {
return DMD_CAPCOM;

// Capcom HD -> DOTCLK: 4168000 | RCLK: 16280 | RDATA: 255
} else if ((dotclk > 3900000) && (dotclk < 4300000) && (rclk > 15500) &&
} else if ((dotclk > 4100000) && (dotclk < 4300000) && (rclk > 15900) &&
(rclk < 16500) && (rdata > 240) && (rdata < 270)) {
return DMD_CAPCOM_HD;
}
Expand Down Expand Up @@ -421,7 +431,7 @@ uint64_t convert_2bit_to_4bit_fast(uint32_t input) {
// ---------------------------------

static constexpr uint8_t map_nibble_de_x16(uint8_t p) {
return (p <= 3) ? p : (p == 4) ? 1 : (p == 8) ? 2 : (p == 10) ? 0 : 3;

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After talking to Vincent, it turned out PinMAME shades for x16 are correct. This corrects the shades

return (p <= 3) ? p : (p == 4) ? 1 : (p == 7) ? 0 : (p == 8) ? 2 : 3;
}

static constexpr uint8_t make_lut_entry_de_x16(uint16_t b) {
Expand Down Expand Up @@ -625,13 +635,11 @@ void dmd_dma_handler() {
// Fix byte order within the buffer
uint32_t *planebuf = (uint32_t *)currentPlaneBuffer;
buf32_t *v;
uint32_t res;
// source_dwordsperframe is not the entire frame buffer if plane history is
// used. So only the new plane data is fixed here.
for (int i = 0; i < source_dwordsperframe; i++) {
v = (buf32_t *)planebuf;
res = (v->byte3 << 24) | (v->byte2 << 16) | (v->byte1 << 8) | (v->byte0);
*planebuf = res;
*planebuf = (v->byte3 << 24) | (v->byte2 << 16) | (v->byte1 << 8) | (v->byte0);
planebuf++;
}

Expand Down Expand Up @@ -676,7 +684,7 @@ void dmd_dma_handler() {
// It seems to be sufficient to check every 8th pixel for these patterns to
// detect sync. So we could avoid bitschifiting of the uint32_t value to
// check every single pixel.
if (dmd_type == DMD_CAPCOM && !locked_in && !plane0_shifted) {
if (dmd_type >= DMD_CAPCOM && !locked_in && !plane0_shifted) {
digitalWrite(LED_BUILTIN, HIGH);
uint8_t value = pixval & 0x0F;
if (value == 2 && (planebuf[px] & 0x0F) != 1 &&
Expand All @@ -689,7 +697,7 @@ void dmd_dma_handler() {
// 0/1/1/1 => 3
// 1/0/1/1 => 3
// 1/1/0/1 => 3
// 1/1/1/0 => 3
// 1/1/1/0 => 3 (checking whether value 3 appears is actually enough)
//
// 0/0/0/1 => 1
// 0/0/1/0 => 1
Expand All @@ -698,11 +706,8 @@ void dmd_dma_handler() {
// 1/0/1/0 => 2
// 1/1/0/0 => 2
// 0/0/1/1 => 2
else if (value == 3 || value > 4 ||

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested. And now the algo also runs for Capcom HD. We successfully lock in each time, and due to less branches it needs to check now Capcom HD doesn't crash the program!

(value == 1 && (planebuf[px] & 0x0F) != 1) ||
(value == 2 && ((planebuf[px] & 0x0F) == 1 ||
planebuf[offset[2] + px] & 0x0F) == 1)) {
// An unsynchronized has been found.
else if (value == 3 || value > 4) {
// An unsynchronized frame has been found.
// Disable the state machine, clean the DMA channel and restart.
// As a result, we will skip exactly one plane.
pio_sm_set_enabled(dmd_pio, dmd_sm, false);
Expand Down Expand Up @@ -749,8 +754,9 @@ void dmd_dma_handler() {
}
}

if (dmd_type == DMD_CAPCOM && !locked_in && !plane0_shifted &&
if (dmd_type >= DMD_CAPCOM && !locked_in && !plane0_shifted &&
detected_0_1_0_1 && detected_1_0_0_0) {
digitalWrite(LED_BUILTIN, LOW);
locked_in = true;
}

Expand Down Expand Up @@ -782,7 +788,7 @@ void dmd_dma_handler() {
for (int l = 0; l < source_height / 2; l++) {
for (int w = 0; w < source_dwordsperline; w++) {
uint32_t out = w >> 1; // Shifting leads to 0, 0, 1, 1, etc
uint16_t v16 = convert_4bit_to_2bit_de_x16((src1[w] + src2[w] * 2));
uint16_t v16 = convert_4bit_to_2bit_de_x16((src1[w] * 2 + src2[w]));
if ((w & 1) == 0) {
// Write first 8 pixel in upper 16 Bit.
dst[out] = (uint32_t)v16 << 16;
Expand Down Expand Up @@ -900,7 +906,7 @@ void dmdreader_programs_init(const pio_program_t *dmd_reader_program,
(DE < SDATA_X16) ? DE : SDATA_X16, 8, true));
pio_sm_config dmd_config = reader_get_default_config(dmd_offset);
dmd_reader_program_init(dmd_clkdiv, dmd_pio, dmd_sm, dmd_offset, dmd_config,
in_base_pin);
dmd_type, in_base_pin);

// The framedetect program just runs and detects the beginning of a new
// frame
Expand All @@ -911,7 +917,6 @@ void dmdreader_programs_init(const pio_program_t *dmd_reader_program,
dmd_framedetect_program_init(dmd_clkdiv, frame_pio, frame_sm, frame_offset,
frame_config, input_pins, num_input_pins,
jump_pin);
pio_sm_set_enabled(frame_pio, frame_sm, true);
}

bool dmdreader_init(bool return_on_no_detection) {
Expand Down Expand Up @@ -945,12 +950,12 @@ bool dmdreader_init(bool return_on_no_detection) {
// Initialize DMD reader
switch (dmd_type) {
case DMD_WPC: {
uint input_pins[] = {RDATA, DE, DOTCLK};
uint input_pins[] = {RDATA};
dmdreader_programs_init(&dmd_reader_2bpp_program,
dmd_reader_2bpp_program_get_default_config,
&dmd_framedetect_generic_program,
dmd_framedetect_generic_program_get_default_config,
input_pins, 3, 0, SDATA);
input_pins, 1, 0, SDATA);

// load 4096 - 1 pixels directly to TX fifo
pio_sm_put(dmd_pio, dmd_sm, 4095);
Expand All @@ -966,6 +971,30 @@ bool dmdreader_init(bool return_on_no_detection) {
break;
}

case DMD_DOTMATION: {
uint input_pins[] = {RDATA, RCLK};
dmdreader_programs_init(&dmd_reader_dotmation_program,
dmd_reader_dotmation_program_get_default_config,
&dmd_framedetect_capcom_program,
dmd_framedetect_capcom_program_get_default_config,
input_pins, 2, 0, SDATA);

// load 12288 - 1 pixels directly to TX fifo
pio_sm_put(dmd_pio, dmd_sm, 12287);
// load 64 - 1 rows directly to TX fifo
pio_sm_put(frame_pio, frame_sm, 63);

source_width = 192;
source_height = 64;
source_bitsperpixel = 2;
target_bitsperpixel = 2;
source_planesperframe = 3;
source_planehistoryperframe = 2;
source_lineoversampling = LINEOVERSAMPLING_NONE;
source_mergeplanes = MERGEPLANES_ADD;
break;
}

case DMD_WHITESTAR: {
uint input_pins[] = {RDATA};
dmdreader_programs_init(
Expand Down Expand Up @@ -993,12 +1022,12 @@ bool dmdreader_init(bool return_on_no_detection) {
}

case DMD_SPIKE1: {
uint input_pins[] = {RCLK, RDATA};
uint input_pins[] = {COLLAT};
dmdreader_programs_init(&dmd_reader_4bpp_program,
dmd_reader_4bpp_program_get_default_config,
&dmd_framedetect_spike_program,
dmd_framedetect_spike_program_get_default_config,
input_pins, 2, RDATA, SDATA);
input_pins, 1, COLLAT, SDATA);

// load 16384 - 1 pixels directly to TX fifo
pio_sm_put(dmd_pio, dmd_sm, 16383);
Expand Down Expand Up @@ -1076,13 +1105,10 @@ bool dmdreader_init(bool return_on_no_detection) {
pio_sm_exec_wait_blocking(dmd_pio, dmd_sm,
pio_encode_mov(pio_y, pio_null));

// load 4096 directly to TX fifo (32uS)
// load 4096 delay cycles directly to TX fifo (32uS)
pio_sm_put(dmd_pio, dmd_sm, 4096);

// load 2500 directly to TX fifo (20uS)
// load 2500 delay cycles directly to TX fifo (20uS)
pio_sm_put(frame_pio, frame_sm, 2500);
// pull 32 bits from the TX fifo into osr
pio_sm_exec(frame_pio, frame_sm, pio_encode_pull(false, false));

source_width = 128;
source_height = 32; // is actually 16, but we process as 32
Expand Down Expand Up @@ -1172,15 +1198,17 @@ bool dmdreader_init(bool return_on_no_detection) {
}

case DMD_ALVING: {
uint input_pins[] = {RDATA, RCLK, COLLAT};
uint input_pins[] = {RDATA, COLLAT};
dmdreader_programs_init(&dmd_reader_4bpp_program,
dmd_reader_4bpp_program_get_default_config,
&dmd_framedetect_alving_program,
dmd_framedetect_alving_program_get_default_config,
input_pins, 3, 0, SDATA);
input_pins, 2, 0, SDATA);

// load 16384 - 1 pixels directly to TX fifo
pio_sm_put(dmd_pio, dmd_sm, 16383);
// load 128 - 1 COLLAT edges directly to TX fifo
pio_sm_put(frame_pio, frame_sm, 127);

source_width = 128;
source_height = 32;
Expand Down Expand Up @@ -1253,6 +1281,8 @@ bool dmdreader_init(bool return_on_no_detection) {

// load 4096 - 1 pixels directly to TX fifo
pio_sm_put(dmd_pio, dmd_sm, 4095);
// load 32 - 1 rows directly to TX fifo
pio_sm_put(frame_pio, frame_sm, 31);

source_width = 128;
source_height = 32;
Expand All @@ -1276,6 +1306,8 @@ bool dmdreader_init(bool return_on_no_detection) {

// load 8192 - 1 pixels directly to TX fifo
pio_sm_put(dmd_pio, dmd_sm, 8191);
// load 6144 delay cycles directly to TX fifo
pio_sm_put(frame_pio, frame_sm, 6144);

source_width = 128;
source_height = 32;
Expand All @@ -1298,6 +1330,8 @@ bool dmdreader_init(bool return_on_no_detection) {

// load 127 directly to TX fifo
pio_sm_put(dmd_pio, dmd_sm, 127);
// load 32 - 1 rows directly to TX fifo
pio_sm_put(frame_pio, frame_sm, 31);

source_width = 128;
source_height = 32;
Expand All @@ -1310,17 +1344,20 @@ bool dmdreader_init(bool return_on_no_detection) {
break;
}

case DMD_ROMSTAR:
case DMD_CAPCOM_HD: {
uint input_pins[] = {RDATA, RCLK};
dmdreader_programs_init(
&dmd_reader_4bpp_program,
dmd_reader_4bpp_program_get_default_config,
&dmd_framedetect_capcom_hd_program,
dmd_framedetect_capcom_hd_program_get_default_config, input_pins, 2,
&dmd_framedetect_capcom_program,
dmd_framedetect_capcom_program_get_default_config, input_pins, 2,
0, SDATA);

// load 16384 - 1 pixels directly to TX fifo
pio_sm_put(dmd_pio, dmd_sm, 16383);
// load 64 - 1 rows directly to TX fifo
pio_sm_put(frame_pio, frame_sm, 63);

source_width = 256;
source_height = 64;
Expand All @@ -1336,6 +1373,7 @@ bool dmdreader_init(bool return_on_no_detection) {

// pull 32 bits of data (if configured) from the TX fifo into osr
pio_sm_exec(dmd_pio, dmd_sm, pio_encode_pull(false, false));
pio_sm_exec(frame_pio, frame_sm, pio_encode_pull(false, false));

// Calculate display parameters
source_pixelsperbyte = 8 / source_bitsperpixel;
Expand Down Expand Up @@ -1436,6 +1474,7 @@ bool dmdreader_init(bool return_on_no_detection) {
#endif
// Finally start DMD reader PIO program and DMA
dmd_set_and_enable_new_dma_target();
pio_sm_set_enabled(frame_pio, frame_sm, true);
pio_sm_set_enabled(dmd_pio, dmd_sm, true);

return true;
Expand Down
2 changes: 2 additions & 0 deletions src/dmdreader.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ enum class Color : uint8_t {
enum DmdType : uint8_t {
DMD_UNKNOWN,
DMD_WPC,
DMD_DOTMATION,
DMD_WHITESTAR,
DMD_SPIKE1,
DMD_SAM,
Expand All @@ -28,6 +29,7 @@ enum DmdType : uint8_t {
DMD_SEGA_HD,
DMD_GOTTLIEB,
DMD_ALVING,
DMD_ROMSTAR,

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

romstar separate addition as it can't lock in like capcom (different patterns)

DMD_ISLAND,
DMD_HOMEPIN,
DMD_SPINBALL,
Expand Down
Loading