/****************************************************************************/ /* Beebem - (c) David Alan Gilbert 1994 */ /* ------------------------------------ */ /* This program may be distributed freely within the following restrictions:*/ /* */ /* 1) You may not charge for this program or for any part of it. */ /* 2) This copyright message must be distributed with all copies. */ /* 3) This program must be distributed complete with source code. Binary */ /* only distribution is not permitted. */ /* 4) The author offers no warrenties, or guarentees etc. - you use it at */ /* your own risk. If it messes something up or destroys your computer */ /* thats YOUR problem. */ /* 5) You may use small sections of code from this program in your own */ /* applications - but you must acknowledge its use. If you plan to use */ /* large sections then please ask the author. */ /* */ /* If you do not agree with any of the above then please do not use this */ /* program. */ /* Please report any problems to the author at gilbertd@cs.man.ac.uk */ /****************************************************************************/ /* Video handling - David Alan Gilbert */ /* Version 2 - 24/12/94 - Designed to emulate start of frame interrupt correctly */ //#include "windows.h" //#include "iostream.h" #include #include #include #include "iostream.h" #include "6502core.h" #include "beebmem.h" #include "main.h" //#include "beebwin.h" #include "xvid.h" #include "mode7fon.h" #include "sysvia.h" #include "video.h" extern BeebWin *mainWin; #ifdef BEEB_DOTIME #include #ifdef SUNOS #include #endif #ifdef HPUX #include #endif #endif /* Bit assignments in control reg: 0 - Flash colour (0=first colour, 1=second) 1 - Teletext select (0=on chip serialiser, 1=teletext) 2,3 - Bytes per line (2,3=1,1 is 80, 1,0=40, 0,1=20, 0,0=10) 4 - CRTC Clock chip select (0 = low frequency, 1= high frequency) 5,6 - Cursor width in bytes (0,0 = 1 byte, 0,1=not defined, 1,0=2, 1,1=4) 7 - Master cursor width (if set causes large cursor) */ EightUChars FastTable[256]; SixteenUChars FastTableDWidth[256]; /* For mode 4,5,6 */ int FastTable_Valid=0; typedef void (*LineRoutinePtr)(void); LineRoutinePtr LineRoutine; /* Translates middle bits of VideoULA_ControlReg to number of colours */ static int NColsLookup[]={16, 4, 2, 0 /* Not supported 16? */, 0 /* ???? */, 16, 4, 2}; /* Based on AUG 379 */ unsigned char VideoULA_ControlReg=0x9c; unsigned char VideoULA_Palette[16]; unsigned char CRTCControlReg=0; unsigned char CRTC_HorizontalTotal=127; /* R0 */ unsigned char CRTC_HorizontalDisplayed=80; /* R1 */ unsigned char CRTC_HorizontalSyncPos=98; /* R2 */ unsigned char CRTC_SyncWidth=0x28; /* R3 - top 4 bits are Vertical (in scan lines) and bottom 4 are horizontal in characters */ unsigned char CRTC_VerticalTotal=38; /* R4 */ unsigned char CRTC_VerticalTotalAdjust=0; /* R5 */ unsigned char CRTC_VerticalDisplayed=32; /* R6 */ unsigned char CRTC_VerticalSyncPos=34; /* R7 */ unsigned char CRTC_InterlaceAndDelay=0; /* R8 - 0,1 are interlace modes, 4,5 display blanking delay, 6,7 cursor blanking delay */ unsigned char CRTC_ScanLinesPerChar=7; /* R9 */ unsigned char CRTC_CursorStart=0; /* R10 */ unsigned char CRTC_CursorEnd=0; /* R11 */ unsigned char CRTC_ScreenStartHigh=6; /* R12 */ unsigned char CRTC_ScreenStartLow=0; /* R13 */ unsigned char CRTC_CursorPosHigh=0; /* R14 */ unsigned char CRTC_CursorPosLow=0; /* R15 */ unsigned char CRTC_LightPenHigh=0; /* R16 */ unsigned char CRTC_LightPenLow=0; /* R17 */ /* CharLine counts from the 'reference point' - i.e. the point at which we reset the address pointer - NOT the point of the sync. If it is -ve its actually in the adjust time */ typedef struct { int Addr; /* Address of start of next visible character line in beeb memory - raw */ int PixmapLine; /* Current line in the pixmap */ int PreviousFinalPixmapLine; /* The last pixmap line on the previous frame */ int IsTeletext; /* This frame is a teletext frame - do things differently */ char *DataPtr; /* Pointer into host memory of video data */ int CharLine; /* 6845 counts in characters vertically - 0 at the top , incs by 1 - -1 means we are in the bit before the actual display starts */ int InCharLine; /* Scanline within a character line - counts down*/ int InCharLineUp; /* Scanline within a character line - counts up*/ int VSyncState; // Cannot =0 in MSVC $NRM; /* When >0 VSync is high */ } VideoStateT; VideoStateT VideoState; int VideoTriggerCount=9999; /* Number of cycles before next scanline service */ /* first subscript is graphics flag (1 for graphics,2 for separated graphics), next is character, then scanline */ /* character is (valu &127)-32 */ static unsigned char Mode7Font[3][96][9]; static int Mode7FlashOn=1; /* True if a flashing character in mode 7 is on */ static int Mode7DoubleHeightFlags[80]; /* Pessimistic size for this flags - if 1 then corresponding character on NEXT line is top half */ /* Flash every half second(???) i.e. 25 x 50Hz fields */ #define MODE7FLASHFREQUENCY 25 int Mode7FlashTrigger=MODE7FLASHFREQUENCY; /* If 1 then refresh on every display, else refresh every n'th display */ int Video_RefreshFrequency=1; /* The number of the current frame - starts at Video_RefreshFrequency - at 0 actually refresh */ static int FrameNum=0; static void LowLevelDoScanLineNarrow(); static void LowLevelDoScanLineWide(); static void LowLevelDoScanLineNarrowNot4Bytes(); static void LowLevelDoScanLineWideNot4Bytes(); /*-------------------------------------------------------------------------------------------------------------*/ static void BuildMode7Font(void) { int presentchar,presentline,presentpixel,presgraph; char *tempptr; unsigned char tempvalue; // cout <<"Building mode 7 font data structures\n"; for(presentchar=0;presentchar<96;presentchar++) { for(presentline=0;presentline<9;presentline++) { /* We build the value of the pixel up */ tempvalue=0; /* Pointer to left pixel in raw font */ tempptr=mode7fontRaw[(presentchar/16)*9+presentline]+8*(presentchar % 16); /* Now each pixel in that character line */ for(presentpixel=0;presentpixel<8;presentpixel++) { if (tempptr[presentpixel]!=' ') tempvalue|=1<<(7-presentpixel); }; /* presentpixel */ /* Store the data into the non graphic as well as the graphic areas - then we will overwrite the graphics one */ Mode7Font[2][presentchar][presentline]=Mode7Font[1][presentchar][presentline]=Mode7Font[0][presentchar][presentline]=tempvalue; }; /* presentline */ }; /* presentchar */ /* fill in all the graphics characters */ for(presgraph=0;presgraph<64;presgraph++) { presentchar=(presgraph & 31) + ((presgraph & 32)?64:0); Mode7Font[1][presentchar][0]=Mode7Font[1][presentchar][1]=Mode7Font[1][presentchar][2]=((presgraph &1)?0xf0:0) | ((presgraph &2)?0xf:0); Mode7Font[1][presentchar][3]=Mode7Font[1][presentchar][4]=Mode7Font[1][presentchar][5]=((presgraph &4)?0xf0:0) | ((presgraph &8)?0xf:0); Mode7Font[1][presentchar][6]=Mode7Font[1][presentchar][7]=Mode7Font[1][presentchar][8]=((presgraph &16)?0xf0:0) | ((presgraph &32)?0xf:0); /* The following lines do separated graphics */ Mode7Font[2][presentchar][2]=Mode7Font[2][presentchar][5]=Mode7Font[2][presentchar][8]=0; Mode7Font[2][presentchar][0]=Mode7Font[2][presentchar][1]=((presgraph &1)?0xe0:0) | ((presgraph &2)?0xe:0); Mode7Font[2][presentchar][3]=Mode7Font[2][presentchar][4]=((presgraph &4)?0xe0:0) | ((presgraph &8)?0xe:0); Mode7Font[2][presentchar][6]=Mode7Font[2][presentchar][7]=((presgraph &16)?0xe0:0) | ((presgraph &32)?0xe:0); } /* presgraph */; }; /* BuildMode7Font */ /*-------------------------------------------------------------------------------------------------------------*/ static void DoFastTable16(void) { unsigned int beebpixvl,beebpixvr; unsigned int bplvopen,bplvtotal; unsigned char tmp; for(beebpixvl=0;beebpixvl<16;beebpixvl++) { bplvopen=((beebpixvl & 8)?128:0) | ((beebpixvl & 4)?32:0) | ((beebpixvl & 2)?8:0) | ((beebpixvl & 1)?2:0); for(beebpixvr=0;beebpixvr<16;beebpixvr++) { bplvtotal=bplvopen | ((beebpixvr & 8)?64:0) | ((beebpixvr & 4)?16:0) | ((beebpixvr & 2)?4:0) | ((beebpixvr & 1)?1:0); tmp=VideoULA_Palette[beebpixvl]; if (tmp>7) { tmp&=7; if (VideoULA_ControlReg & 1) tmp^=7; }; // FastTable[bplvtotal].data[0]=FastTable[bplvtotal].data[1]= // FastTable[bplvtotal].data[2]=FastTable[bplvtotal].data[3]=mainWin->cols[tmp]; FastTable[bplvtotal].data[0]=FastTable[bplvtotal].data[1]=mainWin->cols[tmp]; tmp=VideoULA_Palette[beebpixvr]; if (tmp>7) { tmp&=7; if (VideoULA_ControlReg & 1) tmp^=7; }; // FastTable[bplvtotal].data[4]=FastTable[bplvtotal].data[5]= // FastTable[bplvtotal].data[6]=FastTable[bplvtotal].data[7]=mainWin->cols[tmp]; FastTable[bplvtotal].data[2]=FastTable[bplvtotal].data[3]=mainWin->cols[tmp]; }; /* beebpixr */ }; /* beebpixl */ }; /* DoFastTable16 */ /*-------------------------------------------------------------------------------------------------------------*/ static void DoFastTable16XStep8(void) { unsigned int beebpixvl,beebpixvr; unsigned int bplvopen,bplvtotal; unsigned char tmp; for(beebpixvl=0;beebpixvl<16;beebpixvl++) { bplvopen=((beebpixvl & 8)?128:0) | ((beebpixvl & 4)?32:0) | ((beebpixvl & 2)?8:0) | ((beebpixvl & 1)?2:0); for(beebpixvr=0;beebpixvr<16;beebpixvr++) { bplvtotal=bplvopen | ((beebpixvr & 8)?64:0) | ((beebpixvr & 4)?16:0) | ((beebpixvr & 2)?4:0) | ((beebpixvr & 1)?1:0); tmp=VideoULA_Palette[beebpixvl]; if (tmp>7) { tmp&=7; if (VideoULA_ControlReg & 1) tmp^=7; }; FastTableDWidth[bplvtotal].data[0]=FastTableDWidth[bplvtotal].data[1]= FastTableDWidth[bplvtotal].data[2]=FastTableDWidth[bplvtotal].data[3]= FastTableDWidth[bplvtotal].data[4]=FastTableDWidth[bplvtotal].data[5]= FastTableDWidth[bplvtotal].data[6]=FastTableDWidth[bplvtotal].data[7]=mainWin->cols[tmp]; tmp=VideoULA_Palette[beebpixvr]; if (tmp>7) { tmp&=7; if (VideoULA_ControlReg & 1) tmp^=7; }; FastTableDWidth[bplvtotal].data[8]=FastTableDWidth[bplvtotal].data[9]= FastTableDWidth[bplvtotal].data[10]=FastTableDWidth[bplvtotal].data[11]= FastTableDWidth[bplvtotal].data[12]=FastTableDWidth[bplvtotal].data[13]= FastTableDWidth[bplvtotal].data[14]=FastTableDWidth[bplvtotal].data[15]=mainWin->cols[tmp]; }; /* beebpixr */ }; /* beebpixl */ }; /* DoFastTable16XStep8 */ /*-------------------------------------------------------------------------------------------------------------*/ /* Some guess work and experimentation has determined that the left most pixel uses bits 7,5,3,1 for the */ /* palette address, the next uses 6,4,2,0, the next uses 5,3,1,H (H=High), then 5,2,0,H */ static void DoFastTable4(void) { unsigned char tmp; unsigned long beebpixv,pentry; for(beebpixv=0;beebpixv<256;beebpixv++) { pentry=((beebpixv & 128)?8:0) | ((beebpixv & 32)?4:0) | ((beebpixv & 8)?2:0) | ((beebpixv & 2)?1:0); tmp=VideoULA_Palette[pentry]; if (tmp>7) { tmp&=7; if (VideoULA_ControlReg & 1) tmp^=7; }; // FastTable[beebpixv].data[0]=FastTable[beebpixv].data[1]=mainWin->cols[tmp]; FastTable[beebpixv].data[0]=mainWin->cols[tmp]; pentry=((beebpixv & 64)?8:0) | ((beebpixv & 16)?4:0) | ((beebpixv & 4)?2:0) | ((beebpixv & 1)?1:0); tmp=VideoULA_Palette[pentry]; if (tmp>7) { tmp&=7; if (VideoULA_ControlReg & 1) tmp^=7; }; // FastTable[beebpixv].data[2]=FastTable[beebpixv].data[3]=mainWin->cols[tmp]; FastTable[beebpixv].data[1]=mainWin->cols[tmp]; pentry=((beebpixv & 32)?8:0) | ((beebpixv & 8)?4:0) | ((beebpixv & 2)?2:0) | 1; tmp=VideoULA_Palette[pentry]; if (tmp>7) { tmp&=7; if (VideoULA_ControlReg & 1) tmp^=7; }; // FastTable[beebpixv].data[4]=FastTable[beebpixv].data[5]=mainWin->cols[tmp]; FastTable[beebpixv].data[2]=mainWin->cols[tmp]; pentry=((beebpixv & 16)?8:0) | ((beebpixv & 4)?4:0) | ((beebpixv & 1)?2:0) | 1; tmp=VideoULA_Palette[pentry]; if (tmp>7) { tmp&=7; if (VideoULA_ControlReg & 1) tmp^=7; }; // FastTable[beebpixv].data[6]=FastTable[beebpixv].data[7]=mainWin->cols[tmp]; FastTable[beebpixv].data[3]=mainWin->cols[tmp]; }; /* beebpixv */ }; /* DoFastTable4 */ /*-------------------------------------------------------------------------------------------------------------*/ /* Some guess work and experimentation has determined that the left most pixel uses bits 7,5,3,1 for the */ /* palette address, the next uses 6,4,2,0, the next uses 5,3,1,H (H=High), then 5,2,0,H */ static void DoFastTable4XStep4(void) { unsigned char tmp; unsigned long beebpixv,pentry; for(beebpixv=0;beebpixv<256;beebpixv++) { pentry=((beebpixv & 128)?8:0) | ((beebpixv & 32)?4:0) | ((beebpixv & 8)?2:0) | ((beebpixv & 2)?1:0); tmp=VideoULA_Palette[pentry]; if (tmp>7) { tmp&=7; if (VideoULA_ControlReg & 1) tmp^=7; }; // FastTableDWidth[beebpixv].data[0]=FastTableDWidth[beebpixv].data[1]= // FastTableDWidth[beebpixv].data[2]=FastTableDWidth[beebpixv].data[3]=mainWin->cols[tmp]; FastTableDWidth[beebpixv].data[0]=FastTableDWidth[beebpixv].data[1]=mainWin->cols[tmp]; pentry=((beebpixv & 64)?8:0) | ((beebpixv & 16)?4:0) | ((beebpixv & 4)?2:0) | ((beebpixv & 1)?1:0); tmp=VideoULA_Palette[pentry]; if (tmp>7) { tmp&=7; if (VideoULA_ControlReg & 1) tmp^=7; }; // FastTableDWidth[beebpixv].data[4]=FastTableDWidth[beebpixv].data[5]= // FastTableDWidth[beebpixv].data[6]=FastTableDWidth[beebpixv].data[7]=mainWin->cols[tmp]; FastTableDWidth[beebpixv].data[2]=FastTableDWidth[beebpixv].data[3]=mainWin->cols[tmp]; pentry=((beebpixv & 32)?8:0) | ((beebpixv & 8)?4:0) | ((beebpixv & 2)?2:0) | 1; tmp=VideoULA_Palette[pentry]; if (tmp>7) { tmp&=7; if (VideoULA_ControlReg & 1) tmp^=7; }; // FastTableDWidth[beebpixv].data[8]=FastTableDWidth[beebpixv].data[9]= // FastTableDWidth[beebpixv].data[10]=FastTableDWidth[beebpixv].data[11]=mainWin->cols[tmp]; FastTableDWidth[beebpixv].data[4]=FastTableDWidth[beebpixv].data[5]=mainWin->cols[tmp]; pentry=((beebpixv & 16)?8:0) | ((beebpixv & 4)?4:0) | ((beebpixv & 1)?2:0) | 1; tmp=VideoULA_Palette[pentry]; if (tmp>7) { tmp&=7; if (VideoULA_ControlReg & 1) tmp^=7; }; // FastTableDWidth[beebpixv].data[12]=FastTableDWidth[beebpixv].data[13]= // FastTableDWidth[beebpixv].data[14]=FastTableDWidth[beebpixv].data[15]=mainWin->cols[tmp]; FastTableDWidth[beebpixv].data[6]=FastTableDWidth[beebpixv].data[7]=mainWin->cols[tmp]; }; /* beebpixv */ }; /* DoFastTable4XStep4 */ /*-------------------------------------------------------------------------------------------------------------*/ /* Some guess work and experimentation has determined that the left most pixel uses the same pattern as mode 1 */ /* all the way upto the 5th pixel which uses 31hh then 20hh and hten 1hhh then 0hhhh */ static void DoFastTable2(void) { unsigned char tmp; unsigned long beebpixv,beebpixvt,pentry; int pix; for(beebpixv=0;beebpixv<256;beebpixv++) { beebpixvt=beebpixv; for(pix=0;pix<8;pix++) { pentry=((beebpixvt & 128)?8:0) | ((beebpixvt & 32)?4:0) | ((beebpixvt & 8)?2:0) | ((beebpixvt & 2)?1:0); beebpixvt<<=1; beebpixvt|=1; tmp=VideoULA_Palette[pentry]; if (tmp>7) { tmp&=7; if (VideoULA_ControlReg & 1) tmp^=7; }; FastTable[beebpixv].data[pix]=mainWin->cols[tmp]; }; /* pix */ }; /* beebpixv */ }; /* DoFastTable2 */ /*-------------------------------------------------------------------------------------------------------------*/ /* Some guess work and experimentation has determined that the left most pixel uses the same pattern as mode 1 */ /* all the way upto the 5th pixel which uses 31hh then 20hh and hten 1hhh then 0hhhh */ static void DoFastTable2XStep2(void) { unsigned char tmp; unsigned long beebpixv,beebpixvt,pentry; int pix; for(beebpixv=0;beebpixv<256;beebpixv++) { beebpixvt=beebpixv; for(pix=0;pix<8;pix++) { pentry=((beebpixvt & 128)?8:0) | ((beebpixvt & 32)?4:0) | ((beebpixvt & 8)?2:0) | ((beebpixvt & 2)?1:0); beebpixvt<<=1; beebpixvt|=1; tmp=VideoULA_Palette[pentry]; if (tmp>7) { tmp&=7; if (VideoULA_ControlReg & 1) tmp^=7; }; FastTableDWidth[beebpixv].data[pix/**2*/]/*=FastTableDWidth[beebpixv].data[pix*2+1]*/=mainWin->cols[tmp]; }; /* pix */ }; /* beebpixv */ }; /* DoFastTable2XStep2 */ /*-------------------------------------------------------------------------------------------------------------*/ /* Check validity of fast table, and if invalid rebuild. The fast table accelerates the translation of beeb video memory values into X pixel values */ static void DoFastTable(void) { /* if it's already OK then quit */ if (FastTable_Valid) return; if (!(CRTC_HorizontalDisplayed & 3)) { LineRoutine=(VideoULA_ControlReg & 0x10)?LowLevelDoScanLineNarrow:LowLevelDoScanLineWide; } else { LineRoutine=(VideoULA_ControlReg & 0x10)?LowLevelDoScanLineNarrowNot4Bytes:LowLevelDoScanLineWideNot4Bytes; }; /* What happens next dpends on the number of colours */ switch (NColsLookup[(VideoULA_ControlReg & 0x1c) >> 2]) { case 2: if (VideoULA_ControlReg & 0x10) { DoFastTable2(); } else { DoFastTable2XStep2(); }; FastTable_Valid=1; break; case 4: if (VideoULA_ControlReg & 0x10) { DoFastTable4(); } else { DoFastTable4XStep4(); }; FastTable_Valid=1; break; case 16: if (VideoULA_ControlReg & 0x10) { DoFastTable16(); } else { DoFastTable16XStep8(); }; FastTable_Valid=1; break; default: break; }; /* Colours/pixel switch */ }; /* DoFastTable */ /*-------------------------------------------------------------------------------------------------------------*/ #define BEEB_DOTIME_SAMPLESIZE 50 static void VideoStartOfFrame(void) { int tmp; #ifdef BEEB_DOTIME static int Have_GotTime=0; static struct tms previous,now; static int Time_FrameCount=0; double frametime; static CycleCountT OldCycles=0; if (!Have_GotTime) { times(&previous); Time_FrameCount=-1; Have_GotTime=1; }; if (Time_FrameCount==(BEEB_DOTIME_SAMPLESIZE-1)) { times(&now); frametime=now.tms_utime-previous.tms_utime; #ifndef SUNOS #ifndef HPUX frametime/=(double)CLOCKS_PER_SEC; #else frametime/=(double)sysconf(_SC_CLK_TCK); #endif #else frametime/=(double)HZ; #endif frametime/=(double)BEEB_DOTIME_SAMPLESIZE; cerr << "Frametime: " << frametime << "s fps=" << (1/frametime) << "Total cycles=" << TotalCycles << "Cycles in last unit=" << (TotalCycles-OldCycles) << "\n"; OldCycles=TotalCycles; previous=now; Time_FrameCount=0; } else Time_FrameCount++; #endif /* If FrameNum hits 0 we actually refresh */ if (FrameNum--==0) { FrameNum=Video_RefreshFrequency-1; }; if (CRTC_VerticalTotalAdjust==0) { VideoState.CharLine=0; VideoState.InCharLine=CRTC_ScanLinesPerChar; VideoState.InCharLineUp=0; } else { VideoState.CharLine=-1; VideoState.InCharLine=CRTC_VerticalTotalAdjust; VideoState.InCharLineUp=0; }; VideoState.IsTeletext=(VideoULA_ControlReg &2)>0; if (!VideoState.IsTeletext) { VideoState.Addr=CRTC_ScreenStartLow+(CRTC_ScreenStartHigh<<8); } else { int tmphigh=CRTC_ScreenStartHigh; /* undo wrangling of start address - I don't understand why this should be - see p.372 of AUG for this info */ tmphigh^=0x20; tmphigh+=0x74; tmphigh&=255; VideoState.Addr=CRTC_ScreenStartLow+(tmphigh<<8); Mode7FlashTrigger--; if (Mode7FlashTrigger<0) { Mode7FlashTrigger=MODE7FLASHFREQUENCY; Mode7FlashOn^=1; /* toggle flash state */ }; for(tmp=0;tmp<80;tmp++) { Mode7DoubleHeightFlags[tmp]=1; /* corresponding character on NEXT line is top half */ }; }; IncTrigger(((CRTC_HorizontalTotal+1)*((VideoULA_ControlReg & 16)?1:2)),VideoTriggerCount); /* Number of 2MHz cycles until another scanline needs doing */ }; /* VideoStartOfFrame */ /*--------------------------------------------------------------------------*/ /* Scanline processing for modes with fast 6845 clock - i.e. narrow pixels */ static void LowLevelDoScanLineNarrow() { unsigned char *CurrentPtr; int BytesToGo=CRTC_HorizontalDisplayed; typedef unsigned long FourUChars; FourUChars *vidPtr=(FourUChars*)mainWin->GetLinePtr(VideoState.PixmapLine); // printf("awidth=%d\n",BytesToGo); /* If the step is 4 then each byte corresponds to one entry in the fasttable and thus we can copy it really easily (and fast!) */ CurrentPtr=(unsigned char *)VideoState.DataPtr+VideoState.InCharLineUp; /* This should help the compiler - it doesn't need to test for end of loop except every 4 entries */ BytesToGo/=4; for(;BytesToGo;CurrentPtr+=32,BytesToGo--) { *(vidPtr++)=(FourUChars &)FastTable[*CurrentPtr]; *(vidPtr++)=(FourUChars &)FastTable[*(CurrentPtr+8)]; *(vidPtr++)=(FourUChars &)FastTable[*(CurrentPtr+16)]; *(vidPtr++)=(FourUChars &)FastTable[*(CurrentPtr+24)]; }; }; /* LowLevelDoScanLineNarrow() */ /*--------------------------------------------------------------------------*/ /* Scanline processing for modes with fast 6845 clock - i.e. narrow pixels */ /* This version handles screen modes where there is not a multiple of 4 */ /* bytes per scanline. */ static void LowLevelDoScanLineNarrowNot4Bytes() { unsigned char *CurrentPtr; int BytesToGo=CRTC_HorizontalDisplayed; EightUChars *vidPtr=mainWin->GetLinePtr(VideoState.PixmapLine); /* If the step is 4 then each byte corresponds to one entry in the fasttable and thus we can copy it really easily (and fast!) */ CurrentPtr=(unsigned char *)VideoState.DataPtr+VideoState.InCharLineUp; for(;BytesToGo;CurrentPtr+=8,BytesToGo--) (vidPtr++)->eightbyte=FastTable[*CurrentPtr].eightbyte; }; /* LowLevelDoScanLineNarrowNot4Bytes() */ /*-----------------------------------------------------------------------------*/ /* Scanline processing for the low clock rate modes */ static void LowLevelDoScanLineWide() { unsigned char *CurrentPtr; int BytesToGo=CRTC_HorizontalDisplayed; // // printf("width=%d\n",BytesToGo); // SixteenUChars *vidPtr=mainWin->GetLinePtr16(VideoState.PixmapLine); EightUChars *vidPtr=mainWin->GetLinePtr(VideoState.PixmapLine); /* If the step is 4 then each byte corresponds to one entry in the fasttable and thus we can copy it really easily (and fast!) */ CurrentPtr=(unsigned char *)VideoState.DataPtr+VideoState.InCharLineUp; /* This should help the compiler - it doesn't need to test for end of loop except every 4 entries */ BytesToGo/=4; for(;BytesToGo;CurrentPtr+=32,BytesToGo--) { *(vidPtr++)=(EightUChars &)FastTableDWidth[*CurrentPtr]; *(vidPtr++)=(EightUChars &)FastTableDWidth[*(CurrentPtr+8)]; *(vidPtr++)=(EightUChars &)FastTableDWidth[*(CurrentPtr+16)]; *(vidPtr++)=(EightUChars &)FastTableDWidth[*(CurrentPtr+24)]; }; }; /* LowLevelDoScanLineWide */ /*-----------------------------------------------------------------------------*/ /* Scanline processing for the low clock rate modes */ /* This version handles cases where the screen width is not divisible by 4 */ static void LowLevelDoScanLineWideNot4Bytes() { unsigned char *CurrentPtr; int BytesToGo=CRTC_HorizontalDisplayed; SixteenUChars *vidPtr=mainWin->GetLinePtr16(VideoState.PixmapLine); CurrentPtr=(unsigned char *)VideoState.DataPtr+VideoState.InCharLineUp; for(;BytesToGo;CurrentPtr+=8,BytesToGo--) *(vidPtr++)=FastTableDWidth[*CurrentPtr]; }; /* LowLevelDoScanLineWideNot4Bytes */ /*-------------------------------------------------------------------------------------------------------------*/ /* Do all the pixel rows for one row of teletext characters */ static void DoMode7Row(void) { char *CurrentPtr=VideoState.DataPtr; int CurrentChar; int XStep=320/(CRTC_HorizontalDisplayed*8); unsigned char byte,tmp; unsigned int Foreground=mainWin->cols[7]; unsigned int ActualForeground; unsigned int Background=mainWin->cols[0]; int Flash=0; /* i.e. steady */ int DoubleHeight=0; /* Normal */ int Graphics=0; /* I.e. alpha */ int Separated=0; /* i.e. continuous graphics */ int HoldGraph=0; /* I.e. don't hold graphics - I don't know what hold graphics is anyway! */ unsigned int CurrentCol[9]={0xffffff,0xffffff,0xffffff,0xffffff,0xffffff,0xffffff,0xffffff,0xffffff,0xffffff}; int CurrentLen[9]={0,0,0,0,0,0,0,0,0}; int CurrentStartX[9]={0,0,0,0,0,0,0,0,0}; int CurrentScanLine; int CurrentX=0; int CurrentPixel; int col; int FontTypeIndex=0; /* 0=alpha, 1=continuous graphics, 2=separated graphics */ if (CRTC_HorizontalDisplayed>80) return; /* Not possible on beeb - and would break the double height lookup array */ for(CurrentChar=0;CurrentChar=128) && (byte<=159)) { switch (byte) { case 129: case 130: case 131: case 132: case 133: case 134: case 135: Foreground=mainWin->cols[byte-128]; Graphics=0; break; case 136: Flash=1; break; case 137: Flash=0; break; case 140: DoubleHeight=0; break; case 141: DoubleHeight=1; break; case 145: case 146: case 147: case 148: case 149: case 150: case 151: Foreground=mainWin->cols[byte-144]; Graphics=1; break; case 152: /* Conceal display - not sure about this */ Foreground=Background; break; case 153: Separated=0; break; case 154: Separated=1; break; case 156: Background=mainWin->cols[0]; break; case 157: Background=Foreground; break; case 158: HoldGraph=1; break; case 159: HoldGraph=0; break; }; /* Special character switch */ /* Fudge so that the special character is just displayed in background */ byte=32; FontTypeIndex=Graphics?(Separated?2:1):0; }; /* test for special character */ /* Top bit never reaches character generator */ byte&=127; /* Our font table goes from character 32 up */ if (byte<32) byte=0; else byte-=32; /* Conceal flashed text if necessary */ ActualForeground=(Flash && !Mode7FlashOn)?Background:Foreground; if (!DoubleHeight) { for(CurrentScanLine=0;CurrentScanLine<9;CurrentScanLine++) { tmp=Mode7Font[FontTypeIndex][byte][CurrentScanLine]; if ((tmp==0) || (tmp==255)) { col=(tmp==0)?Background:ActualForeground; if (col==CurrentCol[CurrentScanLine]) CurrentLen[CurrentScanLine]+=8*XStep; else { if (CurrentLen[CurrentScanLine]) mainWin->doHorizLine(CurrentCol[CurrentScanLine],VideoState.PixmapLine+CurrentScanLine,CurrentStartX[CurrentScanLine],CurrentLen[CurrentScanLine]); CurrentCol[CurrentScanLine]=col; CurrentStartX[CurrentScanLine]=CurrentX; CurrentLen[CurrentScanLine]=8*XStep; }; /* same colour */ } else { for(CurrentPixel=0x80;CurrentPixel;CurrentPixel=CurrentPixel>>1) { /* Background or foreground ? */ col=(tmp & CurrentPixel)?ActualForeground:Background; /* Do we need to draw ? */ if (col==CurrentCol[CurrentScanLine]) CurrentLen[CurrentScanLine]+=XStep; else { if (CurrentLen[CurrentScanLine]) mainWin->doHorizLine(CurrentCol[CurrentScanLine],VideoState.PixmapLine+CurrentScanLine,CurrentStartX[CurrentScanLine],CurrentLen[CurrentScanLine]); CurrentCol[CurrentScanLine]=col; CurrentStartX[CurrentScanLine]=CurrentX; CurrentLen[CurrentScanLine]=XStep; }; /* Fore/back ground */ CurrentX+=XStep; }; /* Pixel within byte */ CurrentX-=8*XStep; }; /* tmp!=0 */ }; /* Scanline for */ CurrentX+=8*XStep; Mode7DoubleHeightFlags[CurrentChar]=1; /* Not double height - so if the next line is double height it will be top half */ } else { int ActualScanLine; /* Double height! */ for(CurrentPixel=0x80;CurrentPixel;CurrentPixel=CurrentPixel>>1) { for(CurrentScanLine=0;CurrentScanLine<9;CurrentScanLine++) { if (Mode7DoubleHeightFlags[CurrentChar]) ActualScanLine=CurrentScanLine >> 1; else ActualScanLine=4+((CurrentScanLine+1)>>1); /* Background or foreground ? */ col=(Mode7Font[FontTypeIndex][byte][ActualScanLine] & CurrentPixel)?ActualForeground:Background; /* Do we need to draw ? */ if (col==CurrentCol[CurrentScanLine]) CurrentLen[CurrentScanLine]+=XStep; else { if (CurrentLen[CurrentScanLine]) { mainWin->doHorizLine(CurrentCol[CurrentScanLine],VideoState.PixmapLine+CurrentScanLine,CurrentStartX[CurrentScanLine],CurrentLen[CurrentScanLine]); }; CurrentCol[CurrentScanLine]=col; CurrentStartX[CurrentScanLine]=CurrentX; CurrentLen[CurrentScanLine]=XStep; }; /* Fore/back ground */ }; /* Scanline for */ CurrentX+=XStep; }; /* Pixel within byte */ Mode7DoubleHeightFlags[CurrentChar]^=1; /* Not double height - so if the next line is double height it will be top half */ }; }; /* character loop */ /* Finish off right bits of scan line */ for(CurrentScanLine=0;CurrentScanLine<9;CurrentScanLine++) { if (CurrentLen[CurrentScanLine]) mainWin->doHorizLine(CurrentCol[CurrentScanLine],VideoState.PixmapLine+CurrentScanLine,CurrentStartX[CurrentScanLine],CurrentLen[CurrentScanLine]); }; }; /* DoMode7Row */ /*-------------------------------------------------------------------------------------------------------------*/ /* Actually does the work of decoding beeb memory and plotting the line to X */ static void LowLevelDoScanLine() { /* Update acceleration tables */ DoFastTable(); if (FastTable_Valid) LineRoutine(); }; /* LowLevelDoScanLine */ /*-------------------------------------------------------------------------------------------------------------*/ void VideoDoScanLine(void) { /* cerr << "CharLine=" << VideoState.CharLine << " InCharLine=" << VideoState.InCharLine << "\n"; */ if (VideoState.IsTeletext) { static int DoCA1Int=0; if (DoCA1Int) { SysVIATriggerCA1Int(0); DoCA1Int=0; }; if ((VideoState.CharLine!=-1) && (VideoState.CharLineCRTC_VerticalTotal) { if (!FrameNum) mainWin->updateLines(0,256); VideoStartOfFrame(); VideoState.PreviousFinalPixmapLine=VideoState.PixmapLine; VideoState.PixmapLine=0; SysVIATriggerCA1Int(1); DoCA1Int=1; } else { if (VideoState.CharLine!=-1) { IncTrigger((CRTC_HorizontalTotal+1)*((VideoULA_ControlReg & 16)?1:2)*10,VideoTriggerCount); } else { IncTrigger((CRTC_HorizontalTotal+1)*((VideoULA_ControlReg & 16)?1:2),VideoTriggerCount); }; }; } else { /* Non teletext */ if ((VideoState.CharLine!=-1) && (VideoState.CharLine(CRTC_ScanLinesPerChar-8)) { if (!FrameNum) LowLevelDoScanLine(); VideoState.PixmapLine+=1; } else { if (!FrameNum) mainWin->doHorizLine(mainWin->cols[0],VideoState.PixmapLine++,0,mainWin->bytesPerLine()); }; }; /* !=-1 and is in displayed bit */ /* Move onto next physical scanline as far as timing is concerned */ VideoState.InCharLine-=1; VideoState.InCharLineUp+=1; if (VideoState.VSyncState) { if (!(--VideoState.VSyncState)) { SysVIATriggerCA1Int(0); }; }; if (VideoState.InCharLine<0) { VideoState.CharLine++; /* Suspect the -1 in sync pos is a fudge factor - careful! - DAG - now out */ if ((VideoState.VSyncState==0) && (VideoState.CharLine==(CRTC_VerticalSyncPos))) { /* Fill unscanned lines under picture */ if (VideoState.PixmapLinedoHorizLine(mainWin->cols[0],CurrentLine,0,mainWin->bytesPerLine()); }; }; VideoState.PreviousFinalPixmapLine=VideoState.PixmapLine; VideoState.PixmapLine=0; SysVIATriggerCA1Int(1); VideoState.VSyncState=2; }; VideoState.InCharLine=CRTC_ScanLinesPerChar; VideoState.InCharLineUp=0; }; if (VideoState.CharLine>CRTC_VerticalTotal) { if (!FrameNum) mainWin->updateLines(0,256); VideoStartOfFrame(); } else IncTrigger((CRTC_HorizontalTotal+1)*((VideoULA_ControlReg & 16)?1:2),VideoTriggerCount); }; /* Teletext if */ }; /* VideoDoScanLine */ /*-------------------------------------------------------------------------------------------------------------*/ void VideoInit(void) { char *environptr; VideoStartOfFrame(); VideoState.DataPtr=BeebMemPtrWithWrap(0x3000,320); SetTrigger(99,VideoTriggerCount); /* Give time for OS to set mode up before doing anything silly */ FastTable_Valid=0; BuildMode7Font(); // environptr=getenv("BeebVideoRefreshFreq"); // if (environptr!=NULL) Video_RefreshFrequency=atoi(environptr); // if (Video_RefreshFrequency<1) Video_RefreshFrequency=2; FrameNum=Video_RefreshFrequency; VideoState.PixmapLine=0; VideoState.PreviousFinalPixmapLine=255; }; /* VideoInit */ /*-------------------------------------------------------------------------------------------------------------*/ void CRTCWrite(int Address, int Value) { Value&=0xff; if (Address & 1) { switch (CRTCControlReg) { case 0: CRTC_HorizontalTotal=Value; break; case 1: CRTC_HorizontalDisplayed=Value; FastTable_Valid=0; break; case 2: CRTC_HorizontalSyncPos=Value; break; case 3: CRTC_SyncWidth=Value; break; case 4: CRTC_VerticalTotal=Value; break; case 5: CRTC_VerticalTotalAdjust=Value; break; case 6: CRTC_VerticalDisplayed=Value; break; case 7: CRTC_VerticalSyncPos=Value; break; case 8: CRTC_InterlaceAndDelay=Value; break; case 9: CRTC_ScanLinesPerChar=Value; break; case 10: CRTC_CursorStart=Value; break; case 11: CRTC_CursorEnd=Value; break; case 12: CRTC_ScreenStartHigh=Value; break; case 13: CRTC_ScreenStartLow=Value; break; case 14: CRTC_CursorPosHigh=Value & 0x3f; /* Cursor high only has 6 bits */ break; case 15: CRTC_CursorPosLow=Value & 0xff; break; default: /* In case the user wrote a duff control register value */ break; }; /* CRTCWrite switch */ /* cerr << "CRTCWrite RegNum=" << int(CRTCControlReg) << " Value=" << Value << "\n"; */ } else { CRTCControlReg=Value & 0x1f; }; }; /* CRTCWrite */ /*-------------------------------------------------------------------------------------------------------------*/ int CRTCRead(int Address) { if (Address & 1) { switch (CRTCControlReg) { case 14: return(CRTC_CursorPosHigh); case 15: return(CRTC_CursorPosLow); case 16: return(CRTC_LightPenHigh); /* Perhaps tie to mouse pointer ? */ case 17: return(CRTC_LightPenLow); default: break; }; /* CRTC Read switch */ } else { return(0); /* Rockwell part has bits 5,6,7 used - bit 6 is set when LPEN is received, bit 5 when in vertical retrace */ } return(0); // Keeep MSVC happy $NRM }; /* CRTCRead */ /*-------------------------------------------------------------------------------------------------------------*/ void VideoULAWrite(int Address, int Value) { if (Address & 1) { VideoULA_Palette[(Value & 0xf0)>>4]=(Value & 0xf) ^ 7; FastTable_Valid=0; /* cerr << "Palette reg " << ((Value & 0xf0)>>4) << " now has value " << ((Value & 0xf) ^ 7) << "\n"; */ } else { VideoULA_ControlReg=Value; FastTable_Valid=0; /* Could be more selective and only do it if no.of.cols bit changes */ /* cerr << "VidULA Ctrl reg write " << hex << Value << "\n"; */ }; }; /* VidULAWrite */ /*-------------------------------------------------------------------------------------------------------------*/ int VideoULARead(int Address) { return(Address); /* Read not defined from Video ULA */ }; /* VidULARead */ /*-------------------------------------------------------------------------------------------------------------*/ void video_dumpstate(void) { int tmp; cerr << "video:\n"; cerr << " VideoULA_ControlReg=" << int(VideoULA_ControlReg) << "\n"; cerr << " VideoULA_Palette="; for(tmp=0;tmp<16;tmp++) cerr << int(VideoULA_Palette[tmp]) << " "; cerr << "\n CRTC Control=" << int(CRTCControlReg) << "\n"; cerr << " CRTC_HorizontalTotal=" << int(CRTC_HorizontalTotal) << "\n"; cerr << " CRTC_HorizontalDisplayed=" << int(CRTC_HorizontalDisplayed)<< "\n"; cerr << " CRTC_HorizontalSyncPos=" << int(CRTC_HorizontalSyncPos)<< "\n"; cerr << " CRTC_SyncWidth=" << int(CRTC_SyncWidth)<< "\n"; cerr << " CRTC_VerticalTotal=" << int(CRTC_VerticalTotal)<< "\n"; cerr << " CRTC_VerticalTotalAdjust=" << int(CRTC_VerticalTotalAdjust)<< "\n"; cerr << " CRTC_VerticalDisplayed=" << int(CRTC_VerticalDisplayed)<< "\n"; cerr << " CRTC_VerticalSyncPos=" << int(CRTC_VerticalSyncPos)<< "\n"; cerr << " CRTC_InterlaceAndDelay=" << int(CRTC_InterlaceAndDelay)<< "\n"; cerr << " CRTC_ScanLinesPerChar=" << int(CRTC_ScanLinesPerChar)<< "\n"; cerr << " CRTC_CursorStart=" << int(CRTC_CursorStart)<< "\n"; cerr << " CRTC_CursorEnd=" << int(CRTC_CursorEnd)<< "\n"; cerr << " CRTC_ScreenStartHigh=" << int(CRTC_ScreenStartHigh)<< "\n"; cerr << " CRTC_ScreenStartLow=" << int(CRTC_ScreenStartLow)<< "\n"; cerr << " CRTC_CursorPosHigh=" << int(CRTC_CursorPosHigh)<< "\n"; cerr << " CRTC_CursorPosLow=" << int(CRTC_CursorPosLow)<< "\n"; cerr << " CRTC_LightPenHigh=" << int(CRTC_LightPenHigh)<< "\n"; cerr << " CRTC_LightPenLow=" << int(CRTC_LightPenLow)<< "\n"; }; /* video_dumpstate */