commit 1b87ca8aa3a98600e69f14a83823e67eb90aabf7 Author: Jeremy Penner Date: Sat Jan 5 16:16:08 2019 -0500 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..15e50eb --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.obj +*.bak + diff --git a/testbed.c b/testbed.c new file mode 100755 index 0000000..1c1e90a --- /dev/null +++ b/testbed.c @@ -0,0 +1,511 @@ +#include +#include +#include + +/*** V I D E O ***/ +#define setMode(hexval) asm { mov ax, hexval; int 10h } + +#define setVGAMode() setMode(0013h) +#define setEGAMode() setMode(000Dh) +#define setTextMode() setMode(0003h) + +#define REG_TS 0x03c4 +#define REG_GDC 0x03ce +#define REG_CRTC 0x03d4 + +#define PLANE_B 0x00 +#define PLANE_G 0x01 +#define PLANE_R 0x02 +#define PLANE_I 0x03 +#define setPlane(p) outport(REG_TS, 2 | (0x100 << p)) +#define setAllPlanes() outport(REG_TS, 0x0f02) + +#define VID ((volatile char far *)MK_FP(0xa000, 0)) + +void vid_cleanup() { + setTextMode(); +} + +typedef struct { + unsigned int b[16]; + unsigned int g[16]; + unsigned int r[16]; + unsigned int i[16]; +} Tile_t; + +unsigned int PAGE[] = { 0x00, 0x20, 0x40, 0x60 }; +// todo: HIGH BITS?? +#define setWriteMode(m) outport(REG_GDC, 0x05 | m << 8) + +void prepareEgaMemCopy() { + setAllPlanes(); + setWriteMode(1); +} +#define PAGE_STRIDE 40 +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; + } +} + +#if 0 +void blitTile(Tile_t *tile, int plane, unsigned int page, unsigned int x, unsigned int y) { + unsigned int *data = tile->b + (plane << 4); + volatile unsigned int far *out = (volatile unsigned int far *) + &VID[(y * 640) + (x << 1) + (page << 8)]; + int i; + + for (i = 0; i < 320; i += 20) { + out[i] = *(data++); + } +} + +#endif + +#define flipPage(p) outport(REG_CRTC, 0x0c | (p << 8)) + +/*** K E Y B O A R D ***/ +#define KBD_INT 0x09 +void interrupt (*oldKbdISR)() = NULL; + +void kbd_cleanup() { + if (oldKbdISR != NULL) { + setvect(KBD_INT, oldKbdISR); + oldKbdISR = NULL; + } +} + +volatile char keybuf[128]; +volatile char kbd_triggered = 0; + +void interrupt kbd_isr() { + unsigned char raw; + char ctl; + + asm sti; + raw = inp(0x60); + ctl = inp(0x61) | 0x82; + outp(0x61, ctl); + outp(0x61, ctl & 0x7f); + outp(0x20, 0x20); + + if (raw & 0x80) { + keybuf[raw & 0x7f] = 0; + } else { + keybuf[raw] = 1; + } + kbd_triggered = raw; +} + +#define K_ESC 1 +#define K_1 2 +#define K_2 3 +#define K_3 4 +#define K_4 5 +#define K_5 6 +#define K_6 7 +#define K_7 8 +#define K_8 9 +#define K_9 10 +#define K_0 11 +#define K_MINUS 12 +#define K_EQUAL 13 +#define K_BKSP 14 +#define K_TAB 15 +#define K_Q 16 +#define K_W 17 +#define K_E 18 +#define K_R 19 +#define K_T 20 +#define K_Y 21 +#define K_U 22 +#define K_I 23 +#define K_O 24 +#define K_P 25 +#define K_LBRK 26 +#define K_RBRK 27 +#define K_ENTER 28 +#define K_CTRL 29 +#define K_A 30 +#define K_S 31 +#define K_D 32 +#define K_F 33 +#define K_G 34 +#define K_H 35 +#define K_J 36 +#define K_K 37 +#define K_L 38 +#define K_SEMI 39 +#define K_APOS 40 +#define K_TILDE 41 +#define K_LSHFT 42 +#define K_BSLSH 43 +#define K_Z 44 +#define K_X 45 +#define K_C 46 +#define K_V 47 +#define K_B 48 +#define K_N 49 +#define K_M 50 +#define K_COMMA 51 +#define K_DOT 52 +#define K_SLASH 53 +#define K_RSHFT 54 +#define K_PSCRN 55 +#define K_ALT 56 +#define K_SPACE 57 +#define K_CAPS 58 +#define K_F1 59 +#define K_F2 60 +#define K_F3 61 +#define K_F4 62 +#define K_F5 63 +#define K_F6 64 +#define K_F7 65 +#define K_F8 66 +#define K_F9 67 +#define K_F10 68 +#define K_NUMLK 69 +#define K_SCRL 70 +#define K_HOME 71 +#define K_UP 72 +#define K_PGUP 73 +#define K_NDASH 74 +#define K_LEFT 75 +#define K_CENT 76 +#define K_RIGHT 77 +#define K_NPLUS 78 +#define K_END 79 +#define K_DOWN 80 +#define K_PGDN 81 +#define K_INS 82 +#define K_DEL 83 +#define K_F11 87 +#define K_F12 88 +#define keyPressed(k) keybuf[k] + +unsigned char kbd_wait() { + kbd_triggered = 0; + while (!kbd_triggered) {} + return kbd_triggered; +} + +void kbd_init() { + if (oldKbdISR == NULL) { + memset(keybuf, 0, 128); + oldKbdISR = getvect(KBD_INT); + setvect(KBD_INT, kbd_isr); + atexit(kbd_cleanup); + } +} + +/*** M O U S E ***/ +struct { + unsigned int x; + unsigned int y; + unsigned int buttons; +} MOUSE; + +void far mouse_callback() { + asm { + mov ax, DGROUP + mov ds, ax + shr cx, 1 + mov MOUSE.x, cx + mov MOUSE.y, dx + mov MOUSE.buttons, bx + } +} + +void mouse_cleanup() { + //uninstall handler + asm { + mov ax, 0ch + mov dx, 0 + mov es, dx + mov cx, 0 + int 33h + + xor ax, ax + int 33h + } +} + +void mouse_init() { + unsigned seg_mouse_callback = FP_SEG(mouse_callback); + unsigned off_mouse_callback = FP_OFF(mouse_callback); + unsigned int result; + asm { + xor ax, ax + int 33h + mov result, ax + } + if (result == 0) { + printf("Mouse driver not installed\n"); + exit(1); + } + atexit(mouse_cleanup); + + asm { + mov ax, seg_mouse_callback + mov es, ax + mov dx, off_mouse_callback + mov ax, 0ch + mov cx, 1fh + int 33h + } +} + +#define mouse_hide() asm { mov ax, 02h; int 33h } +#define mouse_show() asm { mov ax, 01h; int 33h } + +/*** T I F F ***/ +typedef struct { + unsigned int endian; + unsigned int version; + unsigned long ifdOffset; +} TifHeader_t; + +#define TIF_WIDTH 256 +#define TIF_HEIGHT 257 +#define TIF_BITSPERSAMPLE 258 +#define TIF_COMPRESSION 259 +#define TIF_STRIPOFFSETS 273 +#define TIF_ROWSPERSTRIP 278 + +typedef struct { + unsigned int id; + unsigned int dataType; + unsigned long dataCount; + unsigned long dataOffset; +} TifTag_t; + +typedef struct { + unsigned int width; + unsigned int height; + unsigned long rowsPerStrip; + unsigned long stripCount; + unsigned long stripOffsets; +} TifImageMeta_t; + +TifImageMeta_t tifLoadMeta(FILE *f) { + TifImageMeta_t meta = {0, 0, 0, 0, 0}; + TifHeader_t header; + TifTag_t tag; + unsigned int i, tagCount; + + fseek(f, 0, SEEK_SET); + fread(&header, 8, 1, f); + + if (header.endian != 0x4949 || header.version != 0x2a) { + goto fail; + } + fseek(f, header.ifdOffset, SEEK_SET); + fread(&tagCount, 2, 1, f); + for (i = 0; i < tagCount; i ++) { + fread(&tag, 12, 1, f); + if (tag.id == TIF_WIDTH) { + meta.width = tag.dataOffset; + } else if (tag.id == TIF_HEIGHT) { + meta.height = tag.dataOffset; + } else if (tag.id == TIF_BITSPERSAMPLE) { + if (tag.dataOffset != 4) goto fail; + } else if (tag.id == TIF_COMPRESSION) { + if (tag.dataOffset != 1) goto fail; + } else if (tag.id == TIF_STRIPOFFSETS) { + meta.stripCount = tag.dataCount; + meta.stripOffsets = tag.dataOffset; + } else if (tag.id == TIF_ROWSPERSTRIP) { + meta.rowsPerStrip = tag.dataOffset; + } + } + return meta; +fail: + meta.stripCount = 0; + return meta; +} + +#define MAX_WIDTH 320 + +int tifLoadEGA(FILE *f, TifImageMeta_t meta, unsigned int vidOffset, int maxY) { + int istrip; + int irow; + int ipixelpair; + int y = 0; + unsigned long offset; + unsigned char rowData[MAX_WIDTH >> 1]; + volatile unsigned char far *out = &VID[vidOffset]; + unsigned char b, g, r, i; + + if (meta.width > MAX_WIDTH || (meta.width % 16) != 0) { + return 0; + } + setWriteMode(0); + + for (istrip = 0; istrip < meta.stripCount; istrip ++) { + fseek(f, meta.stripOffsets + (istrip << 2), SEEK_SET); + fread(&offset, 4, 1, f); + fseek(f, offset, SEEK_SET); + + for (irow = 0; irow < meta.rowsPerStrip; irow ++) { + int ipixelpairLim = meta.width >> 1; + fread(rowData, 1, ipixelpairLim, f); + b = g = r = i = 0; + for (ipixelpair = 0; ipixelpair < ipixelpairLim; ipixelpair ++) { + unsigned char pixelpair = rowData[ipixelpair]; + int bpair = (pixelpair & 0x01) | (pixelpair & 0x10) >> 3; + int gpair = (pixelpair & 0x02) >> 1 | (pixelpair & 0x20) >> 4; + int rpair = (pixelpair & 0x04) >> 2 | (pixelpair & 0x40) >> 5; + int ipair = (pixelpair & 0x08) >> 3 | (pixelpair & 0x80) >> 6; + int shift = (ipixelpair % 4) << 1; + + b |= bpair << shift; + g |= gpair << shift; + r |= rpair << shift; + i |= ipair << shift; + + if (shift == 6 || ipixelpair == ipixelpairLim - 1) { + // todo: use write mode 2, this is slooww + setPlane(PLANE_B); *out = b; + setPlane(PLANE_R); *out = r; + setPlane(PLANE_G); *out = g; + setPlane(PLANE_I); *out = i; + out ++; + b = g = r = i = 0; + } + } + y++; + if (y == maxY) { + return y; + } + } + } + return y; +} + +int tifLoad(FILE *f, TifImageMeta_t meta, unsigned int *planeBuf, int maxY, int yRepeat) { + int istrip; + int irow; + int ipixelpair; + int y = 0; + unsigned long offset; + unsigned char rowData[MAX_WIDTH >> 1]; + unsigned int planeStride = (meta.width >> 4) * yRepeat; + unsigned int *b = planeBuf - 1; + unsigned int *g = b + planeStride; + unsigned int *r = g + planeStride; + unsigned int *i = r + planeStride; + + if (meta.width > MAX_WIDTH || (meta.width % 16) != 0) { + return 0; + } + + for (istrip = 0; istrip < meta.stripCount; istrip ++) { + fseek(f, meta.stripOffsets + (istrip << 2), SEEK_SET); + fread(&offset, 4, 1, f); + fseek(f, offset, SEEK_SET); + + for (irow = 0; irow < meta.rowsPerStrip; irow ++) { + fread(rowData, 1, meta.width >> 1, f); + for (ipixelpair = 0; ipixelpair < meta.width >> 1; ipixelpair ++) { + unsigned char pixelpair = rowData[ipixelpair]; + int bpair = (pixelpair & 0x01) | (pixelpair & 0x10) >> 3; + int gpair = (pixelpair & 0x02) >> 1 | (pixelpair & 0x20) >> 4; + int rpair = (pixelpair & 0x04) >> 2 | (pixelpair & 0x40) >> 5; + int ipair = (pixelpair & 0x08) >> 3 | (pixelpair & 0x80) >> 6; + int shift = (ipixelpair % 8) << 1; + if (shift == 0) { + *++b = bpair; + *++g = gpair; + *++r = rpair; + *++i = ipair; + } else { + *b |= bpair << shift; + *g |= gpair << shift; + *r |= rpair << shift; + *i |= ipair << shift; + } + } + y++; + if (y == maxY) { + return y; + } + if (y % yRepeat == 0) { + b += planeStride * 3; + g += planeStride * 3; + r += planeStride * 3; + i += planeStride * 3; + } + } + } + return y; +} + +/*** S C R A T C H ***/ +void paintPattern(int r, int g, int b, int t) { + int i; + + setPlane(PLANE_R); + for (i = 0; i < 8000; i ++) { + VID[i] = r; + } + setPlane(PLANE_G); + for (i = 0; i < 8000; i ++) { + VID[i] = g; + } + setPlane(PLANE_B); + for (i = 0; i < 8000; i ++) { + VID[i] = b; + } + setPlane(PLANE_I); + for (i = 0; i < 8000; i ++) { + VID[i] = t; + } +} + +int main() { + Tile_t tiles[16]; + FILE *f; + TifImageMeta_t meta; + int plane; + int x; + int y; + int z = 0; + unsigned int drawOffset; + unsigned int page = 0; + + #define OFF_TILES 0x4200 + setEGAMode(); + + f = fopen("TILES.TIF", "rb"); + meta = tifLoadMeta(f); + tifLoadEGA(f, meta, OFF_TILES, 256); + fclose(f); + + mouse_init(); + kbd_init(); + atexit(vid_cleanup); + + while (!keyPressed(K_ESC)) { + page ^= 0x20; + + prepareEgaMemCopy(); + drawOffset = page << 8; + for (y = 0; y < 13; y ++) { + for (x = 0; x < 20; x ++) { + blitTile(OFF_TILES + ((((x + y + z) >> 2) % 3) << 5), drawOffset); + drawOffset += 2; + } + drawOffset += 600; // 40 bytes per line * 15 more lines + } + flipPage(page); + + kbd_wait(); + z++; + } + + return 0; +} diff --git a/testbed.exe b/testbed.exe new file mode 100755 index 0000000..8910cb9 Binary files /dev/null and b/testbed.exe differ diff --git a/tiles.tif b/tiles.tif new file mode 100755 index 0000000..2e2f5df Binary files /dev/null and b/tiles.tif differ