joeylib2/src/port/iigs/hal.c

125 lines
4 KiB
C

// Apple IIgs HAL: enable SHR, write pixels / SCBs / palettes into the
// $E1 bank at the stock addresses the shifter reads from.
//
// Memory map in bank $E1:
// $2000 - $9CFF pixel data (32000 bytes, 160 bytes per scanline)
// $9D00 - $9DC7 SCB bytes (200 used)
// $9E00 - $9FFF 16 palettes x 16 colors x 2 bytes, $0RGB
//
// NEWVIDEO register at $00C029 controls SHR enable. Bit 7 turns SHR on.
// ORCA/C must be built with 32-bit pointer mode (-w or equivalent) so
// that the long addresses resolve to bank $E1.
//
// For M1 this is a simple direct-copy present. PEI-slam (in assembly)
// arrives as an optimization in a later milestone; the structure here
// is unchanged -- only halPresent / halPresentRect get faster inner
// loops.
#include <stddef.h>
#include <string.h>
#include "hal.h"
#include "surfaceInternal.h"
// ----- Hardware addresses (24-bit / long pointers) -----
#define IIGS_NEWVIDEO_REG ((volatile uint8_t *)0x00C029L)
#define IIGS_VBL_STATUS ((volatile uint8_t *)0x00C019L)
#define IIGS_SHR_PIXELS ((uint8_t *)0xE12000L)
#define IIGS_SHR_SCB ((uint8_t *)0xE19D00L)
#define IIGS_SHR_PALETTE ((uint16_t *)0xE19E00L)
#define VBL_BAR_BIT 0x80
// NEWVIDEO bit masks
#define NEWVIDEO_SHR_ON 0x80
#define NEWVIDEO_LINEARIZE 0x40
// Bit 0 is documented as reserved-must-be-1 in the IIgs Hardware
// Reference for forward compatibility. Real silicon doesn't care, but
// GSplus halts on writes that leave it clear (see moremem.c c029
// handler) and bumps its "Code: RED" status. Always include this bit.
#define NEWVIDEO_RESERVED_BIT 0x01
// ----- Module state -----
static uint8_t gPreviousNewVideo = 0;
static bool gModeSet = false;
// ----- HAL API (alphabetical) -----
bool halInit(const JoeyConfigT *config) {
(void)config;
gPreviousNewVideo = *IIGS_NEWVIDEO_REG;
*IIGS_NEWVIDEO_REG = (uint8_t)(NEWVIDEO_SHR_ON | NEWVIDEO_LINEARIZE | NEWVIDEO_RESERVED_BIT);
gModeSet = true;
return true;
}
const char *halLastError(void) {
return NULL;
}
void halPresent(const SurfaceT *src) {
if (src == NULL) {
return;
}
memcpy(IIGS_SHR_PIXELS, src->pixels, SURFACE_PIXELS_SIZE);
memcpy(IIGS_SHR_SCB, src->scb, SURFACE_HEIGHT);
memcpy(IIGS_SHR_PALETTE, src->palette, sizeof(src->palette));
}
void halPresentRect(const SurfaceT *src, int16_t x, int16_t y, uint16_t w, uint16_t h) {
int16_t py;
int16_t yEnd;
uint16_t copyBytes;
int16_t byteStart;
if (src == NULL) {
return;
}
// SCBs and palettes track the whole surface, not just the rect --
// cheap enough on IIgs (200 bytes + 512 bytes) and avoids tracking
// which palette/SCB regions changed. A future optimization can
// limit these to the affected scanlines.
memcpy(IIGS_SHR_SCB, src->scb, SURFACE_HEIGHT);
memcpy(IIGS_SHR_PALETTE, src->palette, sizeof(src->palette));
// Pixel copy: byte-aligned runs per scanline. x is always even
// after API-level clipping for 4bpp packed if caller aligned it;
// otherwise we include the byte containing the leftmost pixel.
byteStart = x >> 1;
copyBytes = (uint16_t)(((x + (int16_t)w + 1) >> 1) - byteStart);
yEnd = y + (int16_t)h;
for (py = y; py < yEnd; py++) {
memcpy(&IIGS_SHR_PIXELS[py * SURFACE_BYTES_PER_ROW + byteStart],
&src->pixels[py * SURFACE_BYTES_PER_ROW + byteStart],
copyBytes);
}
}
// $C019 RDVBLBAR: bit 7 = 0 during vertical blank, 1 during active
// scan. To produce a rising-edge wait (one VBL per call), first spin
// while VBL is currently active (bit 7 = 0), then spin until VBL
// fires again (bit 7 returns to 0). The IIgs SHR refresh is 60 Hz.
void halWaitVBL(void) {
while ((*IIGS_VBL_STATUS & VBL_BAR_BIT) == 0) {
/* already in VBL: wait for active scan */;
}
while ((*IIGS_VBL_STATUS & VBL_BAR_BIT) != 0) {
/* scanning: wait for next VBL */;
}
}
void halShutdown(void) {
if (gModeSet) {
*IIGS_NEWVIDEO_REG = gPreviousNewVideo;
gModeSet = false;
}
}