#include #include #include #include "video.h" #include "tiles.h" #include "egamap.h" /*** T I L E S ***/ // Tiles are 16x16 bitmaps, stored as arrays of words. // Each tile has 4 or 5 planes (depending on whether it is a tile or sprite) // which are stored adjacant to each other; ie. a 16-word array of blue, // followed by a 16-word array of green, etc. // Tiles in RAM are stored byte-swapped to aid in fast bit-shifting, and must // be byte-swapped before being written to video memory. // Because bit-shifting operations happen on little-endian words: // 01234567 89ABCDEF << 3 => 34567XXX BCDEF012 // which is wrong. So instead we do: // 89ABCDEF 01234567 << 3 => BCDEFXXX 3456789A byteswap => 3456789A BCDEFXXX void tile_init() { setLogicalWidth(PAGE_STRIDE >> 1); } void blitTile(unsigned int offsetFrom, unsigned int offsetTo) { int y; for (y = 0; y < 16; y ++) { VID[offsetTo] = VID[offsetFrom ++]; VID[offsetTo + 1] = VID[offsetFrom ++]; offsetTo += PAGE_STRIDE; } } void blitSolidBlock(unsigned int offsetTo, unsigned char color) { int y; setPlaneColor(color); for (y = 0; y < 16; y ++) { VID[offsetTo] = 0xff; VID[offsetTo + 1] = 0xff; offsetTo += PAGE_STRIDE; } } void blit32x32(unsigned int offsetFrom, unsigned int offsetTo) { int y; for (y = 0; y < 32; y ++) { VID[offsetTo] = VID[offsetFrom ++]; VID[offsetTo + 1] = VID[offsetFrom ++]; VID[offsetTo + 2] = VID[offsetFrom ++]; VID[offsetTo + 3] = VID[offsetFrom ++]; offsetTo += PAGE_STRIDE; } } void blitMemToVid(unsigned int offset, unsigned int far *mem, unsigned int planeStride, int count) { int i, j, plane; offset = offset >> 1; // word aligned setWriteMode(0); for (i = 0; i < count; i ++) { for (plane = 0; plane < 4; plane ++) { unsigned int drawOffset = offset; unsigned int bmp; setPlane(plane); for (j = 0; j < planeStride; j ++) { bmp = mem[j]; WVID[drawOffset + j] = (bmp << 8) | (bmp >> 8); } mem += planeStride; } offset += planeStride; } setAllPlanes(); } #define D_NOTHING 0x80 #define D_BGTILE 0x81 #define isBufIndex(d) (!((d) & 0x80)) #define nextBufferIndex(i) ((i + 1) % NUM_BUFFERS) TiledScreen_t screen = { 0, 0, 0, 0, { OFF_PAGE1, OFF_PAGE2 }, 0, 0, NULL, NULL, 0, 0, 0, 0, 0 }; void paintBufferPlane(unsigned int *buf, unsigned int vidOffset, int stride, int plane) { unsigned int drawOffset = vidOffset >> 1; unsigned int y, bmp; for (y = 0; y < 16; y ++) { bmp = buf[y + (BUF_WSTRIDE * plane)]; WVID[drawOffset] = (bmp << 8) | (bmp >> 8); drawOffset += stride >> 1; } } void loadTiles(unsigned int tilesOffset, unsigned int far *memTiles) { int i, plane; screen.tilesOffset = tilesOffset; screen.memTiles = memTiles; setWriteMode(0); for (plane = 0; plane < 4; plane ++) { unsigned int drawOffset = tilesOffset >> 1; setPlane(plane); for (i = 0; i < NUM_TILES; i ++) { unsigned int y, bmp; unsigned int far *buf = &memTiles[(i * BUF_WSIZE) + (BUF_WSTRIDE * plane)]; for (y = 0; y < 16; y ++) { bmp = buf[y]; WVID[drawOffset ++] = (bmp << 8) | (bmp >> 8); } } } setAllPlanes(); } void loadMap(unsigned char *map, unsigned int w, unsigned int h) { screen.map = map; screen.w = w; screen.h = h; memset(screen.dirty, D_BGTILE, PAGE_TILES_COUNT * 2); } void writeTile(unsigned int *buf, unsigned int far *tile) { int i; for (i = 0; i < BUF_WSIZE; i ++) { buf[i] = tile[i]; } } void overlaySprite(unsigned int *buf, unsigned int far *sprite, int shift, int yStart, char *remap) { unsigned int far *mask; unsigned int maskval; int y, h, plane; if (yStart < 0) { sprite = &sprite[-yStart]; h = yStart + 16; } else { buf = &buf[yStart]; h = 16 - yStart; } mask = &sprite[BUF_WSTRIDE * 4]; if (!remap) { if (shift < 0) { shift = -shift; for (plane = 0; plane < 4; plane ++) { for (y = 0; y < h; y ++) { maskval = mask[y] << shift; buf[y] = (buf[y] & ~maskval) | ((sprite[y] << shift) & maskval); } sprite += BUF_WSTRIDE; buf += BUF_WSTRIDE; } } else { for (plane = 0; plane < 4; plane ++) { for (y = 0; y < h; y ++) { maskval = mask[y] >> shift; buf[y] = (buf[y] & ~maskval) | ((sprite[y] >> shift) & maskval); } sprite += BUF_WSTRIDE; buf += BUF_WSTRIDE; } } } else { unsigned int b, bo, g, go, r, ro, i, io, bgri; int bit; if (shift < 0) { shift = -shift; #define SPLANE(b, y, p) b[y + (BUF_WSTRIDE * (p))] #define DO_REMAP(ss, bitstart, bitlim) \ for (y = 0; y < h; y ++) { \ bo = go = ro = io = 0; \ b = SPLANE(sprite, y, 0); \ g = SPLANE(sprite, y, 1); \ r = SPLANE(sprite, y, 2); \ i = SPLANE(sprite, y, 3); \ for (bit = (bitstart); bit < (bitlim); bit ++) { \ int bshift = 1 << bit; \ bgri = ((b & bshift) ? 0x01 : 0x00) | \ ((g & bshift) ? 0x02 : 0x00) | \ ((r & bshift) ? 0x04 : 0x00) | \ ((i & bshift) ? 0x08 : 0x00); \ bgri = remap[bgri]; \ if (bgri & 0x01) bo |= bshift; \ if (bgri & 0x02) go |= bshift; \ if (bgri & 0x04) ro |= bshift; \ if (bgri & 0x08) io |= bshift; \ } \ maskval = mask[y] ss shift; \ SPLANE(buf, y, 0) = (SPLANE(buf, y, 0) & ~maskval) | ((bo ss shift) & maskval); \ SPLANE(buf, y, 1) = (SPLANE(buf, y, 1) & ~maskval) | ((go ss shift) & maskval); \ SPLANE(buf, y, 2) = (SPLANE(buf, y, 2) & ~maskval) | ((ro ss shift) & maskval); \ SPLANE(buf, y, 3) = (SPLANE(buf, y, 3) & ~maskval) | ((io ss shift) & maskval); \ } DO_REMAP(<<, shift, 16) } else { DO_REMAP(>>, 0, 16 - shift) #undef DO_REMAP #undef SPLANE } } } int prepareBuffer(int pageX, int pageY) { unsigned char *dirty = &screen.dirty[screen.currentPage][pageX + (pageY * PAGE_TILES_W)]; int i; if (!isBufIndex(*dirty)) { unsigned int startX = screen.scrollX >> 4; unsigned int startY = screen.scrollY >> 4; unsigned char tile = screen.map[startX + pageX + ((startY + pageY) * screen.w)]; unsigned char ibuffer = screen.nextBuffer; screen.nextBuffer = nextBufferIndex(ibuffer); *dirty = ibuffer; writeTile(screen.buffer[ibuffer], &screen.memTiles[tile * BUF_WSIZE]); screen.bufferOffset[ibuffer] = screen.pageOffset[screen.currentPage] + (pageX << 1) + (pageY * PAGE_STRIDE * 16); } return *dirty; } void drawSpriteToBuf(unsigned int far *sprite, int pageX, int pageY, int shift, int yStart, char *remap) { unsigned int *buf; if (pageX < 0 || pageY < 0 || pageX >= PAGE_TILES_W || pageY >= PAGE_TILES_H || shift >= 16 || shift <= -16 || yStart <= -16 || yStart >= 16) { return; } buf = screen.buffer[prepareBuffer(pageX, pageY)]; overlaySprite(buf, sprite, shift, yStart, remap); } void drawSprite(unsigned int far *sprite, int x, int y, char *remap) { int pageX = (int)(x - (screen.scrollX & 0xfff0)) >> 4; int pageY = (int)(y - (screen.scrollY & 0xfff0)) >> 4; int pageOffsetX = x & 0x0f; int pageOffsetY = y & 0x0f; drawSpriteToBuf(sprite, pageX, pageY, pageOffsetX, pageOffsetY, remap); drawSpriteToBuf(sprite, pageX + 1, pageY, pageOffsetX - 16, pageOffsetY, remap); drawSpriteToBuf(sprite, pageX, pageY + 1, pageOffsetX, pageOffsetY - 16, remap); drawSpriteToBuf(sprite, pageX + 1, pageY + 1, pageOffsetX - 16, pageOffsetY - 16, remap); } void scroll(int newX, int newY) { newX = min(max(newX, 0), (screen.w << 4) - 320); newY = min(max(newY, 0), (screen.h << 4) - 200); if ((screen.scrollX & 0xfff0) != (newX & 0xfff0) || (screen.scrollY & 0xfff0) != (newY & 0xfff0)) { int mapX, mapY; unsigned char page; for (page = 0; page < 2; page ++) { int mapOffsetOld = (screen.scrollX >> 4) + ((screen.scrollY >> 4) * screen.w); int mapOffsetNew = (newX >> 4) + ((newY >> 4) * screen.w); unsigned char *dirty = screen.dirty[page]; for (mapY = 0; mapY < PAGE_TILES_H; mapY ++) { for (mapX = 0; mapX < PAGE_TILES_W; mapX ++) { if (*dirty != D_NOTHING || screen.map[mapOffsetOld + mapX] != screen.map[mapOffsetNew + mapX]) { *dirty = D_BGTILE; } dirty ++; } mapOffsetNew += screen.w; mapOffsetOld += screen.w; } } } screen.scrollX = newX; screen.scrollY = newY; } void paintBuffer(unsigned int *buf, unsigned int vidOffset) { int plane; setWriteMode(0); for (plane = 0; plane < 4; plane ++) { setPlane(plane); paintBufferPlane(buf, vidOffset, 40, plane); } } void drawScreen() { unsigned int startX = screen.scrollX >> 4; unsigned int startY = screen.scrollY >> 4; unsigned int offsetX = screen.scrollX - (startX << 4); unsigned int offsetY = screen.scrollY - (startY << 4); unsigned int drawOffset = screen.pageOffset[screen.currentPage]; unsigned int scrollOffset = drawOffset + (offsetX >> 3) + (offsetY * PAGE_STRIDE); unsigned char *dirty = screen.dirty[screen.currentPage]; unsigned int x, y, di, plane, bmp; setAllPlanes(); setWriteMode(1); di = 0; for (y = startY; y < startY + PAGE_TILES_H; y ++) { for (x = startX; x < startX + PAGE_TILES_W; x ++) { if (dirty[di++] == D_BGTILE) { char tile = screen.map[x + (y * screen.w)]; blitTile(screen.tilesOffset + (tile << 5), drawOffset); } drawOffset += 2; } drawOffset += PAGE_STRIDE * 15; } setWriteMode(0); for(plane = 0; plane < 4; plane ++) { setPlane(plane); for (di = screen.firstBuffer; di != screen.nextBuffer; di = nextBufferIndex(di)) { paintBufferPlane(screen.buffer[di], screen.bufferOffset[di], PAGE_STRIDE, plane); } } setAllPlanes(); setDisplayOffset(scrollOffset); setHorizontalPan(screen.scrollX & 0x07); screen.currentPage ^= 1; screen.firstBuffer = screen.nextBuffer; for (di = 0; di < PAGE_TILES_COUNT; di ++) { dirty[di] = isBufIndex(dirty[di]) ? D_BGTILE : D_NOTHING; } }