#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(); } #define setWriteMode(m) outport(REG_GDC, 0x05 | m << 8) void setSplitScreen(unsigned int y) { int val; outport(REG_CRTC, 0x18 | (y << 8)); outp(REG_CRTC, 7); val = inp(REG_CRTC + 1); val &= ~0x10; val |= (y & 0x100) >> 4; outp(REG_CRTC + 1, val); outp(REG_CRTC, 9); val = inp(REG_CRTC + 1); val &= ~0x40; outp(REG_CRTC + 1, val); } void unsetSplitScreen() { outport(REG_CRTC, 0xff18); outport(REG_CRTC, 0x1107); outport(REG_CRTC, 0x0f09); } #define flipPage(p) outport(REG_CRTC, 0x0c | (p << 8)) void setDisplayOffset(unsigned int offset) { outport(REG_CRTC, 0x0c | (offset & 0xff00)); outport(REG_CRTC, 0x0d | (offset << 8)); } #define setLogicalWidth(w) outport(REG_CRTC, 0x13 | (w << 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, unsigned int w) { 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 = (3 - (ipixelpair % 4)) << 1; b |= bpair << shift; g |= gpair << shift; r |= rpair << shift; i |= ipair << shift; if (shift == 0 || 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; } out += (w - meta.width) >> 3; } } 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; } /*** T I L E S ***/ void prepareEgaMemCopy() { setAllPlanes(); setWriteMode(1); } #define PAGE_STRIDE 42 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; } } typedef struct { unsigned int w; unsigned int h; int scrollX; int scrollY; unsigned int pageOffset; unsigned char *tiles; } TilePage_t; #define OFF_TILES 0x4840 void scrollPage(TilePage_t *page, int x, int y) { x = min(max(x, 0), (page->w << 4) - 320); y = min(max(y, 0), (page->h << 4) - 176); page->scrollX = x; page->scrollY = y; } void drawPage(TilePage_t *page) { unsigned int startX = page->scrollX >> 4; unsigned int startY = page->scrollY >> 4; unsigned int offsetX = page->scrollX - (startX << 4); unsigned int offsetY = page->scrollY - (startY << 4); unsigned int drawOffset = page->pageOffset; unsigned int scrollOffset = drawOffset + (offsetX >> 3) + (offsetY * PAGE_STRIDE); unsigned int x, y; prepareEgaMemCopy(); for (y = startY; y < startY + 13; y ++) { for (x = startX; x < startX + 21; x ++) { blitTile(OFF_TILES + page->tiles[x + (y * page->w)], drawOffset); drawOffset += 2; } drawOffset += PAGE_STRIDE * 15; } setDisplayOffset(scrollOffset); } /*** S C R A T C H ***/ unsigned char tiles[10000]; TilePage_t pages[2] = { { 100, 100, 0, 0, 0x0400, tiles }, { 100, 100, 0, 0, 0x2620, tiles } }; void fillTiles() { unsigned int x, y, z; z = 0; for (y = 0; y < 100; y ++) { for (x = 0; x < 100; x ++) { tiles[x + (y * 100)] = (((x + y + z) >> 2) % 3) << 5; } } } int main() { FILE *f; TifImageMeta_t meta; int plane; int x; int y; int z = 0; unsigned int drawOffset; unsigned int page = 0; setEGAMode(); atexit(vid_cleanup); f = fopen("FOOTER.TIF", "rb"); meta = tifLoadMeta(f); tifLoadEGA(f, meta, 0, 24, 336); fclose(f); f = fopen("TILES.TIF", "rb"); meta = tifLoadMeta(f); tifLoadEGA(f, meta, OFF_TILES, 256, 16); fclose(f); kbd_init(); tile_init(); setSplitScreen(351); fillTiles(); while (!keyPressed(K_ESC)) { page ^= 1; x = pages[page].scrollX; y = pages[page].scrollY; if (keyPressed(K_LEFT)) x -= 4; if (keyPressed(K_RIGHT)) x += 4; if (keyPressed(K_UP)) y -= 4; if (keyPressed(K_DOWN)) y += 4; scrollPage(&pages[0], x, y); scrollPage(&pages[1], x, y); drawPage(&pages[page]); // kbd_wait(); z++; } return 0; }