// 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 #include #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; } }