/*---------------------------------------------------------------------------- Mixed text and "chunky" graphics Chris Giese http://SisAndHappy.com/ChrisGiese/ This code is public domain (no copyright). You can do whatever you want with it. 12 Mar 2014: - Was using "12" for width in create_sprite(), now using "wd" 4 Mar 2014: - Added functions to make it build and run with 16-bit Watcom C 27 Feb 2014: - Added sprite code; now displaying flashing space invaders instead of just diagonal lines on the screen 15 Oct 2002: - Initial release ----------------------------------------------------------------------------*/ #include /* free(), malloc() */ #include /* memcpy(), memset() */ /* FILE, fopen(), fseek(), fwrite(), fputc(), fclose() */ #include #include /* kbhit(), getch() */ /* struct REGPACK, intr(), MK_FP(), FP_SEG(), FP_OFF() */ #include #if 0 #include #else typedef unsigned char uint8_t; typedef unsigned short uint16_t; typedef unsigned long uint32_t; #endif /* when VGA is in text mode, and characters are 9 pixels wide, characters 0xC0-0xDF in the character set will have their 8th (rightmost) pixel extended into the 9th pixel. This is for box- and line-drawing characters */ #define CG_BASE 0xD0 /* Turbo C peekb() returns a signed char value -- d'oh */ #define peek8(S,O) *(uint8_t far *)MK_FP(S,O) #if defined(__TURBOC__) #define R_AX r_ax #define R_BX r_bx #define R_CX r_cx #define R_DX r_dx #define R_BP r_bp #define R_ES r_es typedef struct REGPACK regs_t; #elif defined(__WATCOMC__) #if defined(__386__) #error Sorry, this is a 16-bit program #endif #define pokeb(S,O,V) *(unsigned char far *)MK_FP(S, O) = (V) #define R_AX x.ax #define R_BX x.bx #define R_CX x.cx #define R_DX x.dx #define R_BP x.bp #define R_ES x.es typedef union REGPACK regs_t; unsigned char g_attrib = 0x07; /* white text on black background */ /****************************************************************************/ void textcolor(int c) { g_attrib = c; } /****************************************************************************/ static void my_putch(unsigned c) { union REGS regs; unsigned y, x; /* get cursor position */ regs.h.ah = 0x03; regs.h.bh = 0; /* page number */ int86(0x10, ®s, ®s); y = regs.h.dh; x = regs.h.dl; /* set attribute */ pokeb(0xB800, (80 * y + x) * 2 + 1, g_attrib); /* output the character */ regs.h.ah = 0x0E; regs.h.al = c; regs.h.bh = 0; /* page number */ int86(0x10, ®s, ®s); } /***************************************************************************** override libc cprintf() *****************************************************************************/ #include int cprintf(const char *fmt, ...) { unsigned char buf[128], *s; va_list args; int rv; va_start(args, fmt); rv = vsprintf(buf, fmt, args); va_end(args); for(s = buf; *s != '\0'; s++) my_putch(*s); return rv; } /****************************************************************************/ void clrscr(void) { union REGS regs; regs.w.ax = 0x0600; /* scroll up - erase screen */ regs.h.bh = 0x07; /* attribute = white on black */ regs.w.cx = 0x0000; /* (y,x) of upper left corner */ regs.w.dx = 0x314F; /* (y,x) of lower right corner (=49,79) */ int86(0x10, ®s, ®s); regs.h.ah = 0x02; /* set cursor pos'n */ regs.h.bh = 0; /* page number */ regs.x.dx = 0; /* cursor (y,x) */ int86(0x10, ®s, ®s); } /****************************************************************************/ void gotoxy(unsigned x, unsigned y) { union REGS regs; if(x < 1 || y < 1) return; regs.h.ah = 0x02; regs.h.bh = 0; /* page number */ regs.h.dh = y - 1; regs.h.dl = x - 1; int86(0x10, ®s, ®s); } #else #error Sorry, unsupported compiler #endif static char g_font[2048]; /* 256x8 */ /****************************************************************************/ static void write_le16(void *buf0, unsigned val) { unsigned char *buf = buf0; buf[0] = val; val >>= 8; buf[1] = val; } /****************************************************************************/ static void write_le32(void *buf0, unsigned long val) { unsigned char *buf = buf0; buf[0] = val; val >>= 8; buf[1] = val; val >>= 8; buf[2] = val; val >>= 8; buf[3] = val; } /***************************************************************************** Win95 screen shot function doesn't notice that we changed the font, so it doesn't work. Here's a crappy function to make a 640x400x256 .BMP file of the screen contents. There are only 16 colors in text mode, but I'm making a 256-color .BMP to simplify code here. *****************************************************************************/ static void screen_shot(void) { static unsigned char pal[64] = { 0x00, 0x00, 0x00, 0x00, 0xAA, 0x00, 0x00, 0x00, 0x00, 0xAA, 0x00, 0x00, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0xAA, 0x00, 0xAA, 0x00, 0xAA, 0x00, 0x00, 0xAA, 0xAA, 0x00, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00 }; static const unsigned wd = 640, ht = 400, pal_size = 1024; /**/ unsigned char hdr[54]; unsigned y; FILE *out; /* create header */ memset(hdr, 0, sizeof(hdr)); /* BITMAPFILEHEADER (14 bytes) */ hdr[0] = 'B'; hdr[1] = 'M'; /* .BMP file magic values */ write_le32(&hdr[2], /* file size */ sizeof(hdr) + pal_size + (unsigned long)wd * ht); write_le32(&hdr[10], /* file offset of raster data */ sizeof(hdr) + pal_size); /* BITMAPINFOHEADER (usually 40 bytes) */ write_le32(&hdr[14], 40); /* size of BITMAPINFOHEADER */ write_le32(&hdr[18], wd); write_le32(&hdr[22], ht); write_le16(&hdr[26], 1); /* number of planes */ write_le16(&hdr[28], 8); /* depth (bits/pixel) write_le32(&hdr[30], 0); compression: 0=none */ /* open output file */ if((out = fopen("scrnshot.bmp", "wb")) == NULL) return; /* write header */ fwrite(&hdr, 1, sizeof(hdr), out); /* write palette */ fwrite(&pal, 1, sizeof(pal), out); /* entries 16-255 are bogus...good thing they're not used */ fseek(out, pal_size - sizeof(pal), SEEK_CUR); /* .BMP file raster is upside-down */ y = 400; do { unsigned _char, attrib, byte, mask; unsigned char scn_row, cg_row, x; y--; scn_row = y / 8; cg_row = y % 8; for(x = 0; x < 80; x++) { /* read attribute:character byte pair from the text framebuffer */ _char = peek8(0xB800, (scn_row * 80 + x) * 2 + 0); attrib = peek8(0xB800, (scn_row * 80 + x) * 2 + 1); /* lookup byte from font bitmap */ byte = g_font[_char * 8 + cg_row]; /* dump 8 pixels */ for(mask = 0x80; mask != 0; mask >>= 1) { /* bottom 4 bits of 'attrib' are foreground color... */ if(byte & mask) fputc(attrib & 0x0F, out); /* ...top 4 bits are background color */ else fputc(attrib / 16, out); } } } while(y > 0); fclose(out); } /***************************************************************************** reprograms characters CG_BASE through CG_BASE+15 of g_font for use with chunky graphics *****************************************************************************/ static void reprogram_font(void) { static char my_font[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0x0F, 0x0F, 0x0F, 0x0F, 0xF0, 0xF0, 0xF0, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xF0, 0xF0, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x0F, 0x0F, 0xF0, 0xF0, 0xF0, 0xF0, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xF0, 0xF0, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x0F, 0x0F, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; /**/ char far *src; regs_t regs; unsigned i; /* find 8x8 (#3) ROM font */ regs.R_AX = 0x1130; regs.R_BX = 0x0300; intr(0x10, ®s); src = MK_FP(regs.R_ES, regs.R_BP); /* copy font from ROM to RAM */ for(i = 0; i < sizeof(g_font); i++) { g_font[i] = *src; src++; } /* change 16 characters of font */ memcpy(&g_font[CG_BASE * 8], my_font, sizeof(my_font)); /* set 80x50 text mode (8x8 ROM font) */ regs.R_AX = 0x1112; regs.R_BX = 0; intr(0x10, ®s); /* set the user-defined font */ regs.R_AX = 0x1100; regs.R_BX = 0x800; /* BH=8, BL="block to load in map 2" */ regs.R_CX = 256; /* chars */ regs.R_DX = 0; /* "character offset into map 2 block" */ regs.R_ES = FP_SEG(g_font); regs.R_BP = FP_OFF(g_font); intr(0x10, ®s); } /****************************************************************************/ typedef struct { unsigned wd, ht; unsigned char *s; } sprite_t; void destroy_sprite(sprite_t *s) { if(s == NULL) return; if(s->s != NULL) free(s->s); free(s); } /****************************************************************************/ sprite_t *create_sprite(const char *src, unsigned wd, unsigned ht) { unsigned src_wd, src_ht, dst_wd, dst_ht, y, x, c; sprite_t *rv; src_wd = (wd + 1) & ~1; src_ht = (ht + 1) & ~1; dst_wd = src_wd / 2; dst_ht = src_ht / 2; if((rv = malloc(sizeof(sprite_t))) == NULL) return rv; if((rv->s = malloc(dst_wd * dst_ht)) == NULL) { free(rv); return NULL; } for(y = 0; y < src_ht; y += 2) { for(x = 0; x < src_wd; x += 2) { c = CG_BASE; if(src[wd * (y + 0) + (x + 0)] != ' ') c |= 0x01; if(src[wd * (y + 0) + (x + 1)] != ' ') c |= 0x02; if(src[wd * (y + 1) + (x + 0)] != ' ') c |= 0x04; if(src[wd * (y + 1) + (x + 1)] != ' ') c |= 0x08; rv->s[dst_wd * (y / 2) + (x / 2)] = c; } } rv->wd = dst_wd; rv->ht = dst_ht; return rv; } /****************************************************************************/ void display_sprite(const sprite_t *s, unsigned x, unsigned y, unsigned c) { unsigned i; textcolor(c); for(i = 0; i < s->ht; i++) { gotoxy(1 + x, 1 + y + i); cprintf("%-*.*s", s->wd, s->wd, &s->s[i * s->wd]); } } /****************************************************************************/ int main(void) { static const char invader1[96] = " # # " "# # # # " "# ####### # " "### ### ### " " ######### " " ######### " " # # " " # # "; static const char invader2[96] = " # # " " # # " " ####### " " ## ### ## " "# ####### # " "# ####### # " "# # # # " " ## ## "; /**/ sprite_t *i1, *i2; i1 = create_sprite(invader1, 12, 8); i2 = create_sprite(invader2, 12, 8); if(i1 == NULL || i2 == NULL) { printf("Error: out of memory\n"); return 1; } clrscr(); printf("Press a key to quit\n"); reprogram_font(); while(!kbhit()) { display_sprite(i1, 0, 5, 2); display_sprite(i2, 7, 5, 4); display_sprite(i1, 14, 5, 9); display_sprite(i2, 21, 5, 7); delay(500); if(kbhit()) break; display_sprite(i2, 0, 5, 9); display_sprite(i1, 7, 5, 7); display_sprite(i2, 14, 5, 2); display_sprite(i1, 21, 5, 4); delay(500); } if(getch() == 0) (void)getch(); destroy_sprite(i1); destroy_sprite(i2); /* crappy screenshot */ // screen_shot(); return 0; }