+\r
+void\r
+modexDrawSprite(page_t *page, int x, int y, bitmap_t *bmp) {\r
+ /* draw the whole sprite */\r
+ modexDrawSpriteRegion(page, x, y, 0, 0, bmp->width, bmp->height, bmp);\r
+}\r
+\r
+void\r
+modexDrawSpriteRegion(page_t *page, int x, int y,\r
+ int rx, int ry, int rw, int rh, bitmap_t *bmp) {\r
+ word poffset = (word)page->data + y*(page->width/4) + x/4;\r
+ byte *data = bmp->data;\r
+ word bmpOffset = (word) data + ry * bmp->width + rx;\r
+ word width = rw;\r
+ word height = rh;\r
+ byte plane = 1 << ((byte) x & 0x03);\r
+ word scanCount = width/4 + (width%4 ? 1 :0);\r
+ word nextPageRow = page->width/4 - scanCount;\r
+ word nextBmpRow = (word) bmp->width - width;\r
+ word rowCounter;\r
+ byte planeCounter = 4;\r
+\r
+ __asm {\r
+ MOV AX, SCREEN_SEG ; go to the VGA memory\r
+ MOV ES, AX\r
+\r
+ MOV DX, SC_INDEX ; point at the map mask register\r
+ MOV AL, MAP_MASK ;\r
+ OUT DX, AL ;\r
+\r
+ PLANE_LOOP:\r
+ MOV DX, SC_DATA ; select the current plane\r
+ MOV AL, plane ;\r
+ OUT DX, AL ;\r
+\r
+ ;-- begin plane painting\r
+ MOV AX, height ; start the row counter\r
+ MOV rowCounter, AX ; \r
+ MOV DI, poffset ; go to the first pixel\r
+ MOV SI, bmpOffset ; go to the bmp pixel\r
+ ROW_LOOP:\r
+ MOV CX, width ; count the columns\r
+ SCAN_LOOP:\r
+ LODSB\r
+ DEC SI\r
+ CMP AL, 0\r
+ JNE DRAW_PIXEL ; draw non-zero pixels\r
+\r
+ INC DI ; skip the transparent pixel\r
+ ADD SI, 1\r
+ JMP NEXT_PIXEL\r
+ DRAW_PIXEL:\r
+ MOVSB ; copy the pixel\r
+ NEXT_PIXEL:\r
+ SUB CX, 3 ; we skip the next 3\r
+ ADD SI, 3 ; skip the bmp pixels\r
+ LOOP SCAN_LOOP ; finish the scan\r
+\r
+ MOV AX, nextPageRow\r
+ ADD DI, AX ; go to the next row on screen\r
+ MOV AX, nextBmpRow\r
+ ADD SI, AX ; go to the next row on bmp\r
+\r
+ DEC rowCounter\r
+ JNZ ROW_LOOP ; do all the rows\r
+ ;-- end plane painting\r
+\r
+ MOV AL, plane ; advance to the next plane\r
+ SHL AL, 1 ;\r
+ AND AL, 0x0f ; mask the plane properly\r
+ MOV plane, AL ; store the plane\r
+\r
+ INC bmpOffset ; start bmp at the right spot\r
+\r
+ DEC planeCounter\r
+ JNZ PLANE_LOOP ; do all 4 planes\r
+ }\r
+}\r
+\r
+\r
+/* copy a region of video memory from one page to another.\r
+ * It assumes that the left edge of the tile is the same on both\r
+ * regions and the memory areas do not overlap.\r
+ */\r
+void\r
+modexCopyPageRegion(page_t *dest, page_t *src,\r
+ word sx, word sy,\r
+ word dx, word dy,\r
+ word width, word height)\r
+{\r
+ word doffset = (word)dest->data + dy*(dest->width/4) + dx/4;\r
+ word soffset = (word)src->data + sy*(src->width/4) + sx/4;\r
+ word scans = width/4;\r
+ word nextSrcRow = src->width/4 - scans - 1;\r
+ word nextDestRow = dest->width/4 - scans - 1;\r
+ byte lclip[] = {0x0f, 0x0e, 0x0c, 0x08}; /* clips for rectangles not on 4s */\r
+ byte rclip[] = {0x0f, 0x01, 0x03, 0x07};\r
+ byte left = lclip[sx&0x03];\r
+ byte right = rclip[(sx+width)&0x03];\r
+\r
+ __asm {\r
+ MOV AX, SCREEN_SEG ; work in the vga space\r
+ MOV ES, AX ;\r
+ MOV DI, doffset ;\r
+ MOV SI, soffset ;\r
+\r
+ MOV DX, GC_INDEX ; turn off cpu bits\r
+ MOV AX, 0008h ;\r
+ OUT DX, AX\r
+\r
+ MOV AX, SC_INDEX ; point to the mask register\r
+ MOV DX, AX ;\r
+ MOV AL, MAP_MASK ;\r
+ OUT DX, AL ;\r
+ INC DX ;\r
+\r
+ ROW_START:\r
+ PUSH DS\r
+ MOV AX, ES\r
+ MOV DS, AX\r
+ MOV CX, scans ; the number of latches\r
+\r
+ MOV AL, left ; do the left column\r
+ OUT DX, AL ;\r
+ MOVSB ;\r
+ DEC CX ;\r
+\r
+ MOV AL, 0fh ; do the inner columns\r
+ OUT DX, AL\r
+ REP MOVSB ; copy the pixels\r
+\r
+ MOV AL, right ; do the right column\r
+ OUT DX, AL\r
+ MOVSB\r
+ POP DS\r
+\r
+ MOV AX, SI ; go the start of the next row\r
+ ADD AX, nextSrcRow ;\r
+ MOV SI, AX ;\r
+ MOV AX, DI ;\r
+ ADD AX, nextDestRow ;\r
+ MOV DI, AX ;\r
+\r
+ DEC height ; do the rest of the actions\r
+ JNZ ROW_START ;\r
+\r
+ MOV DX, GC_INDEX+1 ; go back to CPU data\r
+ MOV AL, 0ffh ; none from latches\r
+ OUT DX, AL ;\r
+ }\r
+}\r
+\r
+\r
+/* fade and flash */\r
+void\r
+modexFadeOn(word fade, byte *palette) {\r
+ fadePalette(-fade, 64, 64/fade+1, palette);\r
+}\r
+\r
+\r
+void\r
+modexFadeOff(word fade, byte *palette) {\r
+ fadePalette(fade, 0, 64/fade+1, palette);\r
+}\r
+\r
+\r
+void\r
+modexFlashOn(word fade, byte *palette) {\r
+ fadePalette(fade, -64, 64/fade+1, palette);\r
+}\r
+\r
+\r
+void\r
+modexFlashOff(word fade, byte *palette) {\r
+ fadePalette(-fade, 0, 64/fade+1, palette);\r
+}\r
+\r
+\r
+static void\r
+fadePalette(sbyte fade, sbyte start, word iter, byte *palette) {\r
+ word i;\r
+ byte dim = start;\r
+\r
+ /* handle the case where we just update */\r
+ if(iter == 0) {\r
+ modexPalUpdate(palette);\r
+ return;\r
+ }\r
+\r
+ while(iter > 0) { /* FadeLoop */\r
+ for(i=0; i<PAL_SIZE; i++) { /* loadpal_loop */\r
+ tmppal[i] = palette[i] - dim;\r
+ if(tmppal[i] > 127) {\r
+ tmppal[i] = 0;\r
+ } else if(tmppal[i] > 63) {\r
+ tmppal[i] = 63;\r