/** fMSX: portable MSX emulator ******************************/ /** **/ /** V9938.c **/ /** **/ /** This file contains implementation for the V9938 special **/ /** graphical operations. **/ /** **/ /** Copyright (C) Marat Fayzullin 1994-2005 **/ /** You are not allowed to distribute this software **/ /** commercially. Please, notify me, if you make any **/ /** changes to this file. **/ /** **/ /** Completely rewritten by Alex Wulms: **/ /** - VDP Command execution 'in parallel' with CPU **/ /** - Corrected behaviour of VDP commands **/ /** - Made it easier to implement correct S7/8 mapping **/ /** by concentrating VRAM access in one single place **/ /** - Made use of the 'in parallel' VDP command exec **/ /** and correct timing. You must call the function **/ /** LoopVDP() from LoopZ80 in MSX.c. You must call it **/ /** exactly 256 times per screen refresh. **/ /** Started on : 11-11-1999 **/ /** Beta release 1 on: 9-12-1999 **/ /** Beta release 2 on: 20-01-2000 **/ /** - Corrected behaviour of VRM <-> Z80 transfer **/ /** - Improved performance of the code **/ /** Public release 1.0: 20-04-2000 **/ /*************************************************************/ #include "V9938.h" #include #include "shared.h" /** INLINE ***************************************************/ /** Different compilers inline C functions differently. **/ /*************************************************************/ #ifdef __GNUC__ #define INLINE inline #else #define INLINE static #endif /*************************************************************/ /** Other usefull defines **/ /*************************************************************/ #define VDP_VRMP5(X, Y) (VRAM + ((Y&1023)<<7) + ((X&255)>>1)) #define VDP_VRMP6(X, Y) (VRAM + ((Y&1023)<<7) + ((X&511)>>2)) #define VDP_VRMP7(X, Y) (VRAM + ((Y&511)<<8) + ((X&511)>>1)) #define VDP_VRMP8(X, Y) (VRAM + ((Y&511)<<8) + (X&255)) #define VDP_VRMP(M, X, Y) VDPVRMP(M, X, Y) #define VDP_POINT(M, X, Y) VDPpoint(M, X, Y) #define VDP_PSET(M, X, Y, C, O) VDPpset(M, X, Y, C, O) #define CM_ABRT 0x0 #define CM_POINT 0x4 #define CM_PSET 0x5 #define CM_SRCH 0x6 #define CM_LINE 0x7 #define CM_LMMV 0x8 #define CM_LMMM 0x9 #define CM_LMCM 0xA #define CM_LMMC 0xB #define CM_HMMV 0xC #define CM_HMMM 0xD #define CM_YMMM 0xE #define CM_HMMC 0xF /*************************************************************/ /* Many VDP commands are executed in some kind of loop but */ /* essentially, there are only a few basic loop structures */ /* that are re-used. We define the loop structures that are */ /* re-used here so that they have to be entered only once */ /*************************************************************/ #define pre_loop \ while ((cnt-=delta) > 0) { /* Loop over DX, DY */ #define post__x_y(MX) \ if (!--ANX || ((ADX+=TX)&MX)) { \ if (!(--NY&1023) || (DY+=TY)==-1) \ break; \ else { \ ADX=DX; \ ANX=NX; \ } \ } \ } /* Loop over DX, SY, DY */ #define post__xyy(MX) \ if ((ADX+=TX)&MX) { \ if (!(--NY&1023) || (SY+=TY)==-1 || (DY+=TY)==-1) \ break; \ else \ ADX=DX; \ } \ } /* Loop over SX, DX, SY, DY */ #define post_xxyy(MX) \ if (!--ANX || ((ASX+=TX)&MX) || ((ADX+=TX)&MX)) { \ if (!(--NY&1023) || (SY+=TY)==-1 || (DY+=TY)==-1) \ break; \ else { \ ASX=SX; \ ADX=DX; \ ANX=NX; \ } \ } \ } /*************************************************************/ /** Structures and stuff **/ /*************************************************************/ static struct { int SX,SY; int DX,DY; int TX,TY; int NX,NY; int MX; int ASX,ADX,ANX; byte CL; byte LO; byte CM; } MMC; /*************************************************************/ /** Function prototypes **/ /*************************************************************/ static byte *VDPVRMP(register byte M, register int X, register int Y); static byte VDPpoint5(register int SX, register int SY); static byte VDPpoint6(register int SX, register int SY); static byte VDPpoint7(register int SX, register int SY); static byte VDPpoint8(register int SX, register int SY); static byte VDPpoint(register byte SM, register int SX, register int SY); static void VDPpsetlowlevel(register byte *P, register byte CL, register byte M, register byte OP); static void VDPpset5(register int DX, register int DY, register byte CL, register byte OP); static void VDPpset6(register int DX, register int DY, register byte CL, register byte OP); static void VDPpset7(register int DX, register int DY, register byte CL, register byte OP); static void VDPpset8(register int DX, register int DY, register byte CL, register byte OP); static void VDPpset(register byte SM, register int DX, register int DY, register byte CL, register byte OP); static int GetVdpTimingValue(register int *); static void SrchEngine(void); static void LineEngine(void); static void LmmvEngine(void); static void LmmmEngine(void); static void LmcmEngine(void); static void LmmcEngine(void); static void HmmvEngine(void); static void HmmmEngine(void); static void YmmmEngine(void); static void HmmcEngine(void); void ReportVdpCommand(register byte Op); /*************************************************************/ /** Variables visible only in this module **/ /*************************************************************/ static byte Mask[4] = { 0x0F,0x03,0x0F,0xFF }; static int PPB[4] = { 2,4,2,1 }; static int PPL[4] = { 256,512,512,256 }; static int VdpOpsCnt=1; static void (*VdpEngine)(void)=0; /* SprOn SprOn SprOf SprOf */ /* ScrOf ScrOn ScrOf ScrOn */ static int srch_timing[8]={ 818, 1025, 818, 830, /* ntsc */ 696, 854, 696, 684 }; /* pal */ static int line_timing[8]={ 1063, 1259, 1063, 1161, 904, 1026, 904, 953 }; static int hmmv_timing[8]={ 439, 549, 439, 531, 366, 439, 366, 427 }; static int lmmv_timing[8]={ 873, 1135, 873, 1056, 732, 909, 732, 854 }; static int ymmm_timing[8]={ 586, 952, 586, 610, 488, 720, 488, 500 }; static int hmmm_timing[8]={ 818, 1111, 818, 854, 684, 879, 684, 708 }; static int lmmm_timing[8]={ 1160, 1599, 1160, 1172, 964, 1257, 964, 977 }; /** VDPVRMP() **********************************************/ /** Calculate addr of a pixel in vram **/ /*************************************************************/ INLINE byte *VDPVRMP(byte M,int X,int Y) { switch(M) { case 0: return VDP_VRMP5(X,Y); case 1: return VDP_VRMP6(X,Y); case 2: return VDP_VRMP7(X,Y); case 3: return VDP_VRMP8(X,Y); } return(VRAM); } /** VDPpoint5() ***********************************************/ /** Get a pixel on screen 5 **/ /*************************************************************/ INLINE byte VDPpoint5(int SX, int SY) { return (*VDP_VRMP5(SX, SY) >> (((~SX)&1)<<2) )&15; } /** VDPpoint6() ***********************************************/ /** Get a pixel on screen 6 **/ /*************************************************************/ INLINE byte VDPpoint6(int SX, int SY) { return (*VDP_VRMP6(SX, SY) >> (((~SX)&3)<<1) )&3; } /** VDPpoint7() ***********************************************/ /** Get a pixel on screen 7 **/ /*************************************************************/ INLINE byte VDPpoint7(int SX, int SY) { return (*VDP_VRMP7(SX, SY) >> (((~SX)&1)<<2) )&15; } /** VDPpoint8() ***********************************************/ /** Get a pixel on screen 8 **/ /*************************************************************/ INLINE byte VDPpoint8(int SX, int SY) { return *VDP_VRMP8(SX, SY); } /** VDPpoint() ************************************************/ /** Get a pixel on a screen **/ /*************************************************************/ INLINE byte VDPpoint(byte SM, int SX, int SY) { switch(SM) { case 0: return VDPpoint5(SX,SY); case 1: return VDPpoint6(SX,SY); case 2: return VDPpoint7(SX,SY); case 3: return VDPpoint8(SX,SY); } return(0); } /** VDPpsetlowlevel() ****************************************/ /** Low level function to set a pixel on a screen **/ /** Make it inline to make it fast **/ /*************************************************************/ INLINE void VDPpsetlowlevel(byte *P, byte CL, byte M, byte OP) { switch (OP) { case 0: *P = (*P & M) | CL; break; case 1: *P = *P & (CL | M); break; case 2: *P |= CL; break; case 3: *P ^= CL; break; case 4: *P = (*P & M) | ~(CL | M); break; case 8: if (CL) *P = (*P & M) | CL; break; case 9: if (CL) *P = *P & (CL | M); break; case 10: if (CL) *P |= CL; break; case 11: if (CL) *P ^= CL; break; case 12: if (CL) *P = (*P & M) | ~(CL|M); break; } } /** VDPpset5() ***********************************************/ /** Set a pixel on screen 5 **/ /*************************************************************/ INLINE void VDPpset5(int DX, int DY, byte CL, byte OP) { register byte SH = ((~DX)&1)<<2; VDPpsetlowlevel(VDP_VRMP5(DX, DY), CL << SH, ~(15<>6)&1)|(VDP[8]&2)|((VDP[9]<<1)&4)]); } /** SrchEgine()** ********************************************/ /** Search a dot **/ /*************************************************************/ void SrchEngine(void) { register int SX=MMC.SX; register int SY=MMC.SY; register int TX=MMC.TX; register int ANX=MMC.ANX; register byte CL=MMC.CL; register int cnt; register int delta; delta = GetVdpTimingValue(srch_timing); cnt = VdpOpsCnt; #define pre_srch \ pre_loop \ if (( #define post_srch(MX) \ ==CL) ^ANX) { \ VDPStatus[2]|=0x10; /* Border detected */ \ break; \ } \ if ((SX+=TX) & MX) { \ VDPStatus[2]&=0xEF; /* Border not detected */ \ break; \ } \ } switch (ScrMode) { case 5: pre_srch VDPpoint5(SX, SY) post_srch(256) break; case 6: pre_srch VDPpoint6(SX, SY) post_srch(512) break; case 7: pre_srch VDPpoint7(SX, SY) post_srch(512) break; case 8: pre_srch VDPpoint8(SX, SY) post_srch(256) break; } if ((VdpOpsCnt=cnt)>0) { /* Command execution done */ VDPStatus[2]&=0xFE; VdpEngine=0; /* Update SX in VDP registers */ VDPStatus[8]=SX&0xFF; VDPStatus[9]=(SX>>8)|0xFE; } else { MMC.SX=SX; } } /** LineEgine()** ********************************************/ /** Draw a line **/ /*************************************************************/ void LineEngine(void) { register int DX=MMC.DX; register int DY=MMC.DY; register int TX=MMC.TX; register int TY=MMC.TY; register int NX=MMC.NX; register int NY=MMC.NY; register int ASX=MMC.ASX; register int ADX=MMC.ADX; register byte CL=MMC.CL; register byte LO=MMC.LO; register int cnt; register int delta; delta = GetVdpTimingValue(line_timing); cnt = VdpOpsCnt; #define post_linexmaj(MX) \ DX+=TX; \ if ((ASX-=NY)<0) { \ ASX+=NX; \ DY+=TY; \ } \ ASX&=1023; /* Mask to 10 bits range */ \ if (ADX++==NX || (DX&MX)) \ break; \ } #define post_lineymaj(MX) \ DY+=TY; \ if ((ASX-=NY)<0) { \ ASX+=NX; \ DX+=TX; \ } \ ASX&=1023; /* Mask to 10 bits range */ \ if (ADX++==NX || (DX&MX)) \ break; \ } if ((VDP[45]&0x01)==0) /* X-Axis is major direction */ switch (ScrMode) { case 5: pre_loop VDPpset5(DX, DY, CL, LO); post_linexmaj(256) break; case 6: pre_loop VDPpset6(DX, DY, CL, LO); post_linexmaj(512) break; case 7: pre_loop VDPpset7(DX, DY, CL, LO); post_linexmaj(512) break; case 8: pre_loop VDPpset8(DX, DY, CL, LO); post_linexmaj(256) break; } else /* Y-Axis is major direction */ switch (ScrMode) { case 5: pre_loop VDPpset5(DX, DY, CL, LO); post_lineymaj(256) break; case 6: pre_loop VDPpset6(DX, DY, CL, LO); post_lineymaj(512) break; case 7: pre_loop VDPpset7(DX, DY, CL, LO); post_lineymaj(512) break; case 8: pre_loop VDPpset8(DX, DY, CL, LO); post_lineymaj(256) break; } if ((VdpOpsCnt=cnt)>0) { /* Command execution done */ VDPStatus[2]&=0xFE; VdpEngine=0; VDP[38]=DY & 0xFF; VDP[39]=(DY>>8) & 0x03; } else { MMC.DX=DX; MMC.DY=DY; MMC.ASX=ASX; MMC.ADX=ADX; } } /** LmmvEngine() *********************************************/ /** VDP -> Vram **/ /*************************************************************/ void LmmvEngine(void) { register int DX=MMC.DX; register int DY=MMC.DY; register int TX=MMC.TX; register int TY=MMC.TY; register int NX=MMC.NX; register int NY=MMC.NY; register int ADX=MMC.ADX; register int ANX=MMC.ANX; register byte CL=MMC.CL; register byte LO=MMC.LO; register int cnt; register int delta; delta = GetVdpTimingValue(lmmv_timing); cnt = VdpOpsCnt; switch (ScrMode) { case 5: pre_loop VDPpset5(ADX, DY, CL, LO); post__x_y(256) break; case 6: pre_loop VDPpset6(ADX, DY, CL, LO); post__x_y(512) break; case 7: pre_loop VDPpset7(ADX, DY, CL, LO); post__x_y(512) break; case 8: pre_loop VDPpset8(ADX, DY, CL, LO); post__x_y(256) break; } if ((VdpOpsCnt=cnt)>0) { /* Command execution done */ VDPStatus[2]&=0xFE; VdpEngine=0; if (!NY) DY+=TY; VDP[38]=DY & 0xFF; VDP[39]=(DY>>8) & 0x03; VDP[42]=NY & 0xFF; VDP[43]=(NY>>8) & 0x03; } else { MMC.DY=DY; MMC.NY=NY; MMC.ANX=ANX; MMC.ADX=ADX; } } /** LmmmEngine() *********************************************/ /** Vram -> Vram **/ /*************************************************************/ void LmmmEngine(void) { register int SX=MMC.SX; register int SY=MMC.SY; register int DX=MMC.DX; register int DY=MMC.DY; register int TX=MMC.TX; register int TY=MMC.TY; register int NX=MMC.NX; register int NY=MMC.NY; register int ASX=MMC.ASX; register int ADX=MMC.ADX; register int ANX=MMC.ANX; register byte LO=MMC.LO; register int cnt; register int delta; delta = GetVdpTimingValue(lmmm_timing); cnt = VdpOpsCnt; switch (ScrMode) { case 5: pre_loop VDPpset5(ADX, DY, VDPpoint5(ASX, SY), LO); post_xxyy(256) break; case 6: pre_loop VDPpset6(ADX, DY, VDPpoint6(ASX, SY), LO); post_xxyy(512) break; case 7: pre_loop VDPpset7(ADX, DY, VDPpoint7(ASX, SY), LO); post_xxyy(512) break; case 8: pre_loop VDPpset8(ADX, DY, VDPpoint8(ASX, SY), LO); post_xxyy(256) break; } if ((VdpOpsCnt=cnt)>0) { /* Command execution done */ VDPStatus[2]&=0xFE; VdpEngine=0; if (!NY) { SY+=TY; DY+=TY; } else if (SY==-1) DY+=TY; VDP[42]=NY & 0xFF; VDP[43]=(NY>>8) & 0x03; VDP[34]=SY & 0xFF; VDP[35]=(SY>>8) & 0x03; VDP[38]=DY & 0xFF; VDP[39]=(DY>>8) & 0x03; } else { MMC.SY=SY; MMC.DY=DY; MMC.NY=NY; MMC.ANX=ANX; MMC.ASX=ASX; MMC.ADX=ADX; } } /** LmcmEngine() *********************************************/ /** Vram -> CPU **/ /*************************************************************/ void LmcmEngine() { if ((VDPStatus[2]&0x80)!=0x80) { VDPStatus[7]=VDP[44]=VDP_POINT(ScrMode-5, MMC.ASX, MMC.SY); VdpOpsCnt-=GetVdpTimingValue(lmmv_timing); VDPStatus[2]|=0x80; if (!--MMC.ANX || ((MMC.ASX+=MMC.TX)&MMC.MX)) { if (!(--MMC.NY & 1023) || (MMC.SY+=MMC.TY)==-1) { VDPStatus[2]&=0xFE; VdpEngine=0; if (!MMC.NY) MMC.DY+=MMC.TY; VDP[42]=MMC.NY & 0xFF; VDP[43]=(MMC.NY>>8) & 0x03; VDP[34]=MMC.SY & 0xFF; VDP[35]=(MMC.SY>>8) & 0x03; } else { MMC.ASX=MMC.SX; MMC.ANX=MMC.NX; } } } } /** LmmcEngine() *********************************************/ /** CPU -> Vram **/ /*************************************************************/ void LmmcEngine(void) { if ((VDPStatus[2]&0x80)!=0x80) { register byte SM=ScrMode-5; VDPStatus[7]=VDP[44]&=Mask[SM]; VDP_PSET(SM, MMC.ADX, MMC.DY, VDP[44], MMC.LO); VdpOpsCnt-=GetVdpTimingValue(lmmv_timing); VDPStatus[2]|=0x80; if (!--MMC.ANX || ((MMC.ADX+=MMC.TX)&MMC.MX)) { if (!(--MMC.NY&1023) || (MMC.DY+=MMC.TY)==-1) { VDPStatus[2]&=0xFE; VdpEngine=0; if (!MMC.NY) MMC.DY+=MMC.TY; VDP[42]=MMC.NY & 0xFF; VDP[43]=(MMC.NY>>8) & 0x03; VDP[38]=MMC.DY & 0xFF; VDP[39]=(MMC.DY>>8) & 0x03; } else { MMC.ADX=MMC.DX; MMC.ANX=MMC.NX; } } } } /** HmmvEngine() *********************************************/ /** VDP --> Vram **/ /*************************************************************/ void HmmvEngine(void) { register int DX=MMC.DX; register int DY=MMC.DY; register int TX=MMC.TX; register int TY=MMC.TY; register int NX=MMC.NX; register int NY=MMC.NY; register int ADX=MMC.ADX; register int ANX=MMC.ANX; register byte CL=MMC.CL; register int cnt; register int delta; delta = GetVdpTimingValue(hmmv_timing); cnt = VdpOpsCnt; switch (ScrMode) { case 5: pre_loop *VDP_VRMP5(ADX, DY) = CL; post__x_y(256) break; case 6: pre_loop *VDP_VRMP6(ADX, DY) = CL; post__x_y(512) break; case 7: pre_loop *VDP_VRMP7(ADX, DY) = CL; post__x_y(512) break; case 8: pre_loop *VDP_VRMP8(ADX, DY) = CL; post__x_y(256) break; } if ((VdpOpsCnt=cnt)>0) { /* Command execution done */ VDPStatus[2]&=0xFE; VdpEngine=0; if (!NY) DY+=TY; VDP[42]=NY & 0xFF; VDP[43]=(NY>>8) & 0x03; VDP[38]=DY & 0xFF; VDP[39]=(DY>>8) & 0x03; } else { MMC.DY=DY; MMC.NY=NY; MMC.ANX=ANX; MMC.ADX=ADX; } } /** HmmmEngine() *********************************************/ /** Vram -> Vram **/ /*************************************************************/ void HmmmEngine(void) { register int SX=MMC.SX; register int SY=MMC.SY; register int DX=MMC.DX; register int DY=MMC.DY; register int TX=MMC.TX; register int TY=MMC.TY; register int NX=MMC.NX; register int NY=MMC.NY; register int ASX=MMC.ASX; register int ADX=MMC.ADX; register int ANX=MMC.ANX; register int cnt; register int delta; delta = GetVdpTimingValue(hmmm_timing); cnt = VdpOpsCnt; switch (ScrMode) { case 5: pre_loop *VDP_VRMP5(ADX, DY) = *VDP_VRMP5(ASX, SY); post_xxyy(256) break; case 6: pre_loop *VDP_VRMP6(ADX, DY) = *VDP_VRMP6(ASX, SY); post_xxyy(512) break; case 7: pre_loop *VDP_VRMP7(ADX, DY) = *VDP_VRMP7(ASX, SY); post_xxyy(512) break; case 8: pre_loop *VDP_VRMP8(ADX, DY) = *VDP_VRMP8(ASX, SY); post_xxyy(256) break; } if ((VdpOpsCnt=cnt)>0) { /* Command execution done */ VDPStatus[2]&=0xFE; VdpEngine=0; if (!NY) { SY+=TY; DY+=TY; } else if (SY==-1) DY+=TY; VDP[42]=NY & 0xFF; VDP[43]=(NY>>8) & 0x03; VDP[34]=SY & 0xFF; VDP[35]=(SY>>8) & 0x03; VDP[38]=DY & 0xFF; VDP[39]=(DY>>8) & 0x03; } else { MMC.SY=SY; MMC.DY=DY; MMC.NY=NY; MMC.ANX=ANX; MMC.ASX=ASX; MMC.ADX=ADX; } } /** YmmmEngine() *********************************************/ /** Vram -> Vram **/ /*************************************************************/ void YmmmEngine(void) { register int SY=MMC.SY; register int DX=MMC.DX; register int DY=MMC.DY; register int TX=MMC.TX; register int TY=MMC.TY; register int NY=MMC.NY; register int ADX=MMC.ADX; register int cnt; register int delta; delta = GetVdpTimingValue(ymmm_timing); cnt = VdpOpsCnt; switch (ScrMode) { case 5: pre_loop *VDP_VRMP5(ADX, DY) = *VDP_VRMP5(ADX, SY); post__xyy(256) break; case 6: pre_loop *VDP_VRMP6(ADX, DY) = *VDP_VRMP6(ADX, SY); post__xyy(512) break; case 7: pre_loop *VDP_VRMP7(ADX, DY) = *VDP_VRMP7(ADX, SY); post__xyy(512) break; case 8: pre_loop *VDP_VRMP8(ADX, DY) = *VDP_VRMP8(ADX, SY); post__xyy(256) break; } if ((VdpOpsCnt=cnt)>0) { /* Command execution done */ VDPStatus[2]&=0xFE; VdpEngine=0; if (!NY) { SY+=TY; DY+=TY; } else if (SY==-1) DY+=TY; VDP[42]=NY & 0xFF; VDP[43]=(NY>>8) & 0x03; VDP[34]=SY & 0xFF; VDP[35]=(SY>>8) & 0x03; VDP[38]=DY & 0xFF; VDP[39]=(DY>>8) & 0x03; } else { MMC.SY=SY; MMC.DY=DY; MMC.NY=NY; MMC.ADX=ADX; } } /** HmmcEngine() *********************************************/ /** CPU -> Vram **/ /*************************************************************/ void HmmcEngine(void) { if ((VDPStatus[2]&0x80)!=0x80) { *VDP_VRMP(ScrMode-5, MMC.ADX, MMC.DY)=VDP[44]; VdpOpsCnt-=GetVdpTimingValue(hmmv_timing); VDPStatus[2]|=0x80; if (!--MMC.ANX || ((MMC.ADX+=MMC.TX)&MMC.MX)) { if (!(--MMC.NY&1023) || (MMC.DY+=MMC.TY)==-1) { VDPStatus[2]&=0xFE; VdpEngine=0; if (!MMC.NY) MMC.DY+=MMC.TY; VDP[42]=MMC.NY & 0xFF; VDP[43]=(MMC.NY>>8) & 0x03; VDP[38]=MMC.DY & 0xFF; VDP[39]=(MMC.DY>>8) & 0x03; } else { MMC.ADX=MMC.DX; MMC.ANX=MMC.NX; } } } } /** VDPWrite() ***********************************************/ /** Use this function to transfer pixel(s) from CPU to VDP. **/ /*************************************************************/ void VDPWrite(byte V) { VDPStatus[2]&=0x7F; VDPStatus[7]=VDP[44]=V; if(VdpEngine&&(VdpOpsCnt>0)) VdpEngine(); } /** VDPRead() ************************************************/ /** Use this function to transfer pixel(s) from VDP to CPU. **/ /*************************************************************/ byte VDPRead(void) { VDPStatus[2]&=0x7F; if(VdpEngine&&(VdpOpsCnt>0)) VdpEngine(); return(VDP[44]); } /** ReportVdpCommand() ***************************************/ /** Report VDP Command to be executed **/ /*************************************************************/ void ReportVdpCommand(register byte Op) { static char *Ops[16] = { "SET ","AND ","OR ","XOR ","NOT ","NOP ","NOP ","NOP ", "TSET","TAND","TOR ","TXOR","TNOT","NOP ","NOP ","NOP " }; static char *Commands[16] = { " ABRT"," ????"," ????"," ????","POINT"," PSET"," SRCH"," LINE", " LMMV"," LMMM"," LMCM"," LMMC"," HMMV"," HMMM"," YMMM"," HMMC" }; register byte CL, CM, LO; register int SX,SY, DX,DY, NX,NY; /* Fetch arguments */ CL = VDP[44]; SX = (VDP[32]+((int)VDP[33]<<8)) & 511; SY = (VDP[34]+((int)VDP[35]<<8)) & 1023; DX = (VDP[36]+((int)VDP[37]<<8)) & 511; DY = (VDP[38]+((int)VDP[39]<<8)) & 1023; NX = (VDP[40]+((int)VDP[41]<<8)) & 1023; NY = (VDP[42]+((int)VDP[43]<<8)) & 1023; CM = Op>>4; LO = Op&0x0F; printf("V9938: Opcode %02Xh %s-%s (%d,%d)->(%d,%d),%d [%d,%d]%s\n", Op, Commands[CM], Ops[LO], SX,SY, DX,DY, CL, VDP[45]&0x04? -NX:NX, VDP[45]&0x08? -NY:NY, VDP[45]&0x70? " on ExtVRAM":"" ); } /** VDPDraw() ************************************************/ /** Perform a given V9938 operation Op. **/ /*************************************************************/ byte VDPDraw(byte Op) { register int SM; /* V9938 ops only work in SCREENs 5-8 */ if (ScrMode<5) return(0); SM = ScrMode-5; /* Screen mode index 0..3 */ MMC.CM = Op>>4; if ((MMC.CM & 0x0C) != 0x0C && MMC.CM != 0) /* Dot operation: use only relevant bits of color */ VDPStatus[7]=(VDP[44]&=Mask[SM]); if(Verbose&0x02) ReportVdpCommand(Op); switch(Op>>4) { case CM_ABRT: VDPStatus[2]&=0xFE; VdpEngine=0; return 1; case CM_POINT: VDPStatus[2]&=0xFE; VdpEngine=0; VDPStatus[7]=VDP[44]= VDP_POINT(SM, VDP[32]+((int)VDP[33]<<8), VDP[34]+((int)VDP[35]<<8)); return 1; case CM_PSET: VDPStatus[2]&=0xFE; VdpEngine=0; VDP_PSET(SM, VDP[36]+((int)VDP[37]<<8), VDP[38]+((int)VDP[39]<<8), VDP[44], Op&0x0F); return 1; case CM_SRCH: VdpEngine=SrchEngine; break; case CM_LINE: VdpEngine=LineEngine; break; case CM_LMMV: VdpEngine=LmmvEngine; break; case CM_LMMM: VdpEngine=LmmmEngine; break; case CM_LMCM: VdpEngine=LmcmEngine; break; case CM_LMMC: VdpEngine=LmmcEngine; break; case CM_HMMV: VdpEngine=HmmvEngine; break; case CM_HMMM: VdpEngine=HmmmEngine; break; case CM_YMMM: VdpEngine=YmmmEngine; break; case CM_HMMC: VdpEngine=HmmcEngine; break; default: if(Verbose&0x02) printf("V9938: Unrecognized opcode %02Xh\n",Op); return(0); } /* Fetch unconditional arguments */ MMC.SX = (VDP[32]+((int)VDP[33]<<8)) & 511; MMC.SY = (VDP[34]+((int)VDP[35]<<8)) & 1023; MMC.DX = (VDP[36]+((int)VDP[37]<<8)) & 511; MMC.DY = (VDP[38]+((int)VDP[39]<<8)) & 1023; MMC.NY = (VDP[42]+((int)VDP[43]<<8)) & 1023; MMC.TY = VDP[45]&0x08? -1:1; MMC.MX = PPL[SM]; MMC.CL = VDP[44]; MMC.LO = Op&0x0F; /* Argument depends on byte or dot operation */ if ((MMC.CM & 0x0C) == 0x0C) { MMC.TX = VDP[45]&0x04? -PPB[SM]:PPB[SM]; MMC.NX = ((VDP[40]+((int)VDP[41]<<8)) & 1023)/PPB[SM]; } else { MMC.TX = VDP[45]&0x04? -1:1; MMC.NX = (VDP[40]+((int)VDP[41]<<8)) & 1023; } /* X loop variables are treated specially for LINE command */ if (MMC.CM == CM_LINE) { MMC.ASX=((MMC.NX-1)>>1); MMC.ADX=0; } else { MMC.ASX = MMC.SX; MMC.ADX = MMC.DX; } /* NX loop variable is treated specially for SRCH command */ if (MMC.CM == CM_SRCH) MMC.ANX=(VDP[45]&0x02)!=0; /* Do we look for "==" or "!="? */ else MMC.ANX = MMC.NX; /* Command execution started */ VDPStatus[2]|=0x01; /* Start execution if we still have time slices */ if(VdpEngine&&(VdpOpsCnt>0)) VdpEngine(); /* Operation successfull initiated */ return(1); } /** LoopVDP() ************************************************/ /** Run X steps of active VDP command **/ /*************************************************************/ void LoopVDP(void) { if(VdpOpsCnt<=0) { VdpOpsCnt+=12500; if(VdpEngine&&(VdpOpsCnt>0)) VdpEngine(); } else { VdpOpsCnt=12500; if(VdpEngine) VdpEngine(); } }