330 lines
10 KiB
C
Executable file
330 lines
10 KiB
C
Executable file
#include <dos.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#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;
|
|
}
|
|
}
|