A Tetris clone, my first big C project. I wrote this using Borland C/C++ 4.5, back before you'd get a funny look for saying that. I started in 1996, and went back and touched it up a few times over the next 2 or 3 years. Rather than using Borland's supplied graphics interface, I wrote my own, directly accessing VGA video memory.
Looking back at this project now, I'll only say that I've learned a lot since. I'm not going to post a download, due to intellectual property issues, but I wil include two screen shots and a source sample.


The code below is from video.c, the file that contains all the graphics code for Tetris. I have chosen two functions, pcx_2_raw(), and blit_raw(). These functions make up the most basic building blocks of the graphics code, reading and writing pixels. While these two handle the majority of the graphics in Tetris, there are other functions, for converting raw image data into (and drawing) a run length encoded format with transparency support, as well as loading image palettes, etc.
I picked pcx_2_raw() for the difficult to find bug, the byte padding on uneven width images. This code worked fine for years, until I decided to up the block size from 8 to 11 pixels, with disastrous results. I especially liked this bug because it wasn't my fault, I was working with incomplete file format specs.
blit_raw() is included, because, I admit it, I had to show off the Assembly language. This version is much faster than the code that the compiler was creating, about 4 times faster if I remember correctly. Ironically, speed in this function is limited much more so by the 33 mhz bus I was working with than by the processor, so even a significant decrease in the number of instructions didn't equate to better performance.
Disclaimer: This was written in the late 1990's. If I were to rewrite it today I would brace and indent differently, name variables more clearly and restructure at least a few things. A good example is the goofy business of trying to pass a pair of integers through a character array (lines 41-44, 101-104). For some reason I can't remember, I thought this was a better solution than creating a structure to properly define the data. Looking at it now, it's ridiculous. Which is to say:
I don't write code like this anymore.
001: ////////////////////////////////////////////////////////////////////////////// 002: // PCX 2 RAW // 003: /////////////// 004: char * pcx_2_raw(char * pcx_file) 005: { 006: FILE * in_file; 007: int width, height, c, filler; 008: unsigned int raw_offset; 009: unsigned char run_length, data_byte; 010: char * raw; // pointer to the raw data 011: 012: // Open file 013: in_file = fopen(pcx_file, "r+b"); // Read Random Binary 014: if (!in_file) 015: { 016: printf("pcx_2_raw: File Open Error: %s", pcx_file); 017: exit(1); 018: } 019: 020: // Get format data 021: fseek(in_file, 8, SEEK_SET); // Jump ahead 8 bytes from start 022: fread(&width, 2, 1, in_file); // read 1 2 byte integer 023: fread(&height, 2, 1, in_file); // read 1 more 2 byte int 024: fseek(in_file, 116, SEEK_CUR); // Jump ahead 116 from current position 025: 026: height++; 027: width++; 028: 029: // Allocate RAM 030: raw = (char *)malloc(width * height + 4); // image size + format data 031: if (!raw) 032: { 033: printf("Out of RAM!\n"); 034: exit(1); 035: } 036: 037: // Insert format info 038: // width, height low byte first, then high 039: // (call it passing integers through a char array) 040: // this really should be a struct 041: *(raw + 0) = (unsigned char)(width & 0x00FF); // low byte 042: *(raw + 1) = (unsigned char)(width >> 8); // high byte 043: *(raw + 2) = (unsigned char)(height & 0x00FF); // low byte 044: *(raw + 3) = (unsigned char)(height >> 8); // high byte 045: raw_offset = 4; 046: 047: // Read PCX data 048: // How's this for a tough bug, Photostyler (Aldus v2.0) is adding 049: // a 255 to the end of each line WHEN THE IMAGE WIDTH IS ODD. 050: // Why, I have no idea, except that maybe it has to do with the 051: // internal data structure so they can achieve some goofy byte-alignment. 052: // Anyway, I can't test a different image software package now, 053: // So I have to adjust for this behavior here, and hope that 054: // It's part of the PCX file format (doubt it). 055: while (raw_offset < width * height + 4) // +4 to skip passed the width & height data. 056: { 057: fread(&data_byte, 1, 1, in_file); // Read 1 byte size of data. 058: if (data_byte >= (unsigned char)193) // 193 or higher indicates a run. 059: { 060: run_length = (data_byte - (unsigned char)192); 061: fread(&data_byte, 1, 1, in_file); // Read the color value of the run. 062: 063: // check for P Styler EOL marker on odd width images 064: filler = 0; 065: if ((width & 0x0001) // If image width is odd, 066: && raw_offset != 4) // and we aren't at the beginning. 067: { 068: if ((raw_offset-4) % (width) == 0) // We're at the end of a line. 069: if ((data_byte == (unsigned char)255) && run_length == 1) 070: filler = 1; // This is a filler btye. 071: } 072: 073: if (!filler) { // Only write output if this isn't a filler. 074: for (c = 0; c < run_length; c++) 075: { 076: *(raw + raw_offset) = data_byte; 077: raw_offset++; 078: } 079: } 080: } 081: else // Was not a run, just output a single pixel. 082: { 083: *(raw + raw_offset) = data_byte; 084: raw_offset++; 085: } 086: } // End While 087: 088: // Done reading data, clean up. 089: fclose(in_file); 090: return raw; 091: } 092: 093: ////////////////////////////////////////////////////////////////////////////// 094: // BLIT RAW // 095: ////////////// 096: void blit_raw(int x, int y, char * raw) 097: { 098: int width, height; 099: unsigned int screen_offset, raw_off, raw_seg; 100: 101: width = (unsigned char)*(raw); 102: width += (*(raw+1) << 8); 103: height = (unsigned char)*(raw+2); 104: height += (*(raw+3) << 8); 105: raw +=4; 106: 107: screen_offset = x; 108: screen_offset += ((y << 8) + (y << 6)); // y*256 + y*64 = y*320 109: 110: /* C version ** 111: for (y = 0; y < height; y++) 112: { 113: for (x = 0; x < width; x++) 114: { 115: *(screen + screen_offset) = *(raw); 116: raw++; 117: screen_offset++; 118: } 119: screen_offset += 320-width; 120: } 121: */ 122: 123: // ASM version // 124: // ex:di = destination 125: // ds:si = source 126: // cx = number of rep's 127: 128: raw_off = FP_OFF(raw); 129: raw_seg = FP_SEG(raw); 130: 131: asm{ 132: 133: push ds; // save segments 134: push es; 135: 136: mov ax, width; // get data format info 137: mov bx, height; 138: 139: mov dx, 0xA000; // setup destination reg 140: mov es, dx; 141: mov di, screen_offset; // setup destination offset 142: 143: mov cx, raw_off; // setup source seg and off 144: mov dx, raw_seg; 145: mov si, cx; 146: mov ds, dx; 147: 148: mov dx, 0x0140; // decimal 320, screen width 149: sub dx, ax; // screen width - sprite width 150: 151: blit_raw_line: 152: mov cx, ax; // blit width (1 line) 153: rep movsb; // write data 154: add di, dx; // wrap to next line 155: dec bx; // dec height counter 156: jnz blit_raw_line; // while (counter) 157: 158: pop es; // restore segments 159: pop ds; 160: } 161: return; 162: }