pete286/tiles.c

223 lines
7 KiB
C
Executable file

#include <dos.h>
#include <stdio.h>
#include <stdlib.h>
#include "video.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
#define PAGE_TILES_W 21
#define PAGE_TILES_H 14
#define PAGE_TILES_COUNT (PAGE_TILES_H * PAGE_TILES_W)
#define PAGE_STRIDE (PAGE_TILES_W << 1)
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;
}
}
#define D_NOTHING 0x80
#define D_BGTILE 0x81
#define isBufIndex(d) (!((d) & 0x80))
#define NUM_BUFFERS 32
#define nextBufferIndex(i) ((i + 1) % 32)
#define BUF_WSTRIDE 16
#define BUF_WSIZE (BUF_WSTRIDE * 4)
typedef struct {
unsigned int w;
unsigned int h;
int scrollX;
int scrollY;
unsigned int pageOffset[2];
unsigned char dirty[2][PAGE_TILES_COUNT];
unsigned int tilesOffset;
unsigned int *memTiles;
unsigned char *map;
unsigned int buffer[NUM_BUFFERS][BUF_WSIZE];
unsigned int bufferOffset[NUM_BUFFERS];
unsigned char currentPage;
unsigned char nextBuffer;
unsigned char firstBuffer;
} TiledScreen_t;
TiledScreen_t screen = { 0, 0, 0, 0, { 0x0600, 0x2B00 }, 0, 0, NULL, NULL,
0, 0, 0, 0, 0 };
void loadTiles(unsigned int tilesOffset, unsigned int *memTiles) {
screen.tilesOffset = tilesOffset;
screen.memTiles = memTiles;
}
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);
}
int prepareBuffer(int pageX, int pageY) {
unsigned char *dirty = &screen.dirty[screen.currentPage][pageX + (pageY * PAGE_TILES_W)];
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;
memcpy(screen.buffer[ibuffer], &screen.memTiles[tile * BUF_WSIZE], BUF_WSIZE << 1);
screen.bufferOffset[ibuffer] = screen.pageOffset[screen.currentPage]
+ (pageX << 1) + (pageY * PAGE_STRIDE * 16);
}
return *dirty;
}
void drawSpriteToBuf(unsigned int *sprite, int pageX, int pageY, int shift, int yStart) {
unsigned int *buf, *mask;
unsigned int maskval;
int y, h, plane;
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)];
if (yStart < 0) {
sprite = &sprite[-yStart];
h = yStart + 16;
} else {
buf = &buf[yStart];
h = 16 - yStart;
}
mask = &sprite[BUF_WSTRIDE * 4];
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;
}
}
}
void drawSprite(unsigned int *sprite, int x, int y) {
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);
drawSpriteToBuf(sprite, pageX + 1, pageY, pageOffsetX - 16, pageOffsetY);
drawSpriteToBuf(sprite, pageX, pageY + 1, pageOffsetX, pageOffsetY - 16);
drawSpriteToBuf(sprite, pageX + 1, pageY + 1, pageOffsetX - 16, pageOffsetY - 16);
}
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 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) {
blitTile(
screen.tilesOffset + (screen.map[x + (y * screen.w)] << 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)) {
drawOffset = screen.bufferOffset[di] >> 1;
for (y = 0; y < 16; y ++) {
bmp = screen.buffer[di][y + (BUF_WSTRIDE * plane)];
WVID[drawOffset] = (bmp << 8) | (bmp >> 8);
drawOffset += PAGE_STRIDE >> 1;
}
}
}
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;
}
}