Annotation of hatari/src/video.c, revision 1.1.1.9

1.1       root        1: /*
1.1.1.5   root        2:   Hatari - video.c
                      3: 
                      4:   This file is distributed under the GNU Public License, version 2 or at
                      5:   your option any later version. Read the file gpl.txt for details.
1.1       root        6: 
1.1.1.7   root        7:   Video hardware handling. This code handling all to do with the video chip.
                      8:   So, we handle VBLs, HBLs, copying the ST screen to a buffer to simulate the
                      9:   TV raster trace, border removal, palette changes per HBL, the 'video address
                     10:   pointer' etc...
1.1       root       11: */
1.1.1.9 ! root       12: char Video_rcsid[] = "Hatari $Id: video.c,v 1.41 2005/09/01 19:20:49 eerot Exp $";
1.1       root       13: 
1.1.1.4   root       14: #include <SDL.h>
1.1.1.7   root       15: #include <SDL_endian.h>
1.1.1.4   root       16: 
1.1       root       17: #include "main.h"
1.1.1.6   root       18: #include "configuration.h"
1.1       root       19: #include "fdc.h"
                     20: #include "int.h"
                     21: #include "ikbd.h"
1.1.1.8   root       22: #include "ioMem.h"
1.1.1.4   root       23: #include "keymap.h"
1.1       root       24: #include "m68000.h"
                     25: #include "memorySnapShot.h"
                     26: #include "mfp.h"
                     27: #include "screen.h"
                     28: #include "shortcut.h"
                     29: #include "sound.h"
                     30: #include "spec512.h"
                     31: #include "stMemory.h"
                     32: #include "vdi.h"
                     33: #include "video.h"
                     34: #include "ymFormat.h"
1.1.1.4   root       35: 
                     36: 
1.1.1.9 ! root       37: #define BORDERMASK_NONE    0x00                 /* Borders masks */
        !            38: #define BORDERMASK_LEFT    0x01
        !            39: #define BORDERMASK_RIGHT   0x02
        !            40: 
        !            41: 
1.1.1.2   root       42: BOOL bUseHighRes = FALSE;                       /* Use hi-res (ie Mono monitor) */
1.1       root       43: int nVBLs, nHBL;                                /* VBL Counter, HBL line */
1.1.1.8   root       44: int nStartHBL, nEndHBL;                         /* Start/End HBL for visible screen(64 lines in Top border) */
1.1       root       45: int OverscanMode;                               /* OVERSCANMODE_xxxx for current display frame */
1.1.1.8   root       46: Uint16 HBLPalettes[(NUM_VISIBLE_LINES+1)*16];   /* 1x16 colour palette per screen line, +1 line just incase write after line 200 */
                     47: Uint16 *pHBLPalettes;                           /* Pointer to current palette lists, one per HBL */
1.1.1.9 ! root       48: Uint32 HBLPaletteMasks[NUM_VISIBLE_LINES+1];    /* Bit mask of palette colours changes, top bit set is resolution change */
        !            49: Uint32 *pHBLPaletteMasks;
1.1.1.6   root       50: int nScreenRefreshRate = 50;                    /* 50 or 60 Hz in color, 70 Hz in mono */
1.1.1.8   root       51: Uint32 VideoBase;                               /* Base address in ST Ram for screen (read on each VBL) */
1.1.1.9 ! root       52: 
        !            53: static Uint8 HWScrollCount;                     /* HW scroll pixel offset, STe only (0...15) */
        !            54: static Uint8 ScanLineSkip;                      /* Scan line width add, STe only (words, minus 1) */
1.1.1.8   root       55: static Uint8 *pVideoRaster;                     /* Pointer to Video raster, after VideoBase in PC address space. Use to copy data on HBL */
                     56: static Uint8 VideoShifterByte;                  /* VideoShifter (0xff8260) value store in video chip */
                     57: static int LeftRightBorder;                     /* BORDERMASK_xxxx used to simulate left/right border removal */
1.1       root       58: 
                     59: 
                     60: /*-----------------------------------------------------------------------*/
                     61: /*
                     62:   Save/Restore snapshot of local variables('MemorySnapShot_Store' handles type)
                     63: */
                     64: void Video_MemorySnapShot_Capture(BOOL bSave)
                     65: {
                     66:   /* Save/Restore details */
                     67:   MemorySnapShot_Store(&VideoShifterByte,sizeof(VideoShifterByte));
                     68:   MemorySnapShot_Store(&bUseHighRes,sizeof(bUseHighRes));
                     69:   MemorySnapShot_Store(&nVBLs,sizeof(nVBLs));
                     70:   MemorySnapShot_Store(&nHBL,sizeof(nHBL));
                     71:   MemorySnapShot_Store(&nStartHBL,sizeof(nStartHBL));
                     72:   MemorySnapShot_Store(&nEndHBL,sizeof(nEndHBL));
                     73:   MemorySnapShot_Store(&OverscanMode,sizeof(OverscanMode));
                     74:   MemorySnapShot_Store(HBLPalettes,sizeof(HBLPalettes));
                     75:   MemorySnapShot_Store(HBLPaletteMasks,sizeof(HBLPaletteMasks));
                     76:   MemorySnapShot_Store(&VideoBase,sizeof(VideoBase));
1.1.1.9 ! root       77:   MemorySnapShot_Store(&ScanLineSkip,sizeof(ScanLineSkip));
        !            78:   MemorySnapShot_Store(&HWScrollCount,sizeof(HWScrollCount));
1.1.1.8   root       79:   MemorySnapShot_Store(&pVideoRaster,sizeof(pVideoRaster));
1.1       root       80: }
                     81: 
1.1.1.8   root       82: 
1.1       root       83: /*-----------------------------------------------------------------------*/
                     84: /*
1.1.1.8   root       85:   Calculate and return video address pointer.
1.1       root       86: */
1.1.1.8   root       87: static Uint32 Video_CalculateAddress(void)
1.1       root       88: {
1.1.1.9 ! root       89:   int X, FrameCycles;
        !            90:   Uint32 VideoAddress;      /* Address of video display in ST screen space */
1.1       root       91: 
                     92:   /* Find number of cycles passed during frame */
                     93:   FrameCycles = Int_FindFrameCycles();
                     94: 
1.1.1.9 ! root       95:   /* Top of screen is usually 64 lines from VBL (64x512=32768 cycles) */
        !            96:   if (FrameCycles < nStartHBL*CYCLES_PER_LINE)
1.1.1.8   root       97:   {
1.1.1.9 ! root       98:     VideoAddress = VideoBase;
1.1.1.8   root       99:   }
                    100:   else
                    101:   {
1.1.1.9 ! root      102:     /* Now find which pixel we are on (ignore left/right borders) */
        !           103:     /* 96 + 320 + 96 = 512 pixels per scan line (each pixel is one cycle) */
        !           104:     X = FrameCycles % CYCLES_PER_LINE;
        !           105:     if (X < SCREEN_START_CYCLE)       /* Limit if in NULL area outside screen */
1.1       root      106:       X = SCREEN_START_CYCLE;
1.1.1.9 ! root      107:     if (X > (CYCLES_PER_LINE - SCREEN_START_CYCLE))
        !           108:       X = (CYCLES_PER_LINE - SCREEN_START_CYCLE);
        !           109:     /* X is now from 96 to 416 (320 pixels), subtract 96 to give X pixel across screen! */
1.1       root      110:     X = ((X-SCREEN_START_CYCLE)>>1)&(~1);
1.1.1.6   root      111: 
1.1.1.9 ! root      112:     VideoAddress = pVideoRaster - STRam;
        !           113:     /* Add line cycles if we have not reached end of screen yet: */
        !           114:     if (FrameCycles < nEndHBL*CYCLES_PER_LINE)
        !           115:       VideoAddress += X;
1.1       root      116:   }
                    117: 
1.1.1.8   root      118:   return VideoAddress;
1.1       root      119: }
                    120: 
1.1.1.7   root      121: 
1.1       root      122: /*-----------------------------------------------------------------------*/
                    123: /*
                    124:   HBL interrupt - this is very inaccurate on ST and appears to occur around 1/3rd way into
                    125:   the display!
                    126: */
                    127: void Video_InterruptHandler_HBL(void)
                    128: {
1.1.1.2   root      129:   /* Remove this interrupt from list and re-order */
1.1       root      130:   Int_AcknowledgeInterrupt();
1.1.1.7   root      131: 
1.1.1.2   root      132:   /* Generate new Timer AB, if need to - there are 313 HBLs per frame */
1.1.1.7   root      133:   if(nHBL < (SCANLINES_PER_FRAME-1))
1.1       root      134:     Int_AddAbsoluteInterrupt(CYCLES_PER_LINE,INTERRUPT_VIDEO_HBL);
                    135: 
1.1.1.7   root      136:   M68000_Exception(EXCEPTION_HBLANK);   /* Horizontal blank interrupt, level 2! */
1.1       root      137: }
                    138: 
                    139: 
1.1.1.2   root      140: /*-----------------------------------------------------------------------*/
1.1       root      141: /*
1.1.1.7   root      142:   Write to VideoShifter (0xff8260), resolution bits
1.1       root      143: */
1.1.1.9 ! root      144: static void Video_WriteToShifter(Uint8 Byte)
1.1       root      145: {
1.1.1.7   root      146:   static int nLastHBL = -1, LastByte, nLastCycles;
                    147:   int nFrameCycles, nLineCycles;
                    148:   
                    149:   nFrameCycles = Int_FindFrameCycles();
1.1       root      150: 
1.1.1.7   root      151:   /* We only care for cycle position in the actual screen line */
                    152:   nLineCycles = nFrameCycles % CYCLES_PER_LINE;
1.1       root      153: 
1.1.1.7   root      154:   /*fprintf(stderr,"Shifter=0x%2.2X %d (%d) @ %d\n",
                    155:           Byte, nFrameCycles, nLineCycles, nHBL);*/
1.1       root      156: 
1.1.1.7   root      157:   /* Check if program tries to open left border.
                    158:    * FIXME: This is a very inaccurate test that should be improved,
                    159:    * but we probably need better CPU cycles emulation first. There's
                    160:    * also no support for sync-scrolling yet :-( */
                    161:   if (nHBL == nLastHBL && LastByte == 0x02 && Byte == 0x00
                    162:       && nLineCycles <= 48 && nLineCycles-nLastCycles <= 16)
                    163:   {
                    164:     LeftRightBorder |= BORDERMASK_LEFT;
                    165:   }
1.1.1.2   root      166: 
1.1.1.7   root      167:   nLastHBL = nHBL;
                    168:   LastByte = Byte;
                    169:   nLastCycles = nLineCycles;
1.1       root      170: }
                    171: 
1.1.1.2   root      172: 
                    173: /*-----------------------------------------------------------------------*/
1.1       root      174: /*
1.1.1.7   root      175:   Write to VideoSync (0xff820a), Hz setting
1.1       root      176: */
1.1.1.8   root      177: void Video_Sync_WriteByte(void)
1.1       root      178: {
1.1.1.7   root      179:   static int nLastHBL = -1, LastByte, nLastCycles;
                    180:   int nFrameCycles, nLineCycles;
1.1.1.8   root      181:   Uint8 Byte;
                    182: 
1.1.1.9 ! root      183:   /* Note: We're only interested in lower 2 bits (50/60Hz) */
        !           184:   Byte = IoMem[0xff820a] & 3;
1.1.1.8   root      185: 
1.1.1.7   root      186:   nFrameCycles = Int_FindFrameCycles();
1.1       root      187: 
1.1.1.7   root      188:   /* We only care for cycle position in the actual screen line */
                    189:   nLineCycles = nFrameCycles % CYCLES_PER_LINE;
1.1.1.2   root      190: 
1.1.1.7   root      191:   /*fprintf(stderr,"Sync=0x%2.2X %d (%d) @ %d\n",
                    192:           Byte, nFrameCycles, nLineCycles, nHBL);*/
1.1       root      193: 
1.1.1.7   root      194:   /* Check if program tries to open a border.
                    195:    * FIXME: These are very inaccurate tests that should be improved,
                    196:    * but we probably need better CPU cycles emulation first. There's
                    197:    * also no support for sync-scrolling yet :-( */
                    198:   if (LastByte == 0x00 && Byte == 0x02)   /* switched from 50 Hz to 60 Hz and back to 50 Hz? */
                    199:   {
                    200:     if (nHBL >= OVERSCAN_TOP && nHBL <= 39 && nStartHBL > FIRST_VISIBLE_HBL)
                    201:     {
                    202:       /* Top border */
                    203:       OverscanMode |= OVERSCANMODE_TOP;       /* Set overscan bit */
                    204:       nStartHBL = FIRST_VISIBLE_HBL;          /* New start screen line */
                    205:       pHBLPaletteMasks -= OVERSCAN_TOP;
                    206:       pHBLPalettes -= OVERSCAN_TOP;
1.1       root      207:     }
1.1.1.7   root      208:     else if (nHBL == SCREEN_START_HBL+SCREEN_HEIGHT_HBL)
                    209:     {
                    210:       /* Bottom border */
                    211:       OverscanMode |= OVERSCANMODE_BOTTOM;    /* Set overscan bit */
                    212:       nEndHBL = SCREEN_START_HBL+SCREEN_HEIGHT_HBL+OVERSCAN_BOTTOM;  /* New end screen line */
1.1       root      213:     }
                    214: 
1.1.1.7   root      215:     if (nHBL == nLastHBL && nLineCycles >= 400 && nLineCycles <= 480
                    216:         && nLineCycles-nLastCycles <= 16)
                    217:     {
                    218:       /* Right border */
                    219:       LeftRightBorder |= BORDERMASK_RIGHT;
                    220:     }
1.1       root      221:   }
                    222: 
1.1.1.7   root      223:   nLastHBL = nHBL;
                    224:   LastByte = Byte;
                    225:   nLastCycles = nLineCycles;
1.1       root      226: }
                    227: 
1.1.1.2   root      228: 
                    229: /*-----------------------------------------------------------------------*/
1.1       root      230: /*
                    231:   Reset Sync/Shifter table at start of each HBL
                    232: */
1.1.1.9 ! root      233: static void Video_StartHBL(void)
1.1       root      234: {
1.1.1.9 ! root      235:   LeftRightBorder = BORDERMASK_NONE;
1.1       root      236: }
                    237: 
1.1.1.2   root      238: 
                    239: /*-----------------------------------------------------------------------*/
1.1       root      240: /*
                    241:   Store whole palette on first line so have reference to work from
                    242: */
1.1.1.7   root      243: static void Video_StoreFirstLinePalette(void)
1.1       root      244: {
1.1.1.8   root      245:   Uint16 *pp2;
1.1       root      246:   int i;
                    247: 
1.1.1.8   root      248:   pp2 = (Uint16 *)&IoMem[0xff8240];
1.1       root      249:   for(i=0; i<16; i++)
1.1.1.7   root      250:     HBLPalettes[i] = SDL_SwapBE16(*pp2++);
1.1.1.2   root      251:   /* And set mask flag with palette and resolution */
1.1.1.8   root      252:   HBLPaletteMasks[0] = (PALETTEMASK_RESOLUTION|PALETTEMASK_PALETTE) | (((unsigned long)IoMem_ReadByte(0xff8260)&0x3)<<16);
1.1       root      253: }
                    254: 
1.1.1.2   root      255: 
                    256: /*-----------------------------------------------------------------------*/
1.1       root      257: /*
                    258:   Store resolution on each line(used to test if mixed low/medium resolutions)
                    259: */
1.1.1.7   root      260: static void Video_StoreResolution(int y)
1.1       root      261: {
1.1.1.2   root      262:   /* Clear resolution, and set with current value */
1.1.1.8   root      263:   if (!(bUseHighRes || bUseVDIRes))
                    264:   {
1.1       root      265:     HBLPaletteMasks[y] &= ~(0x3<<16);
1.1.1.8   root      266:     HBLPaletteMasks[y] |= ((unsigned long)IoMem_ReadByte(0xff8260)&0x3)<<16;
1.1       root      267:   }
                    268: }
                    269: 
1.1.1.2   root      270: 
                    271: /*-----------------------------------------------------------------------*/
1.1       root      272: /*
1.1.1.9 ! root      273:   Copy one line of monochrome screen into buffer for conversion later.
1.1       root      274: */
1.1.1.9 ! root      275: static void Video_CopyScreenLineMono(void)
1.1       root      276: {
1.1.1.9 ! root      277:   int i;
1.1       root      278: 
1.1.1.9 ! root      279:   /* Since Hatari does not emulate monochrome HBLs correctly yet
        !           280:    * (only 200 are raised, just like in low resolution), we have to
        !           281:    * copy two lines each HBL to finally copy all 400 lines. */
        !           282:   for (i = 0; i < 2; i++)
        !           283:   {
        !           284:     /* Copy one line - 80 bytes in ST high resolution */
        !           285:     memcpy(pSTScreen, pVideoRaster, SCREENBYTES_MONOLINE);
        !           286:     pVideoRaster += SCREENBYTES_MONOLINE;
        !           287: 
        !           288:     /* Handle STE fine scrolling (HWScrollCount is zero on ST). */
        !           289:     if (HWScrollCount)
        !           290:     {
        !           291:       Uint16 *pScrollAdj;
        !           292:       int nNegScrollCnt;
        !           293: 
        !           294:       pScrollAdj = (Uint16 *)pSTScreen;
        !           295:       nNegScrollCnt = 16 - HWScrollCount;
        !           296: 
        !           297:       /* Shift the whole line by the given scroll count */
        !           298:       while ((Uint8*)pScrollAdj < pSTScreen + SCREENBYTES_MONOLINE-2)
        !           299:       {
        !           300:         do_put_mem_word(pScrollAdj, (do_get_mem_word(pScrollAdj) << HWScrollCount)
        !           301:                         | (do_get_mem_word(pScrollAdj+1) >> nNegScrollCnt));
        !           302:         ++pScrollAdj;
        !           303:       }
        !           304: 
        !           305:       /* Handle the last 16 pixels of the line */
        !           306:       do_put_mem_word(pScrollAdj, (do_get_mem_word(pScrollAdj) << HWScrollCount)
        !           307:                       | (do_get_mem_word(pVideoRaster) >> nNegScrollCnt));
        !           308: 
        !           309:       /* HW scrolling advances Shifter video counter by one */
        !           310:       pVideoRaster += 1 * 2;
        !           311:     }
        !           312: 
        !           313:     /* ScanLineSkip is zero on ST. */
        !           314:     /* On STE, the Shifter skips the given amount of words. */
        !           315:     pVideoRaster += ScanLineSkip*2;
        !           316: 
        !           317:     /* Each screen line copied to buffer is always same length */
        !           318:     pSTScreen += SCREENBYTES_MONOLINE;
        !           319:   }
        !           320: }
        !           321: 
        !           322: 
        !           323: /*-----------------------------------------------------------------------*/
        !           324: /*
        !           325:   Copy one line of color screen into buffer for conversion later.
        !           326:   Possible lines may be top/bottom border, and/or left/right borders.
        !           327: */
        !           328: static void Video_CopyScreenLineColor(void)
        !           329: {
        !           330:   /* Is total blank line? I.e. top/bottom border? */
        !           331:   if (nHBL < nStartHBL || nHBL >= nEndHBL)
        !           332:   {
        !           333:     /* Clear line to color '0' */
        !           334:     memset(pSTScreen, 0, SCREENBYTES_LINE);
        !           335:   }
        !           336:   else
        !           337:   {
        !           338:     /* Does have left border? If not, clear to color '0' */
        !           339:     if (LeftRightBorder & BORDERMASK_LEFT)
        !           340:     {
        !           341:       pVideoRaster += 24-SCREENBYTES_LEFT;
        !           342:       memcpy(pSTScreen, pVideoRaster, SCREENBYTES_LEFT);
        !           343:       pVideoRaster += SCREENBYTES_LEFT;
        !           344:     }
        !           345:     else
        !           346:       memset(pSTScreen,0,SCREENBYTES_LEFT);
        !           347: 
        !           348:     /* Copy middle - always present */
        !           349:     memcpy(pSTScreen+SCREENBYTES_LEFT, pVideoRaster, SCREENBYTES_MIDDLE);
        !           350:     pVideoRaster += SCREENBYTES_MIDDLE;
        !           351: 
        !           352:     /* Does have right border? If not, clear to color '0' */
        !           353:     if (LeftRightBorder & BORDERMASK_RIGHT)
        !           354:     {
        !           355:       memcpy(pSTScreen+SCREENBYTES_LEFT+SCREENBYTES_MIDDLE, pVideoRaster, SCREENBYTES_RIGHT);
        !           356:       pVideoRaster += 46-SCREENBYTES_RIGHT;
        !           357:       pVideoRaster += SCREENBYTES_RIGHT;
        !           358:     }
        !           359:     else
        !           360:       memset(pSTScreen+SCREENBYTES_LEFT+SCREENBYTES_MIDDLE,0,SCREENBYTES_RIGHT);
        !           361: 
        !           362:     /* Handle STE fine scrolling (HWScrollCount is zero on ST). */
        !           363:     if (HWScrollCount)
        !           364:     {
        !           365:       Uint16 *pScrollAdj;       /* Pointer to actual position in line */
        !           366:       int nNegScrollCnt;
        !           367:       Uint16 *pScrollEndAddr;   /* Pointer to end of the line */
        !           368: 
        !           369:       nNegScrollCnt = 16 - HWScrollCount;
        !           370:       if (LeftRightBorder & BORDERMASK_LEFT)
        !           371:         pScrollAdj = (Uint16 *)pSTScreen;
        !           372:       else
        !           373:         pScrollAdj = (Uint16 *)(pSTScreen + SCREENBYTES_LEFT);
        !           374:       if (LeftRightBorder & BORDERMASK_RIGHT)
        !           375:         pScrollEndAddr = (Uint16 *)(pSTScreen + SCREENBYTES_LINE - 8);
        !           376:       else
        !           377:         pScrollEndAddr = (Uint16 *)(pSTScreen + SCREENBYTES_LEFT + SCREENBYTES_MIDDLE - 8);
        !           378: 
        !           379:       if (STRes == ST_MEDIUM_RES)
        !           380:       {
        !           381:         /* TODO: Implement fine scrolling for medium resolution, too */
        !           382:         /* HW scrolling advances Shifter video counter by one */
        !           383:         pVideoRaster += 2 * 2;
1.1       root      384:       }
1.1.1.9 ! root      385:       else
        !           386:       {
        !           387:         /* Shift the whole line by the given scroll count */
        !           388:         while (pScrollAdj < pScrollEndAddr)
        !           389:         {
        !           390:           do_put_mem_word(pScrollAdj, (do_get_mem_word(pScrollAdj) << HWScrollCount)
        !           391:                           | (do_get_mem_word(pScrollAdj+4) >> nNegScrollCnt));
        !           392:           ++pScrollAdj;
1.1       root      393:         }
1.1.1.9 ! root      394:         /* Handle the last 16 pixels of the line */
        !           395:         if (LeftRightBorder & BORDERMASK_RIGHT)
        !           396:         {
        !           397:           /* When right border is open, we have to deal with this ugly offset
        !           398:            * of 46-SCREENBYTES_RIGHT=30 - The demo "Mind rewind" is a good example */
        !           399:           do_put_mem_word(pScrollAdj+0, (do_get_mem_word(pScrollAdj+0) << HWScrollCount)
        !           400:                           | (do_get_mem_word(pVideoRaster-30) >> nNegScrollCnt));
        !           401:           do_put_mem_word(pScrollAdj+1, (do_get_mem_word(pScrollAdj+1) << HWScrollCount)
        !           402:                           | (do_get_mem_word(pVideoRaster-28) >> nNegScrollCnt));
        !           403:           do_put_mem_word(pScrollAdj+2, (do_get_mem_word(pScrollAdj+2) << HWScrollCount)
        !           404:                           | (do_get_mem_word(pVideoRaster-26) >> nNegScrollCnt));
        !           405:           do_put_mem_word(pScrollAdj+3, (do_get_mem_word(pScrollAdj+3) << HWScrollCount)
        !           406:                           | (do_get_mem_word(pVideoRaster-24) >> nNegScrollCnt));
1.1       root      407:         }
                    408:         else
1.1.1.9 ! root      409:         {
        !           410:           do_put_mem_word(pScrollAdj+0, (do_get_mem_word(pScrollAdj+0) << HWScrollCount)
        !           411:                           | (do_get_mem_word(pVideoRaster+0) >> nNegScrollCnt));
        !           412:           do_put_mem_word(pScrollAdj+1, (do_get_mem_word(pScrollAdj+1) << HWScrollCount)
        !           413:                           | (do_get_mem_word(pVideoRaster+2) >> nNegScrollCnt));
        !           414:           do_put_mem_word(pScrollAdj+2, (do_get_mem_word(pScrollAdj+2) << HWScrollCount)
        !           415:                           | (do_get_mem_word(pVideoRaster+4) >> nNegScrollCnt));
        !           416:           do_put_mem_word(pScrollAdj+3, (do_get_mem_word(pScrollAdj+3) << HWScrollCount)
        !           417:                           | (do_get_mem_word(pVideoRaster+6) >> nNegScrollCnt));
        !           418:         }
        !           419:         /* HW scrolling advances Shifter video counter by one */
        !           420:         pVideoRaster += 4 * 2;
1.1       root      421:       }
1.1.1.9 ! root      422:     }
        !           423: 
        !           424:     /* ScanLineSkip is zero on ST. */
        !           425:     /* On STE, the Shifter skips the given amount of words. */
        !           426:     pVideoRaster += ScanLineSkip*2;
        !           427:   }
        !           428: 
        !           429:   /* Each screen line copied to buffer is always same length */
        !           430:   pSTScreen += SCREENBYTES_LINE;
        !           431: }
        !           432: 
1.1.1.6   root      433: 
1.1.1.9 ! root      434: /*-----------------------------------------------------------------------*/
        !           435: /*
        !           436:   Copy line of screen into buffer for conversion later.
        !           437: */
        !           438: static void Video_CopyScreenLine(void)
        !           439: {
        !           440:   /* Read STE fine scrolling registers */
        !           441:   if (ConfigureParams.System.nMachineType != MACHINE_ST)
        !           442:   {
        !           443:     ScanLineSkip = STMemory_ReadByte(0xff820f);
        !           444:     HWScrollCount = STMemory_ReadByte(0xff8265) & 0x0f;
        !           445:   }
        !           446: 
        !           447:   /* Only copy screen line if not doing high VDI resolution */
        !           448:   if (!bUseVDIRes)
        !           449:   {
        !           450:     if (bUseHighRes)
        !           451:     {
        !           452:       /* Copy for hi-res (no overscan) */
        !           453:       Video_CopyScreenLineMono();
        !           454:     }
        !           455:     else
        !           456:     {
        !           457:       /* Copy for low and medium resolution */
        !           458:       Video_CopyScreenLineColor();
1.1       root      459:     }
                    460:   }
                    461: }
                    462: 
1.1.1.2   root      463: 
                    464: /*-----------------------------------------------------------------------*/
1.1       root      465: /*
                    466:   Copy extended GEM resolution screen
                    467: */
1.1.1.9 ! root      468: static void Video_CopyVDIScreen(void)
1.1       root      469: {
1.1.1.2   root      470:   /* Copy whole screen, don't care about being exact as for GEM only */
1.1.1.8   root      471:   memcpy(pSTScreen, pVideoRaster, ((VDIWidth*VDIPlanes)/8)*VDIHeight);  /* 640x400x16colour */
1.1       root      472: }
                    473: 
1.1.1.2   root      474: 
                    475: /*-----------------------------------------------------------------------*/
1.1       root      476: /*
                    477:   Check at end of each HBL to see if any Sync/Shifter hardware tricks have been attempted
                    478: */
1.1.1.9 ! root      479: static void Video_EndHBL(void)
1.1       root      480: {
1.1.1.2   root      481:   /* Are we in possible visible display (including borders)? */
1.1.1.9 ! root      482:   if (nHBL >= FIRST_VISIBLE_HBL && nHBL < FIRST_VISIBLE_HBL+NUM_VISIBLE_LINES)
        !           483:   {
        !           484:     /* Copy line of screen to buffer to simulate TV raster trace
        !           485:      * - required for mouse cursor display/game updates
        !           486:      * Eg, Lemmings and The Killing Game Show are good examples */
        !           487:     Video_CopyScreenLine();
1.1       root      488: 
1.1.1.9 ! root      489:     if (nHBL == FIRST_VISIBLE_HBL)    /* Very first line on screen - HBLPalettes[0] */
        !           490:     {
1.1.1.2   root      491:       /* Store ALL palette for this line into raster table for datum */
1.1       root      492:       Video_StoreFirstLinePalette();
                    493:     }
1.1.1.2   root      494:     /* Store resolution for every line so can check for mix low/medium screens */
1.1       root      495:     Video_StoreResolution(nHBL-FIRST_VISIBLE_HBL);
                    496:   }
                    497: 
1.1.1.2   root      498:   /* Finally increase HBL count */
1.1       root      499:   nHBL++;
                    500: 
1.1.1.2   root      501:   Video_StartHBL();                  /* Setup next one */
1.1       root      502: }
                    503: 
1.1.1.2   root      504: 
1.1.1.9 ! root      505: /*-----------------------------------------------------------------------*/
        !           506: /*
        !           507:   End Of Line interrupt
        !           508:   As this occurs at the end of a line we cannot get timing for START of first
        !           509:   line (as in Spectrum 512)
        !           510: */
        !           511: void Video_InterruptHandler_EndLine(void)
        !           512: {
        !           513:   /* Remove this interrupt from list and re-order */
        !           514:   Int_AcknowledgeInterrupt();
        !           515:   /* Generate new HBL, if need to - there are 313 HBLs per frame */
        !           516:   if (nHBL < SCANLINES_PER_FRAME-1)
        !           517:     Int_AddAbsoluteInterrupt(CYCLES_PER_LINE,INTERRUPT_VIDEO_ENDLINE);
        !           518: 
        !           519:   /* Is this a good place to send the keyboard packets? Done once per frame */
        !           520:   if (nHBL == nStartHBL)
        !           521:   {
        !           522:     /* On each VBL send automatic keyboard packets for mouse, joysticks etc... */
        !           523:     IKBD_SendAutoKeyboardCommands();
        !           524:   }
        !           525: 
        !           526:   /* Timer A/B occur at END of first visible screen line in Event Count mode */
        !           527:   if (nHBL >= nStartHBL && nHBL < nEndHBL)
        !           528:    {
        !           529:     /* Handle Timers A and B when using Event Count mode(timer taken from HBL) */
        !           530: // FIXME: Really raise Timer A here?
        !           531: //    if (MFP_TACR==0x08)        /* Is timer in Event Count mode? */
        !           532: //      MFP_TimerA_EventCount_Interrupt();
        !           533:     if (MFP_TBCR==0x08)        /* Is timer in Event Count mode? */
        !           534:       MFP_TimerB_EventCount_Interrupt();
        !           535:    }
        !           536: 
        !           537:   FDC_UpdateHBL();             /* Update FDC motion */
        !           538:   Video_EndHBL();              /* Increase HBL count, copy line to display buffer and do any video trickery */
        !           539: 
        !           540:   /* If we don't often pump data into the event queue, the SDL misses events... grr... */
        !           541:   if (!(nHBL & 63))
        !           542:   {
        !           543:     Main_EventHandler();
        !           544:   }
        !           545: }
        !           546: 
        !           547: 
        !           548: /*-----------------------------------------------------------------------*/
        !           549: /*
        !           550:   Clear raster line table to store changes in palette/resolution on a line
        !           551:   basic. Called once on VBL interrupt.
        !           552: */
1.1       root      553: void Video_SetScreenRasters(void)
                    554: {
                    555:   pHBLPaletteMasks = HBLPaletteMasks;
                    556:   pHBLPalettes = HBLPalettes;
1.1.1.9 ! root      557:   memset(pHBLPaletteMasks, 0, sizeof(Uint32)*NUM_VISIBLE_LINES);  /* Clear array */
1.1       root      558: }
                    559: 
1.1.1.2   root      560: 
                    561: /*-----------------------------------------------------------------------*/
1.1       root      562: /*
                    563:   Set pointers to HBLPalette tables to store correct colours/resolutions
                    564: */
1.1.1.9 ! root      565: static void Video_SetHBLPaletteMaskPointers(void)
1.1       root      566: {
                    567:   int FrameCycles;
                    568:   int Line;
                    569: 
                    570:   /* Top of standard screen is 64 lines from VBL(64x512=32768 cycles) */
                    571:   /* Each line is 64+320+64+64(Blank) = 512 pixels per scan line      */
                    572:   /* Timer occurs at end of 64+320+64; Display Enable(DE)=Low         */
                    573:   /* HBL is incorrect on machine and occurs around 96+ cycles in      */
                    574: 
                    575:   /* Top of standard screen is 64 lines from VBL(64x512=32768 cycles) */
                    576:   /* Each line is 96 + 320 + 96 = 512 pixels per scan line(each pixel is one cycle) */
                    577:   FrameCycles = Int_FindFrameCycles();
                    578: 
                    579:   /* Find 'line' into palette - screen starts 64 lines down, less 28 for top overscan */
                    580:   /* And if write to last 96 cycle of line it will count as the NEXT line(needed else games may flicker) */
                    581:   Line = (FrameCycles-(FIRST_VISIBLE_HBL*CYCLES_PER_LINE)+SCREEN_START_CYCLE)/CYCLES_PER_LINE;
                    582:   if (Line<0)          /* Limit to top/bottom of possible visible screen */
                    583:     Line = 0;
                    584:   if (Line>=NUM_VISIBLE_LINES)
                    585:     Line = NUM_VISIBLE_LINES-1;
                    586: 
1.1.1.2   root      587:   /* Store pointers */
1.1       root      588:   pHBLPaletteMasks = &HBLPaletteMasks[Line];  /* Next mask entry */
                    589:   pHBLPalettes = &HBLPalettes[16*Line];       /* Next colour raster list x16 colours */
                    590: }
1.1.1.8   root      591: 
                    592: 
1.1.1.9 ! root      593: /*-----------------------------------------------------------------------*/
        !           594: /*
        !           595:   Called on VBL, set registers ready for frame
        !           596: */
        !           597: static void Video_ClearOnVBL(void)
        !           598: {
        !           599:   /* New screen, so first HBL */
        !           600:   nHBL = 0;
        !           601:   nStartHBL = SCREEN_START_HBL;
        !           602:   nEndHBL = SCREEN_START_HBL+SCREEN_HEIGHT_HBL;
        !           603:   OverscanMode = OVERSCANMODE_NONE;
        !           604: 
        !           605:   /* Get screen address pointer, aligned to 256 bytes on ST (ie ignore lowest byte) */
        !           606:   VideoBase = (Uint32)STMemory_ReadByte(0xff8201)<<16 | (Uint32)STMemory_ReadByte(0xff8203)<<8;
        !           607:   if (ConfigureParams.System.nMachineType != MACHINE_ST)
        !           608:   {
        !           609:     /* on STe 2 aligned, on Falcon 4 aligned, on TT 8 aligned. We do STe. */
        !           610:     VideoBase |= STMemory_ReadByte(0xff820d) & ~1;
        !           611:   }
        !           612:   pVideoRaster = &STRam[VideoBase];
        !           613:   pSTScreen = pFrameBuffer->pSTScreen;
        !           614: 
        !           615:   Video_StartHBL();
        !           616:   Video_SetScreenRasters();
        !           617:   Spec512_StartVBL();
        !           618: }
        !           619: 
        !           620: 
        !           621: /*-----------------------------------------------------------------------*/
        !           622: /*
        !           623:   VBL interrupt, draw screen and reset counters
        !           624: */
        !           625: void Video_InterruptHandler_VBL(void)
        !           626: {
        !           627:   int PendingCyclesOver;
        !           628:   int nNewMilliTicks;
        !           629:   static int nOldMilliTicks = 0;
        !           630:   signed int nDelay;
        !           631: 
        !           632:   /* Store cycles we went over for this frame(this is our inital count) */
        !           633:   PendingCyclesOver = -PendingInterruptCount;    /* +ve */
        !           634: 
        !           635:   /* Remove this interrupt from list and re-order */
        !           636:   Int_AcknowledgeInterrupt();
        !           637:   /* Start HBL interrupts - MUST do before add in cycles */
        !           638:   Int_AddAbsoluteInterrupt(CYCLES_ENDLINE,INTERRUPT_VIDEO_ENDLINE);
        !           639:   Int_AddAbsoluteInterrupt(CYCLES_HBL,INTERRUPT_VIDEO_HBL);
        !           640:   Int_AddAbsoluteInterrupt(CYCLES_PER_FRAME,INTERRUPT_VIDEO_VBL);
        !           641: 
        !           642:   /* Set frame cycles, used for Video Address */
        !           643:   nFrameCyclesOver = PendingCyclesOver;      /* Number of cycles into frame */
        !           644: 
        !           645:   /* Set the screen refresh rate */
        !           646: #if 0
        !           647:   if(bUseHighRes)
        !           648:     nScreenRefreshRate = 70;
        !           649:   else if(IoMem[0xff820a] & 2)               /* Is it 50Hz or is it 60Hz? */
        !           650:     nScreenRefreshRate = 50;
        !           651:   else
        !           652:     nScreenRefreshRate = 60;
        !           653: #endif
        !           654: 
        !           655:   /* Clear any key presses which are due to be de-bounced (held for one ST frame) */
        !           656:   Keymap_DebounceAllKeys();
        !           657:   /* Check shortcut keys */
        !           658:   ShortCut_CheckKeys();
        !           659: 
        !           660:   /* Draw screen, skip frame if need to */
        !           661:   if (!ConfigureParams.Screen.bFrameSkip || (nVBLs&1))
        !           662:   {
        !           663:     /* Use extended VDI resolution?
        !           664:      * If so, just copy whole screen on VBL rather than per HBL */
        !           665:     if (bUseVDIRes)
        !           666:       Video_CopyVDIScreen();
        !           667: 
        !           668:     /* Now draw the screen! */
        !           669:     Screen_Draw();
        !           670:   }
        !           671: 
        !           672:   /* Update counter for number of screen refreshes per second(for debugging) */
        !           673:   nVBLs++;
        !           674:   /* Set video registers for frame */
        !           675:   Video_ClearOnVBL();
        !           676:   /* Store off PSG registers for YM file, is enabled */
        !           677:   YMFormat_UpdateRecording();
        !           678:   /* Generate 1/50th second of sound sample data, to be played by sound thread */
        !           679:   Sound_Update_VBL();
        !           680: 
        !           681:   M68000_Exception(EXCEPTION_VBLANK);   /* Vertical blank interrupt, level 4! */
        !           682: 
        !           683:   /* And handle any messages, check for quit message */
        !           684:   Main_EventHandler();         /* Process messages, set 'bQuitProgram' if user tries to quit */
        !           685:   if (bQuitProgram)
        !           686:   {
        !           687:     Int_AddAbsoluteInterrupt(4, 0L);  /* Pass NULL interrupt function to quit cleanly */
        !           688:     set_special(SPCFLAG_BRK);         /* Assure that CPU core shuts down */
        !           689:   }
        !           690: 
        !           691:   if (ConfigureParams.System.nMinMaxSpeed != MINMAXSPEED_MAX)
        !           692:   {
        !           693:     /* Wait, so we stay in sync with the sound */
        !           694:     do
        !           695:     {
        !           696:       nNewMilliTicks = SDL_GetTicks();
        !           697:       nDelay = 1000/nScreenRefreshRate - (nNewMilliTicks-nOldMilliTicks);
        !           698:       if(nDelay > 2)
        !           699:       {
        !           700:         /* SDL_Delay seems to be quite inaccurate, so we don't wait the whole time */
        !           701:         SDL_Delay(nDelay - 1);
        !           702:       }
        !           703:     }
        !           704:     while(nDelay > 0);
        !           705:     nOldMilliTicks = nNewMilliTicks;
        !           706:   }
        !           707: }
        !           708: 
        !           709: 
        !           710: /*-----------------------------------------------------------------------*/
        !           711: /*
        !           712:   Reset video chip
        !           713: */
        !           714: void Video_Reset(void)
        !           715: {
        !           716:   /* NOTE! Must reset all of these register type things here!!!! */
        !           717: 
        !           718:   /* Are we in high-res? */
        !           719:   if (bUseHighRes)
        !           720:     VideoShifterByte = ST_HIGH_RES;    /* Boot up for mono monitor */
        !           721:   else
        !           722:     VideoShifterByte = ST_LOW_RES;
        !           723:   if(bUseVDIRes)
        !           724:     VideoShifterByte = VDIRes;
        !           725: 
        !           726:   /* Reset VBL counter */
        !           727:   nVBLs = 0;
        !           728:   /* Reset addresses */
        !           729:   VideoBase = 0L;
        !           730:   /* Reset STe screen variables */
        !           731:   ScanLineSkip = 0;
        !           732:   HWScrollCount = 0;
        !           733:   /* Clear ready for new VBL */
        !           734:   Video_ClearOnVBL();
        !           735: }
        !           736: 
        !           737: 
        !           738: /*-----------------------------------------------------------------------*/
        !           739: /*
        !           740:   Write to video address base high and med register (0xff8201 and 0xff8203).
        !           741:   When a program writes to these registers, some other video registers
        !           742:   are reset to zero.
        !           743: */
        !           744: void Video_ScreenBaseSTE_WriteByte(void)
        !           745: {
        !           746:   IoMem[0xff820d] = 0;          /* Reset screen base low register */
        !           747: }
1.1.1.8   root      748: 
                    749: /*-----------------------------------------------------------------------*/
                    750: /*
                    751:   Read video address counter high byte (0xff8205)
                    752: */
                    753: void Video_ScreenCounterHigh_ReadByte(void)
                    754: {
                    755:   IoMem[0xff8205] = Video_CalculateAddress() >> 16;   /* Get video address counter high byte */
                    756: }
                    757: 
                    758: /*-----------------------------------------------------------------------*/
                    759: /*
                    760:   Read video address counter med byte (0xff8207)
                    761: */
                    762: void Video_ScreenCounterMed_ReadByte(void)
                    763: {
                    764:   IoMem[0xff8207] = Video_CalculateAddress() >> 8;    /* Get video address counter med byte */
                    765: }
                    766: 
                    767: /*-----------------------------------------------------------------------*/
                    768: /*
                    769:   Read video address counter low byte (0xff8209)
                    770: */
                    771: void Video_ScreenCounterLow_ReadByte(void)
                    772: {
                    773:   IoMem[0xff8209] = Video_CalculateAddress();         /* Get video address counter low byte */
                    774: }
                    775: 
1.1.1.9 ! root      776: /*-----------------------------------------------------------------------*/
        !           777: /*
        !           778:   Write to video address counter (0xff8205, 0xff8207 and 0xff8209).
        !           779:   Called on STE only and like with base address, you cannot set lowest bit.
        !           780: */
        !           781: void Video_ScreenCounter_WriteByte(void)
        !           782: {
        !           783:   Uint32 addr;
        !           784:   addr = (IoMem[0xff8205] << 16) | (IoMem[0xff8207] << 8) | IoMem[0xff8209];
        !           785:   pVideoRaster = &STRam[addr & ~1];
        !           786: }
1.1.1.8   root      787: 
                    788: /*-----------------------------------------------------------------------*/
                    789: /*
                    790:   Read video sync register (0xff820a)
                    791: */
                    792: void Video_Sync_ReadByte(void)
                    793: {
                    794:   /* Nothing... */
                    795: }
                    796: 
                    797: /*-----------------------------------------------------------------------*/
                    798: /*
                    799:   Read video base address low byte (0xff820d). A plain ST can only store
                    800:   screen addresses rounded to 256 bytes (i.e. no lower byte).
                    801: */
                    802: void Video_BaseLow_ReadByte(void)
                    803: {
                    804:   if (ConfigureParams.System.nMachineType == MACHINE_ST)
1.1.1.9 ! root      805:     IoMem[0xff820d] = 0;        /* On ST this is always 0 */
        !           806: 
        !           807:   /* Note that you should not do anything here for STe because
        !           808:    * VideoBase address is set in an interrupt and would be wrong
        !           809:    * here.   It's fine like this.
        !           810:    */
1.1.1.8   root      811: }
                    812: 
                    813: /*-----------------------------------------------------------------------*/
                    814: /*
                    815:   Read video line width register (0xff820f)
                    816: */
                    817: void Video_LineWidth_ReadByte(void)
                    818: {
1.1.1.9 ! root      819:   if (ConfigureParams.System.nMachineType == MACHINE_ST)
        !           820:     IoMem[0xff820f] = 0;        /* On ST this is always 0 */
1.1.1.8   root      821: }
                    822: 
                    823: /*-----------------------------------------------------------------------*/
                    824: /*
                    825:   Read video shifter mode register (0xff8260)
                    826: */
                    827: void Video_ShifterMode_ReadByte(void)
                    828: {
                    829:   if (bUseHighRes)
                    830:     IoMem[0xff8260] = 2;                  /* If mono monitor, force to high resolution */
                    831:   else
                    832:     IoMem[0xff8260] = VideoShifterByte;   /* Read shifter register */
                    833: }
                    834: 
                    835: 
                    836: 
                    837: /*-----------------------------------------------------------------------*/
                    838: /*
                    839:   Write to video shifter palette registers (0xff8240-0xff825e)
                    840: */
                    841: static void Video_ColorReg_WriteWord(Uint32 addr)
                    842: {
1.1.1.9 ! root      843:   if (!bUseHighRes)                        /* Don't store if hi-res */
1.1.1.8   root      844:   {
1.1.1.9 ! root      845:     int idx;
1.1.1.8   root      846:     Uint16 col;
1.1.1.9 ! root      847:     Video_SetHBLPaletteMaskPointers();     /* Set 'pHBLPalettes' etc.. according cycles into frame */
1.1.1.8   root      848:     col = IoMem_ReadWord(addr);
1.1.1.9 ! root      849:     if (ConfigureParams.System.nMachineType == MACHINE_ST)
        !           850:       col &= 0x777;                          /* Mask off to ST 512 palette */
        !           851:     else
        !           852:       col &= 0xfff;                          /* Mask off to STe 4096 palette */
        !           853:     IoMem_WriteWord(addr, col);            /* (some games write 0xFFFF and read back to see if STe) */
        !           854:     Spec512_StoreCyclePalette(col, addr);  /* Store colour into CyclePalettes[] */
        !           855:     idx = (addr-0xff8240)/2;               /* words */
        !           856:     pHBLPalettes[idx] = col;               /* Set colour x */
        !           857:     *pHBLPaletteMasks |= 1 << idx;         /* And mask */
1.1.1.8   root      858:   }
                    859: }
                    860: 
                    861: void Video_Color0_WriteWord(void)
                    862: {
                    863:   Video_ColorReg_WriteWord(0xff8240);
                    864: }
                    865: 
                    866: void Video_Color1_WriteWord(void)
                    867: {
                    868:   Video_ColorReg_WriteWord(0xff8242);
                    869: }
                    870: 
                    871: void Video_Color2_WriteWord(void)
                    872: {
                    873:   Video_ColorReg_WriteWord(0xff8244);
                    874: }
                    875: 
                    876: void Video_Color3_WriteWord(void)
                    877: {
                    878:   Video_ColorReg_WriteWord(0xff8246);
                    879: }
                    880: 
                    881: void Video_Color4_WriteWord(void)
                    882: {
                    883:   Video_ColorReg_WriteWord(0xff8248);
                    884: }
                    885: 
                    886: void Video_Color5_WriteWord(void)
                    887: {
                    888:   Video_ColorReg_WriteWord(0xff824a);
                    889: }
                    890: 
                    891: void Video_Color6_WriteWord(void)
                    892: {
                    893:   Video_ColorReg_WriteWord(0xff824c);
                    894: }
                    895: 
                    896: void Video_Color7_WriteWord(void)
                    897: {
                    898:   Video_ColorReg_WriteWord(0xff824e);
                    899: }
                    900: 
                    901: void Video_Color8_WriteWord(void)
                    902: {
                    903:   Video_ColorReg_WriteWord(0xff8250);
                    904: }
                    905: 
                    906: void Video_Color9_WriteWord(void)
                    907: {
                    908:   Video_ColorReg_WriteWord(0xff8252);
                    909: }
                    910: 
                    911: void Video_Color10_WriteWord(void)
                    912: {
                    913:   Video_ColorReg_WriteWord(0xff8254);
                    914: }
                    915: 
                    916: void Video_Color11_WriteWord(void)
                    917: {
                    918:   Video_ColorReg_WriteWord(0xff8256);
                    919: }
                    920: 
                    921: void Video_Color12_WriteWord(void)
                    922: {
                    923:   Video_ColorReg_WriteWord(0xff8258);
                    924: }
                    925: 
                    926: void Video_Color13_WriteWord(void)
                    927: {
                    928:   Video_ColorReg_WriteWord(0xff825a);
                    929: }
                    930: 
                    931: void Video_Color14_WriteWord(void)
                    932: {
                    933:   Video_ColorReg_WriteWord(0xff825c);
                    934: }
                    935: 
                    936: void Video_Color15_WriteWord(void)
                    937: {
                    938:   Video_ColorReg_WriteWord(0xff825e);
                    939: }
                    940: 
                    941: 
                    942: /*-----------------------------------------------------------------------*/
                    943: /*
                    944:   Write video shifter mode register (0xff860)
                    945: */
                    946: void Video_ShifterMode_WriteByte(void)
                    947: {
                    948:   if (!bUseHighRes && !bUseVDIRes)                    /* Don't store if hi-res and don't store if VDI resolution */
                    949:   {
                    950:     VideoShifterByte = IoMem[0xff8260] & 3;           /* We only care for lower 2-bits */
                    951:     Video_WriteToShifter(VideoShifterByte);
                    952:     Video_SetHBLPaletteMaskPointers();
                    953:     *pHBLPaletteMasks &= 0xff00ffff;
                    954:     /* Store resolution after palette mask and set resolution write bit: */
                    955:     *pHBLPaletteMasks |= (((unsigned long)VideoShifterByte|0x04)<<16);
                    956:   }
                    957: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.