kopia lustrzana https://github.com/Jean-MarcHarvengt/MCUME
1059 wiersze
30 KiB
C
1059 wiersze
30 KiB
C
/** 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 <string.h>
|
|
|
|
#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<<SH), OP);
|
|
}
|
|
|
|
/** VDPpset6() ***********************************************/
|
|
/** Set a pixel on screen 6 **/
|
|
/*************************************************************/
|
|
INLINE void VDPpset6(int DX, int DY, byte CL, byte OP)
|
|
{
|
|
register byte SH = ((~DX)&3)<<1;
|
|
|
|
VDPpsetlowlevel(VDP_VRMP6(DX, DY),
|
|
CL << SH, ~(3<<SH), OP);
|
|
}
|
|
|
|
/** VDPpset7() ***********************************************/
|
|
/** Set a pixel on screen 7 **/
|
|
/*************************************************************/
|
|
INLINE void VDPpset7(int DX, int DY, byte CL, byte OP)
|
|
{
|
|
register byte SH = ((~DX)&1)<<2;
|
|
|
|
VDPpsetlowlevel(VDP_VRMP7(DX, DY),
|
|
CL << SH, ~(15<<SH), OP);
|
|
}
|
|
|
|
/** VDPpset8() ***********************************************/
|
|
/** Set a pixel on screen 8 **/
|
|
/*************************************************************/
|
|
INLINE void VDPpset8(int DX, int DY, byte CL, byte OP)
|
|
{
|
|
VDPpsetlowlevel(VDP_VRMP8(DX, DY),
|
|
CL, 0, OP);
|
|
}
|
|
|
|
/** VDPpset() ************************************************/
|
|
/** Set a pixel on a screen **/
|
|
/*************************************************************/
|
|
INLINE void VDPpset(byte SM, int DX, int DY, byte CL, byte OP)
|
|
{
|
|
switch (SM) {
|
|
case 0: VDPpset5(DX, DY, CL, OP); break;
|
|
case 1: VDPpset6(DX, DY, CL, OP); break;
|
|
case 2: VDPpset7(DX, DY, CL, OP); break;
|
|
case 3: VDPpset8(DX, DY, CL, OP); break;
|
|
}
|
|
}
|
|
|
|
/** GetVdpTimingValue() **************************************/
|
|
/** Get timing value for a certain VDP command **/
|
|
/*************************************************************/
|
|
static int GetVdpTimingValue(register int *timing_values)
|
|
{
|
|
return(timing_values[((VDP[1]>>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();
|
|
}
|
|
}
|
|
|