125 lines
4 KiB
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;
|
|
}
|
|
}
|