From a044b2e69d45361e3882348eedae11793fbaa0a5 Mon Sep 17 00:00:00 2001 From: Troy Schrapel Date: Fri, 28 Jun 2024 17:47:47 +0930 Subject: [PATCH 01/19] Reducing response time: From 260nS to 190nS --- src/main.c | 85 ++++++++++++++++++++++++------------------------------ 1 file changed, 37 insertions(+), 48 deletions(-) diff --git a/src/main.c b/src/main.c index 6521595..8d38fc3 100644 --- a/src/main.c +++ b/src/main.c @@ -79,49 +79,35 @@ #define TMS_CRYSTAL_FREQ_HZ 10738635.0f -#define GPIO_CD_REVERSED 0 /* unset for v0.3 PCB */ #define LED_BLINK_ON_WRITE 0 - -#if GPIO_CD_REVERSED - - /* In revision 0.2 of my prototype PCB, CD0 through CD7 are inconveniently reversed into the - Pi Pico GPIO pins. Quickest way to deal with that is this lookup table. - v0.3+ of the pico9918 hardware will have this fix in hardware, so will be removed soon - */ - -static uint8_t __aligned(4) reversed[] = -{ - 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, - 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, - 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, - 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, - 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, - 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, - 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, - 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, - 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, - 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, - 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, - 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, - 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, - 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, - 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, - 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF -}; -#define REVERSE(x) reversed[x] -#else -#define REVERSE(x) x -#endif - /* file globals */ static VrEmuTms9918* tms = NULL; /* our vrEmuTms9918 instance handle */ static uint32_t nextValue = 0; /* TMS9918A read-ahead value */ static uint32_t currentInt = GPIO_INT_MASK; /* current interrupt pin state */ +static uint32_t currentStatus = 0; /* current status register value */ static uint8_t __aligned(4) tmsScanlineBuffer[TMS9918_PIXELS_X]; + +/* + * enable gpio interrupts inline + */ +static inline void enableGpioInterrupts() +{ + *((io_rw_32*)(PPB_BASE + M0PLUS_NVIC_ICPR_OFFSET)) = 1u << IO_IRQ_BANK0; + *((io_rw_32*)(PPB_BASE + M0PLUS_NVIC_ISER_OFFSET)) = 1u << IO_IRQ_BANK0; +} + +/* + * disable gpio interrupts inline + */ +static inline void disableGpioInterrupts() +{ + *((io_rw_32*)(PPB_BASE + M0PLUS_NVIC_ICER_OFFSET)) = 1u << IO_IRQ_BANK0; +} + /* * RP2040 exclusive GPIO interrupt callback for PROC1 * Called whenever CSR or CSW changes and reads or writes the data @@ -132,16 +118,14 @@ void __time_critical_func(gpioExclusiveCallbackProc1)() { uint32_t gpios = sio_hw->gpio_in; - /* interrupt handled */ - iobank0_hw->intr[GPIO_CSR >> 3u] = iobank0_hw->proc1_irq_ctrl.ints[GPIO_CSR >> 3u]; - if ((gpios & GPIO_CSR_MASK) == 0) /* read? */ { sio_hw->gpio_oe_set = GPIO_CD_MASK; if (gpios & GPIO_MODE_MASK) /* read status register */ { - sio_hw->gpio_out = ((uint32_t)REVERSE(vrEmuTms9918ReadStatus(tms)) << GPIO_CD0) | currentInt; + sio_hw->gpio_out = currentStatus | currentInt; + currentStatus = vrEmuTms9918ReadStatus(tms) << GPIO_CD0; currentInt = GPIO_INT_MASK; } else /* read data */ @@ -152,7 +136,7 @@ void __time_critical_func(gpioExclusiveCallbackProc1)() } else if ((gpios & GPIO_CSW_MASK) == 0) /* write? */ { - uint8_t value = REVERSE((sio_hw->gpio_in >> GPIO_CD0) & 0xff); + uint8_t value = gpios >> GPIO_CD0; if (gpios & GPIO_MODE_MASK) /* write register/address */ { @@ -176,8 +160,11 @@ void __time_critical_func(gpioExclusiveCallbackProc1)() sio_hw->gpio_out = currentInt; } + /* interrupt handled */ + iobank0_hw->intr[GPIO_CSR >> 3u] = iobank0_hw->proc1_irq_ctrl.ints[GPIO_CSR >> 3u]; + /* update read-ahead */ - nextValue = REVERSE(vrEmuTms9918ReadDataNoInc(tms)) << GPIO_CD0; + nextValue = vrEmuTms9918ReadDataNoInc(tms) << GPIO_CD0; } /* @@ -268,16 +255,17 @@ static void __time_critical_func(tmsScanline)(uint16_t y, VgaParams* params, uin } /*** main display region ***/ - irq_set_mask_enabled(1u << IO_IRQ_BANK0, false); - uint8_t status = vrEmuTms9918PeekStatus(tms); - irq_set_mask_enabled(1u << IO_IRQ_BANK0, true); /* generate the scanline */ - status = vrEmuTms9918ScanLine(tms, y, tmsScanlineBuffer, status); + uint8_t newStatus = vrEmuTms9918ScanLine(tms, y, tmsScanlineBuffer, currentStatus >> GPIO_CD0) & 0x7f; + + disableGpioInterrupts(); + if (!currentInt) newStatus |= 0x80; + vrEmuTms9918SetStatus(tms, newStatus); + currentStatus = newStatus << GPIO_CD0; + enableGpioInterrupts(); + - irq_set_mask_enabled(1u << IO_IRQ_BANK0, false); - vrEmuTms9918SetStatus(tms, (status & 0x7f) | (currentInt ? 0 : 0x80)); - irq_set_mask_enabled(1u << IO_IRQ_BANK0, true); /* convert from tms palette to bgr12 */ int tmsX = 0; @@ -307,11 +295,12 @@ static void __time_critical_func(tmsScanline)(uint16_t y, VgaParams* params, uin /*** interrupt signal? ***/ if (y == TMS9918_PIXELS_Y - 1) { - irq_set_mask_enabled(1u << IO_IRQ_BANK0, false); + disableGpioInterrupts(); vrEmuTms9918InterruptSet(tms); currentInt = vrEmuTms9918InterruptStatus(tms) ? 0 : GPIO_INT_MASK; + currentStatus |= 0x80 << GPIO_CD0; gpio_put(GPIO_INT, !!currentInt); - irq_set_mask_enabled(1u << IO_IRQ_BANK0, true); + enableGpioInterrupts(); } } From 57e6d423173f1455e4558e0ed3a79d9d0c117d71 Mon Sep 17 00:00:00 2001 From: Troy Schrapel Date: Fri, 5 Jul 2024 12:32:33 +0930 Subject: [PATCH 02/19] testing gpio irq performance improvements --- src/main.c | 167 ++++++++++++++++++++++++++++++++++------------------- 1 file changed, 109 insertions(+), 58 deletions(-) diff --git a/src/main.c b/src/main.c index 8d38fc3..82e841b 100644 --- a/src/main.c +++ b/src/main.c @@ -25,6 +25,7 @@ #include "hardware/pio.h" #include "hardware/clocks.h" +#include "hardware/vreg.h" #include @@ -87,6 +88,8 @@ static VrEmuTms9918* tms = NULL; /* our vrEmuTms9918 instance handle */ static uint32_t nextValue = 0; /* TMS9918A read-ahead value */ static uint32_t currentInt = GPIO_INT_MASK; /* current interrupt pin state */ static uint32_t currentStatus = 0; /* current status register value */ +static uint32_t currentStatusInt = 0; /* TMS9918A read-ahead value */ +static uint32_t nextValueInt = 0; /* TMS9918A read-ahead value */ static uint8_t __aligned(4) tmsScanlineBuffer[TMS9918_PIXELS_X]; @@ -114,38 +117,73 @@ static inline void disableGpioInterrupts() * * Note: This needs to be extremely responsive. No dilly-dallying here. */ +void test_irq(); + +#define XXX (&iobank0_hw->intr - iobank0_hw) + void __time_critical_func(gpioExclusiveCallbackProc1)() { - uint32_t gpios = sio_hw->gpio_in; - - if ((gpios & GPIO_CSR_MASK) == 0) /* read? */ + switch ((sio_hw->gpio_in >> GPIO_CSR) & 0x07) { - sio_hw->gpio_oe_set = GPIO_CD_MASK; - - if (gpios & GPIO_MODE_MASK) /* read status register */ - { - sio_hw->gpio_out = currentStatus | currentInt; + case 1: + iobank0_hw->intr[GPIO_CSR >> 3u] = iobank0_hw->proc1_irq_ctrl.ints[GPIO_CSR >> 3u]; + vrEmuTms9918WriteData(tms, sio_hw->gpio_in >> GPIO_CD0); + break; + case 2: + sio_hw->gpio_oe_set = GPIO_CD_MASK; + sio_hw->gpio_out = nextValueInt; + iobank0_hw->intr[GPIO_CSR >> 3u] = iobank0_hw->proc1_irq_ctrl.ints[GPIO_CSR >> 3u]; + vrEmuTms9918ReadData(tms); + break; + case 5: + iobank0_hw->intr[GPIO_CSR >> 3u] = iobank0_hw->proc1_irq_ctrl.ints[GPIO_CSR >> 3u]; + vrEmuTms9918WriteAddr(tms, sio_hw->gpio_in >> GPIO_CD0); + currentInt = vrEmuTms9918InterruptStatus(tms) ? 0 : GPIO_INT_MASK; + currentStatusInt = currentStatus | currentInt; + break; + case 6: + sio_hw->gpio_oe_set = GPIO_CD_MASK; + sio_hw->gpio_out = currentStatus | GPIO_INT_MASK; + iobank0_hw->intr[GPIO_CSR >> 3u] = iobank0_hw->proc1_irq_ctrl.ints[GPIO_CSR >> 3u]; currentStatus = vrEmuTms9918ReadStatus(tms) << GPIO_CD0; currentInt = GPIO_INT_MASK; - } - else /* read data */ + currentStatusInt = currentStatus | currentInt; + break; + default: + iobank0_hw->intr[GPIO_CSR >> 3u] = iobank0_hw->proc1_irq_ctrl.ints[GPIO_CSR >> 3u]; + break; + } + /* + if ((gpio & GPIO_CSR_MASK) == 0) // read? + { + //iobank0_hw->intr[GPIO_CSR >> 3u] = iobank0_hw->proc1_irq_ctrl.ints[GPIO_CSR >> 3u] & (GPIO_IRQ_EDGE_FALL << ((GPIO_CSR & 0x07) << 2)); + sio_hw->gpio_oe_set = GPIO_CD_MASK; + + if (gpio & GPIO_MODE_MASK) // read status register + { + sio_hw->gpio_out = currentStatus | currentInt; + currentStatus = vrEmuTms9918ReadStatus(tms) << GPIO_CD0; + currentInt = GPIO_INT_MASK; + } + else // read data { sio_hw->gpio_out = nextValue | currentInt; vrEmuTms9918ReadData(tms); } - } - else if ((gpios & GPIO_CSW_MASK) == 0) /* write? */ +} + else if ((gpio & GPIO_CSW_MASK) == 0) // write? { - uint8_t value = gpios >> GPIO_CD0; + //iobank0_hw->intr[GPIO_CSW >> 3u] = iobank0_hw->proc1_irq_ctrl.ints[GPIO_CSW >> 3u] & (GPIO_IRQ_EDGE_FALL << ((GPIO_CSW & 0x07) << 2)); + uint8_t value = gpio >> GPIO_CD0; - if (gpios & GPIO_MODE_MASK) /* write register/address */ + if (gpio & GPIO_MODE_MASK) // write register/address { vrEmuTms9918WriteAddr(tms, value); currentInt = vrEmuTms9918InterruptStatus(tms) ? 0 : GPIO_INT_MASK; sio_hw->gpio_out = nextValue | currentInt; } - else /* write data */ + else // write data { vrEmuTms9918WriteData(tms, value); @@ -154,43 +192,21 @@ void __time_critical_func(gpioExclusiveCallbackProc1)() #endif } } - else /* both CSR and CSW are high (inactive). Go High-Z */ + else // both CSR and CSW are high (inactive). Go High-Z { - sio_hw->gpio_oe_clr = GPIO_CD_MASK; - sio_hw->gpio_out = currentInt; - } + //iobank0_hw->intr[GPIO_CSR >> 3u] = iobank0_hw->proc1_irq_ctrl.ints[GPIO_CSR >> 3u] +// & ((GPIO_IRQ_EDGE_RISE << ((GPIO_CSR & 0x07) << 2)) & (GPIO_IRQ_EDGE_RISE << ((GPIO_CSW & 0x07) << 2))); - /* interrupt handled */ - iobank0_hw->intr[GPIO_CSR >> 3u] = iobank0_hw->proc1_irq_ctrl.ints[GPIO_CSR >> 3u]; +sio_hw->gpio_oe_clr = GPIO_CD_MASK; +sio_hw->gpio_out = currentInt; + } + */ /* update read-ahead */ nextValue = vrEmuTms9918ReadDataNoInc(tms) << GPIO_CD0; + nextValueInt = nextValue | currentInt; } -/* - * 2nd CPU core (proc1) entry - */ -void proc1Entry() -{ - // set up gpio pins - gpio_init_mask(GPIO_CD_MASK | GPIO_CSR_MASK | GPIO_CSW_MASK | GPIO_MODE_MASK | GPIO_INT_MASK | GPIO_LED_MASK); - gpio_put_all(GPIO_INT_MASK); - gpio_set_dir_all_bits(GPIO_INT_MASK | GPIO_LED_MASK); // int is an output - - // ensure CSR and CSW are high (inactive) - while (!gpio_get(GPIO_CSW) || !gpio_get(GPIO_CSR)) - tight_loop_contents(); - - // set up gpio interrupts - irq_set_exclusive_handler(IO_IRQ_BANK0, gpioExclusiveCallbackProc1); - gpio_set_irq_enabled(GPIO_CSW, GPIO_IRQ_EDGE_FALL | GPIO_IRQ_EDGE_RISE, true); - gpio_set_irq_enabled(GPIO_CSR, GPIO_IRQ_EDGE_FALL | GPIO_IRQ_EDGE_RISE, true); - irq_set_enabled(IO_IRQ_BANK0, true); - - // wait until everything else is ready, then run the vga loop - multicore_fifo_pop_blocking(); - vgaLoop(); -} /* * generate a single VGA scanline (called by vgaLoop(), runs on proc1) @@ -258,12 +274,15 @@ static void __time_critical_func(tmsScanline)(uint16_t y, VgaParams* params, uin /* generate the scanline */ uint8_t newStatus = vrEmuTms9918ScanLine(tms, y, tmsScanlineBuffer, currentStatus >> GPIO_CD0) & 0x7f; - - disableGpioInterrupts(); - if (!currentInt) newStatus |= 0x80; - vrEmuTms9918SetStatus(tms, newStatus); - currentStatus = newStatus << GPIO_CD0; - enableGpioInterrupts(); + // if (vrEmuTms9918DisplayEnabled(tms)) + { + // disableGpioInterrupts(); + if (!currentInt) newStatus |= 0x80; + vrEmuTms9918SetStatus(tms, newStatus); + currentStatus = newStatus << GPIO_CD0; + currentStatusInt = currentStatus | currentInt; + // enableGpioInterrupts(); + } @@ -295,12 +314,14 @@ static void __time_critical_func(tmsScanline)(uint16_t y, VgaParams* params, uin /*** interrupt signal? ***/ if (y == TMS9918_PIXELS_Y - 1) { - disableGpioInterrupts(); + //disableGpioInterrupts(); vrEmuTms9918InterruptSet(tms); currentInt = vrEmuTms9918InterruptStatus(tms) ? 0 : GPIO_INT_MASK; currentStatus |= 0x80 << GPIO_CD0; + currentStatusInt = currentStatus | currentInt; + nextValueInt = nextValue | currentInt; gpio_put(GPIO_INT, !!currentInt); - enableGpioInterrupts(); + //enableGpioInterrupts(); } } @@ -329,6 +350,37 @@ uint initClock(uint gpio, float freqHz) return clkSm; } + +/* + * 2nd CPU core (proc1) entry + */ +void proc1Entry() +{ + // set up gpio pins + gpio_init_mask(GPIO_CD_MASK | GPIO_CSR_MASK | GPIO_CSW_MASK | GPIO_MODE_MASK | GPIO_INT_MASK | GPIO_LED_MASK); + gpio_put_all(GPIO_INT_MASK); + gpio_set_dir_all_bits(GPIO_INT_MASK | GPIO_LED_MASK); // int is an output + + // ensure CSR and CSW are high (inactive) + while (!gpio_get(GPIO_CSW) || !gpio_get(GPIO_CSR)) + tight_loop_contents(); + + // set up gpio interrupts + irq_set_exclusive_handler(IO_IRQ_BANK0, gpioExclusiveCallbackProc1); + gpio_set_irq_enabled(GPIO_CSW, GPIO_IRQ_EDGE_FALL | GPIO_IRQ_EDGE_RISE, true); + gpio_set_irq_enabled(GPIO_CSR, GPIO_IRQ_EDGE_FALL | GPIO_IRQ_EDGE_RISE, true); + irq_set_enabled(IO_IRQ_BANK0, true); + irq_set_priority(IO_IRQ_BANK0, 0); + + // set up the GROMCLK and CPUCLK signals + initClock(GPIO_GROMCL, TMS_CRYSTAL_FREQ_HZ / 24.0f); + initClock(GPIO_CPUCL, TMS_CRYSTAL_FREQ_HZ / 3.0f); + + // wait until everything else is ready, then run the vga loop + multicore_fifo_pop_blocking(); + vgaLoop(); +} + /* * main entry point */ @@ -338,7 +390,10 @@ int main(void) * that comes close to being divisible by 25.175MHz. 252.0 is close... enough :) * I do have code which sets the best clock baased on the chosen VGA mode, * but this'll do for now. */ - set_sys_clock_khz(252000, false); + vreg_set_voltage(VREG_VOLTAGE_1_25); + set_sys_clock_khz(315000, false); + //set_sys_clock_khz(252000, false); + //set_sys_clock_khz(126000, false); /* we need one of these. it's the main guy */ tms = vrEmuTms9918New(); @@ -346,10 +401,6 @@ int main(void) /* set up the GPIO pins and interrupt handler */ multicore_launch_core1(proc1Entry); - /* set up the GROMCLK and CPUCLK signals */ - initClock(GPIO_GROMCL, TMS_CRYSTAL_FREQ_HZ / 24.0f); - initClock(GPIO_CPUCL, TMS_CRYSTAL_FREQ_HZ / 3.0f); - /* then set up VGA output */ VgaInitParams params = { 0 }; params.params = vgaGetParams(VGA_640_480_60HZ); From 45eb62abb99c9276e91dcb06eab7c3752d56aefb Mon Sep 17 00:00:00 2001 From: Troy Schrapel Date: Fri, 5 Jul 2024 17:48:14 +0930 Subject: [PATCH 03/19] clean up gpioExclusiveCallbackProc1() --- src/main.c | 81 ++++++++++++------------------------------------------ 1 file changed, 18 insertions(+), 63 deletions(-) diff --git a/src/main.c b/src/main.c index 82e841b..116bb0a 100644 --- a/src/main.c +++ b/src/main.c @@ -78,6 +78,11 @@ #define GPIO_INT_MASK (0x01 << GPIO_INT) #define GPIO_LED_MASK (0x01 << GPIO_LED) +#define GPIO_IRQ_READ_DATA ((GPIO_CSW_MASK) >> GPIO_CSR) +#define GPIO_IRQ_READ_STATUS ((GPIO_CSW_MASK | GPIO_MODE_MASK) >> GPIO_CSR) +#define GPIO_IRQ_WRITE_DATA ((GPIO_CSR_MASK) >> GPIO_CSR) +#define GPIO_IRQ_WRITE_REG ((GPIO_CSR_MASK | GPIO_MODE_MASK) >> GPIO_CSR) + #define TMS_CRYSTAL_FREQ_HZ 10738635.0f #define LED_BLINK_ON_WRITE 0 @@ -88,7 +93,6 @@ static VrEmuTms9918* tms = NULL; /* our vrEmuTms9918 instance handle */ static uint32_t nextValue = 0; /* TMS9918A read-ahead value */ static uint32_t currentInt = GPIO_INT_MASK; /* current interrupt pin state */ static uint32_t currentStatus = 0; /* current status register value */ -static uint32_t currentStatusInt = 0; /* TMS9918A read-ahead value */ static uint32_t nextValueInt = 0; /* TMS9918A read-ahead value */ static uint8_t __aligned(4) tmsScanlineBuffer[TMS9918_PIXELS_X]; @@ -117,90 +121,43 @@ static inline void disableGpioInterrupts() * * Note: This needs to be extremely responsive. No dilly-dallying here. */ -void test_irq(); - -#define XXX (&iobank0_hw->intr - iobank0_hw) void __time_critical_func(gpioExclusiveCallbackProc1)() { switch ((sio_hw->gpio_in >> GPIO_CSR) & 0x07) { - case 1: + case GPIO_IRQ_WRITE_DATA: iobank0_hw->intr[GPIO_CSR >> 3u] = iobank0_hw->proc1_irq_ctrl.ints[GPIO_CSR >> 3u]; vrEmuTms9918WriteData(tms, sio_hw->gpio_in >> GPIO_CD0); break; - case 2: + + case GPIO_IRQ_READ_DATA: sio_hw->gpio_oe_set = GPIO_CD_MASK; sio_hw->gpio_out = nextValueInt; iobank0_hw->intr[GPIO_CSR >> 3u] = iobank0_hw->proc1_irq_ctrl.ints[GPIO_CSR >> 3u]; vrEmuTms9918ReadData(tms); break; - case 5: + + case GPIO_IRQ_WRITE_REG: iobank0_hw->intr[GPIO_CSR >> 3u] = iobank0_hw->proc1_irq_ctrl.ints[GPIO_CSR >> 3u]; vrEmuTms9918WriteAddr(tms, sio_hw->gpio_in >> GPIO_CD0); currentInt = vrEmuTms9918InterruptStatus(tms) ? 0 : GPIO_INT_MASK; - currentStatusInt = currentStatus | currentInt; break; - case 6: + + case GPIO_IRQ_READ_STATUS: sio_hw->gpio_oe_set = GPIO_CD_MASK; sio_hw->gpio_out = currentStatus | GPIO_INT_MASK; iobank0_hw->intr[GPIO_CSR >> 3u] = iobank0_hw->proc1_irq_ctrl.ints[GPIO_CSR >> 3u]; currentStatus = vrEmuTms9918ReadStatus(tms) << GPIO_CD0; currentInt = GPIO_INT_MASK; - currentStatusInt = currentStatus | currentInt; break; + default: iobank0_hw->intr[GPIO_CSR >> 3u] = iobank0_hw->proc1_irq_ctrl.ints[GPIO_CSR >> 3u]; + sio_hw->gpio_oe_clr = GPIO_CD_MASK; + sio_hw->gpio_out = currentInt; break; } - /* - if ((gpio & GPIO_CSR_MASK) == 0) // read? - { - //iobank0_hw->intr[GPIO_CSR >> 3u] = iobank0_hw->proc1_irq_ctrl.ints[GPIO_CSR >> 3u] & (GPIO_IRQ_EDGE_FALL << ((GPIO_CSR & 0x07) << 2)); - sio_hw->gpio_oe_set = GPIO_CD_MASK; - - if (gpio & GPIO_MODE_MASK) // read status register - { - sio_hw->gpio_out = currentStatus | currentInt; - currentStatus = vrEmuTms9918ReadStatus(tms) << GPIO_CD0; - currentInt = GPIO_INT_MASK; - } - else // read data - { - sio_hw->gpio_out = nextValue | currentInt; - vrEmuTms9918ReadData(tms); - } -} - else if ((gpio & GPIO_CSW_MASK) == 0) // write? - { - //iobank0_hw->intr[GPIO_CSW >> 3u] = iobank0_hw->proc1_irq_ctrl.ints[GPIO_CSW >> 3u] & (GPIO_IRQ_EDGE_FALL << ((GPIO_CSW & 0x07) << 2)); - uint8_t value = gpio >> GPIO_CD0; - - if (gpio & GPIO_MODE_MASK) // write register/address - { - vrEmuTms9918WriteAddr(tms, value); - - currentInt = vrEmuTms9918InterruptStatus(tms) ? 0 : GPIO_INT_MASK; - sio_hw->gpio_out = nextValue | currentInt; - } - else // write data - { - vrEmuTms9918WriteData(tms, value); - -#if LED_BLINK_ON_WRITE - sio_hw->gpio_out = GPIO_LED_MASK | currentInt; -#endif - } - } - else // both CSR and CSW are high (inactive). Go High-Z - { - //iobank0_hw->intr[GPIO_CSR >> 3u] = iobank0_hw->proc1_irq_ctrl.ints[GPIO_CSR >> 3u] -// & ((GPIO_IRQ_EDGE_RISE << ((GPIO_CSR & 0x07) << 2)) & (GPIO_IRQ_EDGE_RISE << ((GPIO_CSW & 0x07) << 2))); - -sio_hw->gpio_oe_clr = GPIO_CD_MASK; -sio_hw->gpio_out = currentInt; - } - */ /* update read-ahead */ nextValue = vrEmuTms9918ReadDataNoInc(tms) << GPIO_CD0; @@ -280,7 +237,6 @@ static void __time_critical_func(tmsScanline)(uint16_t y, VgaParams* params, uin if (!currentInt) newStatus |= 0x80; vrEmuTms9918SetStatus(tms, newStatus); currentStatus = newStatus << GPIO_CD0; - currentStatusInt = currentStatus | currentInt; // enableGpioInterrupts(); } @@ -318,7 +274,6 @@ static void __time_critical_func(tmsScanline)(uint16_t y, VgaParams* params, uin vrEmuTms9918InterruptSet(tms); currentInt = vrEmuTms9918InterruptStatus(tms) ? 0 : GPIO_INT_MASK; currentStatus |= 0x80 << GPIO_CD0; - currentStatusInt = currentStatus | currentInt; nextValueInt = nextValue | currentInt; gpio_put(GPIO_INT, !!currentInt); //enableGpioInterrupts(); @@ -390,9 +345,9 @@ int main(void) * that comes close to being divisible by 25.175MHz. 252.0 is close... enough :) * I do have code which sets the best clock baased on the chosen VGA mode, * but this'll do for now. */ - vreg_set_voltage(VREG_VOLTAGE_1_25); - set_sys_clock_khz(315000, false); - //set_sys_clock_khz(252000, false); + vreg_set_voltage(VREG_VOLTAGE_1_20); + //set_sys_clock_khz(315000, false); + set_sys_clock_khz(252000, false); //set_sys_clock_khz(126000, false); /* we need one of these. it's the main guy */ From 3beb347d937495d3d2bce116dbcb067b62a3e520 Mon Sep 17 00:00:00 2001 From: Troy Schrapel Date: Sat, 6 Jul 2024 10:21:44 +0930 Subject: [PATCH 04/19] cleaned up gpioExclusiveCallbackProc1() performance improvements --- CMakeLists.txt | 3 ++- src/CMakeLists.txt | 2 ++ src/main.c | 62 ++++++++++++++++++++++++++------------------- src/vga/vga.c | 19 +++++++------- tools/img2carray.py | 7 ++++- 5 files changed, 56 insertions(+), 37 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5711eb3..fa6a3b4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,7 +12,8 @@ set(PROJECT pico9918) project(${PROJECT} C CXX) -add_definitions(-DPICO_BUILD) +add_definitions(-DPICO_BUILD=1) +add_definitions(-DPICO_DISABLE_SHARED_IRQ_HANDLERS=1) set(CMAKE_C_STANDARD 11) set(CMAKE_CXX_STANDARD 17) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3bf9a56..a338844 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -16,6 +16,8 @@ pico_add_extra_outputs(${PROGRAM}) pico_enable_stdio_usb(${PROGRAM} 0) pico_enable_stdio_uart(${PROGRAM} 0) +add_compile_options(-O3 -g3) + #pico_set_binary_type(${PROGRAM} copy_to_ram) # TOO SLOW TO BOOT target_link_libraries(${PROGRAM} PUBLIC diff --git a/src/main.c b/src/main.c index 116bb0a..e5c8d0a 100644 --- a/src/main.c +++ b/src/main.c @@ -78,11 +78,6 @@ #define GPIO_INT_MASK (0x01 << GPIO_INT) #define GPIO_LED_MASK (0x01 << GPIO_LED) -#define GPIO_IRQ_READ_DATA ((GPIO_CSW_MASK) >> GPIO_CSR) -#define GPIO_IRQ_READ_STATUS ((GPIO_CSW_MASK | GPIO_MODE_MASK) >> GPIO_CSR) -#define GPIO_IRQ_WRITE_DATA ((GPIO_CSR_MASK) >> GPIO_CSR) -#define GPIO_IRQ_WRITE_REG ((GPIO_CSR_MASK | GPIO_MODE_MASK) >> GPIO_CSR) - #define TMS_CRYSTAL_FREQ_HZ 10738635.0f #define LED_BLINK_ON_WRITE 0 @@ -115,31 +110,55 @@ static inline void disableGpioInterrupts() *((io_rw_32*)(PPB_BASE + M0PLUS_NVIC_ICER_OFFSET)) = 1u << IO_IRQ_BANK0; } +/* + * mark the interrupt as handled. + * Note: all relevant GPIO pins are in the same group + */ +static inline void gpioInterruptHandled() +{ + iobank0_hw->intr[GPIO_CSR >> 3u] = iobank0_hw->proc1_irq_ctrl.ints[GPIO_CSR >> 3u]; +} + /* * RP2040 exclusive GPIO interrupt callback for PROC1 * Called whenever CSR or CSW changes and reads or writes the data * * Note: This needs to be extremely responsive. No dilly-dallying here. + * This function isn't pretty, but is done this way to guide + * gcc in producing optimized assembly. I considered writing + * this in assembly, but decided on this instead. */ void __time_critical_func(gpioExclusiveCallbackProc1)() { + + // shift the bit mask combos down to lowest significant bits + const int GPIO_IRQ_READ_DATA = (GPIO_CSW_MASK) >> GPIO_CSR; + const int GPIO_IRQ_READ_STATUS = (GPIO_CSW_MASK | GPIO_MODE_MASK) >> GPIO_CSR; + const int GPIO_IRQ_WRITE_DATA = (GPIO_CSR_MASK) >> GPIO_CSR; + const int GPIO_IRQ_WRITE_REG = (GPIO_CSR_MASK | GPIO_MODE_MASK) >> GPIO_CSR; + + // test the MODE, /CSW and /CSR pins to determine which action to take + // shifting them down to bits 2, 1 and 0 respectively rather than testing + // them at the higher bit range since ARM can only load 8-bit immediate + // values in one cycle to compare + switch ((sio_hw->gpio_in >> GPIO_CSR) & 0x07) { case GPIO_IRQ_WRITE_DATA: - iobank0_hw->intr[GPIO_CSR >> 3u] = iobank0_hw->proc1_irq_ctrl.ints[GPIO_CSR >> 3u]; + gpioInterruptHandled(); vrEmuTms9918WriteData(tms, sio_hw->gpio_in >> GPIO_CD0); break; case GPIO_IRQ_READ_DATA: sio_hw->gpio_oe_set = GPIO_CD_MASK; sio_hw->gpio_out = nextValueInt; - iobank0_hw->intr[GPIO_CSR >> 3u] = iobank0_hw->proc1_irq_ctrl.ints[GPIO_CSR >> 3u]; + gpioInterruptHandled(); vrEmuTms9918ReadData(tms); break; case GPIO_IRQ_WRITE_REG: - iobank0_hw->intr[GPIO_CSR >> 3u] = iobank0_hw->proc1_irq_ctrl.ints[GPIO_CSR >> 3u]; + gpioInterruptHandled(); vrEmuTms9918WriteAddr(tms, sio_hw->gpio_in >> GPIO_CD0); currentInt = vrEmuTms9918InterruptStatus(tms) ? 0 : GPIO_INT_MASK; break; @@ -147,13 +166,13 @@ void __time_critical_func(gpioExclusiveCallbackProc1)() case GPIO_IRQ_READ_STATUS: sio_hw->gpio_oe_set = GPIO_CD_MASK; sio_hw->gpio_out = currentStatus | GPIO_INT_MASK; - iobank0_hw->intr[GPIO_CSR >> 3u] = iobank0_hw->proc1_irq_ctrl.ints[GPIO_CSR >> 3u]; + gpioInterruptHandled(); currentStatus = vrEmuTms9918ReadStatus(tms) << GPIO_CD0; currentInt = GPIO_INT_MASK; break; default: - iobank0_hw->intr[GPIO_CSR >> 3u] = iobank0_hw->proc1_irq_ctrl.ints[GPIO_CSR >> 3u]; + gpioInterruptHandled(); sio_hw->gpio_oe_clr = GPIO_CD_MASK; sio_hw->gpio_out = currentInt; break; @@ -172,6 +191,7 @@ static void __time_critical_func(tmsScanline)(uint16_t y, VgaParams* params, uin { #if 1 + // better compile-time optimizations if we hard-code these #define VIRTUAL_PIXELS_X 640 #define VIRTUAL_PIXELS_Y 240 #else @@ -193,7 +213,6 @@ static void __time_critical_func(tmsScanline)(uint16_t y, VgaParams* params, uin pixels[x] = bg; } - /* source: C:/Users/troy/OneDrive/Documents/projects/pico9918/src/res/splash.png * size : 172px x 10px * : 3440 bytes @@ -202,10 +221,10 @@ static void __time_critical_func(tmsScanline)(uint16_t y, VgaParams* params, uin if (y >= vBorder + TMS9918_PIXELS_Y + 12) { y -= vBorder + TMS9918_PIXELS_Y + 12; - if (y < 10) + if (y < splashHeight) { - uint16_t* splashPtr = splash + (y * 172); - for (int x = 4; x < 4 + 172; ++x) + uint16_t* splashPtr = splash + (y * splashWidth); + for (int x = 4; x < 4 + splashWidth; ++x) { uint16_t c = *(splashPtr++); if (c & 0xf000) @@ -231,16 +250,9 @@ static void __time_critical_func(tmsScanline)(uint16_t y, VgaParams* params, uin /* generate the scanline */ uint8_t newStatus = vrEmuTms9918ScanLine(tms, y, tmsScanlineBuffer, currentStatus >> GPIO_CD0) & 0x7f; - // if (vrEmuTms9918DisplayEnabled(tms)) - { - // disableGpioInterrupts(); - if (!currentInt) newStatus |= 0x80; - vrEmuTms9918SetStatus(tms, newStatus); - currentStatus = newStatus << GPIO_CD0; - // enableGpioInterrupts(); - } - - + if (!currentInt) newStatus |= 0x80; + vrEmuTms9918SetStatus(tms, newStatus); + currentStatus = newStatus << GPIO_CD0; /* convert from tms palette to bgr12 */ int tmsX = 0; @@ -270,13 +282,11 @@ static void __time_critical_func(tmsScanline)(uint16_t y, VgaParams* params, uin /*** interrupt signal? ***/ if (y == TMS9918_PIXELS_Y - 1) { - //disableGpioInterrupts(); vrEmuTms9918InterruptSet(tms); currentInt = vrEmuTms9918InterruptStatus(tms) ? 0 : GPIO_INT_MASK; currentStatus |= 0x80 << GPIO_CD0; nextValueInt = nextValue | currentInt; gpio_put(GPIO_INT, !!currentInt); - //enableGpioInterrupts(); } } diff --git a/src/vga/vga.c b/src/vga/vga.c index 627fbb2..49ed193 100644 --- a/src/vga/vga.c +++ b/src/vga/vga.c @@ -38,10 +38,9 @@ #define END_OF_SCANLINE_MSG 0x40000000 #define END_OF_FRAME_MSG 0x80000000 -#define CRT_EFFECT 0 +#define CRT_EFFECT 1 #define SCANLINE_TIME_DEBUG 0 - /* * sync pio dma data buffers */ @@ -89,7 +88,6 @@ static bool buildSyncData() if (sysClockKHz < minClockKHz) { - printf("Error: System clock is %d KHz. Minimum required is %d KHz\n", sysClockKHz, minClockKHz); return false; } @@ -291,14 +289,14 @@ static void __time_critical_func(dmaIrqHandler)(void) divmod_result_t pxLineVal = divmod_u32u32(currentDisplayLine++, vgaParams.params.vPixelScale); uint32_t pxLine = to_quotient_u32(pxLineVal); uint32_t pxLineRpt = to_remainder_u32(pxLineVal); - uint16_t* currentBuffer = rgbDataBuffer[pxLine & 0x01]; + uint32_t* currentBuffer = (uint32_t*)rgbDataBuffer[pxLine & 0x01]; #if CRT_EFFECT if (pxLineRpt != 0) { - for (int i = 0; i < 10; ++i) + for (int i = 0; i < 5; ++i) { - currentBuffer[i] = (currentBuffer[i] >> 1) & 0x0777; + currentBuffer[i] = (currentBuffer[i] >> 1) & 0x07770777; } } #endif @@ -306,7 +304,7 @@ static void __time_critical_func(dmaIrqHandler)(void) #if SCANLINE_TIME_DEBUG if (pxLineRpt != 0 && hasRenderedNext) { - currentBuffer = rgbDataBuffer[2]; + currentBuffer = (uint32_t*)rgbDataBuffer[2]; } #endif @@ -332,9 +330,10 @@ static void __time_critical_func(dmaIrqHandler)(void) #if CRT_EFFECT else { - for (int i = 10; i < vgaParams.params.hVirtualPixels; ++i) + int end = vgaParams.params.hVirtualPixels / 2; + for (int i = 5; i < end; ++i) { - currentBuffer[i] = (currentBuffer[i] >> 1) & 0x0777; + currentBuffer[i] = (currentBuffer[i] >> 1) & 0x07770777; } } #endif @@ -353,6 +352,7 @@ static void initDma() dma_channel_start(rgbDmaChan); } + /* * main vga loop */ @@ -363,6 +363,7 @@ void __time_critical_func(vgaLoop)() vgaParams.initFn(); } + uint64_t frameNumber = 0; while (1) { diff --git a/tools/img2carray.py b/tools/img2carray.py index 9e08c5b..c0c20ac 100644 --- a/tools/img2carray.py +++ b/tools/img2carray.py @@ -117,7 +117,7 @@ def getFileHeader(fileName, romFileList, ramFileList, args, isHeaderFile) -> str if isHeaderFile: baseName = args['prefix'] + "_" + os.path.basename(fileName) sanitizedFile = re.sub('[^0-9a-zA-Z]+', '_', baseName.upper()) - hdrText += f"#pragma once\n\n" + hdrText += f"#pragma once\n" else: hdrText += "#include \"pico/platform.h\"\n" hdrText += "#include " @@ -195,6 +195,11 @@ def generateProto(varName, src, inRam, isHeader) -> str: # output the image array prototype proto += typePrefix + dataType + varNamePrefix + varName + "[]" + suffix + if isHeader: + proto += "\nconst int " + varNamePrefix + varName + \ + "Width = " + str(src.width) + ";\n" + proto += "const int " + varNamePrefix + varName + \ + "Height = " + str(src.height) + ";\n" return proto From 552d08bf6040a2631d76b571f603a62a253629a2 Mon Sep 17 00:00:00 2001 From: Troy Schrapel Date: Sat, 6 Jul 2024 13:08:46 +0930 Subject: [PATCH 05/19] Move to single-instance vrEmuTms9918 --- CMakeLists.txt | 1 + src/main.c | 27 +++++++++++++-------------- submodules/vrEmuTms9918 | 2 +- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fa6a3b4..020038c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,6 +14,7 @@ project(${PROJECT} C CXX) add_definitions(-DPICO_BUILD=1) add_definitions(-DPICO_DISABLE_SHARED_IRQ_HANDLERS=1) +add_definitions(-DVR_EMU_TMS9918_SINGLE_INSTANCE=1) set(CMAKE_C_STANDARD 11) set(CMAKE_CXX_STANDARD 17) diff --git a/src/main.c b/src/main.c index e5c8d0a..a143d3c 100644 --- a/src/main.c +++ b/src/main.c @@ -84,7 +84,6 @@ /* file globals */ -static VrEmuTms9918* tms = NULL; /* our vrEmuTms9918 instance handle */ static uint32_t nextValue = 0; /* TMS9918A read-ahead value */ static uint32_t currentInt = GPIO_INT_MASK; /* current interrupt pin state */ static uint32_t currentStatus = 0; /* current status register value */ @@ -147,27 +146,27 @@ void __time_critical_func(gpioExclusiveCallbackProc1)() { case GPIO_IRQ_WRITE_DATA: gpioInterruptHandled(); - vrEmuTms9918WriteData(tms, sio_hw->gpio_in >> GPIO_CD0); + vrEmuTms9918WriteData(sio_hw->gpio_in >> GPIO_CD0); break; case GPIO_IRQ_READ_DATA: sio_hw->gpio_oe_set = GPIO_CD_MASK; sio_hw->gpio_out = nextValueInt; gpioInterruptHandled(); - vrEmuTms9918ReadData(tms); + vrEmuTms9918ReadData(); break; case GPIO_IRQ_WRITE_REG: gpioInterruptHandled(); - vrEmuTms9918WriteAddr(tms, sio_hw->gpio_in >> GPIO_CD0); - currentInt = vrEmuTms9918InterruptStatus(tms) ? 0 : GPIO_INT_MASK; + vrEmuTms9918WriteAddr(sio_hw->gpio_in >> GPIO_CD0); + currentInt = vrEmuTms9918InterruptStatus() ? 0 : GPIO_INT_MASK; break; case GPIO_IRQ_READ_STATUS: sio_hw->gpio_oe_set = GPIO_CD_MASK; sio_hw->gpio_out = currentStatus | GPIO_INT_MASK; gpioInterruptHandled(); - currentStatus = vrEmuTms9918ReadStatus(tms) << GPIO_CD0; + currentStatus = vrEmuTms9918ReadStatus() << GPIO_CD0; currentInt = GPIO_INT_MASK; break; @@ -179,7 +178,7 @@ void __time_critical_func(gpioExclusiveCallbackProc1)() } /* update read-ahead */ - nextValue = vrEmuTms9918ReadDataNoInc(tms) << GPIO_CD0; + nextValue = vrEmuTms9918ReadDataNoInc() << GPIO_CD0; nextValueInt = nextValue | currentInt; } @@ -203,7 +202,7 @@ static void __time_critical_func(tmsScanline)(uint16_t y, VgaParams* params, uin const uint32_t vBorder = (VIRTUAL_PIXELS_Y - TMS9918_PIXELS_Y) / 2; const uint32_t hBorder = (VIRTUAL_PIXELS_X - TMS9918_PIXELS_X * 2) / 2; - uint16_t bg = tms9918PaletteBGR12[vrEmuTms9918RegValue(tms, TMS_REG_FG_BG_COLOR) & 0x0f]; + uint16_t bg = tms9918PaletteBGR12[vrEmuTms9918RegValue(TMS_REG_FG_BG_COLOR) & 0x0f]; /*** top and bottom borders ***/ if (y < vBorder || y >= (vBorder + TMS9918_PIXELS_Y)) @@ -249,12 +248,12 @@ static void __time_critical_func(tmsScanline)(uint16_t y, VgaParams* params, uin /*** main display region ***/ /* generate the scanline */ - uint8_t newStatus = vrEmuTms9918ScanLine(tms, y, tmsScanlineBuffer, currentStatus >> GPIO_CD0) & 0x7f; + uint8_t newStatus = vrEmuTms9918ScanLine(y, tmsScanlineBuffer, currentStatus >> GPIO_CD0) & 0x7f; if (!currentInt) newStatus |= 0x80; - vrEmuTms9918SetStatus(tms, newStatus); + vrEmuTms9918SetStatus(newStatus); currentStatus = newStatus << GPIO_CD0; - /* convert from tms palette to bgr12 */ + /* convert from palette to bgr12 */ int tmsX = 0; if (tmsScanlineBuffer[0] & 0xf0) { @@ -282,8 +281,8 @@ static void __time_critical_func(tmsScanline)(uint16_t y, VgaParams* params, uin /*** interrupt signal? ***/ if (y == TMS9918_PIXELS_Y - 1) { - vrEmuTms9918InterruptSet(tms); - currentInt = vrEmuTms9918InterruptStatus(tms) ? 0 : GPIO_INT_MASK; + vrEmuTms9918InterruptSet(); + currentInt = vrEmuTms9918InterruptStatus() ? 0 : GPIO_INT_MASK; currentStatus |= 0x80 << GPIO_CD0; nextValueInt = nextValue | currentInt; gpio_put(GPIO_INT, !!currentInt); @@ -361,7 +360,7 @@ int main(void) //set_sys_clock_khz(126000, false); /* we need one of these. it's the main guy */ - tms = vrEmuTms9918New(); + vrEmuTms9918Init(); /* set up the GPIO pins and interrupt handler */ multicore_launch_core1(proc1Entry); diff --git a/submodules/vrEmuTms9918 b/submodules/vrEmuTms9918 index 98e53a5..48e0e0f 160000 --- a/submodules/vrEmuTms9918 +++ b/submodules/vrEmuTms9918 @@ -1 +1 @@ -Subproject commit 98e53a51120648c22cb568f4ff6cc2b78ba12cf1 +Subproject commit 48e0e0f45b4bf445372b13c11816b8248df3c570 From 5d5be11b7a0d575a5fb8c23578787cd7811cd4db Mon Sep 17 00:00:00 2001 From: Troy Schrapel Date: Tue, 9 Jul 2024 21:08:29 +0930 Subject: [PATCH 06/19] initial trials with pio tms-cpu interface --- .vscode/settings.json | 4 +- CMakeLists.txt | 2 + src/CMakeLists.txt | 7 +- src/main.c | 384 +++++++++++++++++++++++++++----------- src/pio-utils/pio_utils.c | 8 - src/pio-utils/pio_utils.h | 1 - src/tms9918.pio | 50 +++++ src/vga/vga-modes.c | 4 +- src/vga/vga.c | 74 +++++--- src/vga/vga.h | 2 +- 10 files changed, 385 insertions(+), 151 deletions(-) create mode 100644 src/tms9918.pio diff --git a/.vscode/settings.json b/.vscode/settings.json index 3c1a20c..6266525 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,6 +3,8 @@ "*.md": "markdown", "xtr1common": "c", "stdlib.h": "c", - "multicore.h": "c" + "multicore.h": "c", + "stdio.h": "c", + "tms9918.pio.h": "c" } } \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 020038c..cba7afb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,6 +15,8 @@ project(${PROJECT} C CXX) add_definitions(-DPICO_BUILD=1) add_definitions(-DPICO_DISABLE_SHARED_IRQ_HANDLERS=1) add_definitions(-DVR_EMU_TMS9918_SINGLE_INSTANCE=1) +add_definitions(-DPICO_PANIC_FUNCTION=) +add_definitions(-DPICO_TIME_DEFAULT_ALARM_POOL_DISABLED=1) set(CMAKE_C_STANDARD 11) set(CMAKE_CXX_STANDARD 17) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a338844..49b3a20 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -4,21 +4,20 @@ set(PROGRAM pico9918) add_executable(${PROGRAM}) -target_sources(${PROGRAM} PRIVATE main.c palette.c clocks.pio.h) +target_sources(${PROGRAM} PRIVATE main.c palette.c clocks.pio.h tms9918.pio.h) # generate image array source files from png images visrealm_generate_image_source_ram(${PROGRAM} splash res/splash.png ) # generate header file from pio pico_generate_pio_header(${PROGRAM} ${CMAKE_CURRENT_LIST_DIR}/clocks.pio) +pico_generate_pio_header(${PROGRAM} ${CMAKE_CURRENT_LIST_DIR}/tms9918.pio) pico_add_extra_outputs(${PROGRAM}) pico_enable_stdio_usb(${PROGRAM} 0) pico_enable_stdio_uart(${PROGRAM} 0) -add_compile_options(-O3 -g3) - -#pico_set_binary_type(${PROGRAM} copy_to_ram) # TOO SLOW TO BOOT +pico_set_binary_type(${PROGRAM} copy_to_ram) # TOO SLOW TO BOOT target_link_libraries(${PROGRAM} PUBLIC pico_stdlib diff --git a/src/main.c b/src/main.c index a143d3c..1b70ae3 100644 --- a/src/main.c +++ b/src/main.c @@ -13,11 +13,13 @@ #include "vga-modes.h" #include "clocks.pio.h" +#include "tms9918.pio.h" #include "palette.h" #include "splash.h" +#include "impl/vrEmuTms9918Priv.h" #include "vrEmuTms9918Util.h" #include "pico/stdlib.h" @@ -27,40 +29,40 @@ #include "hardware/clocks.h" #include "hardware/vreg.h" -#include - - /* - * Pin mapping - * - * Pin | GPIO | Name | TMS9918A Pin - * -----+------+-----------+------------- - * 19 | 14 | CD0 | 24 - * 20 | 15 | CD1 | 23 - * 21 | 16 | CD2 | 22 - * 22 | 17 | CD3 | 21 - * 24 | 18 | CD4 | 20 - * 25 | 19 | CD5 | 19 - * 26 | 20 | CD6 | 18 - * 27 | 21 | CD7 | 17 - * 29 | 22 | /INT | 16 - * 30 | RUN | RST | 34 - * 31 | 26 | /CSR | 15 - * 32 | 27 | /CSW | 14 - * 34 | 28 | MODE | 13 - * 35 | 29 | GROMCLK | 37 - * 37 | 23 | CPUCLK | 38 - * - * Note: Due to GROMCLK and CPUCLK using GPIO23 and GPIO29 - * a genuine Raspberry Pi Pico can't be used. - * v0.3 of the PCB is designed for the DWEII? - * RP2040 USB-C module which exposes these additional - * GPIOs. A future pico9918 revision will do without - * an external RP2040 board and use the RP2040 directly. - * - * Purchase links: - * https://www.amazon.com/RP2040-Board-Type-C-Raspberry-Micropython/dp/B0CG9BY48X - * https://www.aliexpress.com/item/1005007066733934.html - */ + //#include + + /* + * Pin mapping + * + * Pin | GPIO | Name | TMS9918A Pin + * -----+------+-----------+------------- + * 19 | 14 | CD0 | 24 + * 20 | 15 | CD1 | 23 + * 21 | 16 | CD2 | 22 + * 22 | 17 | CD3 | 21 + * 24 | 18 | CD4 | 20 + * 25 | 19 | CD5 | 19 + * 26 | 20 | CD6 | 18 + * 27 | 21 | CD7 | 17 + * 29 | 22 | /INT | 16 + * 30 | RUN | RST | 34 + * 31 | 26 | /CSR | 15 + * 32 | 27 | /CSW | 14 + * 34 | 28 | MODE | 13 + * 35 | 29 | GROMCLK | 37 + * 37 | 23 | CPUCLK | 38 + * + * Note: Due to GROMCLK and CPUCLK using GPIO23 and GPIO29 + * a genuine Raspberry Pi Pico can't be used. + * v0.3 of the PCB is designed for the DWEII? + * RP2040 USB-C module which exposes these additional + * GPIOs. A future pico9918 revision will do without + * an external RP2040 board and use the RP2040 directly. + * + * Purchase links: + * https://www.amazon.com/RP2040-Board-Type-C-Raspberry-Micropython/dp/B0CG9BY48X + * https://www.aliexpress.com/item/1005007066733934.html + */ #define GPIO_CD0 14 #define GPIO_CSR 26 @@ -82,15 +84,22 @@ #define LED_BLINK_ON_WRITE 0 - /* file globals */ +#define PICO_CLOCK_HZ 252000000 + //#define PICO_CLOCK_HZ 315000000 + + + /* file globals */ -static uint32_t nextValue = 0; /* TMS9918A read-ahead value */ -static uint32_t currentInt = GPIO_INT_MASK; /* current interrupt pin state */ -static uint32_t currentStatus = 0; /* current status register value */ +static uint8_t nextValue = 0; /* TMS9918A read-ahead value */ +static bool currentInt = false;//GPIO_INT_MASK; /* current interrupt pin state */ +static uint8_t currentStatus = 0; /* current status register value */ static uint32_t nextValueInt = 0; /* TMS9918A read-ahead value */ static uint8_t __aligned(4) tmsScanlineBuffer[TMS9918_PIXELS_X]; +const uint tmsWriteSm = 0;//pio_claim_unused_sm(pio1, true); +const uint tmsReadSm = 1;//pio_claim_unused_sm(pio1, true); + /* * enable gpio interrupts inline @@ -115,9 +124,24 @@ static inline void disableGpioInterrupts() */ static inline void gpioInterruptHandled() { - iobank0_hw->intr[GPIO_CSR >> 3u] = iobank0_hw->proc1_irq_ctrl.ints[GPIO_CSR >> 3u]; + uint32_t* addr = (uint32_t*)0x400140FC; + *(uint32_t*)addr = *(uint32_t*)(addr + 24); + //iobank0_hw->intr[GPIO_CSR >> 3u] = iobank0_hw->proc1_irq_ctrl.ints[GPIO_CSR >> 3u]; } +static +uint16_t bg = 0; + + +static void updateTmsReadAhead() +{ + uint32_t readAhead = 0xff; // pin direction + readAhead |= nextValue << 8; + readAhead |= currentStatus << 16; + pio_sm_put(pio1, tmsReadSm, readAhead); +} + + /* * RP2040 exclusive GPIO interrupt callback for PROC1 * Called whenever CSR or CSW changes and reads or writes the data @@ -127,61 +151,117 @@ static inline void gpioInterruptHandled() * gcc in producing optimized assembly. I considered writing * this in assembly, but decided on this instead. */ + /* + void __isr __scratch_x("isr") gpioExclusiveCallbackProc1() + { + // shift the bit mask combos down to lowest significant bits + const int GPIO_IRQ_READ_DATA = (GPIO_CSW_MASK) >> GPIO_CSR; + const int GPIO_IRQ_READ_STATUS = (GPIO_CSW_MASK | GPIO_MODE_MASK) >> GPIO_CSR; + const int GPIO_IRQ_WRITE_DATA = (GPIO_CSR_MASK) >> GPIO_CSR; + const int GPIO_IRQ_WRITE_REG = (GPIO_CSR_MASK | GPIO_MODE_MASK) >> GPIO_CSR; + const int GPIO_IRQ_INACTIVE1 = (GPIO_CSW_MASK | GPIO_CSR_MASK) >> GPIO_CSR; + const int GPIO_IRQ_INACTIVE2 = (GPIO_CSW_MASK | GPIO_CSR_MASK | GPIO_MODE_MASK) >> GPIO_CSR; + + // test the MODE, /CSW and /CSR pins to determine which action to take + // shifting them down to bits 2, 1 and 0 respectively rather than testing + // them at the higher bit range since ARM can only load 8-bit immediate + // values in one cycle to compare + gpioInterruptHandled(); + + switch ((sio_hw->gpio_in >> GPIO_CSR) & 0x07) + { + case GPIO_IRQ_WRITE_DATA: + vrEmuTms9918WriteDataImpl(sio_hw->gpio_in >> GPIO_CD0); + break; + + case GPIO_IRQ_READ_DATA: + sio_hw->gpio_oe_set = GPIO_CD_MASK; + sio_hw->gpio_out = nextValueInt; + vrEmuTms9918ReadDataImpl(); + break; + + case GPIO_IRQ_WRITE_REG: + vrEmuTms9918WriteAddrImpl(sio_hw->gpio_in >> GPIO_CD0); + currentInt = vrEmuTms9918InterruptStatusImpl() ? 0 : GPIO_INT_MASK; + break; + + case GPIO_IRQ_READ_STATUS: + sio_hw->gpio_oe_set = GPIO_CD_MASK; + sio_hw->gpio_out = currentStatus; + currentInt = GPIO_INT_MASK; + currentStatus = (vrEmuTms9918ReadStatusImpl() << GPIO_CD0) | currentInt; + break; + + case GPIO_IRQ_INACTIVE1: + case GPIO_IRQ_INACTIVE2: + sio_hw->gpio_oe_clr = GPIO_CD_MASK; + sio_hw->gpio_out = currentInt; + break; + } + + // update read-ahead + nextValue = vrEmuTms9918ReadDataNoIncImpl() << GPIO_CD0; + nextValueInt = nextValue | currentInt; + } + */ + +uint32_t writeVals[16] = { 0 }; +uint32_t readVals[16] = { 0 }; +int nextReadVal = 0; +int nextWriteVal = 0; -void __time_critical_func(gpioExclusiveCallbackProc1)() + +void __isr __scratch_x("isr") pio_irq_handler() { + if (pio_interrupt_get(pio1, 0)) // write? + { + uint32_t writeVal = pio_sm_get(pio1, tmsWriteSm); + writeVals[nextWriteVal++] = writeVal; + nextWriteVal &= 0x0f; - // shift the bit mask combos down to lowest significant bits - const int GPIO_IRQ_READ_DATA = (GPIO_CSW_MASK) >> GPIO_CSR; - const int GPIO_IRQ_READ_STATUS = (GPIO_CSW_MASK | GPIO_MODE_MASK) >> GPIO_CSR; - const int GPIO_IRQ_WRITE_DATA = (GPIO_CSR_MASK) >> GPIO_CSR; - const int GPIO_IRQ_WRITE_REG = (GPIO_CSR_MASK | GPIO_MODE_MASK) >> GPIO_CSR; + if (writeVal & (1 << 14))//(GPIO_MODE_MASK >> GPIO_CD0)) // red/addr + { + // bg |= 0xf00; + vrEmuTms9918WriteAddrImpl(writeVal & 0xff); + currentInt = vrEmuTms9918InterruptStatusImpl(); + gpio_put(GPIO_INT, !currentInt); + } + else // data + { + bg |= 0x0f; + vrEmuTms9918WriteDataImpl(writeVal & 0xff); + } - // test the MODE, /CSW and /CSR pins to determine which action to take - // shifting them down to bits 2, 1 and 0 respectively rather than testing - // them at the higher bit range since ARM can only load 8-bit immediate - // values in one cycle to compare + nextValue = vrEmuTms9918ReadDataNoIncImpl(); + updateTmsReadAhead(); - switch ((sio_hw->gpio_in >> GPIO_CSR) & 0x07) - { - case GPIO_IRQ_WRITE_DATA: - gpioInterruptHandled(); - vrEmuTms9918WriteData(sio_hw->gpio_in >> GPIO_CD0); - break; - - case GPIO_IRQ_READ_DATA: - sio_hw->gpio_oe_set = GPIO_CD_MASK; - sio_hw->gpio_out = nextValueInt; - gpioInterruptHandled(); - vrEmuTms9918ReadData(); - break; - - case GPIO_IRQ_WRITE_REG: - gpioInterruptHandled(); - vrEmuTms9918WriteAddr(sio_hw->gpio_in >> GPIO_CD0); - currentInt = vrEmuTms9918InterruptStatus() ? 0 : GPIO_INT_MASK; - break; - - case GPIO_IRQ_READ_STATUS: - sio_hw->gpio_oe_set = GPIO_CD_MASK; - sio_hw->gpio_out = currentStatus | GPIO_INT_MASK; - gpioInterruptHandled(); - currentStatus = vrEmuTms9918ReadStatus() << GPIO_CD0; - currentInt = GPIO_INT_MASK; - break; - - default: - gpioInterruptHandled(); - sio_hw->gpio_oe_clr = GPIO_CD_MASK; - sio_hw->gpio_out = currentInt; - break; + pio_interrupt_clear(pio1, 0); } + else if (pio_interrupt_get(pio1, 1)) // read? + { + uint32_t readVal = pio_sm_get(pio1, tmsReadSm); + readVals[nextReadVal++] = readVal;//0xf0f0f0f0; + nextReadVal &= 0x0f; - /* update read-ahead */ - nextValue = vrEmuTms9918ReadDataNoInc() << GPIO_CD0; - nextValueInt = nextValue | currentInt; -} + if (readVal == 0) // data + { + // + vrEmuTms9918ReadDataImpl(); + nextValue = vrEmuTms9918ReadDataNoIncImpl(); + } + else // status + { + bg |= 0xf0; + currentStatus = vrEmuTms9918ReadStatusImpl(); + currentInt = false; + gpio_put(GPIO_INT, !currentInt); + } + updateTmsReadAhead(); + pio_interrupt_clear(pio1, 1); + } + irq_clear(PIO1_IRQ_0); +} /* * generate a single VGA scanline (called by vgaLoop(), runs on proc1) @@ -202,7 +282,10 @@ static void __time_critical_func(tmsScanline)(uint16_t y, VgaParams* params, uin const uint32_t vBorder = (VIRTUAL_PIXELS_Y - TMS9918_PIXELS_Y) / 2; const uint32_t hBorder = (VIRTUAL_PIXELS_X - TMS9918_PIXELS_X * 2) / 2; - uint16_t bg = tms9918PaletteBGR12[vrEmuTms9918RegValue(TMS_REG_FG_BG_COLOR) & 0x0f]; + //uint16_t bg = tms9918PaletteBGR12[vrEmuTms9918RegValue(TMS_REG_FG_BG_COLOR) & 0x0f]; + + if (y == 0) bg = 0; + /*** top and bottom borders ***/ if (y < vBorder || y >= (vBorder + TMS9918_PIXELS_Y)) @@ -212,11 +295,38 @@ static void __time_critical_func(tmsScanline)(uint16_t y, VgaParams* params, uin pixels[x] = bg; } + if (y < vBorder) + { + uint32_t writeVal = writeVals[y & 0x0f]; + for (int i = 0; i < 32; ++i) + { + uint16_t bitColor = ((writeVal << i) & 0x80000000) ? 0x0fff : 0x000; + int offset = i * 16 + hBorder; + for (int x = 0; x < 16; ++x) + { + pixels[offset + x] = bitColor; + } + } + } + else + { + uint32_t readVal = readVals[y & 0x0f]; + for (int i = 0; i < 32; ++i) + { + uint16_t bitColor = ((readVal << i) & 0x80000000) ? 0x0fff : 0x000; + int offset = i * 16 + hBorder; + for (int x = 0; x < 16; ++x) + { + pixels[offset + x] = bitColor; + } + } + } + /* source: C:/Users/troy/OneDrive/Documents/projects/pico9918/src/res/splash.png * size : 172px x 10px * : 3440 bytes * format: 16bpp abgr image - */ + * if (y >= vBorder + TMS9918_PIXELS_Y + 12) { y -= vBorder + TMS9918_PIXELS_Y + 12; @@ -233,6 +343,7 @@ static void __time_critical_func(tmsScanline)(uint16_t y, VgaParams* params, uin } } } + */ return; } @@ -248,10 +359,15 @@ static void __time_critical_func(tmsScanline)(uint16_t y, VgaParams* params, uin /*** main display region ***/ /* generate the scanline */ - uint8_t newStatus = vrEmuTms9918ScanLine(y, tmsScanlineBuffer, currentStatus >> GPIO_CD0) & 0x7f; - if (!currentInt) newStatus |= 0x80; - vrEmuTms9918SetStatus(newStatus); - currentStatus = newStatus << GPIO_CD0; + uint8_t newStatus = vrEmuTms9918ScanLine(y, tmsScanlineBuffer, currentStatus) & 0x7f; + + //disableGpioInterrupts(); + if (currentInt) newStatus |= 0x80; + vrEmuTms9918SetStatusImpl(newStatus); + currentStatus = newStatus; + updateTmsReadAhead(); + //currentStatus = (newStatus << GPIO_CD0) | GPIO_INT_MASK; + //enableGpioInterrupts(); /* convert from palette to bgr12 */ int tmsX = 0; @@ -281,11 +397,14 @@ static void __time_critical_func(tmsScanline)(uint16_t y, VgaParams* params, uin /*** interrupt signal? ***/ if (y == TMS9918_PIXELS_Y - 1) { - vrEmuTms9918InterruptSet(); - currentInt = vrEmuTms9918InterruptStatus() ? 0 : GPIO_INT_MASK; - currentStatus |= 0x80 << GPIO_CD0; - nextValueInt = nextValue | currentInt; - gpio_put(GPIO_INT, !!currentInt); + // disableGpioInterrupts(); + vrEmuTms9918InterruptSetImpl(); + currentInt = vrEmuTms9918InterruptStatusImpl();// ? 0 : GPIO_INT_MASK; + currentStatus |= 0x80; + updateTmsReadAhead(); + + // enableGpioInterrupts(); + gpio_put(GPIO_INT, !currentInt); } } @@ -301,17 +420,58 @@ uint initClock(uint gpio, float freqHz) clocksPioOffset = pio_add_program(pio1, &clock_program); } - uint clkSm = pio_claim_unused_sm(pio1, true); + static uint clkSm = 2;//pio_claim_unused_sm(pio1, true); clock_program_init(pio1, clkSm, clocksPioOffset, gpio); - float clockDiv = (float)clock_get_hz(clk_sys) / (TMS_CRYSTAL_FREQ_HZ * 10.0f); + float clockDiv = (float)PICO_CLOCK_HZ / (TMS_CRYSTAL_FREQ_HZ * 10.0f); pio_sm_set_clkdiv(pio1, clkSm, clockDiv); pio_sm_set_enabled(pio1, clkSm, true); - pio_sm_put(pio1, clkSm, (uint)(clock_get_hz(clk_sys) / clockDiv / (2.0f * freqHz)) - 3.0f); + pio_sm_put(pio1, clkSm, (uint)(PICO_CLOCK_HZ / clockDiv / (2.0f * freqHz)) - 3.0f); - return clkSm; + return clkSm++; +} + +void tmsPioInit() +{ + uint tmsWriteProgram = pio_add_program(pio1, &tmsWrite_program); + + + pio_sm_config writeConfig = tmsWrite_program_get_default_config(tmsWriteProgram); + sm_config_set_in_pins(&writeConfig, GPIO_CD0); + sm_config_set_in_shift(&writeConfig, false, true/*true*/, 16); // R shift, autopush @ 16 bits + + pio_sm_init(pio1, tmsWriteSm, tmsWriteProgram, &writeConfig); + pio_sm_set_enabled(pio1, tmsWriteSm, true); + + uint tmsReadProgram = pio_add_program(pio1, &tmsRead_program); + + for (uint i = 0; i < 8; ++i) + { + pio_gpio_init(pio1, GPIO_CD0 + i); + } + + pio_sm_config readConfig = tmsRead_program_get_default_config(tmsReadProgram); + sm_config_set_in_pins(&readConfig, GPIO_CSR); + sm_config_set_jmp_pin(&readConfig, GPIO_MODE); + sm_config_set_out_pins(&readConfig, GPIO_CD0, 8); + sm_config_set_in_shift(&readConfig, true, false, 32); // R shift, autopush @ 16 bits + sm_config_set_out_shift(&readConfig, false, false, 32); // R shift, autopush @ 16 bits + + pio_sm_init(pio1, tmsReadSm, tmsReadProgram, &readConfig); + pio_sm_set_enabled(pio1, tmsReadSm, true); + + irq_set_exclusive_handler(PIO1_IRQ_0, pio_irq_handler); + //irq_set_exclusive_handler(PIO1_IRQ_1, pio_irq_handler1); + irq_set_enabled(PIO1_IRQ_0, true); + //irq_set_enabled(PIO1_IRQ_1, true); + pio_set_irq0_source_enabled(pio1, pis_interrupt0, true); + pio_set_irq0_source_enabled(pio1, pis_interrupt1, true); + pio_interrupt_clear(pio1, 0); + pio_interrupt_clear(pio1, 1); + + pio_sm_put(pio1, tmsReadSm, 0x000000ff); } @@ -325,16 +485,16 @@ void proc1Entry() gpio_put_all(GPIO_INT_MASK); gpio_set_dir_all_bits(GPIO_INT_MASK | GPIO_LED_MASK); // int is an output - // ensure CSR and CSW are high (inactive) - while (!gpio_get(GPIO_CSW) || !gpio_get(GPIO_CSR)) - tight_loop_contents(); + tmsPioInit(); // set up gpio interrupts + /* + irq_set_priority(IO_IRQ_BANK0, 0); irq_set_exclusive_handler(IO_IRQ_BANK0, gpioExclusiveCallbackProc1); gpio_set_irq_enabled(GPIO_CSW, GPIO_IRQ_EDGE_FALL | GPIO_IRQ_EDGE_RISE, true); gpio_set_irq_enabled(GPIO_CSR, GPIO_IRQ_EDGE_FALL | GPIO_IRQ_EDGE_RISE, true); irq_set_enabled(IO_IRQ_BANK0, true); - irq_set_priority(IO_IRQ_BANK0, 0); + */ // set up the GROMCLK and CPUCLK signals initClock(GPIO_GROMCL, TMS_CRYSTAL_FREQ_HZ / 24.0f); @@ -345,6 +505,8 @@ void proc1Entry() vgaLoop(); } + + /* * main entry point */ @@ -356,13 +518,19 @@ int main(void) * but this'll do for now. */ vreg_set_voltage(VREG_VOLTAGE_1_20); //set_sys_clock_khz(315000, false); - set_sys_clock_khz(252000, false); - //set_sys_clock_khz(126000, false); + //set_sys_clock_khz(PICO_CLOCK_HZ / 1000, false); + + + set_sys_clock_pll(1260000000, 5, 1); // 252000 + //set_sys_clock_pll(1260000000, 4, 1); // 315000 + //set_sys_clock_pll(1512000000, 5, 1); // 302000 /* we need one of these. it's the main guy */ - vrEmuTms9918Init(); + vrEmuTms9918Init(); /* set up the GPIO pins and interrupt handler */ + + multicore_launch_core1(proc1Entry); /* then set up VGA output */ diff --git a/src/pio-utils/pio_utils.c b/src/pio-utils/pio_utils.c index 998d69c..c0b175b 100644 --- a/src/pio-utils/pio_utils.c +++ b/src/pio-utils/pio_utils.c @@ -41,14 +41,6 @@ static void pio_set_xy(PIO pio, uint sm, uint32_t val, enum pio_src_dest dest) pio_sm_exec(pio, sm, pio_encode_mov(dest, pio_isr)); } -/* - * set the pio state machine x register - */ -void pio_set_x(PIO pio, uint sm, uint32_t x) -{ - pio_set_xy(pio, sm, x, pio_x); -} - /* * set the pio state machine y register */ diff --git a/src/pio-utils/pio_utils.h b/src/pio-utils/pio_utils.h index 65f730c..62cae09 100644 --- a/src/pio-utils/pio_utils.h +++ b/src/pio-utils/pio_utils.h @@ -13,5 +13,4 @@ #include "hardware/pio.h" -void pio_set_x(PIO pio, uint sm, uint32_t y); void pio_set_y(PIO pio, uint sm, uint32_t y); \ No newline at end of file diff --git a/src/tms9918.pio b/src/tms9918.pio new file mode 100644 index 0000000..00454f5 --- /dev/null +++ b/src/tms9918.pio @@ -0,0 +1,50 @@ +/* + * Project: pico9918 + * + * Copyright (c) 2024 Troy Schrapel + * + * This code is licensed under the MIT license + * + * https://github.com/visrealm/pico9918 + * + */ + +.program tmsRead + pull block + mov x, osr +.wrap_target +entry: + push ; push the last type of data we read + irq set 1 + wait 1 gpio 26 ; once CSR is high, change pindirs to inputs + mov osr, null + out pindirs, 8 + +pullLoop: + pull noblock ; continuously empty the rx fifo + mov x, osr ; since we want the latest value + mov isr, pins ; set y to CSR state + in y, 1 + jmp y--, pullLoop ; still 1? loop, otherwise, let's read + +; wait 0 gpio 26 ; wait for CSR to go low +; pull block + out pindirs, 8 + jmp pin readStatus ; if 'mode' is high, read status bits +readData: + out pins, 8 ; output + mov isr, null ; tell cpu we read data + jmp entry +readStatus: + out null, 8 ; skip data bits + out pins, 8 ; output + mov isr, !null ; tell cpu we read status +.wrap + +.program tmsWrite + wait 0 gpio 27 + in pins, 16 ; auto-push + ;push + irq set 0 + wait 1 gpio 27 +.wrap diff --git a/src/vga/vga-modes.c b/src/vga/vga-modes.c index 4764516..16aa118 100644 --- a/src/vga/vga-modes.c +++ b/src/vga/vga-modes.c @@ -135,7 +135,7 @@ bool setVgaParamsScaleX(VgaParams* params, int pixelScale) if (!params || pixelScale < 1) return false; params->hPixelScale = pixelScale; - params->hVirtualPixels = (params->hSyncParams.displayPixels / params->hPixelScale); + params->hVirtualPixels = (params->hSyncParams.displayPixels / 1);//params->hPixelScale); return true; } @@ -144,7 +144,7 @@ bool setVgaParamsScaleY(VgaParams* params, int pixelScale) if (!params || pixelScale < 1) return false; params->vPixelScale = pixelScale; - params->vVirtualPixels = (params->vSyncParams.displayPixels / params->vPixelScale); + params->vVirtualPixels = (params->vSyncParams.displayPixels / 2);//params->vPixelScale); return true; } diff --git a/src/vga/vga.c b/src/vga/vga.c index 49ed193..c2f39a2 100644 --- a/src/vga/vga.c +++ b/src/vga/vga.c @@ -13,17 +13,17 @@ #include "vga.pio.h" #include "pio_utils.h" -#include "pico/divider.h" + //#include "pico/divider.h" #include "pico/multicore.h" #include "hardware/dma.h" #include "hardware/pio.h" #include "hardware/clocks.h" -#include -#include -#include -#include + //#include + //#include + //#include + //#include #define SYNC_PINS_START 0 // first sync pin gpio number #define SYNC_PINS_COUNT 2 // number of sync pins (h and v) @@ -41,14 +41,32 @@ #define CRT_EFFECT 1 #define SCANLINE_TIME_DEBUG 0 - /* - * sync pio dma data buffers - */ +#if 1 +#define VIRTUAL_PIXELS_X 640 +#define VIRTUAL_PIXELS_Y 240 +#else +#define VIRTUAL_PIXELS_X vgaParams.params.hVirtualPixels +#define VIRTUAL_PIXELS_Y vgaParams.params.vVirtualPixels +#endif + +int roundflt(float x) +{ + if (x < 0.0f) + return (int)(x - 0.5f); + else + return (int)(x + 0.5f); +} + +#define round roundflt + +/* + * sync pio dma data buffers + */ uint32_t __aligned(4) syncDataActive[4]; // active display area uint32_t __aligned(4) syncDataPorch[4]; // vertical porch uint32_t __aligned(4) syncDataSync[4]; // vertical sync -uint16_t* __aligned(4) rgbDataBuffer[2 + SCANLINE_TIME_DEBUG] = { 0 }; // two scanline buffers (odd and even) +uint16_t __aligned(4) rgbDataBuffer[2 + SCANLINE_TIME_DEBUG][640 * sizeof(uint16_t)] = { 0 }; // two scanline buffers (odd and even) /* * file scope @@ -91,11 +109,11 @@ static bool buildSyncData() return false; } - rgbDataBuffer[0] = malloc(vgaParams.params.hVirtualPixels * sizeof(uint16_t)); - rgbDataBuffer[1] = malloc(vgaParams.params.hVirtualPixels * sizeof(uint16_t)); + //rgbDataBuffer[0] = malloc(vgaParams.params.hVirtualPixels * sizeof(uint16_t)); + //rgbDataBuffer[1] = malloc(vgaParams.params.hVirtualPixels * sizeof(uint16_t)); #if SCANLINE_TIME_DEBUG - rgbDataBuffer[2] = malloc(vgaParams.params.hVirtualPixels * sizeof(uint16_t)); + //rgbDataBuffer[2] = malloc(vgaParams.params.hVirtualPixels * sizeof(uint16_t)); for (int i = 0; i < vgaParams.params.hVirtualPixels; ++i) rgbDataBuffer[2][i] = 0x0f00; @@ -178,7 +196,7 @@ static void vgaInitSync() pio_sm_init(VGA_PIO, SYNC_SM, syncProgOffset, &syncConfig); // initialise sync dma - syncDmaChan = dma_claim_unused_channel(true); + syncDmaChan = 0;//dma_claim_unused_channel(true); syncDmaChanMask = 0x01 << syncDmaChan; dma_channel_config syncDmaChanConfig = dma_channel_get_default_config(syncDmaChan); channel_config_set_transfer_data_size(&syncDmaChanConfig, DMA_SIZE_32); // transfer 32 bits at a time @@ -201,7 +219,10 @@ static void vgaInitRgb() // copy the rgb program and set the appropriate pixel delay uint16_t rgbProgramInstr[vga_rgb_program.length]; - memcpy(rgbProgramInstr, vga_rgb_program.instructions, sizeof(rgbProgramInstr)); + for (int i = 0; i < vga_rgb_program.length; ++i) + { + rgbProgramInstr[i] = vga_rgb_program.instructions[i]; + } rgbProgramInstr[vga_rgb_DELAY_INSTR] |= pio_encode_delay(rgbCyclesPerPixel - vga_rgb_LOOP_TICKS); pio_program_t rgbProgram = { @@ -218,7 +239,7 @@ static void vgaInitRgb() // add rgb pio program pio_sm_set_consecutive_pindirs(VGA_PIO, RGB_SM, RGB_PINS_START, RGB_PINS_COUNT, true); - pio_set_y(VGA_PIO, RGB_SM, vgaParams.params.hVirtualPixels - 1); + pio_set_y(VGA_PIO, RGB_SM, VIRTUAL_PIXELS_X - 1); uint rgbProgOffset = pio_add_program(VGA_PIO, &rgbProgram); pio_sm_config rgbConfig = vga_rgb_program_get_default_config(rgbProgOffset); @@ -232,7 +253,7 @@ static void vgaInitRgb() pio_sm_init(VGA_PIO, RGB_SM, rgbProgOffset, &rgbConfig); // initialise rgb dma - rgbDmaChan = dma_claim_unused_channel(true); + rgbDmaChan = 1;//dma_claim_unused_channel(true); rgbDmaChanMask = 0x01 << rgbDmaChan; dma_channel_config rgbDmaChanConfig = dma_channel_get_default_config(rgbDmaChan); channel_config_set_transfer_data_size(&rgbDmaChanConfig, DMA_SIZE_16); // transfer 16 bits at a time @@ -241,14 +262,14 @@ static void vgaInitRgb() channel_config_set_dreq(&rgbDmaChanConfig, pio_get_dreq(VGA_PIO, RGB_SM, true)); // setup the dma channel and set it going - dma_channel_configure(rgbDmaChan, &rgbDmaChanConfig, &VGA_PIO->txf[RGB_SM], rgbDataBuffer[0], vgaParams.params.hVirtualPixels, false); + dma_channel_configure(rgbDmaChan, &rgbDmaChanConfig, &VGA_PIO->txf[RGB_SM], rgbDataBuffer[0], VIRTUAL_PIXELS_X, false); dma_channel_set_irq0_enabled(rgbDmaChan, true); } /* * dma interrupt handler */ -static void __time_critical_func(dmaIrqHandler)(void) +static void __isr __time_critical_func(dmaIrqHandler)(void) { static int currentTimingLine = -1; static int currentDisplayLine = -1; @@ -286,10 +307,11 @@ static void __time_critical_func(dmaIrqHandler)(void) { dma_hw->ints0 = rgbDmaChanMask; - divmod_result_t pxLineVal = divmod_u32u32(currentDisplayLine++, vgaParams.params.vPixelScale); - uint32_t pxLine = to_quotient_u32(pxLineVal); - uint32_t pxLineRpt = to_remainder_u32(pxLineVal); + //divmod_result_t pxLineVal = divmod_u32u32(, vgaParams.params.vPixelScale); + uint32_t pxLine = currentDisplayLine >> 1; + uint32_t pxLineRpt = currentDisplayLine & 0x01; uint32_t* currentBuffer = (uint32_t*)rgbDataBuffer[pxLine & 0x01]; + currentDisplayLine++; #if CRT_EFFECT if (pxLineRpt != 0) @@ -315,14 +337,14 @@ static void __time_critical_func(dmaIrqHandler)(void) if ((pxLineRpt == 0)) { uint32_t requestLine = pxLine + 1; - if (requestLine >= vgaParams.params.vVirtualPixels) requestLine -= vgaParams.params.vVirtualPixels; + if (requestLine >= VIRTUAL_PIXELS_Y) requestLine -= VIRTUAL_PIXELS_Y; multicore_fifo_push_timeout_us(requestLine, 0); #if SCANLINE_TIME_DEBUG hasRenderedNext = false; #endif - if (requestLine == vgaParams.params.vVirtualPixels - 1) + if (requestLine == VIRTUAL_PIXELS_Y - 1) { multicore_fifo_push_timeout_us(END_OF_FRAME_MSG, 0); } @@ -330,7 +352,7 @@ static void __time_critical_func(dmaIrqHandler)(void) #if CRT_EFFECT else { - int end = vgaParams.params.hVirtualPixels / 2; + int end = VIRTUAL_PIXELS_X / 2; for (int i = 5; i < end; ++i) { currentBuffer[i] = (currentBuffer[i] >> 1) & 0x07770777; @@ -364,7 +386,7 @@ void __time_critical_func(vgaLoop)() } - uint64_t frameNumber = 0; + uint32_t frameNumber = 0; while (1) { uint32_t message = multicore_fifo_pop_blocking(); @@ -374,8 +396,8 @@ void __time_critical_func(vgaLoop)() if (vgaParams.endOfFrameFn) { vgaParams.endOfFrameFn(frameNumber); + ++frameNumber; } - ++frameNumber; } else if ((message & END_OF_SCANLINE_MSG) != 0) { diff --git a/src/vga/vga.h b/src/vga/vga.h index 94be64c..573cdc5 100644 --- a/src/vga/vga.h +++ b/src/vga/vga.h @@ -42,7 +42,7 @@ typedef struct typedef void (*vgaScanlineRgbFn)(uint16_t y, VgaParams* params, uint16_t* pixels); -typedef void (*vgaEndOfFrameFn)(uint64_t frameNumber); +typedef void (*vgaEndOfFrameFn)(uint32_t frameNumber); typedef void (*vgaInitFn)(); typedef void (*vgaEndOfScanlineFn)(); From 3420300e1f46edd043c851b31f82c0773a437239 Mon Sep 17 00:00:00 2001 From: Troy Schrapel Date: Tue, 9 Jul 2024 21:29:22 +0930 Subject: [PATCH 07/19] write works - needed delay --- src/main.c | 22 ++++++++++++---------- src/tms9918.pio | 17 +++++++++-------- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/src/main.c b/src/main.c index 1b70ae3..2c0f542 100644 --- a/src/main.c +++ b/src/main.c @@ -138,6 +138,7 @@ static void updateTmsReadAhead() uint32_t readAhead = 0xff; // pin direction readAhead |= nextValue << 8; readAhead |= currentStatus << 16; + pio_sm_exec(pio1, tmsReadSm, 0x8080); pio_sm_put(pio1, tmsReadSm, readAhead); } @@ -213,9 +214,9 @@ int nextWriteVal = 0; void __isr __scratch_x("isr") pio_irq_handler() { - if (pio_interrupt_get(pio1, 0)) // write? + if (pio1->irq & (1u << 0)) // write? { - uint32_t writeVal = pio_sm_get(pio1, tmsWriteSm); + uint32_t writeVal = pio1->rxf[tmsWriteSm];;//pio_sm_get(pio1, tmsWriteSm); writeVals[nextWriteVal++] = writeVal; nextWriteVal &= 0x0f; @@ -228,18 +229,18 @@ void __isr __scratch_x("isr") pio_irq_handler() } else // data { - bg |= 0x0f; + //bg |= 0x0f; vrEmuTms9918WriteDataImpl(writeVal & 0xff); } nextValue = vrEmuTms9918ReadDataNoIncImpl(); updateTmsReadAhead(); - pio_interrupt_clear(pio1, 0); + pio1->irq = (1u << 0); } - else if (pio_interrupt_get(pio1, 1)) // read? + else if (pio1->irq & (1u << 1)) // read? { - uint32_t readVal = pio_sm_get(pio1, tmsReadSm); + uint32_t readVal = pio1->rxf[tmsReadSm];//pio_sm_get(pio1, tmsReadSm); readVals[nextReadVal++] = readVal;//0xf0f0f0f0; nextReadVal &= 0x0f; @@ -251,14 +252,14 @@ void __isr __scratch_x("isr") pio_irq_handler() } else // status { - bg |= 0xf0; + //bg |= 0xf0; currentStatus = vrEmuTms9918ReadStatusImpl(); currentInt = false; gpio_put(GPIO_INT, !currentInt); } updateTmsReadAhead(); - pio_interrupt_clear(pio1, 1); + pio1->irq = (1u << 1); } irq_clear(PIO1_IRQ_0); } @@ -284,7 +285,8 @@ static void __time_critical_func(tmsScanline)(uint16_t y, VgaParams* params, uin //uint16_t bg = tms9918PaletteBGR12[vrEmuTms9918RegValue(TMS_REG_FG_BG_COLOR) & 0x0f]; - if (y == 0) bg = 0; + //if (y == 0) bg = 0; + if (currentInt) bg = 0x000f; else bg = 0x00f0; /*** top and bottom borders ***/ @@ -456,7 +458,7 @@ void tmsPioInit() sm_config_set_in_pins(&readConfig, GPIO_CSR); sm_config_set_jmp_pin(&readConfig, GPIO_MODE); sm_config_set_out_pins(&readConfig, GPIO_CD0, 8); - sm_config_set_in_shift(&readConfig, true, false, 32); // R shift, autopush @ 16 bits + sm_config_set_in_shift(&readConfig, false, false, 32); // R shift, autopush @ 16 bits sm_config_set_out_shift(&readConfig, false, false, 32); // R shift, autopush @ 16 bits pio_sm_init(pio1, tmsReadSm, tmsReadProgram, &readConfig); diff --git a/src/tms9918.pio b/src/tms9918.pio index 00454f5..1bdba1e 100644 --- a/src/tms9918.pio +++ b/src/tms9918.pio @@ -19,13 +19,14 @@ entry: wait 1 gpio 26 ; once CSR is high, change pindirs to inputs mov osr, null out pindirs, 8 + wait 0 gpio 26 ; once CSR is high, change pindirs to inputs -pullLoop: - pull noblock ; continuously empty the rx fifo - mov x, osr ; since we want the latest value - mov isr, pins ; set y to CSR state - in y, 1 - jmp y--, pullLoop ; still 1? loop, otherwise, let's read +;pullLoop: + pull block ; continuously empty the rx fifo +; mov x, osr ; since we want the latest value +; mov isr, pins ; set y to CSR state +; in y, 1 +; jmp y--, pullLoop ; still 1? loop, otherwise, let's read ; wait 0 gpio 26 ; wait for CSR to go low ; pull block @@ -42,9 +43,9 @@ readStatus: .wrap .program tmsWrite - wait 0 gpio 27 + wait 0 gpio 27 [10] in pins, 16 ; auto-push ;push irq set 0 - wait 1 gpio 27 + wait 1 gpio 27 [10] .wrap From 34bb6b28a28864d0e06a973c1e6e41d2278bd3ba Mon Sep 17 00:00:00 2001 From: Troy Schrapel Date: Tue, 9 Jul 2024 22:15:24 +0930 Subject: [PATCH 08/19] it works! --- src/main.c | 17 +++++++++-------- src/tms9918.pio | 24 ++++++++++++------------ 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/src/main.c b/src/main.c index 2c0f542..30fe75a 100644 --- a/src/main.c +++ b/src/main.c @@ -138,7 +138,8 @@ static void updateTmsReadAhead() uint32_t readAhead = 0xff; // pin direction readAhead |= nextValue << 8; readAhead |= currentStatus << 16; - pio_sm_exec(pio1, tmsReadSm, 0x8080); + //pio_sm_exec(pio1, tmsReadSm, 0x8080); + //pio_sm_exec(pio1, tmsReadSm, 0x8080); pio_sm_put(pio1, tmsReadSm, readAhead); } @@ -244,7 +245,7 @@ void __isr __scratch_x("isr") pio_irq_handler() readVals[nextReadVal++] = readVal;//0xf0f0f0f0; nextReadVal &= 0x0f; - if (readVal == 0) // data + if ((readVal & 0x04) == 0) // data { // vrEmuTms9918ReadDataImpl(); @@ -419,18 +420,18 @@ uint initClock(uint gpio, float freqHz) if (clocksPioOffset == -1) { - clocksPioOffset = pio_add_program(pio1, &clock_program); + clocksPioOffset = pio_add_program(pio0, &clock_program); } static uint clkSm = 2;//pio_claim_unused_sm(pio1, true); - clock_program_init(pio1, clkSm, clocksPioOffset, gpio); + clock_program_init(pio0, clkSm, clocksPioOffset, gpio); float clockDiv = (float)PICO_CLOCK_HZ / (TMS_CRYSTAL_FREQ_HZ * 10.0f); - pio_sm_set_clkdiv(pio1, clkSm, clockDiv); - pio_sm_set_enabled(pio1, clkSm, true); + pio_sm_set_clkdiv(pio0, clkSm, clockDiv); + pio_sm_set_enabled(pio0, clkSm, true); - pio_sm_put(pio1, clkSm, (uint)(PICO_CLOCK_HZ / clockDiv / (2.0f * freqHz)) - 3.0f); + pio_sm_put(pio0, clkSm, (uint)(PICO_CLOCK_HZ / clockDiv / (2.0f * freqHz)) - 3.0f); return clkSm++; } @@ -459,7 +460,7 @@ void tmsPioInit() sm_config_set_jmp_pin(&readConfig, GPIO_MODE); sm_config_set_out_pins(&readConfig, GPIO_CD0, 8); sm_config_set_in_shift(&readConfig, false, false, 32); // R shift, autopush @ 16 bits - sm_config_set_out_shift(&readConfig, false, false, 32); // R shift, autopush @ 16 bits + sm_config_set_out_shift(&readConfig, true, false, 32); // R shift, autopush @ 16 bits pio_sm_init(pio1, tmsReadSm, tmsReadProgram, &readConfig); pio_sm_set_enabled(pio1, tmsReadSm, true); diff --git a/src/tms9918.pio b/src/tms9918.pio index 1bdba1e..7279f40 100644 --- a/src/tms9918.pio +++ b/src/tms9918.pio @@ -16,30 +16,30 @@ entry: push ; push the last type of data we read irq set 1 - wait 1 gpio 26 ; once CSR is high, change pindirs to inputs + wait 1 gpio 26 [10] ; once CSR is high, change pindirs to inputs mov osr, null out pindirs, 8 - wait 0 gpio 26 ; once CSR is high, change pindirs to inputs -;pullLoop: - pull block ; continuously empty the rx fifo -; mov x, osr ; since we want the latest value -; mov isr, pins ; set y to CSR state -; in y, 1 -; jmp y--, pullLoop ; still 1? loop, otherwise, let's read +pullLoop: + pull noblock ; continuously empty the rx fifo + mov x, osr ; since we want the latest value + mov isr, null + in pins, 1 + mov y, isr + jmp y--, pullLoop ; still 1? loop, otherwise, let's read -; wait 0 gpio 26 ; wait for CSR to go low -; pull block out pindirs, 8 jmp pin readStatus ; if 'mode' is high, read status bits readData: out pins, 8 ; output - mov isr, null ; tell cpu we read data + ;mov isr, null ; tell cpu we read data + in pins, 8 jmp entry readStatus: out null, 8 ; skip data bits out pins, 8 ; output - mov isr, !null ; tell cpu we read status + in pins, 3 +; mov isr, !null ; tell cpu we read status .wrap .program tmsWrite From 88e8bf28208225532f86e86e105d561600957d22 Mon Sep 17 00:00:00 2001 From: Troy Schrapel Date: Tue, 9 Jul 2024 23:32:17 +0930 Subject: [PATCH 09/19] need many delays. look at clkdiv next --- src/tms9918.pio | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/tms9918.pio b/src/tms9918.pio index 7279f40..350575c 100644 --- a/src/tms9918.pio +++ b/src/tms9918.pio @@ -14,9 +14,7 @@ mov x, osr .wrap_target entry: - push ; push the last type of data we read - irq set 1 - wait 1 gpio 26 [10] ; once CSR is high, change pindirs to inputs + wait 1 gpio 26 ; once CSR is high, change pindirs to inputs mov osr, null out pindirs, 8 @@ -26,26 +24,27 @@ pullLoop: mov isr, null in pins, 1 mov y, isr - jmp y--, pullLoop ; still 1? loop, otherwise, let's read + jmp y--, pullLoop [31]; still 1? loop, otherwise, let's read out pindirs, 8 jmp pin readStatus ; if 'mode' is high, read status bits readData: out pins, 8 ; output - ;mov isr, null ; tell cpu we read data in pins, 8 - jmp entry + jmp endPart readStatus: out null, 8 ; skip data bits out pins, 8 ; output in pins, 3 -; mov isr, !null ; tell cpu we read status +endPart: + push + irq set 1 [31] ; push the last type of data we read .wrap .program tmsWrite - wait 0 gpio 27 [10] + wait 0 gpio 27 [31] + nop [10] in pins, 16 ; auto-push - ;push irq set 0 - wait 1 gpio 27 [10] + wait 1 gpio 27 [31] .wrap From 02556a82d24e77da636fb97e46506c634a63e8c3 Mon Sep 17 00:00:00 2001 From: Troy Schrapel Date: Tue, 9 Jul 2024 23:38:38 +0930 Subject: [PATCH 10/19] timing works on hbc-56. ~120nS response to reads --- src/main.c | 23 ++++++++++++----------- src/tms9918.pio | 9 ++++----- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/main.c b/src/main.c index 30fe75a..e0f0f0f 100644 --- a/src/main.c +++ b/src/main.c @@ -129,8 +129,7 @@ static inline void gpioInterruptHandled() //iobank0_hw->intr[GPIO_CSR >> 3u] = iobank0_hw->proc1_irq_ctrl.ints[GPIO_CSR >> 3u]; } -static -uint16_t bg = 0; +//static uint16_t bg = 0; static void updateTmsReadAhead() @@ -284,15 +283,16 @@ static void __time_critical_func(tmsScanline)(uint16_t y, VgaParams* params, uin const uint32_t vBorder = (VIRTUAL_PIXELS_Y - TMS9918_PIXELS_Y) / 2; const uint32_t hBorder = (VIRTUAL_PIXELS_X - TMS9918_PIXELS_X * 2) / 2; - //uint16_t bg = tms9918PaletteBGR12[vrEmuTms9918RegValue(TMS_REG_FG_BG_COLOR) & 0x0f]; + uint16_t bg = tms9918PaletteBGR12[vrEmuTms9918RegValue(TMS_REG_FG_BG_COLOR) & 0x0f]; //if (y == 0) bg = 0; - if (currentInt) bg = 0x000f; else bg = 0x00f0; + //if (currentInt) bg = 0x000f; else bg = 0x00f0; /*** top and bottom borders ***/ if (y < vBorder || y >= (vBorder + TMS9918_PIXELS_Y)) { + /* for (int x = 0; x < VIRTUAL_PIXELS_X; ++x) { pixels[x] = bg; @@ -324,12 +324,12 @@ static void __time_critical_func(tmsScanline)(uint16_t y, VgaParams* params, uin } } } - - /* source: C:/Users/troy/OneDrive/Documents/projects/pico9918/src/res/splash.png - * size : 172px x 10px - * : 3440 bytes - * format: 16bpp abgr image - * +*/ +/* source: C:/Users/troy/OneDrive/Documents/projects/pico9918/src/res/splash.png + * size : 172px x 10px + * : 3440 bytes + * format: 16bpp abgr image + */ if (y >= vBorder + TMS9918_PIXELS_Y + 12) { y -= vBorder + TMS9918_PIXELS_Y + 12; @@ -346,7 +346,6 @@ static void __time_critical_func(tmsScanline)(uint16_t y, VgaParams* params, uin } } } - */ return; } @@ -444,6 +443,7 @@ void tmsPioInit() pio_sm_config writeConfig = tmsWrite_program_get_default_config(tmsWriteProgram); sm_config_set_in_pins(&writeConfig, GPIO_CD0); sm_config_set_in_shift(&writeConfig, false, true/*true*/, 16); // R shift, autopush @ 16 bits + sm_config_set_clkdiv(&writeConfig, 4.0f); pio_sm_init(pio1, tmsWriteSm, tmsWriteProgram, &writeConfig); pio_sm_set_enabled(pio1, tmsWriteSm, true); @@ -461,6 +461,7 @@ void tmsPioInit() sm_config_set_out_pins(&readConfig, GPIO_CD0, 8); sm_config_set_in_shift(&readConfig, false, false, 32); // R shift, autopush @ 16 bits sm_config_set_out_shift(&readConfig, true, false, 32); // R shift, autopush @ 16 bits + sm_config_set_clkdiv(&readConfig, 4.0f); pio_sm_init(pio1, tmsReadSm, tmsReadProgram, &readConfig); pio_sm_set_enabled(pio1, tmsReadSm, true); diff --git a/src/tms9918.pio b/src/tms9918.pio index 350575c..2c9142f 100644 --- a/src/tms9918.pio +++ b/src/tms9918.pio @@ -24,7 +24,7 @@ pullLoop: mov isr, null in pins, 1 mov y, isr - jmp y--, pullLoop [31]; still 1? loop, otherwise, let's read + jmp y--, pullLoop; still 1? loop, otherwise, let's read out pindirs, 8 jmp pin readStatus ; if 'mode' is high, read status bits @@ -38,13 +38,12 @@ readStatus: in pins, 3 endPart: push - irq set 1 [31] ; push the last type of data we read + irq set 1 ; push the last type of data we read .wrap .program tmsWrite - wait 0 gpio 27 [31] - nop [10] + wait 0 gpio 27 [12] in pins, 16 ; auto-push irq set 0 - wait 1 gpio 27 [31] + wait 1 gpio 27 [12] .wrap From 54e23066b6518658317796fcbdf7eccf2e1ca5af Mon Sep 17 00:00:00 2001 From: Troy Schrapel Date: Tue, 9 Jul 2024 23:41:26 +0930 Subject: [PATCH 11/19] pio-tms is working on both HBC-56 and TI-99 --- src/main.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main.c b/src/main.c index e0f0f0f..50ae76e 100644 --- a/src/main.c +++ b/src/main.c @@ -292,12 +292,11 @@ static void __time_critical_func(tmsScanline)(uint16_t y, VgaParams* params, uin /*** top and bottom borders ***/ if (y < vBorder || y >= (vBorder + TMS9918_PIXELS_Y)) { - /* for (int x = 0; x < VIRTUAL_PIXELS_X; ++x) { pixels[x] = bg; } - +/* if (y < vBorder) { uint32_t writeVal = writeVals[y & 0x0f]; From f55e0b7a7201f55ac378627e2bd1399e4fc959ee Mon Sep 17 00:00:00 2001 From: Troy Schrapel Date: Wed, 10 Jul 2024 09:58:29 +0930 Subject: [PATCH 12/19] cleanup --- src/main.c | 220 ++++++------------------------------------------ src/tms9918.pio | 50 ++++++----- 2 files changed, 58 insertions(+), 212 deletions(-) diff --git a/src/main.c b/src/main.c index 50ae76e..3670d4d 100644 --- a/src/main.c +++ b/src/main.c @@ -71,165 +71,57 @@ #define GPIO_INT 22 #define GPIO_GROMCL 29 #define GPIO_CPUCL 23 -#define GPIO_LED 25 #define GPIO_CD_MASK (0xff << GPIO_CD0) #define GPIO_CSR_MASK (0x01 << GPIO_CSR) #define GPIO_CSW_MASK (0x01 << GPIO_CSW) #define GPIO_MODE_MASK (0x01 << GPIO_MODE) #define GPIO_INT_MASK (0x01 << GPIO_INT) -#define GPIO_LED_MASK (0x01 << GPIO_LED) #define TMS_CRYSTAL_FREQ_HZ 10738635.0f -#define LED_BLINK_ON_WRITE 0 - #define PICO_CLOCK_HZ 252000000 - //#define PICO_CLOCK_HZ 315000000 - /* file globals */ + /* file globals */ -static uint8_t nextValue = 0; /* TMS9918A read-ahead value */ -static bool currentInt = false;//GPIO_INT_MASK; /* current interrupt pin state */ +static uint8_t nextValue = 0; /* TMS9918A read-ahead value */ +static bool currentInt = false; /* current interrupt state */ static uint8_t currentStatus = 0; /* current status register value */ -static uint32_t nextValueInt = 0; /* TMS9918A read-ahead value */ static uint8_t __aligned(4) tmsScanlineBuffer[TMS9918_PIXELS_X]; -const uint tmsWriteSm = 0;//pio_claim_unused_sm(pio1, true); -const uint tmsReadSm = 1;//pio_claim_unused_sm(pio1, true); - - -/* - * enable gpio interrupts inline - */ -static inline void enableGpioInterrupts() -{ - *((io_rw_32*)(PPB_BASE + M0PLUS_NVIC_ICPR_OFFSET)) = 1u << IO_IRQ_BANK0; - *((io_rw_32*)(PPB_BASE + M0PLUS_NVIC_ISER_OFFSET)) = 1u << IO_IRQ_BANK0; -} +const uint tmsWriteSm = 0; +const uint tmsReadSm = 1; /* - * disable gpio interrupts inline + * update the value send to the read PIO */ -static inline void disableGpioInterrupts() +static void __attribute__((noinline)) updateTmsReadAhead() { - *((io_rw_32*)(PPB_BASE + M0PLUS_NVIC_ICER_OFFSET)) = 1u << IO_IRQ_BANK0; -} - -/* - * mark the interrupt as handled. - * Note: all relevant GPIO pins are in the same group - */ -static inline void gpioInterruptHandled() -{ - uint32_t* addr = (uint32_t*)0x400140FC; - *(uint32_t*)addr = *(uint32_t*)(addr + 24); - //iobank0_hw->intr[GPIO_CSR >> 3u] = iobank0_hw->proc1_irq_ctrl.ints[GPIO_CSR >> 3u]; -} - -//static uint16_t bg = 0; - - -static void updateTmsReadAhead() -{ - uint32_t readAhead = 0xff; // pin direction + uint32_t readAhead = 0xff; // pin direction readAhead |= nextValue << 8; readAhead |= currentStatus << 16; - //pio_sm_exec(pio1, tmsReadSm, 0x8080); - //pio_sm_exec(pio1, tmsReadSm, 0x8080); pio_sm_put(pio1, tmsReadSm, readAhead); } - /* - * RP2040 exclusive GPIO interrupt callback for PROC1 - * Called whenever CSR or CSW changes and reads or writes the data - * - * Note: This needs to be extremely responsive. No dilly-dallying here. - * This function isn't pretty, but is done this way to guide - * gcc in producing optimized assembly. I considered writing - * this in assembly, but decided on this instead. + * handle interrupts from the TMS9918<->CPU interface */ - /* - void __isr __scratch_x("isr") gpioExclusiveCallbackProc1() - { - // shift the bit mask combos down to lowest significant bits - const int GPIO_IRQ_READ_DATA = (GPIO_CSW_MASK) >> GPIO_CSR; - const int GPIO_IRQ_READ_STATUS = (GPIO_CSW_MASK | GPIO_MODE_MASK) >> GPIO_CSR; - const int GPIO_IRQ_WRITE_DATA = (GPIO_CSR_MASK) >> GPIO_CSR; - const int GPIO_IRQ_WRITE_REG = (GPIO_CSR_MASK | GPIO_MODE_MASK) >> GPIO_CSR; - const int GPIO_IRQ_INACTIVE1 = (GPIO_CSW_MASK | GPIO_CSR_MASK) >> GPIO_CSR; - const int GPIO_IRQ_INACTIVE2 = (GPIO_CSW_MASK | GPIO_CSR_MASK | GPIO_MODE_MASK) >> GPIO_CSR; - - // test the MODE, /CSW and /CSR pins to determine which action to take - // shifting them down to bits 2, 1 and 0 respectively rather than testing - // them at the higher bit range since ARM can only load 8-bit immediate - // values in one cycle to compare - gpioInterruptHandled(); - - switch ((sio_hw->gpio_in >> GPIO_CSR) & 0x07) - { - case GPIO_IRQ_WRITE_DATA: - vrEmuTms9918WriteDataImpl(sio_hw->gpio_in >> GPIO_CD0); - break; - - case GPIO_IRQ_READ_DATA: - sio_hw->gpio_oe_set = GPIO_CD_MASK; - sio_hw->gpio_out = nextValueInt; - vrEmuTms9918ReadDataImpl(); - break; - - case GPIO_IRQ_WRITE_REG: - vrEmuTms9918WriteAddrImpl(sio_hw->gpio_in >> GPIO_CD0); - currentInt = vrEmuTms9918InterruptStatusImpl() ? 0 : GPIO_INT_MASK; - break; - - case GPIO_IRQ_READ_STATUS: - sio_hw->gpio_oe_set = GPIO_CD_MASK; - sio_hw->gpio_out = currentStatus; - currentInt = GPIO_INT_MASK; - currentStatus = (vrEmuTms9918ReadStatusImpl() << GPIO_CD0) | currentInt; - break; - - case GPIO_IRQ_INACTIVE1: - case GPIO_IRQ_INACTIVE2: - sio_hw->gpio_oe_clr = GPIO_CD_MASK; - sio_hw->gpio_out = currentInt; - break; - } - - // update read-ahead - nextValue = vrEmuTms9918ReadDataNoIncImpl() << GPIO_CD0; - nextValueInt = nextValue | currentInt; - } - */ - -uint32_t writeVals[16] = { 0 }; -uint32_t readVals[16] = { 0 }; -int nextReadVal = 0; -int nextWriteVal = 0; - - void __isr __scratch_x("isr") pio_irq_handler() { if (pio1->irq & (1u << 0)) // write? { - uint32_t writeVal = pio1->rxf[tmsWriteSm];;//pio_sm_get(pio1, tmsWriteSm); - writeVals[nextWriteVal++] = writeVal; - nextWriteVal &= 0x0f; + uint32_t writeVal = pio1->rxf[tmsWriteSm]; - if (writeVal & (1 << 14))//(GPIO_MODE_MASK >> GPIO_CD0)) // red/addr + if (writeVal & (GPIO_MODE_MASK >> GPIO_CD0)) // reg/addr { - // bg |= 0xf00; vrEmuTms9918WriteAddrImpl(writeVal & 0xff); currentInt = vrEmuTms9918InterruptStatusImpl(); gpio_put(GPIO_INT, !currentInt); } else // data { - //bg |= 0x0f; vrEmuTms9918WriteDataImpl(writeVal & 0xff); } @@ -240,19 +132,14 @@ void __isr __scratch_x("isr") pio_irq_handler() } else if (pio1->irq & (1u << 1)) // read? { - uint32_t readVal = pio1->rxf[tmsReadSm];//pio_sm_get(pio1, tmsReadSm); - readVals[nextReadVal++] = readVal;//0xf0f0f0f0; - nextReadVal &= 0x0f; - + uint32_t readVal = pio1->rxf[tmsReadSm]; if ((readVal & 0x04) == 0) // data { - // vrEmuTms9918ReadDataImpl(); nextValue = vrEmuTms9918ReadDataNoIncImpl(); } else // status { - //bg |= 0xf0; currentStatus = vrEmuTms9918ReadStatusImpl(); currentInt = false; gpio_put(GPIO_INT, !currentInt); @@ -285,10 +172,6 @@ static void __time_critical_func(tmsScanline)(uint16_t y, VgaParams* params, uin uint16_t bg = tms9918PaletteBGR12[vrEmuTms9918RegValue(TMS_REG_FG_BG_COLOR) & 0x0f]; - //if (y == 0) bg = 0; - //if (currentInt) bg = 0x000f; else bg = 0x00f0; - - /*** top and bottom borders ***/ if (y < vBorder || y >= (vBorder + TMS9918_PIXELS_Y)) { @@ -296,39 +179,12 @@ static void __time_critical_func(tmsScanline)(uint16_t y, VgaParams* params, uin { pixels[x] = bg; } -/* - if (y < vBorder) - { - uint32_t writeVal = writeVals[y & 0x0f]; - for (int i = 0; i < 32; ++i) - { - uint16_t bitColor = ((writeVal << i) & 0x80000000) ? 0x0fff : 0x000; - int offset = i * 16 + hBorder; - for (int x = 0; x < 16; ++x) - { - pixels[offset + x] = bitColor; - } - } - } - else - { - uint32_t readVal = readVals[y & 0x0f]; - for (int i = 0; i < 32; ++i) - { - uint16_t bitColor = ((readVal << i) & 0x80000000) ? 0x0fff : 0x000; - int offset = i * 16 + hBorder; - for (int x = 0; x < 16; ++x) - { - pixels[offset + x] = bitColor; - } - } - } -*/ -/* source: C:/Users/troy/OneDrive/Documents/projects/pico9918/src/res/splash.png - * size : 172px x 10px - * : 3440 bytes - * format: 16bpp abgr image - */ + + /* source: C:/Users/troy/OneDrive/Documents/projects/pico9918/src/res/splash.png + * size : 172px x 10px + * : 3440 bytes + * format: 16bpp abgr image + */ if (y >= vBorder + TMS9918_PIXELS_Y + 12) { y -= vBorder + TMS9918_PIXELS_Y + 12; @@ -362,13 +218,10 @@ static void __time_critical_func(tmsScanline)(uint16_t y, VgaParams* params, uin /* generate the scanline */ uint8_t newStatus = vrEmuTms9918ScanLine(y, tmsScanlineBuffer, currentStatus) & 0x7f; - //disableGpioInterrupts(); if (currentInt) newStatus |= 0x80; vrEmuTms9918SetStatusImpl(newStatus); currentStatus = newStatus; updateTmsReadAhead(); - //currentStatus = (newStatus << GPIO_CD0) | GPIO_INT_MASK; - //enableGpioInterrupts(); /* convert from palette to bgr12 */ int tmsX = 0; @@ -398,13 +251,11 @@ static void __time_critical_func(tmsScanline)(uint16_t y, VgaParams* params, uin /*** interrupt signal? ***/ if (y == TMS9918_PIXELS_Y - 1) { - // disableGpioInterrupts(); vrEmuTms9918InterruptSetImpl(); currentInt = vrEmuTms9918InterruptStatusImpl();// ? 0 : GPIO_INT_MASK; currentStatus |= 0x80; updateTmsReadAhead(); - // enableGpioInterrupts(); gpio_put(GPIO_INT, !currentInt); } } @@ -434,14 +285,16 @@ uint initClock(uint gpio, float freqHz) return clkSm++; } +/* + * Set up PIOs for TMS9918 <-> CPU interface + */ void tmsPioInit() { uint tmsWriteProgram = pio_add_program(pio1, &tmsWrite_program); - pio_sm_config writeConfig = tmsWrite_program_get_default_config(tmsWriteProgram); sm_config_set_in_pins(&writeConfig, GPIO_CD0); - sm_config_set_in_shift(&writeConfig, false, true/*true*/, 16); // R shift, autopush @ 16 bits + sm_config_set_in_shift(&writeConfig, false, true, 16); // L shift, autopush @ 16 bits sm_config_set_clkdiv(&writeConfig, 4.0f); pio_sm_init(pio1, tmsWriteSm, tmsWriteProgram, &writeConfig); @@ -458,17 +311,15 @@ void tmsPioInit() sm_config_set_in_pins(&readConfig, GPIO_CSR); sm_config_set_jmp_pin(&readConfig, GPIO_MODE); sm_config_set_out_pins(&readConfig, GPIO_CD0, 8); - sm_config_set_in_shift(&readConfig, false, false, 32); // R shift, autopush @ 16 bits - sm_config_set_out_shift(&readConfig, true, false, 32); // R shift, autopush @ 16 bits + sm_config_set_in_shift(&readConfig, false, false, 32); // L shift + sm_config_set_out_shift(&readConfig, true, false, 32); // R shift sm_config_set_clkdiv(&readConfig, 4.0f); pio_sm_init(pio1, tmsReadSm, tmsReadProgram, &readConfig); pio_sm_set_enabled(pio1, tmsReadSm, true); irq_set_exclusive_handler(PIO1_IRQ_0, pio_irq_handler); - //irq_set_exclusive_handler(PIO1_IRQ_1, pio_irq_handler1); irq_set_enabled(PIO1_IRQ_0, true); - //irq_set_enabled(PIO1_IRQ_1, true); pio_set_irq0_source_enabled(pio1, pis_interrupt0, true); pio_set_irq0_source_enabled(pio1, pis_interrupt1, true); pio_interrupt_clear(pio1, 0); @@ -484,20 +335,11 @@ void tmsPioInit() void proc1Entry() { // set up gpio pins - gpio_init_mask(GPIO_CD_MASK | GPIO_CSR_MASK | GPIO_CSW_MASK | GPIO_MODE_MASK | GPIO_INT_MASK | GPIO_LED_MASK); + gpio_init_mask(GPIO_CD_MASK | GPIO_CSR_MASK | GPIO_CSW_MASK | GPIO_MODE_MASK | GPIO_INT_MASK); gpio_put_all(GPIO_INT_MASK); - gpio_set_dir_all_bits(GPIO_INT_MASK | GPIO_LED_MASK); // int is an output - + gpio_set_dir_all_bits(GPIO_INT_MASK); // int is an output tmsPioInit(); - // set up gpio interrupts - /* - irq_set_priority(IO_IRQ_BANK0, 0); - irq_set_exclusive_handler(IO_IRQ_BANK0, gpioExclusiveCallbackProc1); - gpio_set_irq_enabled(GPIO_CSW, GPIO_IRQ_EDGE_FALL | GPIO_IRQ_EDGE_RISE, true); - gpio_set_irq_enabled(GPIO_CSR, GPIO_IRQ_EDGE_FALL | GPIO_IRQ_EDGE_RISE, true); - irq_set_enabled(IO_IRQ_BANK0, true); - */ // set up the GROMCLK and CPUCLK signals initClock(GPIO_GROMCL, TMS_CRYSTAL_FREQ_HZ / 24.0f); @@ -519,21 +361,13 @@ int main(void) * that comes close to being divisible by 25.175MHz. 252.0 is close... enough :) * I do have code which sets the best clock baased on the chosen VGA mode, * but this'll do for now. */ - vreg_set_voltage(VREG_VOLTAGE_1_20); - //set_sys_clock_khz(315000, false); - //set_sys_clock_khz(PICO_CLOCK_HZ / 1000, false); - set_sys_clock_pll(1260000000, 5, 1); // 252000 - //set_sys_clock_pll(1260000000, 4, 1); // 315000 - //set_sys_clock_pll(1512000000, 5, 1); // 302000 /* we need one of these. it's the main guy */ vrEmuTms9918Init(); - /* set up the GPIO pins and interrupt handler */ - - + /* launch core 1 which handles TMS9918<->CPU and rendering scanlines */ multicore_launch_core1(proc1Entry); /* then set up VGA output */ diff --git a/src/tms9918.pio b/src/tms9918.pio index 2c9142f..51f8dfc 100644 --- a/src/tms9918.pio +++ b/src/tms9918.pio @@ -9,41 +9,53 @@ * */ +; ----------------------------------------------------------------------------- +; tmsRead - monitor the CSR pin and send either status or data value +; +; due to the read-ahead nature of the TMS9918, we have the +; possible read values already. The FIFO contains the current +; values for status, read data and pin direction + .program tmsRead pull block - mov x, osr + mov x, osr ; ensure we have a valid fifio value in x + .wrap_target -entry: - wait 1 gpio 26 ; once CSR is high, change pindirs to inputs - mov osr, null + wait 1 gpio 26 ; wait for CSR to go high (inactive) + mov osr, null ; change CD0-7 pindirs to inputs out pindirs, 8 pullLoop: pull noblock ; continuously empty the rx fifo mov x, osr ; since we want the latest value mov isr, null - in pins, 1 - mov y, isr - jmp y--, pullLoop; still 1? loop, otherwise, let's read + in pins, 1 ; hacky way to jmp based on the CSR pin + mov y, isr ; since we can only have one jmp pin + jmp y--, pullLoop ; still 1? loop, otherwise, let's read - out pindirs, 8 + out pindirs, 8 ; set up CD0-7 as outputs jmp pin readStatus ; if 'mode' is high, read status bits readData: - out pins, 8 ; output - in pins, 8 + out pins, 8 ; mode is low, send the data byte jmp endPart readStatus: - out null, 8 ; skip data bits - out pins, 8 ; output - in pins, 3 + out null, 8 ; skip data bits + out pins, 8 ; output the status byte endPart: - push - irq set 1 ; push the last type of data we read + in pins, 3 ; push CSR, CSW and MODE back through fifo + push ; push ^^^ back to cpu to process + irq set 1 ; it needs to know if we read status or data .wrap +; ----------------------------------------------------------------------------- +; tmsWrite - monitor the CSW pin and pass on pin state via FIFO +; +; very simple grab the data and send it through... +; + .program tmsWrite - wait 0 gpio 27 [12] - in pins, 16 ; auto-push - irq set 0 - wait 1 gpio 27 [12] + wait 0 gpio 27 [12] ; wait for CSW to go active (low) + in pins, 16 ; grab the data CD0-7 and auto-push + irq set 0 ; inform the cpu it has some data + wait 1 gpio 27 [12] ; wait for CSW high (inactive) .wrap From 266ef3fe1c9d6033d49713c3ff6c9dfd07253fe4 Mon Sep 17 00:00:00 2001 From: Troy Schrapel Date: Wed, 10 Jul 2024 11:01:07 +0930 Subject: [PATCH 13/19] sync --- src/main.c | 104 +++++++++++++++++++++++++++++------------------- src/tms9918.pio | 16 +++++--- 2 files changed, 73 insertions(+), 47 deletions(-) diff --git a/src/main.c b/src/main.c index 3670d4d..1137f28 100644 --- a/src/main.c +++ b/src/main.c @@ -29,40 +29,38 @@ #include "hardware/clocks.h" #include "hardware/vreg.h" - //#include - - /* - * Pin mapping - * - * Pin | GPIO | Name | TMS9918A Pin - * -----+------+-----------+------------- - * 19 | 14 | CD0 | 24 - * 20 | 15 | CD1 | 23 - * 21 | 16 | CD2 | 22 - * 22 | 17 | CD3 | 21 - * 24 | 18 | CD4 | 20 - * 25 | 19 | CD5 | 19 - * 26 | 20 | CD6 | 18 - * 27 | 21 | CD7 | 17 - * 29 | 22 | /INT | 16 - * 30 | RUN | RST | 34 - * 31 | 26 | /CSR | 15 - * 32 | 27 | /CSW | 14 - * 34 | 28 | MODE | 13 - * 35 | 29 | GROMCLK | 37 - * 37 | 23 | CPUCLK | 38 - * - * Note: Due to GROMCLK and CPUCLK using GPIO23 and GPIO29 - * a genuine Raspberry Pi Pico can't be used. - * v0.3 of the PCB is designed for the DWEII? - * RP2040 USB-C module which exposes these additional - * GPIOs. A future pico9918 revision will do without - * an external RP2040 board and use the RP2040 directly. - * - * Purchase links: - * https://www.amazon.com/RP2040-Board-Type-C-Raspberry-Micropython/dp/B0CG9BY48X - * https://www.aliexpress.com/item/1005007066733934.html - */ + /* + * Pin mapping + * + * Pin | GPIO | Name | TMS9918A Pin + * -----+------+-----------+------------- + * 19 | 14 | CD0 | 24 + * 20 | 15 | CD1 | 23 + * 21 | 16 | CD2 | 22 + * 22 | 17 | CD3 | 21 + * 24 | 18 | CD4 | 20 + * 25 | 19 | CD5 | 19 + * 26 | 20 | CD6 | 18 + * 27 | 21 | CD7 | 17 + * 29 | 22 | /INT | 16 + * 30 | RUN | RST | 34 + * 31 | 26 | /CSR | 15 + * 32 | 27 | /CSW | 14 + * 34 | 28 | MODE | 13 + * 35 | 29 | GROMCLK | 37 + * 37 | 23 | CPUCLK | 38 + * + * Note: Due to GROMCLK and CPUCLK using GPIO23 and GPIO29 + * a genuine Raspberry Pi Pico can't be used. + * v0.3 of the PCB is designed for the DWEII? + * RP2040 USB-C module which exposes these additional + * GPIOs. A future pico9918 revision will do without + * an external RP2040 board and use the RP2040 directly. + * + * Purchase links: + * https://www.amazon.com/RP2040-Board-Type-C-Raspberry-Micropython/dp/B0CG9BY48X + * https://www.aliexpress.com/item/1005007066733934.html + */ #define GPIO_CD0 14 #define GPIO_CSR 26 @@ -83,7 +81,7 @@ #define PICO_CLOCK_HZ 252000000 - /* file globals */ + /* file globals */ static uint8_t nextValue = 0; /* TMS9918A read-ahead value */ static bool currentInt = false; /* current interrupt state */ @@ -114,13 +112,13 @@ void __isr __scratch_x("isr") pio_irq_handler() { uint32_t writeVal = pio1->rxf[tmsWriteSm]; - if (writeVal & (GPIO_MODE_MASK >> GPIO_CD0)) // reg/addr + if (writeVal & (GPIO_MODE_MASK >> GPIO_CD0)) // write reg/addr { vrEmuTms9918WriteAddrImpl(writeVal & 0xff); currentInt = vrEmuTms9918InterruptStatusImpl(); gpio_put(GPIO_INT, !currentInt); } - else // data + else // write data { vrEmuTms9918WriteDataImpl(writeVal & 0xff); } @@ -133,12 +131,13 @@ void __isr __scratch_x("isr") pio_irq_handler() else if (pio1->irq & (1u << 1)) // read? { uint32_t readVal = pio1->rxf[tmsReadSm]; - if ((readVal & 0x04) == 0) // data + + if ((readVal & 0x04) == 0) // read data { vrEmuTms9918ReadDataImpl(); nextValue = vrEmuTms9918ReadDataNoIncImpl(); } - else // status + else // read status { currentStatus = vrEmuTms9918ReadStatusImpl(); currentInt = false; @@ -151,6 +150,25 @@ void __isr __scratch_x("isr") pio_irq_handler() irq_clear(PIO1_IRQ_0); } + +/* + * enable gpio interrupts inline + */ +static inline void enableTmsPioInterrupts() +{ + *((io_rw_32*)(PPB_BASE + M0PLUS_NVIC_ICPR_OFFSET)) = 1u << PIO1_IRQ_0; + *((io_rw_32*)(PPB_BASE + M0PLUS_NVIC_ISER_OFFSET)) = 1u << PIO1_IRQ_0; +} + +/* + * disable gpio interrupts inline + */ +static inline void disableTmsPioInterrupts() +{ + *((io_rw_32*)(PPB_BASE + M0PLUS_NVIC_ICER_OFFSET)) = 1u << PIO1_IRQ_0; +} + + /* * generate a single VGA scanline (called by vgaLoop(), runs on proc1) */ @@ -220,8 +238,10 @@ static void __time_critical_func(tmsScanline)(uint16_t y, VgaParams* params, uin if (currentInt) newStatus |= 0x80; vrEmuTms9918SetStatusImpl(newStatus); + disableTmsPioInterrupts(); currentStatus = newStatus; updateTmsReadAhead(); + enableTmsPioInterrupts(); /* convert from palette to bgr12 */ int tmsX = 0; @@ -252,9 +272,11 @@ static void __time_critical_func(tmsScanline)(uint16_t y, VgaParams* params, uin if (y == TMS9918_PIXELS_Y - 1) { vrEmuTms9918InterruptSetImpl(); - currentInt = vrEmuTms9918InterruptStatusImpl();// ? 0 : GPIO_INT_MASK; + currentInt = vrEmuTms9918InterruptStatusImpl(); + disableTmsPioInterrupts(); currentStatus |= 0x80; updateTmsReadAhead(); + enableTmsPioInterrupts(); gpio_put(GPIO_INT, !currentInt); } @@ -272,7 +294,7 @@ uint initClock(uint gpio, float freqHz) clocksPioOffset = pio_add_program(pio0, &clock_program); } - static uint clkSm = 2;//pio_claim_unused_sm(pio1, true); + static uint clkSm = 2; clock_program_init(pio0, clkSm, clocksPioOffset, gpio); float clockDiv = (float)PICO_CLOCK_HZ / (TMS_CRYSTAL_FREQ_HZ * 10.0f); diff --git a/src/tms9918.pio b/src/tms9918.pio index 51f8dfc..6126896 100644 --- a/src/tms9918.pio +++ b/src/tms9918.pio @@ -17,12 +17,14 @@ ; values for status, read data and pin direction .program tmsRead +.define public CSR_PIN 26 + pull block mov x, osr ; ensure we have a valid fifio value in x .wrap_target - wait 1 gpio 26 ; wait for CSR to go high (inactive) - mov osr, null ; change CD0-7 pindirs to inputs + wait 1 gpio CSR_PIN ; wait for CSR to go high (inactive) + mov osr, null ; change CD0-7 pindirs to inputs out pindirs, 8 pullLoop: @@ -54,8 +56,10 @@ endPart: ; .program tmsWrite - wait 0 gpio 27 [12] ; wait for CSW to go active (low) - in pins, 16 ; grab the data CD0-7 and auto-push - irq set 0 ; inform the cpu it has some data - wait 1 gpio 27 [12] ; wait for CSW high (inactive) +.define public CSW_PIN 27 + + wait 0 gpio CSW_PIN [12] ; wait for CSW to go active (low) + in pins, 16 ; grab the data CD0-7 and auto-push + irq set 0 ; inform the cpu it has some data + wait 1 gpio CSW_PIN [12] ; wait for CSW high (inactive) .wrap From ae9a824684c1b68deb472316bee31020cdcd8697 Mon Sep 17 00:00:00 2001 From: Troy Schrapel Date: Thu, 11 Jul 2024 16:04:57 +0930 Subject: [PATCH 14/19] more binary size reduction, int sync fixes --- src/main.c | 94 ++++++++++++++++++++++---------------------- src/res/splash.png | Bin 425 -> 280 bytes src/tms9918.pio | 23 ++++++++--- tools/img2carray.py | 31 ++++++++++----- 4 files changed, 88 insertions(+), 60 deletions(-) diff --git a/src/main.c b/src/main.c index 1137f28..19c22ce 100644 --- a/src/main.c +++ b/src/main.c @@ -54,7 +54,7 @@ * a genuine Raspberry Pi Pico can't be used. * v0.3 of the PCB is designed for the DWEII? * RP2040 USB-C module which exposes these additional - * GPIOs. A future pico9918 revision will do without + * GPIOs. A future pico9918 revision (v0.4+) will do without * an external RP2040 board and use the RP2040 directly. * * Purchase links: @@ -63,8 +63,8 @@ */ #define GPIO_CD0 14 -#define GPIO_CSR 26 -#define GPIO_CSW 27 +#define GPIO_CSR tmsRead_CSR_PIN // defined in tms9918.pio +#define GPIO_CSW tmsWrite_CSW_PIN // defined in tms9918.pio #define GPIO_MODE 28 #define GPIO_INT 22 #define GPIO_GROMCL 29 @@ -80,6 +80,9 @@ #define PICO_CLOCK_HZ 252000000 +#define TMS_PIO pio1 +#define TMS_IRQ PIO1_IRQ_0 + /* file globals */ @@ -95,22 +98,23 @@ const uint tmsReadSm = 1; /* * update the value send to the read PIO */ -static void __attribute__((noinline)) updateTmsReadAhead() +inline static void updateTmsReadAhead() { uint32_t readAhead = 0xff; // pin direction readAhead |= nextValue << 8; readAhead |= currentStatus << 16; - pio_sm_put(pio1, tmsReadSm, readAhead); + pio_sm_put(TMS_PIO, tmsReadSm, readAhead); } /* * handle interrupts from the TMS9918<->CPU interface */ -void __isr __scratch_x("isr") pio_irq_handler() +void __not_in_flash_func(pio_irq_handler)() { - if (pio1->irq & (1u << 0)) // write? + + if ((TMS_PIO->fstat & (1u << (PIO_FSTAT_RXEMPTY_LSB + tmsWriteSm))) == 0) // write? { - uint32_t writeVal = pio1->rxf[tmsWriteSm]; + uint32_t writeVal = TMS_PIO->rxf[tmsWriteSm]; if (writeVal & (GPIO_MODE_MASK >> GPIO_CD0)) // write reg/addr { @@ -125,12 +129,10 @@ void __isr __scratch_x("isr") pio_irq_handler() nextValue = vrEmuTms9918ReadDataNoIncImpl(); updateTmsReadAhead(); - - pio1->irq = (1u << 0); } - else if (pio1->irq & (1u << 1)) // read? + else if ((TMS_PIO->fstat & (1u << (PIO_FSTAT_RXEMPTY_LSB + tmsReadSm))) == 0) // read? { - uint32_t readVal = pio1->rxf[tmsReadSm]; + uint32_t readVal = TMS_PIO->rxf[tmsReadSm]; if ((readVal & 0x04) == 0) // read data { @@ -144,10 +146,7 @@ void __isr __scratch_x("isr") pio_irq_handler() gpio_put(GPIO_INT, !currentInt); } updateTmsReadAhead(); - - pio1->irq = (1u << 1); } - irq_clear(PIO1_IRQ_0); } @@ -156,8 +155,9 @@ void __isr __scratch_x("isr") pio_irq_handler() */ static inline void enableTmsPioInterrupts() { - *((io_rw_32*)(PPB_BASE + M0PLUS_NVIC_ICPR_OFFSET)) = 1u << PIO1_IRQ_0; - *((io_rw_32*)(PPB_BASE + M0PLUS_NVIC_ISER_OFFSET)) = 1u << PIO1_IRQ_0; + __dmb(); + *((io_rw_32*)(PPB_BASE + M0PLUS_NVIC_ICPR_OFFSET)) = 1u << TMS_IRQ; + *((io_rw_32*)(PPB_BASE + M0PLUS_NVIC_ISER_OFFSET)) = 1u << TMS_IRQ; } /* @@ -165,7 +165,8 @@ static inline void enableTmsPioInterrupts() */ static inline void disableTmsPioInterrupts() { - *((io_rw_32*)(PPB_BASE + M0PLUS_NVIC_ICER_OFFSET)) = 1u << PIO1_IRQ_0; + *((io_rw_32*)(PPB_BASE + M0PLUS_NVIC_ICER_OFFSET)) = 1u << TMS_IRQ; + __dmb(); } @@ -200,22 +201,27 @@ static void __time_critical_func(tmsScanline)(uint16_t y, VgaParams* params, uin /* source: C:/Users/troy/OneDrive/Documents/projects/pico9918/src/res/splash.png * size : 172px x 10px - * : 3440 bytes - * format: 16bpp abgr image + * : 430 bytes + * format: 16-bit abgr palette, 2bpp indexed image */ if (y >= vBorder + TMS9918_PIXELS_Y + 12) { y -= vBorder + TMS9918_PIXELS_Y + 12; if (y < splashHeight) { - uint16_t* splashPtr = splash + (y * splashWidth); - for (int x = 4; x < 4 + splashWidth; ++x) + uint8_t* splashPtr = splash + (y * splashWidth / 4); + for (int x = 4; x < 4 + splashWidth; x += 4) { - uint16_t c = *(splashPtr++); - if (c & 0xf000) - { - pixels[x] = c; - } + uint8_t c = *(splashPtr++); + uint8_t p0 = (c & 0xc0); + uint8_t p1 = (c & 0x30); + uint8_t p2 = (c & 0x0c); + uint8_t p3 = (c & 0x03); + + if (p0) { pixels[x] = splash_pal[(p0 >> 6)]; } + if (p1) { pixels[x + 1] = splash_pal[(p1 >> 4)]; } + if (p2) { pixels[x + 2] = splash_pal[(p2 >> 2)]; } + if (p3) { pixels[x + 3] = splash_pal[p3]; } } } } @@ -237,8 +243,8 @@ static void __time_critical_func(tmsScanline)(uint16_t y, VgaParams* params, uin uint8_t newStatus = vrEmuTms9918ScanLine(y, tmsScanlineBuffer, currentStatus) & 0x7f; if (currentInt) newStatus |= 0x80; - vrEmuTms9918SetStatusImpl(newStatus); disableTmsPioInterrupts(); + vrEmuTms9918SetStatusImpl(newStatus); currentStatus = newStatus; updateTmsReadAhead(); enableTmsPioInterrupts(); @@ -271,14 +277,13 @@ static void __time_critical_func(tmsScanline)(uint16_t y, VgaParams* params, uin /*** interrupt signal? ***/ if (y == TMS9918_PIXELS_Y - 1) { + disableTmsPioInterrupts(); vrEmuTms9918InterruptSetImpl(); currentInt = vrEmuTms9918InterruptStatusImpl(); - disableTmsPioInterrupts(); currentStatus |= 0x80; updateTmsReadAhead(); - enableTmsPioInterrupts(); - gpio_put(GPIO_INT, !currentInt); + enableTmsPioInterrupts(); } } @@ -312,21 +317,21 @@ uint initClock(uint gpio, float freqHz) */ void tmsPioInit() { - uint tmsWriteProgram = pio_add_program(pio1, &tmsWrite_program); + uint tmsWriteProgram = pio_add_program(TMS_PIO, &tmsWrite_program); pio_sm_config writeConfig = tmsWrite_program_get_default_config(tmsWriteProgram); sm_config_set_in_pins(&writeConfig, GPIO_CD0); sm_config_set_in_shift(&writeConfig, false, true, 16); // L shift, autopush @ 16 bits sm_config_set_clkdiv(&writeConfig, 4.0f); - pio_sm_init(pio1, tmsWriteSm, tmsWriteProgram, &writeConfig); - pio_sm_set_enabled(pio1, tmsWriteSm, true); + pio_sm_init(TMS_PIO, tmsWriteSm, tmsWriteProgram, &writeConfig); + pio_sm_set_enabled(TMS_PIO, tmsWriteSm, true); - uint tmsReadProgram = pio_add_program(pio1, &tmsRead_program); + uint tmsReadProgram = pio_add_program(TMS_PIO, &tmsRead_program); for (uint i = 0; i < 8; ++i) { - pio_gpio_init(pio1, GPIO_CD0 + i); + pio_gpio_init(TMS_PIO, GPIO_CD0 + i); } pio_sm_config readConfig = tmsRead_program_get_default_config(tmsReadProgram); @@ -337,17 +342,14 @@ void tmsPioInit() sm_config_set_out_shift(&readConfig, true, false, 32); // R shift sm_config_set_clkdiv(&readConfig, 4.0f); - pio_sm_init(pio1, tmsReadSm, tmsReadProgram, &readConfig); - pio_sm_set_enabled(pio1, tmsReadSm, true); - - irq_set_exclusive_handler(PIO1_IRQ_0, pio_irq_handler); - irq_set_enabled(PIO1_IRQ_0, true); - pio_set_irq0_source_enabled(pio1, pis_interrupt0, true); - pio_set_irq0_source_enabled(pio1, pis_interrupt1, true); - pio_interrupt_clear(pio1, 0); - pio_interrupt_clear(pio1, 1); + pio_sm_init(TMS_PIO, tmsReadSm, tmsReadProgram, &readConfig); + pio_sm_set_enabled(TMS_PIO, tmsReadSm, true); + irq_set_exclusive_handler(TMS_IRQ, pio_irq_handler); + irq_set_enabled(TMS_IRQ, true); + pio_set_irq0_source_enabled(TMS_PIO, pis_sm0_rx_fifo_not_empty, true); + pio_set_irq0_source_enabled(TMS_PIO, pis_sm1_rx_fifo_not_empty, true); - pio_sm_put(pio1, tmsReadSm, 0x000000ff); + pio_sm_put(TMS_PIO, tmsReadSm, 0x000000ff); } diff --git a/src/res/splash.png b/src/res/splash.png index b22e199ba51783366fd9c234502d0ac606d84ad6..42575cda39182241f75ded7a8d8edb09ffc29c20 100644 GIT binary patch delta 235 zcmV<{9m`OxIR4C7lkues-FbG4v^nO{?fxV0G<0CLwrz`@yakntY zyzqksZZABItKm&f5Fn|109`^l%(J+oiJ=oFOu4cp^v?Wyvk_vWO#qcg!;(7wSd~|Z z`W$&)Br(tS;cCL>?cruEv4b5SwZSymz}#ul57*D!fy-(odeAHTlELIdZ{ lEZ24H%)+n9@8h??^#uq6#8ypq>ihrz002ovPDHLkV1i%=WI+G` delta 381 zcmV-@0fPRR0;vO#7zqdl0000fbhRsyAt8ToNLh0L01m str: # output the image array prototype proto += typePrefix + dataType + varNamePrefix + varName + "[]" + suffix if isHeader: - proto += "\nconst int " + varNamePrefix + varName + \ - "Width = " + str(src.width) + ";\n" - proto += "const int " + varNamePrefix + varName + \ - "Height = " + str(src.height) + ";\n" + proto += "\nconst int " + varNamePrefix + varName + \ + "Width = " + str(src.width) + ";\n" + proto += "const int " + varNamePrefix + varName + \ + "Height = " + str(src.height) + ";\n" return proto @@ -210,6 +210,7 @@ def imageToArrayContents(src, pix, bpp) -> str: rows = [] for y in range(src.height): row = [] + value = 0 for x in range(src.width): col = pix[x, y] if hasattr(col, "__len__"): @@ -219,12 +220,21 @@ def imageToArrayContents(src, pix, bpp) -> str: row.append(encodeRGBA32ToABGR16Hex(col[0], col[1], col[2], a)) elif bpp == 8: row.append("{0:#0{1}x}".format(col, 4)) - else: - if x & 1: - value = value | col + elif bpp == 4: + value = value | col + if (x & 1): + row.append("{0:#0{1}x}".format(value, 4)) + value = 0 + else: + value <<= bpp + else: # 2bpp + value = value | col + if (x & 3) == 3: row.append("{0:#0{1}x}".format(value, 4)) + value = 0 else: - value = col << 4 + value <<= bpp + rows.append(", ".join(row)) return "\n " + (",\n ".join(rows)) @@ -254,7 +264,10 @@ def processImageFile(infile, srcOutput, hdrOutput, args, inRam) -> None: bpp = 16 if src.palette: - bpp = 4 if len(src.palette.tobytes()) <= (16 * 3) else 8 + if len(src.palette.tobytes()) <= (4 * 3): + bpp = 2 + else: + bpp = 4 if len(src.palette.tobytes()) <= (16 * 3) else 8 comment = generateArrayComment(infile, src, bpp) From be4ef05b52016ff36c9c84bda1fae239d35e4b59 Mon Sep 17 00:00:00 2001 From: Troy Schrapel Date: Thu, 11 Jul 2024 18:24:43 +0930 Subject: [PATCH 15/19] simplified the clock pios. no need for adjustment via fifo. --- src/clocks.pio | 19 ------------------- src/main.c | 22 +++++++++++++--------- 2 files changed, 13 insertions(+), 28 deletions(-) diff --git a/src/clocks.pio b/src/clocks.pio index 4eb31d8..085fc71 100644 --- a/src/clocks.pio +++ b/src/clocks.pio @@ -10,26 +10,7 @@ */ .program clock - pull block .wrap_target set pins, 1 - mov x, osr -onDelay: - jmp x-- onDelay set pins, 0 - mov x, osr -offDelay: - jmp x-- offDelay .wrap - - -% c-sdk { - -void clock_program_init(PIO pio, uint sm, uint offset, uint pin) { - pio_gpio_init(pio, pin); - pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true); - pio_sm_config c = clock_program_get_default_config(offset); - sm_config_set_set_pins(&c, pin, 1); - pio_sm_init(pio, sm, offset, &c); -} -%} diff --git a/src/main.c b/src/main.c index 19c22ce..f0a2afd 100644 --- a/src/main.c +++ b/src/main.c @@ -189,7 +189,9 @@ static void __time_critical_func(tmsScanline)(uint16_t y, VgaParams* params, uin const uint32_t vBorder = (VIRTUAL_PIXELS_Y - TMS9918_PIXELS_Y) / 2; const uint32_t hBorder = (VIRTUAL_PIXELS_X - TMS9918_PIXELS_X * 2) / 2; - uint16_t bg = tms9918PaletteBGR12[vrEmuTms9918RegValue(TMS_REG_FG_BG_COLOR) & 0x0f]; + // uint16_t bg = tms9918PaletteBGR12[vrEmuTms9918RegValue(TMS_REG_FG_BG_COLOR) & 0x0f]; + + uint16_t bg = currentInt ? 0x000f : 0x00f0; /*** top and bottom borders ***/ if (y < vBorder || y >= (vBorder + TMS9918_PIXELS_Y)) @@ -225,7 +227,6 @@ static void __time_critical_func(tmsScanline)(uint16_t y, VgaParams* params, uin } } } - return; } @@ -242,8 +243,8 @@ static void __time_critical_func(tmsScanline)(uint16_t y, VgaParams* params, uin /* generate the scanline */ uint8_t newStatus = vrEmuTms9918ScanLine(y, tmsScanlineBuffer, currentStatus) & 0x7f; - if (currentInt) newStatus |= 0x80; disableTmsPioInterrupts(); + if (currentInt) newStatus |= 0x80; vrEmuTms9918SetStatusImpl(newStatus); currentStatus = newStatus; updateTmsReadAhead(); @@ -263,7 +264,7 @@ static void __time_critical_func(tmsScanline)(uint16_t y, VgaParams* params, uin { for (int x = hBorder; x < hBorder + TMS9918_PIXELS_X * 2; x += 2, ++tmsX) { - pixels[x] = tms9918PaletteBGR12[tmsScanlineBuffer[tmsX] & 0x0f]; + pixels[x] = tms9918PaletteBGR12[tmsScanlineBuffer[tmsX]]; pixels[x + 1] = pixels[x]; } } @@ -300,15 +301,18 @@ uint initClock(uint gpio, float freqHz) } static uint clkSm = 2; - clock_program_init(pio0, clkSm, clocksPioOffset, gpio); - float clockDiv = (float)PICO_CLOCK_HZ / (TMS_CRYSTAL_FREQ_HZ * 10.0f); + pio_gpio_init(pio0, gpio); + pio_sm_set_consecutive_pindirs(pio0, clkSm, gpio, 1, true); + pio_sm_config c = clock_program_get_default_config(clocksPioOffset); + sm_config_set_set_pins(&c, gpio, 1); + + pio_sm_init(pio0, clkSm, clocksPioOffset, &c); + float clockDiv = (float)PICO_CLOCK_HZ / (freqHz * 2.0f); pio_sm_set_clkdiv(pio0, clkSm, clockDiv); pio_sm_set_enabled(pio0, clkSm, true); - pio_sm_put(pio0, clkSm, (uint)(PICO_CLOCK_HZ / clockDiv / (2.0f * freqHz)) - 3.0f); - return clkSm++; } @@ -366,7 +370,7 @@ void proc1Entry() tmsPioInit(); // set up the GROMCLK and CPUCLK signals - initClock(GPIO_GROMCL, TMS_CRYSTAL_FREQ_HZ / 24.0f); + initClock(GPIO_GROMCL, TMS_CRYSTAL_FREQ_HZ / 23.8f); initClock(GPIO_CPUCL, TMS_CRYSTAL_FREQ_HZ / 3.0f); // wait until everything else is ready, then run the vga loop From 8f693583576e236d36a22d50d02c10a93298fae0 Mon Sep 17 00:00:00 2001 From: Troy Schrapel Date: Fri, 12 Jul 2024 12:15:26 +0930 Subject: [PATCH 16/19] Fixed status/interrupt timing issues --- src/main.c | 36 +++++----- src/vga/vga.c | 143 ++++++++++++++++++++++++---------------- submodules/vrEmuTms9918 | 2 +- 3 files changed, 110 insertions(+), 71 deletions(-) diff --git a/src/main.c b/src/main.c index f0a2afd..72c16a5 100644 --- a/src/main.c +++ b/src/main.c @@ -141,7 +141,7 @@ void __not_in_flash_func(pio_irq_handler)() } else // read status { - currentStatus = vrEmuTms9918ReadStatusImpl(); + currentStatus = 0; currentInt = false; gpio_put(GPIO_INT, !currentInt); } @@ -241,14 +241,7 @@ static void __time_critical_func(tmsScanline)(uint16_t y, VgaParams* params, uin /*** main display region ***/ /* generate the scanline */ - uint8_t newStatus = vrEmuTms9918ScanLine(y, tmsScanlineBuffer, currentStatus) & 0x7f; - - disableTmsPioInterrupts(); - if (currentInt) newStatus |= 0x80; - vrEmuTms9918SetStatusImpl(newStatus); - currentStatus = newStatus; - updateTmsReadAhead(); - enableTmsPioInterrupts(); + uint8_t tempStatus = vrEmuTms9918ScanLine(y, tmsScanlineBuffer) & 0x7f; /* convert from palette to bgr12 */ int tmsX = 0; @@ -278,14 +271,27 @@ static void __time_critical_func(tmsScanline)(uint16_t y, VgaParams* params, uin /*** interrupt signal? ***/ if (y == TMS9918_PIXELS_Y - 1) { - disableTmsPioInterrupts(); - vrEmuTms9918InterruptSetImpl(); + tempStatus |= STATUS_INT; + } + + disableTmsPioInterrupts(); + if ((currentStatus & STATUS_INT) == 0) + { + if ((currentStatus & STATUS_5S) == 1) + { + currentStatus |= tempStatus & 0xe0; + } + else + { + currentStatus = tempStatus; + } + + vrEmuTms9918SetStatusImpl(currentStatus); currentInt = vrEmuTms9918InterruptStatusImpl(); - currentStatus |= 0x80; - updateTmsReadAhead(); gpio_put(GPIO_INT, !currentInt); - enableTmsPioInterrupts(); + updateTmsReadAhead(); } + enableTmsPioInterrupts(); } /* @@ -370,7 +376,7 @@ void proc1Entry() tmsPioInit(); // set up the GROMCLK and CPUCLK signals - initClock(GPIO_GROMCL, TMS_CRYSTAL_FREQ_HZ / 23.8f); + initClock(GPIO_GROMCL, TMS_CRYSTAL_FREQ_HZ / 24.0f); initClock(GPIO_CPUCL, TMS_CRYSTAL_FREQ_HZ / 3.0f); // wait until everything else is ready, then run the vga loop diff --git a/src/vga/vga.c b/src/vga/vga.c index c2f39a2..f427788 100644 --- a/src/vga/vga.c +++ b/src/vga/vga.c @@ -13,42 +13,33 @@ #include "vga.pio.h" #include "pio_utils.h" - //#include "pico/divider.h" #include "pico/multicore.h" #include "hardware/dma.h" #include "hardware/pio.h" #include "hardware/clocks.h" - //#include - //#include - //#include - //#include + // compile options +#define VGA_CRT_EFFECT 1 +#define VGA_SCANLINE_TIME_DEBUG 0 +#define VGA_HARDCODED_640 1 +#define VGA_NO_MALLOC 1 +#define VGA_COMBINE_SYNC 0 -#define SYNC_PINS_START 0 // first sync pin gpio number -#define SYNC_PINS_COUNT 2 // number of sync pins (h and v) - -#define RGB_PINS_START 2 // first rgb pin gpio number -#define RGB_PINS_COUNT 12 // number of rgb pins - -#define VGA_PIO pio0_hw // which pio are we using for vga? -#define SYNC_SM 0 // vga sync state machine index -#define RGB_SM 1 // vga rgb state machine index - -#define END_OF_SCANLINE_MSG 0x40000000 -#define END_OF_FRAME_MSG 0x80000000 - -#define CRT_EFFECT 1 -#define SCANLINE_TIME_DEBUG 0 -#if 1 + // a number of compiile-time optimisations can occur + // if it knows some of the VGA parameters +#if VGA_HARDCODED_640 #define VIRTUAL_PIXELS_X 640 #define VIRTUAL_PIXELS_Y 240 #else +#include "pico/divider.h" #define VIRTUAL_PIXELS_X vgaParams.params.hVirtualPixels #define VIRTUAL_PIXELS_Y vgaParams.params.vVirtualPixels #endif + +// avoid bringing in math.h int roundflt(float x) { if (x < 0.0f) @@ -57,7 +48,20 @@ int roundflt(float x) return (int)(x + 0.5f); } -#define round roundflt + +#define SYNC_PINS_START 0 // first sync pin gpio number +#define SYNC_PINS_COUNT 2 // number of sync pins (h and v) + +#define RGB_PINS_START 2 // first rgb pin gpio number +#define RGB_PINS_COUNT 12 // number of rgb pins + +#define VGA_PIO pio0_hw // which pio are we using for vga? +#define SYNC_SM 0 // vga sync state machine index +#define RGB_SM 1 // vga rgb state machine index + +#define END_OF_SCANLINE_MSG 0x40000000 +#define END_OF_FRAME_MSG 0x80000000 + /* * sync pio dma data buffers @@ -66,7 +70,13 @@ uint32_t __aligned(4) syncDataActive[4]; // active display area uint32_t __aligned(4) syncDataPorch[4]; // vertical porch uint32_t __aligned(4) syncDataSync[4]; // vertical sync -uint16_t __aligned(4) rgbDataBuffer[2 + SCANLINE_TIME_DEBUG][640 * sizeof(uint16_t)] = { 0 }; // two scanline buffers (odd and even) +#if VGA_NO_MALLOC +uint16_t __aligned(4) rgbDataBuffer[2 + VGA_SCANLINE_TIME_DEBUG][640 * sizeof(uint16_t)] = { 0 }; // two scanline buffers (odd and even) +#else +#include +uint16_t* __aligned(4) rgbDataBuffer[2 + VGA_SCANLINE_TIME_DEBUG] = { 0 }; // two scanline buffers (odd and even) +#endif + /* * file scope @@ -77,7 +87,7 @@ static uint syncDmaChanMask = 0; static uint rgbDmaChanMask = 0; static VgaInitParams vgaParams = { 0 }; -#if SCANLINE_TIME_DEBUG +#if VGA_SCANLINE_TIME_DEBUG bool hasRenderedNext = false; #endif @@ -109,28 +119,32 @@ static bool buildSyncData() return false; } - //rgbDataBuffer[0] = malloc(vgaParams.params.hVirtualPixels * sizeof(uint16_t)); - //rgbDataBuffer[1] = malloc(vgaParams.params.hVirtualPixels * sizeof(uint16_t)); +#if !VGA_NO_MALLOC + rgbDataBuffer[0] = malloc(VIRTUAL_PIXELS_X * sizeof(uint16_t)); + rgbDataBuffer[1] = malloc(VIRTUAL_PIXELS_X * sizeof(uint16_t)); +#endif -#if SCANLINE_TIME_DEBUG - //rgbDataBuffer[2] = malloc(vgaParams.params.hVirtualPixels * sizeof(uint16_t)); +#if VGA_SCANLINE_TIME_DEBUG +#if !VGA_NO_MALLOC + rgbDataBuffer[2] = malloc(VIRTUAL_PIXELS_X * sizeof(uint16_t)); +#endif - for (int i = 0; i < vgaParams.params.hVirtualPixels; ++i) + for (int i = 0; i < VIRTUAL_PIXELS_X; ++i) rgbDataBuffer[2][i] = 0x0f00; #endif - vgaParams.params.pioDivider = round(sysClockKHz / (float)minClockKHz); + vgaParams.params.pioDivider = roundflt(sysClockKHz / (float)minClockKHz); vgaParams.params.pioFreqKHz = sysClockKHz / vgaParams.params.pioDivider; vgaParams.params.pioClocksPerPixel = vgaParams.params.pioFreqKHz / (float)vgaParams.params.pixelClockKHz; vgaParams.params.pioClocksPerScaledPixel = vgaParams.params.pioFreqKHz * vgaParams.params.hPixelScale / (float)vgaParams.params.pixelClockKHz; - const uint32_t activeTicks = round(vgaParams.params.pioClocksPerPixel * (float)vgaParams.params.hSyncParams.displayPixels) - vga_sync_SETUP_OVERHEAD; - const uint32_t fPorchTicks = round(vgaParams.params.pioClocksPerPixel * (float)vgaParams.params.hSyncParams.frontPorchPixels) - vga_sync_SETUP_OVERHEAD; - const uint32_t syncTicks = round(vgaParams.params.pioClocksPerPixel * (float)vgaParams.params.hSyncParams.syncPixels) - vga_sync_SETUP_OVERHEAD; - const uint32_t bPorchTicks = round(vgaParams.params.pioClocksPerPixel * (float)vgaParams.params.hSyncParams.backPorchPixels) - vga_sync_SETUP_OVERHEAD; + const uint32_t activeTicks = roundflt(vgaParams.params.pioClocksPerPixel * (float)vgaParams.params.hSyncParams.displayPixels) - vga_sync_SETUP_OVERHEAD; + const uint32_t fPorchTicks = roundflt(vgaParams.params.pioClocksPerPixel * (float)vgaParams.params.hSyncParams.frontPorchPixels) - vga_sync_SETUP_OVERHEAD; + const uint32_t syncTicks = roundflt(vgaParams.params.pioClocksPerPixel * (float)vgaParams.params.hSyncParams.syncPixels) - vga_sync_SETUP_OVERHEAD; + const uint32_t bPorchTicks = roundflt(vgaParams.params.pioClocksPerPixel * (float)vgaParams.params.hSyncParams.backPorchPixels) - vga_sync_SETUP_OVERHEAD; - uint32_t rgbCyclesPerPixel = round(vgaParams.params.pioClocksPerScaledPixel); + uint32_t rgbCyclesPerPixel = roundflt(vgaParams.params.pioClocksPerScaledPixel); // compute sync bits @@ -139,6 +153,18 @@ static bool buildSyncData() const uint32_t vSyncOff = !vgaParams.params.vSyncParams.syncHigh << vga_sync_WORD_VSYNC_OFFSET; const uint32_t vSyncOn = vgaParams.params.vSyncParams.syncHigh << vga_sync_WORD_VSYNC_OFFSET; +#if VGA_COMBINE_SYNC + const uint32_t HoffVoff = hSyncOff | vSyncOff; + const uint32_t HonVoff = hSyncOn | vSyncOn; + const uint32_t HoffVon = hSyncOn | vSyncOn; + const uint32_t HonVon = hSyncOff | vSyncOff; +#else + const uint32_t HoffVoff = hSyncOff | vSyncOff; + const uint32_t HonVoff = hSyncOn | vSyncOff; + const uint32_t HoffVon = hSyncOff | vSyncOn; + const uint32_t HonVon = hSyncOn | vSyncOn; +#endif + // compute exec instructions const uint32_t instIrq = pio_encode_irq_set(false, vga_rgb_RGB_IRQ) << vga_sync_WORD_EXEC_OFFSET; const uint32_t instNop = pio_encode_nop() << vga_sync_WORD_EXEC_OFFSET; @@ -149,22 +175,22 @@ static bool buildSyncData() const int SYNC_LINE_HSYNC = 2; const int SYNC_LINE_BPORCH = 3; - syncDataActive[SYNC_LINE_ACTIVE] = instIrq | vSyncOff | hSyncOff | activeTicks; - syncDataActive[SYNC_LINE_FPORCH] = instNop | vSyncOff | hSyncOff | fPorchTicks; - syncDataActive[SYNC_LINE_HSYNC] = instNop | vSyncOff | hSyncOn | syncTicks; - syncDataActive[SYNC_LINE_BPORCH] = instNop | vSyncOff | hSyncOff | bPorchTicks; + syncDataActive[SYNC_LINE_ACTIVE] = instIrq | HoffVoff | activeTicks; + syncDataActive[SYNC_LINE_FPORCH] = instNop | HoffVoff | fPorchTicks; + syncDataActive[SYNC_LINE_HSYNC] = instNop | HonVoff | syncTicks; + syncDataActive[SYNC_LINE_BPORCH] = instNop | HoffVoff | bPorchTicks; // sync data for a front or back porch scanline - syncDataPorch[SYNC_LINE_ACTIVE] = instNop | vSyncOff | hSyncOff | activeTicks; - syncDataPorch[SYNC_LINE_FPORCH] = instNop | vSyncOff | hSyncOff | fPorchTicks; - syncDataPorch[SYNC_LINE_HSYNC] = instNop | vSyncOff | hSyncOn | syncTicks; - syncDataPorch[SYNC_LINE_BPORCH] = instNop | vSyncOff | hSyncOff | bPorchTicks; + syncDataPorch[SYNC_LINE_ACTIVE] = instNop | HoffVoff | activeTicks; + syncDataPorch[SYNC_LINE_FPORCH] = instNop | HoffVoff | fPorchTicks; + syncDataPorch[SYNC_LINE_HSYNC] = instNop | HonVoff | syncTicks; + syncDataPorch[SYNC_LINE_BPORCH] = instNop | HoffVoff | bPorchTicks; // sync data for a vsync scanline - syncDataSync[SYNC_LINE_ACTIVE] = instNop | vSyncOn | hSyncOff | activeTicks; - syncDataSync[SYNC_LINE_FPORCH] = instNop | vSyncOn | hSyncOff | fPorchTicks; - syncDataSync[SYNC_LINE_HSYNC] = instNop | vSyncOn | hSyncOn | syncTicks; - syncDataSync[SYNC_LINE_BPORCH] = instNop | vSyncOn | hSyncOff | bPorchTicks; + syncDataSync[SYNC_LINE_ACTIVE] = instNop | HoffVon | activeTicks; + syncDataSync[SYNC_LINE_FPORCH] = instNop | HoffVon | fPorchTicks; + syncDataSync[SYNC_LINE_HSYNC] = instNop | HonVon | syncTicks; + syncDataSync[SYNC_LINE_BPORCH] = instNop | HoffVon | bPorchTicks; return true; } @@ -215,7 +241,7 @@ static void vgaInitSync() */ static void vgaInitRgb() { - const uint32_t rgbCyclesPerPixel = round(vgaParams.params.pioClocksPerScaledPixel); + const uint32_t rgbCyclesPerPixel = roundflt(vgaParams.params.pioClocksPerScaledPixel); // copy the rgb program and set the appropriate pixel delay uint16_t rgbProgramInstr[vga_rgb_program.length]; @@ -307,13 +333,19 @@ static void __isr __time_critical_func(dmaIrqHandler)(void) { dma_hw->ints0 = rgbDmaChanMask; - //divmod_result_t pxLineVal = divmod_u32u32(, vgaParams.params.vPixelScale); +#if VGA_HARDCODED_640 uint32_t pxLine = currentDisplayLine >> 1; uint32_t pxLineRpt = currentDisplayLine & 0x01; +#else + divmod_result_t pxLineVal = divmod_u32u32(currentDisplayLine, vgaParams.params.vPixelScale); + uint32_t pxLine = to_quotient_u32(pxLineVal); + uint32_t pxLineRpt = to_remainder_u32(pxLineVal); +#endif + uint32_t* currentBuffer = (uint32_t*)rgbDataBuffer[pxLine & 0x01]; currentDisplayLine++; -#if CRT_EFFECT +#if VGA_CRT_EFFECT if (pxLineRpt != 0) { for (int i = 0; i < 5; ++i) @@ -323,7 +355,8 @@ static void __isr __time_critical_func(dmaIrqHandler)(void) } #endif -#if SCANLINE_TIME_DEBUG +#if VGA_SCANLINE_TIME_DEBUG + // apply a few darkened values before passing to dma if (pxLineRpt != 0 && hasRenderedNext) { currentBuffer = (uint32_t*)rgbDataBuffer[2]; @@ -341,7 +374,7 @@ static void __isr __time_critical_func(dmaIrqHandler)(void) multicore_fifo_push_timeout_us(requestLine, 0); -#if SCANLINE_TIME_DEBUG +#if VGA_SCANLINE_TIME_DEBUG hasRenderedNext = false; #endif if (requestLine == VIRTUAL_PIXELS_Y - 1) @@ -349,8 +382,8 @@ static void __isr __time_critical_func(dmaIrqHandler)(void) multicore_fifo_push_timeout_us(END_OF_FRAME_MSG, 0); } } -#if CRT_EFFECT - else +#if VGA_CRT_EFFECT + else // apply a lame CRT effect, darkening every 2nd scanline { int end = VIRTUAL_PIXELS_X / 2; for (int i = 5; i < end; ++i) @@ -410,7 +443,7 @@ void __time_critical_func(vgaLoop)() { // get the next scanline pixels vgaParams.scanlineFn(message & 0xfff, &vgaParams.params, rgbDataBuffer[message & 0x01]); -#if SCANLINE_TIME_DEBUG +#if VGA_SCANLINE_TIME_DEBUG dma_channel_set_read_addr(rgbDmaChan, rgbDataBuffer[2], true); hasRenderedNext = true; #endif diff --git a/submodules/vrEmuTms9918 b/submodules/vrEmuTms9918 index 48e0e0f..49ef229 160000 --- a/submodules/vrEmuTms9918 +++ b/submodules/vrEmuTms9918 @@ -1 +1 @@ -Subproject commit 48e0e0f45b4bf445372b13c11816b8248df3c570 +Subproject commit 49ef229a1aacb37808208d3e12c0ad36fae74679 From 06d9fe63670407dac9db30c3777cae4c3b21690c Mon Sep 17 00:00:00 2001 From: Troy Schrapel Date: Fri, 12 Jul 2024 13:18:18 +0930 Subject: [PATCH 17/19] status --- src/main.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/main.c b/src/main.c index 72c16a5..7727afa 100644 --- a/src/main.c +++ b/src/main.c @@ -142,6 +142,7 @@ void __not_in_flash_func(pio_irq_handler)() else // read status { currentStatus = 0; + vrEmuTms9918SetStatusImpl(currentStatus); currentInt = false; gpio_put(GPIO_INT, !currentInt); } @@ -189,7 +190,7 @@ static void __time_critical_func(tmsScanline)(uint16_t y, VgaParams* params, uin const uint32_t vBorder = (VIRTUAL_PIXELS_Y - TMS9918_PIXELS_Y) / 2; const uint32_t hBorder = (VIRTUAL_PIXELS_X - TMS9918_PIXELS_X * 2) / 2; - // uint16_t bg = tms9918PaletteBGR12[vrEmuTms9918RegValue(TMS_REG_FG_BG_COLOR) & 0x0f]; + //uint16_t bg = tms9918PaletteBGR12[vrEmuTms9918RegValue(TMS_REG_FG_BG_COLOR) & 0x0f]; uint16_t bg = currentInt ? 0x000f : 0x00f0; @@ -241,7 +242,13 @@ static void __time_critical_func(tmsScanline)(uint16_t y, VgaParams* params, uin /*** main display region ***/ /* generate the scanline */ - uint8_t tempStatus = vrEmuTms9918ScanLine(y, tmsScanlineBuffer) & 0x7f; + uint8_t tempStatus = vrEmuTms9918ScanLine(y, tmsScanlineBuffer); + + /*** interrupt signal? ***/ + if (y == TMS9918_PIXELS_Y - 1) + { + tempStatus |= STATUS_INT; + } /* convert from palette to bgr12 */ int tmsX = 0; @@ -268,16 +275,10 @@ static void __time_critical_func(tmsScanline)(uint16_t y, VgaParams* params, uin pixels[x] = bg; } - /*** interrupt signal? ***/ - if (y == TMS9918_PIXELS_Y - 1) - { - tempStatus |= STATUS_INT; - } - disableTmsPioInterrupts(); if ((currentStatus & STATUS_INT) == 0) { - if ((currentStatus & STATUS_5S) == 1) + if ((currentStatus & STATUS_5S) != 0) { currentStatus |= tempStatus & 0xe0; } @@ -287,9 +288,10 @@ static void __time_critical_func(tmsScanline)(uint16_t y, VgaParams* params, uin } vrEmuTms9918SetStatusImpl(currentStatus); + updateTmsReadAhead(); + currentInt = vrEmuTms9918InterruptStatusImpl(); gpio_put(GPIO_INT, !currentInt); - updateTmsReadAhead(); } enableTmsPioInterrupts(); } @@ -427,4 +429,4 @@ int main(void) } return 0; -} \ No newline at end of file +} From 3ba44b6f1a86e1e82c8729a2acbf2f6f7b3d57a0 Mon Sep 17 00:00:00 2001 From: Troy Schrapel Date: Fri, 12 Jul 2024 13:47:38 +0930 Subject: [PATCH 18/19] v0.3.1 --- src/main.c | 13 +++++++------ src/res/splash.png | Bin 280 -> 285 bytes 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/main.c b/src/main.c index 7727afa..ec7acc5 100644 --- a/src/main.c +++ b/src/main.c @@ -78,7 +78,10 @@ #define TMS_CRYSTAL_FREQ_HZ 10738635.0f -#define PICO_CLOCK_HZ 252000000 +#define PICO_CLOCK_PLL 1260000000 +#define PICO_CLOCK_PLL_DIV1 5 +#define PICO_CLOCK_PLL_DIV2 1 +#define PICO_CLOCK_HZ (PICO_CLOCK_PLL / PICO_CLOCK_PLL_DIV1 / PICO_CLOCK_PLL_DIV2) #define TMS_PIO pio1 #define TMS_IRQ PIO1_IRQ_0 @@ -190,9 +193,7 @@ static void __time_critical_func(tmsScanline)(uint16_t y, VgaParams* params, uin const uint32_t vBorder = (VIRTUAL_PIXELS_Y - TMS9918_PIXELS_Y) / 2; const uint32_t hBorder = (VIRTUAL_PIXELS_X - TMS9918_PIXELS_X * 2) / 2; - //uint16_t bg = tms9918PaletteBGR12[vrEmuTms9918RegValue(TMS_REG_FG_BG_COLOR) & 0x0f]; - - uint16_t bg = currentInt ? 0x000f : 0x00f0; + uint16_t bg = tms9918PaletteBGR12[vrEmuTms9918RegValue(TMS_REG_FG_BG_COLOR) & 0x0f]; /*** top and bottom borders ***/ if (y < vBorder || y >= (vBorder + TMS9918_PIXELS_Y)) @@ -284,7 +285,7 @@ static void __time_critical_func(tmsScanline)(uint16_t y, VgaParams* params, uin } else { - currentStatus = tempStatus; + currentStatus |= tempStatus; } vrEmuTms9918SetStatusImpl(currentStatus); @@ -398,7 +399,7 @@ int main(void) * I do have code which sets the best clock baased on the chosen VGA mode, * but this'll do for now. */ - set_sys_clock_pll(1260000000, 5, 1); // 252000 + set_sys_clock_pll(PICO_CLOCK_PLL, PICO_CLOCK_PLL_DIV1, PICO_CLOCK_PLL_DIV2); // 252000 /* we need one of these. it's the main guy */ vrEmuTms9918Init(); diff --git a/src/res/splash.png b/src/res/splash.png index 42575cda39182241f75ded7a8d8edb09ffc29c20..0c65d0b4f472f0f5c0768146aef8632de1f7fa15 100644 GIT binary patch delta 234 zcmV&*#f4=|# z4!;371ydvd005myL_t(2&y|re5(6;^1Ks3**C+*jit~T`1PbewLEwx%sS*kuGwgxD z?SbFpYIu?p1V}0$K$nmX^DORYqUeMPRIY3Zy)*ycbcE<=6-edLw4^>?w2DL(BF|wG z^BfW0Cc5L^PBv``9`ty*A=tTSK%7q0t7I99Fxaf#>3pq~gZoFUw(O&NA2hUlUeNM* k#|15X-`!)-Kd*!94TR;)cTYxNw*UYD07*qoM6N<$g7Yz7n*aa+ delta 229 zcmVLXseAxkLORT|xTA@o6DCZ#vL*D+{Cl$zVxvs}l}E#pI{sLdi259PUL-Nk z_Tg&6=I!BTEwO_gAGath7N-aGJ1JRKA}q!hn~v8od1c>XHCrFQzQsZV=gutGb?nT- fugUM@x4`uU2m{1cO?K-100000NkvXXu0mjf<#An; From db76bb8ce7b6fb686c359b23e1c8c01fcff2e3bb Mon Sep 17 00:00:00 2001 From: Troy Schrapel Date: Fri, 12 Jul 2024 13:59:21 +0930 Subject: [PATCH 19/19] added pcb version #defines --- src/main.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/main.c b/src/main.c index ec7acc5..3e1a6c2 100644 --- a/src/main.c +++ b/src/main.c @@ -30,7 +30,7 @@ #include "hardware/vreg.h" /* - * Pin mapping + * Pin mapping (PCB v0.3) * * Pin | GPIO | Name | TMS9918A Pin * -----+------+-----------+------------- @@ -62,13 +62,31 @@ * https://www.aliexpress.com/item/1005007066733934.html */ +#define PCB_MAJOR_VERSION 0 +#define PCB_MINOR_VERSION 3 + #define GPIO_CD0 14 #define GPIO_CSR tmsRead_CSR_PIN // defined in tms9918.pio #define GPIO_CSW tmsWrite_CSW_PIN // defined in tms9918.pio #define GPIO_MODE 28 #define GPIO_INT 22 + +#if PCB_MAJOR_VERSION != 0 +#error "Time traveller?" +#endif + + // pin-mapping for gromclk and cpuclk changed in PCB v0.4 + // in order to have MODE and MODE1 sequential +#if PCB_MINOR_VERSION < 4 #define GPIO_GROMCL 29 #define GPIO_CPUCL 23 +#else +#define GPIO_GROMCL 25 +#define GPIO_CPUCL 24 +#define GPIO_RESET 23 +#define GPIO_MODE1 29 +#endif + #define GPIO_CD_MASK (0xff << GPIO_CD0) #define GPIO_CSR_MASK (0x01 << GPIO_CSR)