]> 4ch.mooo.com Git - 16.git/blob - modex16.c
modified: 16/modex16/scroll.c
[16.git] / modex16.c
1 #include <dos.h>\r
2 #include <string.h>\r
3 #include <mem.h>\r
4 #include <conio.h>\r
5 #include <stdio.h>\r
6 #include <stdlib.h>\r
7 #include "modex16.h"\r
8 \r
9 \r
10 byte far* VGA=(byte far*) 0xA0000000;   /* this points to video memory. */\r
11 \r
12 static void fadePalette(sbyte fade, sbyte start, word iter, byte *palette);\r
13 static byte tmppal[PAL_SIZE];\r
14 \r
15 static void\r
16 vgaSetMode(byte mode)\r
17 {\r
18   union REGS regs;\r
19 \r
20   regs.h.ah = SET_MODE;\r
21   regs.h.al = mode;\r
22   int86(VIDEO_INT, &regs, &regs);\r
23 }\r
24 \r
25 \r
26 /* -========================= Entry  Points ==========================- */\r
27 void\r
28 modexEnter() {\r
29     word i;\r
30     dword far*ptr=(dword far*)VGA;      /* used for faster screen clearing */\r
31     word CRTParms[] = {\r
32         0x0d06,         /* vertical total */\r
33         0x3e07,         /* overflow (bit 8 of vertical counts) */\r
34         0x4109,         /* cell height (2 to double-scan */\r
35         0xea10,         /* v sync start */\r
36         0xac11,         /* v sync end and protect cr0-cr7 */\r
37         0xdf12,         /* vertical displayed */\r
38         0x0014,         /* turn off dword mode */\r
39         0xe715,         /* v blank start */\r
40         0x0616,         /* v blank end */\r
41         0xe317          /* turn on byte mode */\r
42     };\r
43     int CRTParmCount = sizeof(CRTParms) / sizeof(CRTParms[0]);\r
44 \r
45     /* TODO save current video mode and palette */\r
46     vgaSetMode(VGA_256_COLOR_MODE);\r
47 \r
48     /* disable chain4 mode */\r
49     outpw(SC_INDEX, 0x0604);\r
50 \r
51     /* synchronous reset while setting Misc Output */\r
52     outpw(SC_INDEX, 0x0100);\r
53 \r
54     /* select 25 MHz dot clock & 60 Hz scanning rate */\r
55     outp(MISC_OUTPUT, 0xe3);\r
56 \r
57     /* undo reset (restart sequencer) */\r
58     outpw(SC_INDEX, 0x0300);\r
59 \r
60     /* reprogram the CRT controller */\r
61     outp(CRTC_INDEX, 0x11); /* VSync End reg contains register write prot */\r
62     outp(CRTC_DATA, 0x7f);  /* get current write protect on varios regs */\r
63 \r
64     /* send the CRTParms */\r
65     for(i=0; i<CRTParmCount; i++) {\r
66         outpw(CRTC_INDEX, CRTParms[i]);\r
67     }\r
68 \r
69     /* clear video memory */\r
70     outpw(SC_INDEX, 0x0f02);\r
71     for(i=0; i<0x8000; i++) {\r
72         ptr[i] = 0x0000;\r
73     }\r
74 }\r
75 \r
76 \r
77 void\r
78 modexLeave() {\r
79     /* TODO restore original mode and palette */\r
80     vgaSetMode(TEXT_MODE);\r
81 }\r
82 \r
83 \r
84 page_t\r
85 modexDefaultPage() {\r
86     page_t page;\r
87 \r
88     /* default page values */\r
89     page.data = VGA;\r
90     page.dx = 0;\r
91     page.dy = 0;\r
92     page.width = SCREEN_WIDTH;\r
93     page.height = SCREEN_HEIGHT;\r
94 \r
95     return page;\r
96 }\r
97 \r
98 /* returns the next page in contiguous memory\r
99  * the next page will be the same size as p, by default\r
100  */\r
101 page_t\r
102 modexNextPage(page_t *p) {\r
103     page_t result;\r
104 \r
105     result.data = p->data + (p->width/4)*p->height;  /* compute the offset */\r
106     result.dx = 0;\r
107     result.dy = 0;\r
108     result.width = p->width;\r
109     result.height = p->height;\r
110 \r
111     return result;\r
112 }\r
113 \r
114 \r
115 void\r
116 modexShowPage(page_t *page) {\r
117     word high_address;\r
118     word low_address;\r
119     word offset;\r
120     byte crtcOffset;\r
121 \r
122     /* calculate offset */\r
123     offset = (word) page->data;\r
124     offset += page->dy * (page->width >> 2 );\r
125     offset += page->dx >> 2;\r
126 \r
127     /* calculate crtcOffset according to virtual width */\r
128     crtcOffset = page->width >> 3;\r
129 \r
130     high_address = HIGH_ADDRESS | (offset & 0xff00);\r
131     low_address  = LOW_ADDRESS  | (offset << 8);\r
132 \r
133     /* wait for appropriate timing and then program CRTC */\r
134     while ((inp(INPUT_STATUS_1) & DISPLAY_ENABLE));\r
135     outpw(CRTC_INDEX, high_address);\r
136     outpw(CRTC_INDEX, low_address);\r
137     outp(CRTC_INDEX, 0x13);\r
138     outp(CRTC_DATA, crtcOffset);\r
139 \r
140     /*  wait for one retrace */\r
141     while (!(inp(INPUT_STATUS_1) & VRETRACE)); \r
142 \r
143     /* do PEL panning here */\r
144     outp(AC_INDEX, 0x33);\r
145     outp(AC_INDEX, (page->dx & 0x03) << 1);\r
146 }\r
147 \r
148 \r
149 void\r
150 modexPanPage(page_t *page, int dx, int dy) {\r
151     page->dx = dx;\r
152     page->dy = dy;\r
153 }\r
154 \r
155 \r
156 void\r
157 modexSelectPlane(byte plane) {\r
158     outp(SC_INDEX, MAP_MASK);          /* select plane */\r
159     outp(SC_DATA,  plane);\r
160 }\r
161 \r
162 \r
163 void\r
164 modexClearRegion(page_t *page, int x, int y, int w, int h, byte  color) {\r
165     word pageOff = (word) page->data;\r
166     word xoff=x/4;       /* xoffset that begins each row */\r
167     word scanCount=w/4;  /* number of iterations per row (excluding right clip)*/\r
168     word poffset = pageOff + y*(page->width/4) + xoff; /* starting offset */\r
169     word nextRow = page->width/4-scanCount-1;  /* loc of next row */\r
170     byte lclip[] = {0x0f, 0x0e, 0x0c, 0x08};  /* clips for rectangles not on 4s */\r
171     byte rclip[] = {0x00, 0x01, 0x03, 0x07};\r
172     byte left = lclip[x&0x03];\r
173     byte right = rclip[(x+w)&0x03];\r
174 \r
175     /* handle the case which requires an extra group */\r
176     if((x & 0x03) && !((x+w) & 0x03)) {\r
177       right=0x0f;\r
178     }\r
179 \r
180     __asm {\r
181                 MOV AX, SCREEN_SEG      ; go to the VGA memory\r
182                 MOV ES, AX\r
183                 MOV DI, poffset         ; go to the first pixel\r
184                 MOV DX, SC_INDEX        ; point to the map mask\r
185                 MOV AL, MAP_MASK\r
186                 OUT DX, AL\r
187                 INC DX\r
188                 MOV AL, color           ; get ready to write colors\r
189         SCAN_START:\r
190                 MOV CX, scanCount       ; count the line\r
191                 MOV BL, AL              ; remember color\r
192                 MOV AL, left            ; do the left clip\r
193                 OUT DX, AL              ; set the left clip\r
194                 MOV AL, BL              ; restore color\r
195                 STOSB                   ; write the color\r
196                 DEC CX\r
197                 JZ SCAN_DONE            ; handle 1 group stuff\r
198 \r
199                 ;-- write the main body of the scanline\r
200                 MOV BL, AL              ; remember color\r
201                 MOV AL, 0x0f            ; write to all pixels\r
202                 OUT DX, AL\r
203                 MOV AL, BL              ; restore color\r
204                 REP STOSB               ; write the color\r
205         SCAN_DONE:\r
206                 MOV BL, AL              ; remeber color\r
207                 MOV AL, right\r
208                 OUT DX, AL              ; do the right clip\r
209                 MOV AL, BL              ; restore color\r
210                 STOSB                   ; write pixel\r
211                 ADD DI, nextRow         ; go to the next row\r
212                 DEC h\r
213                 JNZ SCAN_START\r
214     }\r
215 }\r
216
217
218 void\r
219 modexClearPlayer(page_t *page, int x, int y, int w, int h) {\r
220     word pageOff = (word) page->data;\r
221     word xoff=x/4;       /* xoffset that begins each row */\r
222     word scanCount=w/4;  /* number of iterations per row (excluding right clip)*/\r
223     word poffset = pageOff + y*(page->width/4) + xoff; /* starting offset */\r
224     word nextRow = page->width/4-scanCount-1;  /* loc of next row */\r
225     byte lclip[] = {0x0f, 0x0e, 0x0c, 0x08};  /* clips for rectangles not on 4s */\r
226     byte rclip[] = {0x00, 0x01, 0x03, 0x07};\r
227     byte left = lclip[x&0x03];\r
228     byte right = rclip[(x+w)&0x03];\r
229 \r
230     /* handle the case which requires an extra group */\r
231     if((x & 0x03) && !((x+w) & 0x03)) {\r
232       right=0x0f;\r
233     }\r
234 \r
235     __asm {\r
236                 MOV AX, SCREEN_SEG      ; go to the VGA memory\r
237                 MOV ES, AX\r
238                 MOV DI, poffset         ; go to the first pixel\r
239                 MOV DX, SC_INDEX        ; point to the map mask\r
240                 MOV AL, MAP_MASK\r
241                 OUT DX, AL\r
242                 INC DX\r
243                 MOV AL, NULL            ; get ready to write colors\r
244         SCAN_START:\r
245                 MOV CX, scanCount       ; count the line\r
246                 MOV BL, AL              ; remember color\r
247                 MOV AL, left            ; do the left clip\r
248                 OUT DX, AL              ; set the left clip\r
249                 MOV AL, BL              ; restore color\r
250                 STOSB                   ; write the color\r
251                 DEC CX\r
252                 JZ SCAN_DONE            ; handle 1 group stuff\r
253 \r
254                 ;-- write the main body of the scanline\r
255                 MOV BL, AL              ; remember color\r
256                 MOV AL, 0x0f            ; write to all pixels\r
257                 OUT DX, AL\r
258                 MOV AL, BL              ; restore color\r
259                 REP STOSB               ; write the color\r
260         SCAN_DONE:\r
261                 MOV BL, AL              ; remeber color\r
262                 MOV AL, right\r
263                 OUT DX, AL              ; do the right clip\r
264                 MOV AL, BL              ; restore color\r
265                 STOSB                   ; write pixel\r
266                 ADD DI, nextRow         ; go to the next row\r
267                 DEC h\r
268                 JNZ SCAN_START\r
269     }\r
270 }\r
271
272 \r
273 void\r
274 modexDrawBmp(page_t *page, int x, int y, bitmap_t *bmp) {\r
275     /* draw the region (the entire freakin bitmap) */\r
276     modexDrawBmpRegion(page, x, y, 0, 0, bmp->width, bmp->height, bmp);\r
277 }\r
278 \r
279 \r
280 void\r
281 modexDrawBmpRegion(page_t *page, int x, int y,\r
282                    int rx, int ry, int rw, int rh, bitmap_t *bmp) {\r
283     word poffset = (word) page->data  + y*(page->width/4) + x/4;\r
284     byte *data = bmp->data;\r
285     word bmpOffset = (word) data + ry * bmp->width + rx;\r
286     word width = rw;\r
287     word height = rh;\r
288     byte plane = 1 << ((byte) x & 0x03);\r
289     word scanCount = width/4 + (width%4 ? 1 :0);\r
290     word nextPageRow = page->width/4 - scanCount;\r
291     word nextBmpRow = (word) bmp->width - width;\r
292     word rowCounter;\r
293     byte planeCounter = 4;\r
294 \r
295     __asm {\r
296                 MOV AX, SCREEN_SEG      ; go to the VGA memory\r
297                 MOV ES, AX\r
298 \r
299                 MOV DX, SC_INDEX        ; point at the map mask register\r
300                 MOV AL, MAP_MASK        ;\r
301                 OUT DX, AL              ;\r
302 \r
303         PLANE_LOOP:\r
304                 MOV DX, SC_DATA         ; select the current plane\r
305                 MOV AL, plane           ;\r
306                 OUT DX, AL              ;\r
307 \r
308                 ;-- begin plane painting\r
309                 MOV AX, height          ; start the row counter\r
310                 MOV rowCounter, AX      ; \r
311                 MOV DI, poffset         ; go to the first pixel\r
312                 MOV SI, bmpOffset       ; go to the bmp pixel\r
313         ROW_LOOP:\r
314                 MOV CX, width           ; count the columns\r
315         SCAN_LOOP:\r
316                 MOVSB                   ; copy the pixel\r
317                 SUB CX, 3               ; we skip the next 3\r
318                 ADD SI, 3               ; skip the bmp pixels\r
319                 LOOP SCAN_LOOP          ; finish the scan\r
320 \r
321                 MOV AX, nextPageRow\r
322                 ADD DI, AX              ; go to the next row on screen\r
323                 MOV AX, nextBmpRow\r
324                 ADD SI, AX              ; go to the next row on bmp\r
325 \r
326                 DEC rowCounter\r
327                 JNZ ROW_LOOP            ; do all the rows\r
328                 ;-- end plane painting\r
329 \r
330                 MOV AL, plane           ; advance to the next plane\r
331                 SHL AL, 1               ;\r
332                 AND AL, 0x0f            ; mask the plane properly\r
333                 MOV plane, AL           ; store the plane\r
334 \r
335                 INC bmpOffset           ; start bmp at the right spot\r
336 \r
337                 DEC planeCounter\r
338                 JNZ PLANE_LOOP          ; do all 4 planes\r
339     }\r
340 }\r
341 \r
342 \r
343 void\r
344 modexDrawSprite(page_t *page, int x, int y, bitmap_t *bmp) {\r
345     /* draw the whole sprite */\r
346     modexDrawSpriteRegion(page, x, y, 0, 0, bmp->width, bmp->height, bmp);\r
347 }\r
348 \r
349 void\r
350 modexDrawSpriteRegion(page_t *page, int x, int y,\r
351                       int rx, int ry, int rw, int rh, bitmap_t *bmp) {\r
352     word poffset = (word)page->data + y*(page->width/4) + x/4;\r
353     byte *data = bmp->data;\r
354     word bmpOffset = (word) data + ry * bmp->width + rx;\r
355     word width = rw;\r
356     word height = rh;\r
357     byte plane = 1 << ((byte) x & 0x03);\r
358     word scanCount = width/4 + (width%4 ? 1 :0);\r
359     word nextPageRow = page->width/4 - scanCount;\r
360     word nextBmpRow = (word) bmp->width - width;\r
361     word rowCounter;\r
362     byte planeCounter = 4;\r
363 \r
364     __asm {\r
365                 MOV AX, SCREEN_SEG      ; go to the VGA memory\r
366                 MOV ES, AX\r
367 \r
368                 MOV DX, SC_INDEX        ; point at the map mask register\r
369                 MOV AL, MAP_MASK        ;\r
370                 OUT DX, AL              ;\r
371 \r
372         PLANE_LOOP:\r
373                 MOV DX, SC_DATA         ; select the current plane\r
374                 MOV AL, plane           ;\r
375                 OUT DX, AL              ;\r
376 \r
377                 ;-- begin plane painting\r
378                 MOV AX, height          ; start the row counter\r
379                 MOV rowCounter, AX      ; \r
380                 MOV DI, poffset         ; go to the first pixel\r
381                 MOV SI, bmpOffset       ; go to the bmp pixel\r
382         ROW_LOOP:\r
383                 MOV CX, width           ; count the columns\r
384         SCAN_LOOP:\r
385                 LODSB\r
386                 DEC SI\r
387                 CMP AL, 0\r
388                 JNE DRAW_PIXEL          ; draw non-zero pixels\r
389 \r
390                 INC DI                  ; skip the transparent pixel\r
391                 ADD SI, 1\r
392                 JMP NEXT_PIXEL\r
393         DRAW_PIXEL:\r
394                 MOVSB                   ; copy the pixel\r
395         NEXT_PIXEL:\r
396                 SUB CX, 3               ; we skip the next 3\r
397                 ADD SI, 3               ; skip the bmp pixels\r
398                 LOOP SCAN_LOOP          ; finish the scan\r
399 \r
400                 MOV AX, nextPageRow\r
401                 ADD DI, AX              ; go to the next row on screen\r
402                 MOV AX, nextBmpRow\r
403                 ADD SI, AX              ; go to the next row on bmp\r
404 \r
405                 DEC rowCounter\r
406                 JNZ ROW_LOOP            ; do all the rows\r
407                 ;-- end plane painting\r
408 \r
409                 MOV AL, plane           ; advance to the next plane\r
410                 SHL AL, 1               ;\r
411                 AND AL, 0x0f            ; mask the plane properly\r
412                 MOV plane, AL           ; store the plane\r
413 \r
414                 INC bmpOffset           ; start bmp at the right spot\r
415 \r
416                 DEC planeCounter\r
417                 JNZ PLANE_LOOP          ; do all 4 planes\r
418     }\r
419 }\r
420 \r
421 \r
422 /* copy a region of video memory from one page to another.\r
423  * It assumes that the left edge of the tile is the same on both\r
424  * regions and the memory areas do not overlap.\r
425  */\r
426 void\r
427 modexCopyPageRegion(page_t *dest, page_t *src,\r
428                     word sx, word sy,\r
429                     word dx, word dy,\r
430                     word width, word height)\r
431 {\r
432     word doffset = (word)dest->data + dy*(dest->width/4) + dx/4;\r
433     word soffset = (word)src->data + sy*(src->width/4) + sx/4;\r
434     word scans   = width/4;\r
435     word nextSrcRow = src->width/4 - scans - 1;\r
436     word nextDestRow = dest->width/4 - scans - 1;\r
437     byte lclip[] = {0x0f, 0x0e, 0x0c, 0x08};  /* clips for rectangles not on 4s */\r
438     byte rclip[] = {0x0f, 0x01, 0x03, 0x07};\r
439     byte left = lclip[sx&0x03];\r
440     byte right = rclip[(sx+width)&0x03];\r
441 \r
442     __asm {\r
443                 MOV AX, SCREEN_SEG      ; work in the vga space\r
444                 MOV ES, AX              ;\r
445                 MOV DI, doffset         ;\r
446                 MOV SI, soffset         ;\r
447 \r
448                 MOV DX, GC_INDEX        ; turn off cpu bits\r
449                 MOV AX, 0008h           ;\r
450                 OUT DX, AX\r
451 \r
452                 MOV AX, SC_INDEX        ; point to the mask register\r
453                 MOV DX, AX              ;\r
454                 MOV AL, MAP_MASK        ;\r
455                 OUT DX, AL              ;\r
456                 INC DX                  ;\r
457 \r
458         ROW_START:\r
459                 PUSH DS\r
460                 MOV AX, ES\r
461                 MOV DS, AX\r
462                 MOV CX, scans           ; the number of latches\r
463 \r
464                 MOV AL, left            ; do the left column\r
465                 OUT DX, AL              ;\r
466                 MOVSB                   ;\r
467                 DEC CX                  ;\r
468 \r
469                 MOV AL, 0fh             ; do the inner columns\r
470                 OUT DX, AL\r
471                 REP MOVSB               ; copy the pixels\r
472 \r
473                 MOV AL, right           ; do the right column\r
474                 OUT DX, AL\r
475                 MOVSB\r
476                 POP DS\r
477 \r
478                 MOV AX, SI              ; go the start of the next row\r
479                 ADD AX, nextSrcRow      ;\r
480                 MOV SI, AX              ;\r
481                 MOV AX, DI              ;\r
482                 ADD AX, nextDestRow     ;\r
483                 MOV DI, AX              ;\r
484 \r
485                 DEC height              ; do the rest of the actions\r
486                 JNZ ROW_START           ;\r
487 \r
488                 MOV DX, GC_INDEX+1      ; go back to CPU data\r
489                 MOV AL, 0ffh            ; none from latches\r
490                 OUT DX, AL              ;\r
491     }\r
492 }\r
493 \r
494 \r
495 /* fade and flash */\r
496 void\r
497 modexFadeOn(word fade, byte *palette) {\r
498     fadePalette(-fade, 64, 64/fade+1, palette);\r
499 }\r
500 \r
501 \r
502 void\r
503 modexFadeOff(word fade, byte *palette) {\r
504     fadePalette(fade, 0, 64/fade+1, palette);\r
505 }\r
506 \r
507 \r
508 void\r
509 modexFlashOn(word fade, byte *palette) {\r
510     fadePalette(fade, -64, 64/fade+1, palette);\r
511 }\r
512 \r
513 \r
514 void\r
515 modexFlashOff(word fade, byte *palette) {\r
516     fadePalette(-fade, 0, 64/fade+1, palette);\r
517 }\r
518 \r
519 \r
520 static void\r
521 fadePalette(sbyte fade, sbyte start, word iter, byte *palette) {\r
522     word i;\r
523     byte dim = start;\r
524 \r
525     /* handle the case where we just update */\r
526     if(iter == 0) {\r
527         modexPalUpdate(palette);\r
528         return;\r
529     }\r
530 \r
531     while(iter > 0) {  /* FadeLoop */\r
532         for(i=0; i<PAL_SIZE; i++) { /* loadpal_loop */\r
533             tmppal[i] = palette[i] - dim;\r
534             if(tmppal[i] > 127) {\r
535                 tmppal[i] = 0;\r
536             } else if(tmppal[i] > 63) {\r
537                 tmppal[i] = 63;\r
538             }\r
539         }\r
540         modexPalUpdate(tmppal);\r
541         iter--;\r
542         dim += fade;\r
543     }\r
544 }\r
545 \r
546 \r
547 /* save and load */\r
548 void\r
549 modexPalSave(byte *palette) {\r
550     int  i;\r
551 \r
552     outp(PAL_READ_REG, 0);      /* start at palette entry 0 */\r
553     for(i=0; i<PAL_SIZE; i++) {\r
554         palette[i] = inp(PAL_DATA_REG); /* read the palette data */\r
555     }\r
556 }\r
557 \r
558 \r
559 byte *\r
560 modexNewPal() {\r
561     byte *ptr;\r
562     ptr = malloc(PAL_SIZE);\r
563 \r
564     /* handle errors */\r
565     if(!ptr) {\r
566         printf("Could not allocate palette.\n");\r
567         exit(-1);\r
568     }\r
569 \r
570     return ptr;\r
571 }\r
572 \r
573 \r
574 void\r
575 modexLoadPalFile(byte *filename, byte **palette) {\r
576     FILE *file;\r
577     byte *ptr;\r
578 \r
579     /* free the palette if it exists */\r
580     if(*palette) {\r
581         free(*palette);\r
582     }\r
583 \r
584     /* allocate the new palette */\r
585     *palette = modexNewPal();\r
586 \r
587     /* open the file */\r
588     file = fopen(filename, "rb");\r
589     if(!file) {\r
590         printf("Could not open palette file: %s\n", filename);\r
591         exit(-2);\r
592     }\r
593 \r
594     /* read the file */\r
595     ptr = *palette;\r
596     while(!feof(file)) {\r
597         *ptr++ = fgetc(file);\r
598     }\r
599 \r
600     fclose(file);\r
601 }\r
602 \r
603 \r
604 void\r
605 modexSavePalFile(char *filename, byte *pal) {\r
606     unsigned int i;\r
607     FILE *file;\r
608 \r
609     /* open the file for writing */\r
610     file = fopen(filename, "wb");\r
611     if(!file) {\r
612         printf("Could not open %s for writing\n", filename);\r
613         exit(-2);\r
614     }\r
615 \r
616     /* write the data to the file */\r
617     fwrite(pal, 1, PAL_SIZE, file);\r
618     fclose(file);\r
619 }\r
620 \r
621 \r
622 /* blanking */\r
623 void\r
624 modexPalBlack() {\r
625     fadePalette(-1, 64, 1, tmppal);\r
626 }\r
627 \r
628 \r
629 void\r
630 modexPalWhite() {\r
631     fadePalette(-1, -64, 1, tmppal);\r
632 }\r
633 \r
634 \r
635 /* utility */\r
636 void\r
637 modexPalUpdate(byte *p) {\r
638     int i;\r
639     modexWaitBorder();\r
640     outp(PAL_WRITE_REG, 0);  /* start at the beginning of palette */\r
641     for(i=0; i<PAL_SIZE/2; i++) {\r
642         outp(PAL_DATA_REG, p[i]);\r
643     }\r
644     modexWaitBorder();      /* waits one retrace -- less flicker */\r
645     for(i=PAL_SIZE/2; i<PAL_SIZE; i++) {\r
646         outp(PAL_DATA_REG, p[i]);\r
647     }\r
648 }\r
649 \r
650 \r
651 void\r
652 modexWaitBorder() {\r
653     while(inp(INPUT_STATUS_1)  & 8)  {\r
654         /* spin */\r
655     }\r
656 \r
657     while(!(inp(INPUT_STATUS_1)  & 8))  {\r
658         /* spin */\r
659     }\r
660 }\r