Skip to content

Commit

Permalink
80 column mode, scanline timing debugging
Browse files Browse the repository at this point in the history
  • Loading branch information
visrealm committed Jun 13, 2024
1 parent 7a86b84 commit 90e9116
Show file tree
Hide file tree
Showing 9 changed files with 225 additions and 63 deletions.
3 changes: 3 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ add_executable(${PROGRAM})

target_sources(${PROGRAM} PRIVATE main.c palette.c clocks.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)

Expand Down
85 changes: 70 additions & 15 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

#include "palette.h"

#include "splash.h"

#include "vrEmuTms9918Util.h"

#include "pico/stdlib.h"
Expand Down Expand Up @@ -67,15 +69,22 @@
#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 GPIO_CD_REVERSED 1
#define LED_BLINK_ON_WRITE 1


#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.
Expand All @@ -101,9 +110,12 @@ static uint8_t __aligned(4) reversed[] =
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 */
/* file globals */

static VrEmuTms9918* tms = NULL; /* our vrEmuTms9918 instance handle */
static uint32_t nextValue = 0; /* TMS9918A read-ahead value */
Expand All @@ -128,7 +140,7 @@ void __time_critical_func(gpioExclusiveCallbackProc1)()

if (gpios & GPIO_MODE_MASK) /* read status register */
{
sio_hw->gpio_out = ((uint32_t)reversed[vrEmuTms9918ReadStatus(tms)] << GPIO_CD0) | GPIO_INT_MASK;
sio_hw->gpio_out = ((uint32_t)REVERSE(vrEmuTms9918ReadStatus(tms)) << GPIO_CD0) | GPIO_INT_MASK;
currentInt = GPIO_INT_MASK;
}
else /* read data */
Expand All @@ -139,7 +151,7 @@ void __time_critical_func(gpioExclusiveCallbackProc1)()
}
else if ((gpios & GPIO_CSW_MASK) == 0) /* write? */
{
uint8_t value = reversed[(sio_hw->gpio_in >> GPIO_CD0) & 0xff];
uint8_t value = REVERSE((sio_hw->gpio_in >> GPIO_CD0) & 0xff);

if (gpios & GPIO_MODE_MASK) /* write register/address */
{
Expand All @@ -151,15 +163,20 @@ void __time_critical_func(gpioExclusiveCallbackProc1)()
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 */
{
sio_hw->gpio_oe_clr = GPIO_CD_MASK;
sio_hw->gpio_out = currentInt;
}

/* update read-ahead */
nextValue = reversed[vrEmuTms9918ReadDataNoInc(tms)] << GPIO_CD0;
nextValue = REVERSE(vrEmuTms9918ReadDataNoInc(tms)) << GPIO_CD0;

/* interrupt handled */
iobank0_hw->intr[GPIO_CSR >> 3u] = iobank0_hw->proc1_irq_ctrl.ints[GPIO_CSR >> 3u];
Expand All @@ -171,9 +188,9 @@ void __time_critical_func(gpioExclusiveCallbackProc1)()
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_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); // int is an output
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))
Expand All @@ -196,7 +213,7 @@ void proc1Entry()
static void __time_critical_func(tmsScanline)(uint16_t y, VgaParams* params, uint16_t* pixels)
{
const uint32_t vBorder = (params->vVirtualPixels - TMS9918_PIXELS_Y) / 2;
const uint32_t hBorder = (params->hVirtualPixels - TMS9918_PIXELS_X) / 2;
const uint32_t hBorder = (params->hVirtualPixels - TMS9918_PIXELS_X * 2) / 2;

uint16_t bg = tms9918PaletteBGR12[vrEmuTms9918RegValue(tms, TMS_REG_FG_BG_COLOR) & 0x0f];

Expand All @@ -207,6 +224,30 @@ 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
* format: 16bpp abgr image
*/
if (y >= vBorder + TMS9918_PIXELS_Y + 12)
{
y -= vBorder + TMS9918_PIXELS_Y + 12;
if (y < 10)
{
uint16_t* splashPtr = splash + (y * 172);
for (int x = 4; x < 4 + 172; ++x)
{
uint16_t c = *(splashPtr++);
if (c & 0xf000)
{
pixels[x] = c;
}
}
}
}

return;
}

Expand All @@ -225,13 +266,24 @@ static void __time_critical_func(tmsScanline)(uint16_t y, VgaParams* params, uin

/* convert from tms palette to bgr12 */
int tmsX = 0;
for (int x = hBorder; x < hBorder + TMS9918_PIXELS_X; ++x, ++tmsX)
if (tmsScanlineBuffer[0] & 0xf0)
{
pixels[x] = tms9918PaletteBGR12[tmsScanlineBuffer[tmsX]];
for (int x = hBorder; x < hBorder + TMS9918_PIXELS_X * 2; x += 2, ++tmsX)
{
pixels[x] = tms9918PaletteBGR12[(tmsScanlineBuffer[tmsX] & 0xf0) >> 4];
pixels[x + 1] = tms9918PaletteBGR12[tmsScanlineBuffer[tmsX] & 0x0f];
}
}
else
{
for (int x = hBorder; x < hBorder + TMS9918_PIXELS_X * 2; x += 2, ++tmsX)
{
pixels[x] = pixels[x + 1] = tms9918PaletteBGR12[tmsScanlineBuffer[tmsX] & 0x0f];
}
}

/*** right border ***/
for (int x = hBorder + TMS9918_PIXELS_X; x < params->hVirtualPixels; ++x)
for (int x = hBorder + TMS9918_PIXELS_X * 2; x < params->hVirtualPixels; ++x)
{
pixels[x] = bg;
}
Expand All @@ -246,9 +298,6 @@ static void __time_critical_func(tmsScanline)(uint16_t y, VgaParams* params, uin

/*
* initialise a clock output using PIO
*
* GROMCLK is crystal frequency / 24 (~447KHz)
* CPUCLK is crystal frequency / 3 (~3.58MHz)
*/
uint initClock(uint gpio, float freqHz)
{
Expand Down Expand Up @@ -295,8 +344,14 @@ int main(void)

/* then set up VGA output */
VgaInitParams params = { 0 };
params.params = vgaGetParams(VGA_640_480_60HZ, 2);
params.params = vgaGetParams(VGA_640_480_60HZ);

/* virtual size will be 640 x 320 to accomodate 80-column mode */
setVgaParamsScaleY(&params.params, 2);

/* set vga scanline callback to generate tms9918 scanlines */
params.scanlineFn = tmsScanline;

vgaInit(params);

/* signal proc1 that we're ready to start the display */
Expand Down
42 changes: 35 additions & 7 deletions src/vga/vga-modes.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,8 @@ void vgaUpdateTotalPixels(VgaSyncParams* params);
/*
* populate VgaParams for known vga modes
*/
VgaParams vgaGetParams(VgaMode mode, int pixelScale)
VgaParams vgaGetParams(VgaMode mode)
{
if (pixelScale < 1) pixelScale = 1;

VgaParams params;

switch (mode)
Expand Down Expand Up @@ -112,14 +110,44 @@ VgaParams vgaGetParams(VgaMode mode, int pixelScale)
params.vSyncParams.freqHz = 1.0f / frameTimeSeconds;
}

params.hPixelScale = pixelScale;
params.vPixelScale = pixelScale;
params.hVirtualPixels = (params.hSyncParams.displayPixels / params.hPixelScale);
params.vVirtualPixels = (params.vSyncParams.displayPixels / params.vPixelScale);
setVgaParamsScale(&params, 1);

return params;
}

/*
* set the scale/multiplier of virtual pixel size
*/
bool setVgaParamsScale(VgaParams* params, int pixelScale)
{
return setVgaParamsScaleX(params, pixelScale) &&
setVgaParamsScaleY(params, pixelScale);
}

bool setVgaParamsScaleXY(VgaParams* params, int pixelScaleX, int pixelScaleY)
{
return setVgaParamsScaleX(params, pixelScaleX) &&
setVgaParamsScaleY(params, pixelScaleY);
}

bool setVgaParamsScaleX(VgaParams* params, int pixelScale)
{
if (!params || pixelScale < 1) return false;

params->hPixelScale = pixelScale;
params->hVirtualPixels = (params->hSyncParams.displayPixels / params->hPixelScale);
return true;
}

bool setVgaParamsScaleY(VgaParams* params, int pixelScale)
{
if (!params || pixelScale < 1) return false;

params->vPixelScale = pixelScale;
params->vVirtualPixels = (params->vSyncParams.displayPixels / params->vPixelScale);
return true;
}

/*
* update total number of pixels
*/
Expand Down
14 changes: 13 additions & 1 deletion src/vga/vga-modes.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,16 @@ typedef enum
VGA_1280_1024_60HZ,
} VgaMode;

extern VgaParams vgaGetParams(VgaMode mode, int pixelScale);

/*
* get the vga parameters for known modes
*/
VgaParams vgaGetParams(VgaMode mode);

/*
* set the scale/multiplier of virtual pixel size
*/
bool setVgaParamsScale(VgaParams* params, int pixelScale);
bool setVgaParamsScaleXY(VgaParams* params, int pixelScaleX, int pixelScaleY);
bool setVgaParamsScaleX(VgaParams* params, int pixelScale);
bool setVgaParamsScaleY(VgaParams* params, int pixelScale);
Loading

0 comments on commit 90e9116

Please sign in to comment.