2025-01-02 17:55:16 +08:00
using System ;
using System.Collections.Generic ;
using System.Linq ;
using System.Text ;
using System.Threading.Tasks ;
using System.Diagnostics ;
using Essgee.EventArguments ;
using Essgee.Utilities ;
using static Essgee . Emulation . Utilities ;
2025-01-03 19:53:38 +08:00
using System.Runtime.InteropServices ;
2025-01-02 17:55:16 +08:00
namespace Essgee.Emulation.Video
{
/* Sega 315-5124 (Mark III, SMS) and 315-5246 (SMS 2); differences see 'VDPDIFF' comments */
public class SegaSMSVDP : TMS99xxA
{
/* VDPDIFF: switch for Mk3/SMS1 vs SMS2/GG behavior; configurable via SetRevision, maybe still split into separate classes instead? */
protected VDPTypes vdpType = VDPTypes . Mk3SMS1 ;
public const int NumActiveScanlinesLow = 192 ;
public const int NumActiveScanlinesMed = 224 ;
public const int NumActiveScanlinesHigh = 240 ;
protected const int NumSpritesMode4 = 64 ;
protected const int NumSpritesPerLineMode4 = 8 ;
public const int PortVCounter = 0x40 ; // 0x7E canonically, but mirrored across bus
public const int PortHCounter = 0x41 ; // 0x7F canonically, but mirrored across bus
[StateRequired]
protected byte [ ] cram ;
[StateRequired]
protected int vCounter , hCounter ;
protected int nametableHeight , vCounterTableIndex ;
[StateRequired]
protected int lineInterruptCounter ;
protected int screenHeight ;
bool isLineInterruptEnabled = > IsBitSet ( registers [ 0x00 ] , 4 ) ;
[StateRequired]
bool isLineInterruptPending ;
bool isColumn0MaskEnabled = > IsBitSet ( registers [ 0x00 ] , 5 ) ;
bool isVScrollPartiallyDisabled = > IsBitSet ( registers [ 0x00 ] , 7 ) ; /* Columns 24-31, i.e. pixels 192-255 */
bool isHScrollPartiallyDisabled = > IsBitSet ( registers [ 0x00 ] , 6 ) ; /* Rows 0-1, i.e. pixels 0-15 */
bool isBitM4Set = > IsBitSet ( registers [ 0x00 ] , 2 ) ;
protected override bool isModeGraphics1 = > ! ( isBitM1Set | | isBitM2Set | | isBitM3Set | | isBitM4Set ) ;
protected override bool isModeText = > ( isBitM1Set & & ! ( isBitM2Set | | isBitM3Set | | isBitM4Set ) ) ;
protected override bool isModeGraphics2 = > ( isBitM2Set & & ! ( isBitM1Set | | isBitM3Set | | isBitM4Set ) ) ;
protected override bool isModeMulticolor = > ( isBitM3Set & & ! ( isBitM1Set | | isBitM2Set | | isBitM4Set ) ) ;
protected bool isSMS240LineMode = > ( ! isBitM1Set & & isBitM2Set & & isBitM3Set & & isBitM4Set ) ;
protected bool isSMS224LineMode = > ( isBitM1Set & & isBitM2Set & & ! isBitM3Set & & isBitM4Set ) ;
bool isSpriteShiftLeft8 = > IsBitSet ( registers [ 0x00 ] , 3 ) ;
protected override ushort nametableBaseAddress
{
get
{
if ( isBitM4Set )
{
if ( isSMS224LineMode | | isSMS240LineMode )
return ( ushort ) ( ( ( registers [ 0x02 ] & 0x0C ) < < 10 ) | 0x700 ) ;
else
return ( ushort ) ( ( registers [ 0x02 ] & 0x0E ) < < 10 ) ;
}
else
return ( ushort ) ( ( registers [ 0x02 ] & 0x0F ) < < 10 ) ;
}
}
protected override ushort spriteAttribTableBaseAddress = > ( ushort ) ( ( registers [ 0x05 ] & ( isBitM4Set ? 0x7E : 0x7F ) ) < < 7 ) ;
protected override ushort spritePatternGenBaseAddress = > ( ushort ) ( ( registers [ 0x06 ] & ( isBitM4Set ? 0x04 : 0x07 ) ) < < 11 ) ;
/* http://www.smspower.org/Development/Palette */
// TODO: verify these, SMSPower has some mistakes (RGB approx correct, palette value wrong)
// (not that we'll really use this, aside from for F-16 Fighting Falcon, as SG1000 games should always be loaded into the SG1000 core...)
readonly byte [ ] legacyColorMap = new byte [ ]
{
0x00 , /* Transparent */
0x00 , /* Black */
0x08 , /* Medium green */
0x0C , /* Light green */
0x10 , /* Dark blue */
0x30 , /* Light blue */
0x01 , /* Dark red */
0x3C , /* Cyan */
0x02 , /* Medium red */
0x03 , /* Light red */
0x05 , /* Dark yellow */
0x0F , /* Light yellow */
0x04 , /* Dark green */
0x33 , /* Magenta */
0x15 , /* Gray */
0x3F /* White */
} ;
readonly byte [ ] [ ] vCounterTables = new byte [ ] [ ]
{
/* NTSC, 192 lines */
new byte [ ]
{
/* Top blanking */
0xD8 , 0xD9 , 0xDA , 0xDB , 0xDC , 0xDD , 0xDE , 0xDF , 0xE0 , 0xE1 , 0xE2 , 0xE3 , 0xE4 ,
/* Top border */
0xE5 , 0xE6 , 0xE7 , 0xE8 , 0xE9 , 0xEA , 0xEB , 0xEC , 0xED , 0xEE , 0xEF , 0xF0 , 0xF1 , 0xF2 , 0xF3 , 0xF4 ,
0xF5 , 0xF6 , 0xF7 , 0xF8 , 0xF9 , 0xFA , 0xFB , 0xFC , 0xFD , 0xFE , 0xFF ,
/* Active display */
0x00 , 0x01 , 0x02 , 0x03 , 0x04 , 0x05 , 0x06 , 0x07 , 0x08 , 0x09 , 0x0A , 0x0B , 0x0C , 0x0D , 0x0E , 0x0F ,
0x10 , 0x11 , 0x12 , 0x13 , 0x14 , 0x15 , 0x16 , 0x17 , 0x18 , 0x19 , 0x1A , 0x1B , 0x1C , 0x1D , 0x1E , 0x1F ,
0x20 , 0x21 , 0x22 , 0x23 , 0x24 , 0x25 , 0x26 , 0x27 , 0x28 , 0x29 , 0x2A , 0x2B , 0x2C , 0x2D , 0x2E , 0x2F ,
0x30 , 0x31 , 0x32 , 0x33 , 0x34 , 0x35 , 0x36 , 0x37 , 0x38 , 0x39 , 0x3A , 0x3B , 0x3C , 0x3D , 0x3E , 0x3F ,
0x40 , 0x41 , 0x42 , 0x43 , 0x44 , 0x45 , 0x46 , 0x47 , 0x48 , 0x49 , 0x4A , 0x4B , 0x4C , 0x4D , 0x4E , 0x4F ,
0x50 , 0x51 , 0x52 , 0x53 , 0x54 , 0x55 , 0x56 , 0x57 , 0x58 , 0x59 , 0x5A , 0x5B , 0x5C , 0x5D , 0x5E , 0x5F ,
0x60 , 0x61 , 0x62 , 0x63 , 0x64 , 0x65 , 0x66 , 0x67 , 0x68 , 0x69 , 0x6A , 0x6B , 0x6C , 0x6D , 0x6E , 0x6F ,
0x70 , 0x71 , 0x72 , 0x73 , 0x74 , 0x75 , 0x76 , 0x77 , 0x78 , 0x79 , 0x7A , 0x7B , 0x7C , 0x7D , 0x7E , 0x7F ,
0x80 , 0x81 , 0x82 , 0x83 , 0x84 , 0x85 , 0x86 , 0x87 , 0x88 , 0x89 , 0x8A , 0x8B , 0x8C , 0x8D , 0x8E , 0x8F ,
0x90 , 0x91 , 0x92 , 0x93 , 0x94 , 0x95 , 0x96 , 0x97 , 0x98 , 0x99 , 0x9A , 0x9B , 0x9C , 0x9D , 0x9E , 0x9F ,
0xA0 , 0xA1 , 0xA2 , 0xA3 , 0xA4 , 0xA5 , 0xA6 , 0xA7 , 0xA8 , 0xA9 , 0xAA , 0xAB , 0xAC , 0xAD , 0xAE , 0xAF ,
0xB0 , 0xB1 , 0xB2 , 0xB3 , 0xB4 , 0xB5 , 0xB6 , 0xB7 , 0xB8 , 0xB9 , 0xBA , 0xBB , 0xBC , 0xBD , 0xBE , 0xBF ,
/* Bottom border */
0xC0 , 0xC1 , 0xC2 , 0xC3 , 0xC4 , 0xC5 , 0xC6 , 0xC7 , 0xC8 , 0xC9 , 0xCA , 0xCB , 0xCC , 0xCD , 0xCE , 0xCF ,
0xD0 , 0xD1 , 0xD2 , 0xD3 , 0xD4 , 0xD5 , 0xD6 , 0xD7 ,
/* Bottom blanking */
0xD8 , 0xD9 , 0xDA ,
/* Vertical blanking */
0xD5 , 0xD6 , 0xD7
} ,
/* NTSC, 224 lines */
new byte [ ]
{
/* Top blanking */
0xE8 , 0xE9 , 0xEA , 0xEB , 0xEC , 0xED , 0xEE , 0xEF , 0xF0 , 0xF1 , 0xF2 , 0xF3 , 0xF4 ,
/* Top border */
0xF5 , 0xF6 , 0xF7 , 0xF8 , 0xF9 , 0xFA , 0xFB , 0xFC , 0xFD , 0xFE , 0xFF ,
/* Active display */
0x00 , 0x01 , 0x02 , 0x03 , 0x04 , 0x05 , 0x06 , 0x07 , 0x08 , 0x09 , 0x0A , 0x0B , 0x0C , 0x0D , 0x0E , 0x0F ,
0x10 , 0x11 , 0x12 , 0x13 , 0x14 , 0x15 , 0x16 , 0x17 , 0x18 , 0x19 , 0x1A , 0x1B , 0x1C , 0x1D , 0x1E , 0x1F ,
0x20 , 0x21 , 0x22 , 0x23 , 0x24 , 0x25 , 0x26 , 0x27 , 0x28 , 0x29 , 0x2A , 0x2B , 0x2C , 0x2D , 0x2E , 0x2F ,
0x30 , 0x31 , 0x32 , 0x33 , 0x34 , 0x35 , 0x36 , 0x37 , 0x38 , 0x39 , 0x3A , 0x3B , 0x3C , 0x3D , 0x3E , 0x3F ,
0x40 , 0x41 , 0x42 , 0x43 , 0x44 , 0x45 , 0x46 , 0x47 , 0x48 , 0x49 , 0x4A , 0x4B , 0x4C , 0x4D , 0x4E , 0x4F ,
0x50 , 0x51 , 0x52 , 0x53 , 0x54 , 0x55 , 0x56 , 0x57 , 0x58 , 0x59 , 0x5A , 0x5B , 0x5C , 0x5D , 0x5E , 0x5F ,
0x60 , 0x61 , 0x62 , 0x63 , 0x64 , 0x65 , 0x66 , 0x67 , 0x68 , 0x69 , 0x6A , 0x6B , 0x6C , 0x6D , 0x6E , 0x6F ,
0x70 , 0x71 , 0x72 , 0x73 , 0x74 , 0x75 , 0x76 , 0x77 , 0x78 , 0x79 , 0x7A , 0x7B , 0x7C , 0x7D , 0x7E , 0x7F ,
0x80 , 0x81 , 0x82 , 0x83 , 0x84 , 0x85 , 0x86 , 0x87 , 0x88 , 0x89 , 0x8A , 0x8B , 0x8C , 0x8D , 0x8E , 0x8F ,
0x90 , 0x91 , 0x92 , 0x93 , 0x94 , 0x95 , 0x96 , 0x97 , 0x98 , 0x99 , 0x9A , 0x9B , 0x9C , 0x9D , 0x9E , 0x9F ,
0xA0 , 0xA1 , 0xA2 , 0xA3 , 0xA4 , 0xA5 , 0xA6 , 0xA7 , 0xA8 , 0xA9 , 0xAA , 0xAB , 0xAC , 0xAD , 0xAE , 0xAF ,
0xB0 , 0xB1 , 0xB2 , 0xB3 , 0xB4 , 0xB5 , 0xB6 , 0xB7 , 0xB8 , 0xB9 , 0xBA , 0xBB , 0xBC , 0xBD , 0xBE , 0xBF ,
0xC0 , 0xC1 , 0xC2 , 0xC3 , 0xC4 , 0xC5 , 0xC6 , 0xC7 , 0xC8 , 0xC9 , 0xCA , 0xCB , 0xCC , 0xCD , 0xCE , 0xCF ,
0xD0 , 0xD1 , 0xD2 , 0xD3 , 0xD4 , 0xD5 , 0xD6 , 0xD7 , 0xD8 , 0xD9 , 0xDA , 0xDB , 0xDC , 0xDD , 0xDE , 0xDF ,
/* Bottom border */
0xE0 , 0xE1 , 0xE2 , 0xE3 , 0xE4 , 0xE5 , 0xE6 , 0xE7 ,
/* Bottom blanking */
0xE8 , 0xE9 , 0xEA ,
/* Vertical blanking */
0xE5 , 0xE6 , 0xE7 ,
} ,
/* NTSC, 240 lines (invalid) */
new byte [ ]
{
0x00 , 0x01 , 0x02 , 0x03 , 0x04 , 0x05 , 0x06 , 0x07 , 0x08 , 0x09 , 0x0A , 0x0B , 0x0C , 0x0D , 0x0E , 0x0F ,
0x10 , 0x11 , 0x12 , 0x13 , 0x14 , 0x15 , 0x16 , 0x17 , 0x18 , 0x19 , 0x1A , 0x1B , 0x1C , 0x1D , 0x1E , 0x1F ,
0x20 , 0x21 , 0x22 , 0x23 , 0x24 , 0x25 , 0x26 , 0x27 , 0x28 , 0x29 , 0x2A , 0x2B , 0x2C , 0x2D , 0x2E , 0x2F ,
0x30 , 0x31 , 0x32 , 0x33 , 0x34 , 0x35 , 0x36 , 0x37 , 0x38 , 0x39 , 0x3A , 0x3B , 0x3C , 0x3D , 0x3E , 0x3F ,
0x40 , 0x41 , 0x42 , 0x43 , 0x44 , 0x45 , 0x46 , 0x47 , 0x48 , 0x49 , 0x4A , 0x4B , 0x4C , 0x4D , 0x4E , 0x4F ,
0x50 , 0x51 , 0x52 , 0x53 , 0x54 , 0x55 , 0x56 , 0x57 , 0x58 , 0x59 , 0x5A , 0x5B , 0x5C , 0x5D , 0x5E , 0x5F ,
0x60 , 0x61 , 0x62 , 0x63 , 0x64 , 0x65 , 0x66 , 0x67 , 0x68 , 0x69 , 0x6A , 0x6B , 0x6C , 0x6D , 0x6E , 0x6F ,
0x70 , 0x71 , 0x72 , 0x73 , 0x74 , 0x75 , 0x76 , 0x77 , 0x78 , 0x79 , 0x7A , 0x7B , 0x7C , 0x7D , 0x7E , 0x7F ,
0x80 , 0x81 , 0x82 , 0x83 , 0x84 , 0x85 , 0x86 , 0x87 , 0x88 , 0x89 , 0x8A , 0x8B , 0x8C , 0x8D , 0x8E , 0x8F ,
0x90 , 0x91 , 0x92 , 0x93 , 0x94 , 0x95 , 0x96 , 0x97 , 0x98 , 0x99 , 0x9A , 0x9B , 0x9C , 0x9D , 0x9E , 0x9F ,
0xA0 , 0xA1 , 0xA2 , 0xA3 , 0xA4 , 0xA5 , 0xA6 , 0xA7 , 0xA8 , 0xA9 , 0xAA , 0xAB , 0xAC , 0xAD , 0xAE , 0xAF ,
0xB0 , 0xB1 , 0xB2 , 0xB3 , 0xB4 , 0xB5 , 0xB6 , 0xB7 , 0xB8 , 0xB9 , 0xBA , 0xBB , 0xBC , 0xBD , 0xBE , 0xBF ,
0xC0 , 0xC1 , 0xC2 , 0xC3 , 0xC4 , 0xC5 , 0xC6 , 0xC7 , 0xC8 , 0xC9 , 0xCA , 0xCB , 0xCC , 0xCD , 0xCE , 0xCF ,
0xD0 , 0xD1 , 0xD2 , 0xD3 , 0xD4 , 0xD5 , 0xD6 , 0xD7 , 0xD8 , 0xD9 , 0xDA , 0xDB , 0xDC , 0xDD , 0xDE , 0xDF ,
0xE0 , 0xE1 , 0xE2 , 0xE3 , 0xE4 , 0xE5 , 0xE6 , 0xE7 , 0xE8 , 0xE9 , 0xEA , 0xEB , 0xEC , 0xED , 0xEE , 0xEF ,
0xF0 , 0xF1 , 0xF2 , 0xF3 , 0xF4 , 0xF5 , 0xF6 , 0xF7 , 0xF8 , 0xF9 , 0xFA , 0xFB , 0xFC , 0xFD , 0xFE , 0xFF ,
0x00 , 0x01 , 0x02 , 0x03 , 0x04 , 0x05 , 0x06
} ,
/* PAL, 192 lines */
new byte [ ]
{
/* Top blanking */
0xBD , 0xBE , 0xBF , 0xC0 , 0xC1 , 0xC2 , 0xC3 , 0xC4 , 0xC5 , 0xC6 , 0xC7 , 0xC8 , 0xC9 ,
/* Top border */
0xCA , 0xCB , 0xCC , 0xCD , 0xCE , 0xCF , 0xD0 , 0xD1 , 0xD2 , 0xD3 , 0xD4 , 0xD5 , 0xD6 , 0xD7 , 0xD8 , 0xD9 ,
0xDA , 0xDB , 0xDC , 0xDD , 0xDE , 0xDF , 0xE0 , 0xE1 , 0xE2 , 0xE3 , 0xE4 , 0xE5 , 0xE6 , 0xE7 , 0xE8 , 0xE9 ,
0xEA , 0xEB , 0xEC , 0xED , 0xEE , 0xEF , 0xF0 , 0xF1 , 0xF2 , 0xF3 , 0xF4 , 0xF5 , 0xF6 , 0xF7 , 0xF8 , 0xF9 ,
0xFA , 0xFB , 0xFC , 0xFD , 0xFE , 0xFF ,
/* Active display */
0x00 , 0x01 , 0x02 , 0x03 , 0x04 , 0x05 , 0x06 , 0x07 , 0x08 , 0x09 , 0x0A , 0x0B , 0x0C , 0x0D , 0x0E , 0x0F ,
0x10 , 0x11 , 0x12 , 0x13 , 0x14 , 0x15 , 0x16 , 0x17 , 0x18 , 0x19 , 0x1A , 0x1B , 0x1C , 0x1D , 0x1E , 0x1F ,
0x20 , 0x21 , 0x22 , 0x23 , 0x24 , 0x25 , 0x26 , 0x27 , 0x28 , 0x29 , 0x2A , 0x2B , 0x2C , 0x2D , 0x2E , 0x2F ,
0x30 , 0x31 , 0x32 , 0x33 , 0x34 , 0x35 , 0x36 , 0x37 , 0x38 , 0x39 , 0x3A , 0x3B , 0x3C , 0x3D , 0x3E , 0x3F ,
0x40 , 0x41 , 0x42 , 0x43 , 0x44 , 0x45 , 0x46 , 0x47 , 0x48 , 0x49 , 0x4A , 0x4B , 0x4C , 0x4D , 0x4E , 0x4F ,
0x50 , 0x51 , 0x52 , 0x53 , 0x54 , 0x55 , 0x56 , 0x57 , 0x58 , 0x59 , 0x5A , 0x5B , 0x5C , 0x5D , 0x5E , 0x5F ,
0x60 , 0x61 , 0x62 , 0x63 , 0x64 , 0x65 , 0x66 , 0x67 , 0x68 , 0x69 , 0x6A , 0x6B , 0x6C , 0x6D , 0x6E , 0x6F ,
0x70 , 0x71 , 0x72 , 0x73 , 0x74 , 0x75 , 0x76 , 0x77 , 0x78 , 0x79 , 0x7A , 0x7B , 0x7C , 0x7D , 0x7E , 0x7F ,
0x80 , 0x81 , 0x82 , 0x83 , 0x84 , 0x85 , 0x86 , 0x87 , 0x88 , 0x89 , 0x8A , 0x8B , 0x8C , 0x8D , 0x8E , 0x8F ,
0x90 , 0x91 , 0x92 , 0x93 , 0x94 , 0x95 , 0x96 , 0x97 , 0x98 , 0x99 , 0x9A , 0x9B , 0x9C , 0x9D , 0x9E , 0x9F ,
0xA0 , 0xA1 , 0xA2 , 0xA3 , 0xA4 , 0xA5 , 0xA6 , 0xA7 , 0xA8 , 0xA9 , 0xAA , 0xAB , 0xAC , 0xAD , 0xAE , 0xAF ,
0xB0 , 0xB1 , 0xB2 , 0xB3 , 0xB4 , 0xB5 , 0xB6 , 0xB7 , 0xB8 , 0xB9 , 0xBA , 0xBB , 0xBC , 0xBD , 0xBE , 0xBF ,
/* Bottom border */
0xC0 , 0xC1 , 0xC2 , 0xC3 , 0xC4 , 0xC5 , 0xC6 , 0xC7 , 0xC8 , 0xC9 , 0xCA , 0xCB , 0xCC , 0xCD , 0xCE , 0xCF ,
0xD0 , 0xD1 , 0xD2 , 0xD3 , 0xD4 , 0xD5 , 0xD6 , 0xD7 , 0xD8 , 0xD9 , 0xDA , 0xDB , 0xDC , 0xDD , 0xDE , 0xDF ,
0xE0 , 0xE1 , 0xE2 , 0xE3 , 0xE4 , 0xE5 , 0xE6 , 0xE7 , 0xE8 , 0xE9 , 0xEA , 0xEB , 0xEC , 0xED , 0xEE , 0xEF ,
/* Bottom blanking */
0xF0 , 0xF1 , 0xF2 ,
/* Vertical blanking */
0xBA , 0xBB , 0xBC
} ,
/* PAL, 224 lines */
new byte [ ]
{
/* Top blanking */
0xCD , 0xCE , 0xCF , 0xD0 , 0xD1 , 0xD2 , 0xD3 , 0xD4 , 0xD5 , 0xD6 , 0xD7 , 0xD8 , 0xD9 ,
/* Top border */
0xDA , 0xDB , 0xDC , 0xDD , 0xDE , 0xDF , 0xE0 , 0xE1 , 0xE2 , 0xE3 , 0xE4 , 0xE5 , 0xE6 , 0xE7 , 0xE8 , 0xE9 ,
0xEA , 0xEB , 0xEC , 0xED , 0xEE , 0xEF , 0xF0 , 0xF1 , 0xF2 , 0xF3 , 0xF4 , 0xF5 , 0xF6 , 0xF7 , 0xF8 , 0xF9 ,
0xFA , 0xFB , 0xFC , 0xFD , 0xFE , 0xFF ,
/* Active display */
0x00 , 0x01 , 0x02 , 0x03 , 0x04 , 0x05 , 0x06 , 0x07 , 0x08 , 0x09 , 0x0A , 0x0B , 0x0C , 0x0D , 0x0E , 0x0F ,
0x10 , 0x11 , 0x12 , 0x13 , 0x14 , 0x15 , 0x16 , 0x17 , 0x18 , 0x19 , 0x1A , 0x1B , 0x1C , 0x1D , 0x1E , 0x1F ,
0x20 , 0x21 , 0x22 , 0x23 , 0x24 , 0x25 , 0x26 , 0x27 , 0x28 , 0x29 , 0x2A , 0x2B , 0x2C , 0x2D , 0x2E , 0x2F ,
0x30 , 0x31 , 0x32 , 0x33 , 0x34 , 0x35 , 0x36 , 0x37 , 0x38 , 0x39 , 0x3A , 0x3B , 0x3C , 0x3D , 0x3E , 0x3F ,
0x40 , 0x41 , 0x42 , 0x43 , 0x44 , 0x45 , 0x46 , 0x47 , 0x48 , 0x49 , 0x4A , 0x4B , 0x4C , 0x4D , 0x4E , 0x4F ,
0x50 , 0x51 , 0x52 , 0x53 , 0x54 , 0x55 , 0x56 , 0x57 , 0x58 , 0x59 , 0x5A , 0x5B , 0x5C , 0x5D , 0x5E , 0x5F ,
0x60 , 0x61 , 0x62 , 0x63 , 0x64 , 0x65 , 0x66 , 0x67 , 0x68 , 0x69 , 0x6A , 0x6B , 0x6C , 0x6D , 0x6E , 0x6F ,
0x70 , 0x71 , 0x72 , 0x73 , 0x74 , 0x75 , 0x76 , 0x77 , 0x78 , 0x79 , 0x7A , 0x7B , 0x7C , 0x7D , 0x7E , 0x7F ,
0x80 , 0x81 , 0x82 , 0x83 , 0x84 , 0x85 , 0x86 , 0x87 , 0x88 , 0x89 , 0x8A , 0x8B , 0x8C , 0x8D , 0x8E , 0x8F ,
0x90 , 0x91 , 0x92 , 0x93 , 0x94 , 0x95 , 0x96 , 0x97 , 0x98 , 0x99 , 0x9A , 0x9B , 0x9C , 0x9D , 0x9E , 0x9F ,
0xA0 , 0xA1 , 0xA2 , 0xA3 , 0xA4 , 0xA5 , 0xA6 , 0xA7 , 0xA8 , 0xA9 , 0xAA , 0xAB , 0xAC , 0xAD , 0xAE , 0xAF ,
0xB0 , 0xB1 , 0xB2 , 0xB3 , 0xB4 , 0xB5 , 0xB6 , 0xB7 , 0xB8 , 0xB9 , 0xBA , 0xBB , 0xBC , 0xBD , 0xBE , 0xBF ,
0xC0 , 0xC1 , 0xC2 , 0xC3 , 0xC4 , 0xC5 , 0xC6 , 0xC7 , 0xC8 , 0xC9 , 0xCA , 0xCB , 0xCC , 0xCD , 0xCE , 0xCF ,
0xD0 , 0xD1 , 0xD2 , 0xD3 , 0xD4 , 0xD5 , 0xD6 , 0xD7 , 0xD8 , 0xD9 , 0xDA , 0xDB , 0xDC , 0xDD , 0xDE , 0xDF ,
/* Bottom border */
0xE0 , 0xE1 , 0xE2 , 0xE3 , 0xE4 , 0xE5 , 0xE6 , 0xE7 , 0xE8 , 0xE9 , 0xEA , 0xEB , 0xEC , 0xED , 0xEE , 0xEF ,
0xF0 , 0xF1 , 0xF2 , 0xF3 , 0xF4 , 0xF5 , 0xF6 , 0xF7 , 0xF8 , 0xF9 , 0xFA , 0xFB , 0xFC , 0xFD , 0xFE , 0xFF ,
/* Bottom blanking */
0x00 , 0x01 , 0x02 ,
/* Vertical blanking */
0xCA , 0xCB , 0xCC ,
} ,
/* PAL, 240 lines */
new byte [ ]
{
/* Top blanking */
0xD5 , 0xD6 , 0xD7 , 0xD8 , 0xD9 , 0xDA , 0xDB , 0xDC , 0xDD , 0xDE , 0xDF , 0xE0 , 0xE1 ,
/* Top border */
0xE2 , 0xE3 , 0xE4 , 0xE5 , 0xE6 , 0xE7 , 0xE8 , 0xE9 , 0xEA , 0xEB , 0xEC , 0xED , 0xEE , 0xEF , 0xF0 , 0xF1 ,
0xF2 , 0xF3 , 0xF4 , 0xF5 , 0xF6 , 0xF7 , 0xF8 , 0xF9 , 0xFA , 0xFB , 0xFC , 0xFD , 0xFE , 0xFF ,
/* Active display */
0x00 , 0x01 , 0x02 , 0x03 , 0x04 , 0x05 , 0x06 , 0x07 , 0x08 , 0x09 , 0x0A , 0x0B , 0x0C , 0x0D , 0x0E , 0x0F ,
0x10 , 0x11 , 0x12 , 0x13 , 0x14 , 0x15 , 0x16 , 0x17 , 0x18 , 0x19 , 0x1A , 0x1B , 0x1C , 0x1D , 0x1E , 0x1F ,
0x20 , 0x21 , 0x22 , 0x23 , 0x24 , 0x25 , 0x26 , 0x27 , 0x28 , 0x29 , 0x2A , 0x2B , 0x2C , 0x2D , 0x2E , 0x2F ,
0x30 , 0x31 , 0x32 , 0x33 , 0x34 , 0x35 , 0x36 , 0x37 , 0x38 , 0x39 , 0x3A , 0x3B , 0x3C , 0x3D , 0x3E , 0x3F ,
0x40 , 0x41 , 0x42 , 0x43 , 0x44 , 0x45 , 0x46 , 0x47 , 0x48 , 0x49 , 0x4A , 0x4B , 0x4C , 0x4D , 0x4E , 0x4F ,
0x50 , 0x51 , 0x52 , 0x53 , 0x54 , 0x55 , 0x56 , 0x57 , 0x58 , 0x59 , 0x5A , 0x5B , 0x5C , 0x5D , 0x5E , 0x5F ,
0x60 , 0x61 , 0x62 , 0x63 , 0x64 , 0x65 , 0x66 , 0x67 , 0x68 , 0x69 , 0x6A , 0x6B , 0x6C , 0x6D , 0x6E , 0x6F ,
0x70 , 0x71 , 0x72 , 0x73 , 0x74 , 0x75 , 0x76 , 0x77 , 0x78 , 0x79 , 0x7A , 0x7B , 0x7C , 0x7D , 0x7E , 0x7F ,
0x80 , 0x81 , 0x82 , 0x83 , 0x84 , 0x85 , 0x86 , 0x87 , 0x88 , 0x89 , 0x8A , 0x8B , 0x8C , 0x8D , 0x8E , 0x8F ,
0x90 , 0x91 , 0x92 , 0x93 , 0x94 , 0x95 , 0x96 , 0x97 , 0x98 , 0x99 , 0x9A , 0x9B , 0x9C , 0x9D , 0x9E , 0x9F ,
0xA0 , 0xA1 , 0xA2 , 0xA3 , 0xA4 , 0xA5 , 0xA6 , 0xA7 , 0xA8 , 0xA9 , 0xAA , 0xAB , 0xAC , 0xAD , 0xAE , 0xAF ,
0xB0 , 0xB1 , 0xB2 , 0xB3 , 0xB4 , 0xB5 , 0xB6 , 0xB7 , 0xB8 , 0xB9 , 0xBA , 0xBB , 0xBC , 0xBD , 0xBE , 0xBF ,
0xC0 , 0xC1 , 0xC2 , 0xC3 , 0xC4 , 0xC5 , 0xC6 , 0xC7 , 0xC8 , 0xC9 , 0xCA , 0xCB , 0xCC , 0xCD , 0xCE , 0xCF ,
0xD0 , 0xD1 , 0xD2 , 0xD3 , 0xD4 , 0xD5 , 0xD6 , 0xD7 , 0xD8 , 0xD9 , 0xDA , 0xDB , 0xDC , 0xDD , 0xDE , 0xDF ,
0xE0 , 0xE1 , 0xE2 , 0xE3 , 0xE4 , 0xE5 , 0xE6 , 0xE7 , 0xE8 , 0xE9 , 0xEA , 0xEB , 0xEC , 0xED , 0xEE , 0xEF ,
/* Bottom border */
0xF0 , 0xF1 , 0xF2 , 0xF3 , 0xF4 , 0xF5 , 0xF6 , 0xF7 , 0xF8 , 0xF9 , 0xFA , 0xFB , 0xFC , 0xFD , 0xFE , 0xFF ,
0x00 , 0x01 , 0x02 , 0x03 , 0x04 , 0x05 , 0x06 , 0x07 ,
/* Bottom blanking */
0x08 , 0x09 , 0x0A ,
/* Vertical blanking */
0xD2 , 0xD3 , 0xD4 ,
}
} ;
/* For H-counter emulation */
readonly byte [ ] hCounterTable = new byte [ ]
{
0x00 , 0x01 , 0x02 , 0x02 , 0x03 , 0x04 , 0x05 , 0x05 , 0x06 , 0x07 , 0x08 , 0x08 , 0x09 , 0x0A , 0x0B , 0x0B ,
0x0C , 0x0D , 0x0E , 0x0E , 0x0F , 0x10 , 0x11 , 0x11 , 0x12 , 0x13 , 0x14 , 0x14 , 0x15 , 0x16 , 0x17 , 0x17 ,
0x18 , 0x19 , 0x1A , 0x1A , 0x1B , 0x1C , 0x1D , 0x1D , 0x1E , 0x1F , 0x20 , 0x20 , 0x21 , 0x22 , 0x23 , 0x23 ,
0x24 , 0x25 , 0x26 , 0x26 , 0x27 , 0x28 , 0x29 , 0x29 , 0x2A , 0x2B , 0x2C , 0x2C , 0x2D , 0x2E , 0x2F , 0x2F ,
0x30 , 0x31 , 0x32 , 0x32 , 0x33 , 0x34 , 0x35 , 0x35 , 0x36 , 0x37 , 0x38 , 0x38 , 0x39 , 0x3A , 0x3B , 0x3B ,
0x3C , 0x3D , 0x3E , 0x3E , 0x3F , 0x40 , 0x41 , 0x41 , 0x42 , 0x43 , 0x44 , 0x44 , 0x45 , 0x46 , 0x47 , 0x47 ,
0x48 , 0x49 , 0x4A , 0x4A , 0x4B , 0x4C , 0x4D , 0x4D , 0x4E , 0x4F , 0x50 , 0x50 , 0x51 , 0x52 , 0x53 , 0x53 ,
0x54 , 0x55 , 0x56 , 0x56 , 0x57 , 0x58 , 0x59 , 0x59 , 0x5A , 0x5B , 0x5C , 0x5C , 0x5D , 0x5E , 0x5F , 0x5F ,
0x60 , 0x61 , 0x62 , 0x62 , 0x63 , 0x64 , 0x65 , 0x65 , 0x66 , 0x67 , 0x68 , 0x68 , 0x69 , 0x6A , 0x6B , 0x6B ,
0x6C , 0x6D , 0x6E , 0x6E , 0x6F , 0x70 , 0x71 , 0x71 , 0x72 , 0x73 , 0x74 , 0x74 , 0x75 , 0x76 , 0x77 , 0x77 ,
0x78 , 0x79 , 0x7A , 0x7A , 0x7B , 0x7C , 0x7D , 0x7D , 0x7E , 0x7F , 0x80 , 0x80 , 0x81 , 0x82 , 0x83 , 0x83 ,
0x84 , 0x85 , 0x86 , 0x86 , 0x87 , 0x88 , 0x89 , 0x89 , 0x8A , 0x8B , 0x8C , 0x8C , 0x8D , 0x8E , 0x8F , 0x8F ,
0x90 , 0x91 , 0x92 , 0x92 , 0x93 ,
0xE9 , 0xEA , 0xEA , 0xEB , 0xEC , 0xED , 0xED , 0xEE , 0xEF , 0xF0 , 0xF0 , 0xF1 , 0xF2 , 0xF3 , 0xF3 , 0xF4 ,
0xF5 , 0xF6 , 0xF6 , 0xF7 , 0xF8 , 0xF9 , 0xF9 , 0xFA , 0xFB , 0xFC , 0xFC , 0xFD , 0xFE , 0xFF , 0xFF
} ;
[StateRequired]
byte horizontalScrollLatched , verticalScrollLatched ;
const byte screenUsageBgLowPriority = screenUsageBackground ;
const byte screenUsageBgHighPriority = ( 1 < < 2 ) ;
public int ScreenHeight = > screenHeight ;
public int CurrentScanline = > currentScanline ;
public SegaSMSVDP ( ) : base ( )
{
registers = new byte [ 0x0B ] ;
cram = new byte [ 0x20 ] ;
spriteBuffer = new ( int Number , int Y , int X , int Pattern , int Attribute ) [ NumActiveScanlinesHigh ] [ ] ;
for ( int i = 0 ; i < spriteBuffer . Length ; i + + ) spriteBuffer [ i ] = new ( int Number , int Y , int X , int Pattern , int Attribute ) [ NumSpritesPerLineMode4 ] ;
}
public override void Reset ( )
{
base . Reset ( ) ;
WriteRegister ( 0x00 , 0x36 ) ;
WriteRegister ( 0x01 , 0x80 ) ;
WriteRegister ( 0x02 , 0xFF ) ;
WriteRegister ( 0x03 , 0xFF ) ;
WriteRegister ( 0x04 , 0xFF ) ;
WriteRegister ( 0x05 , 0xFF ) ;
WriteRegister ( 0x06 , 0xFB ) ;
WriteRegister ( 0x07 , 0x00 ) ;
WriteRegister ( 0x08 , 0x00 ) ;
WriteRegister ( 0x09 , 0x00 ) ;
WriteRegister ( 0x0A , 0xFF ) ;
for ( int i = 0 ; i < cram . Length ; i + + ) cram [ i ] = 0 ;
vCounter = hCounter = 0 ;
lineInterruptCounter = registers [ 0x0A ] ;
isLineInterruptPending = false ;
horizontalScrollLatched = verticalScrollLatched = 0 ;
UpdateResolution ( ) ;
}
public override void SetRevision ( int rev )
{
VDPTypes type = ( VDPTypes ) rev ;
Debug . Assert ( Enum . IsDefined ( typeof ( VDPTypes ) , type ) , "Invalid revision" , "{0} revision is invalid; only rev 0 (MK3/SMS1) or 1 (SMS2/GG) is valid" , GetType ( ) . FullName ) ;
vdpType = type ;
}
protected override void ReconfigureTimings ( )
{
/* Calculate cycles/line */
clockCyclesPerLine = ( int ) Math . Round ( ( clockRate / refreshRate ) / numTotalScanlines ) ;
/* Create arrays */
screenUsage = new byte [ numVisiblePixels * numVisibleScanlines ] ;
outputFramebuffer = new byte [ ( numVisiblePixels * numVisibleScanlines ) * 4 ] ;
/* Update resolution/display timing */
UpdateResolution ( ) ;
}
public override void Step ( int clockCyclesInStep )
{
InterruptLine = ( ( ( isFrameInterruptEnabled & & isFrameInterruptPending ) | | ( isLineInterruptEnabled & & isLineInterruptPending ) ) ? InterruptState . Assert : InterruptState . Clear ) ;
cycleCount + = clockCyclesInStep ;
hCounter = hCounterTable [ ( int ) Math . Round ( ( cycleCount + 578 ) / 3.0 ) % hCounterTable . Length ] ;
if ( cycleCount > = clockCyclesPerLine )
{
OnEndOfScanline ( EventArgs . Empty ) ;
horizontalScrollLatched = registers [ 0x08 ] ;
if ( currentScanline = = scanlineActiveDisplay )
verticalScrollLatched = registers [ 0x09 ] ;
CheckSpriteOverflow ( currentScanline ) ;
RenderLine ( currentScanline ) ;
if ( currentScanline > = scanlineActiveDisplay & & currentScanline < = scanlineBottomBorder )
{
lineInterruptCounter - - ;
if ( lineInterruptCounter < 0 )
{
lineInterruptCounter = registers [ 0x0A ] ;
isLineInterruptPending = true ;
}
}
else
lineInterruptCounter = registers [ 0x0A ] ;
if ( currentScanline = = ( scanlineBottomBorder + 1 ) )
isFrameInterruptPending = true ;
vCounter = vCounterTables [ vCounterTableIndex ] [ currentScanline ] ;
currentScanline + + ;
if ( currentScanline = = numTotalScanlines )
{
currentScanline = 0 ;
ClearScreenUsage ( ) ;
PrepareRenderScreen ( ) ;
}
ParseSpriteTable ( currentScanline ) ;
cycleCount - = clockCyclesPerLine ;
if ( cycleCount < = - clockCyclesPerLine ) cycleCount = 0 ;
}
}
protected override void PrepareRenderScreen ( )
{
2025-01-03 19:53:38 +08:00
// 固定数组,防止垃圾回收器移动它
var bitmapcolorRect_handle = GCHandle . Alloc ( outputFramebuffer . Clone ( ) as byte [ ] , GCHandleType . Pinned ) ;
// 获取数组的指针
IntPtr mFrameDataPtr = bitmapcolorRect_handle . AddrOfPinnedObject ( ) ;
OnRenderScreen ( new RenderScreenEventArgs ( numVisiblePixels , numVisibleScanlines , mFrameDataPtr ) ) ;
//OnRenderScreen(new RenderScreenEventArgs(numVisiblePixels, numVisibleScanlines, outputFramebuffer.Clone() as byte[]));
2025-01-02 17:55:16 +08:00
}
protected override byte ReadVram ( ushort address )
{
return vram [ address & vramMask16k ] ;
}
protected override void WriteVram ( ushort address , byte value )
{
vram [ address & vramMask16k ] = value ;
}
protected override void RenderLine ( int y )
{
if ( y > = scanlineTopBorder & & y < scanlineActiveDisplay )
{
if ( layerBordersForceEnable ) SetLine ( y , 1 , backgroundColor ) ;
else SetLine ( y , 0x00 , 0x00 , 0x00 ) ;
}
else if ( y > = scanlineActiveDisplay & & y < scanlineBottomBorder )
{
if ( layerBackgroundForceEnable )
{
if ( isBitM4Set )
RenderLineMode4Background ( y ) ;
else if ( isModeGraphics1 )
RenderLineGraphics1Background ( y ) ;
else if ( isModeGraphics2 )
RenderLineGraphics2Background ( y ) ;
else if ( isModeMulticolor )
RenderLineMulticolorBackground ( y ) ;
else if ( isModeText )
RenderLineTextBackground ( y ) ;
}
else
SetLine ( y , 0x00 , 0x00 , 0x00 ) ;
if ( layerSpritesForceEnable )
{
if ( isBitM4Set )
RenderLineMode4Sprites ( y ) ;
else if ( ! isModeText & & ! isBitM4Set )
RenderLineSprites ( y ) ;
}
RenderBorders ( y ) ;
}
else if ( y > = scanlineBottomBorder & & y < numVisibleScanlines )
{
if ( layerBordersForceEnable ) SetLine ( y , 1 , backgroundColor ) ;
else SetLine ( y , 0x00 , 0x00 , 0x00 ) ;
}
}
protected override void RenderBorders ( int y )
{
for ( int x = pixelLeftBorder ; x < pixelActiveDisplay ; x + + )
{
if ( layerBordersForceEnable ) SetPixel ( y , x , 1 , backgroundColor ) ;
else SetPixel ( y , x , 0x00 , 0x00 , 0x00 ) ;
}
for ( int x = pixelRightBorder ; x < numVisiblePixels ; x + + )
{
if ( layerBordersForceEnable ) SetPixel ( y , x , 1 , backgroundColor ) ;
else SetPixel ( y , x , 0x00 , 0x00 , 0x00 ) ;
}
}
protected void SetLine ( int y , int palette , int color )
{
for ( int x = 0 ; x < numVisiblePixels ; x + + )
SetPixel ( y , x , palette , color ) ;
}
protected virtual void SetPixel ( int y , int x , int palette , int color )
{
WriteColorToFramebuffer ( palette , color , ( ( y * numVisiblePixels ) + ( x % numVisiblePixels ) ) * 4 ) ;
}
private void RenderLineMode4Background ( int y )
{
/* Determine coordinates in active display */
int activeDisplayY = ( y - scanlineActiveDisplay ) ;
/* Determine H scrolling parameters */
int currentHorizontalScroll = ( ( isHScrollPartiallyDisabled & & activeDisplayY < 16 ) ? 0 : horizontalScrollLatched ) ;
int horizontalScrollCoarse = ( currentHorizontalScroll > > 3 ) ;
int horizontalScrollFine = ( currentHorizontalScroll & 0x07 ) ;
ushort currentNametableBaseAddress = nametableBaseAddress ;
bool currentIsVScrollPartiallyDisabled = isVScrollPartiallyDisabled ;
bool currentIsColumn0MaskEnabled = isColumn0MaskEnabled ;
for ( int x = 0 ; x < numTotalPixelsPerScanline ; x + + )
{
int activeDisplayX = ( x - pixelActiveDisplay ) ;
if ( activeDisplayX < 0 | | activeDisplayX > = NumActivePixelsPerScanline ) continue ;
/* Determine V scrolling parameters */
int currentVerticalScroll = ( ( currentIsVScrollPartiallyDisabled & & activeDisplayX > = 192 ) ? 0 : verticalScrollLatched ) ;
int verticalScrollCoarse = ( currentVerticalScroll > > 3 ) ;
int verticalScrollFine = ( currentVerticalScroll & 0x07 ) ;
/* Calculate current scrolled column and row */
int numColumns = 32 ;
int currentColumn = ( ( ( activeDisplayX - horizontalScrollFine ) / 8 ) - horizontalScrollCoarse ) & ( numColumns - 1 ) ;
int currentRow = ( ( ( activeDisplayY + verticalScrollFine ) / 8 ) + verticalScrollCoarse ) % ( nametableHeight / 8 ) ;
/ * VDPDIFF : Mk3 / SMS1 VDP only , adjust current row according to mask bit ; http : //www.smspower.org/Development/TilemapMirroring
* NOTE : Emulating this breaks 224 / 240 - line mode games ( ex . Fantastic Dizzy , Cosmic Spacehead , Micro Machines ) ?
* /
if ( vdpType = = VDPTypes . Mk3SMS1 )
currentRow & = ( ( ( registers [ 0x02 ] & 0x01 ) < < 4 ) | 0x0F ) ;
/* Fetch data from nametable & extract properties */
ushort nametableAddress = ( ushort ) ( currentNametableBaseAddress + ( currentRow * ( numColumns * 2 ) ) + ( currentColumn * 2 ) ) ;
ushort nametableData = ( ushort ) ( ReadVram ( ( ushort ) ( nametableAddress + 1 ) ) < < 8 | ReadVram ( nametableAddress ) ) ;
int tileIndex = ( nametableData & 0x01FF ) ;
bool hFlip = ( ( nametableData & 0x0200 ) = = 0x0200 ) ;
bool vFlip = ( ( nametableData & 0x0400 ) = = 0x0400 ) ;
int palette = ( ( ( nametableData & 0x0800 ) > > 11 ) & 0x0001 ) ;
bool priority = ( ( nametableData & 0x1000 ) = = 0x1000 ) ;
/* Fetch pixel data for current pixel line */
int hPixel = ( ( activeDisplayX - horizontalScrollFine ) % 8 ) ;
hPixel = ( hFlip ? hPixel : ( 7 - hPixel ) ) ;
int vPixel = ( ( activeDisplayY + verticalScrollFine ) % 8 ) ;
vPixel = ( ! vFlip ? vPixel : ( 7 - vPixel ) ) ;
ushort tileAddress = ( ushort ) ( ( tileIndex < < 5 ) + ( vPixel < < 2 ) ) ;
int c = ( ( ( ReadVram ( ( ushort ) ( tileAddress + 0 ) ) > > hPixel ) & 0x01 ) < < 0 ) ;
c | = ( ( ( ReadVram ( ( ushort ) ( tileAddress + 1 ) ) > > hPixel ) & 0x01 ) < < 1 ) ;
c | = ( ( ( ReadVram ( ( ushort ) ( tileAddress + 2 ) ) > > hPixel ) & 0x01 ) < < 2 ) ;
c | = ( ( ( ReadVram ( ( ushort ) ( tileAddress + 3 ) ) > > hPixel ) & 0x01 ) < < 3 ) ;
/* Record screen usage, write to framebuffer */
if ( GetScreenUsageFlag ( y , x ) = = screenUsageEmpty )
{
if ( ( currentIsColumn0MaskEnabled & & ( activeDisplayX / 8 ) = = 0 ) | | isDisplayBlanked )
SetPixel ( y , x , 1 , backgroundColor ) ;
else
SetPixel ( y , x , palette , c ) ;
SetScreenUsageFlag ( y , x , ( c ! = 0 & & priority ) ? screenUsageBgHighPriority : screenUsageBgLowPriority ) ;
}
}
}
protected override void CheckSpriteOverflow ( int y )
{
if ( ! isBitM4Set )
{
/* Not in Master System video mode */
base . CheckSpriteOverflow ( y ) ;
}
else
{
/* Ensure current scanline is within active display */
if ( y > = scanlineActiveDisplay & & y < scanlineBottomBorder )
{
int activeDisplayY = ( y - scanlineActiveDisplay ) ;
/* If last sprite in buffer is valid, sprite overflow occured */
int lastSpriteInBuffer = spriteBuffer [ activeDisplayY ] [ NumSpritesPerLineMode4 - 1 ] . Number ;
if ( lastSpriteInBuffer ! = - 1 )
{
isSpriteOverflow = true ;
/* Store sprite number in status register */
/* NOTE: the last illegal sprite is *technically* only stored here in TMS99xxA modes, but still emulating it in Mode 4 should be fine */
WriteSpriteNumberToStatus ( lastSpriteInBuffer ) ;
}
}
}
}
protected override void ParseSpriteTable ( int y )
{
if ( ! isBitM4Set )
{
/* Not in Master System video mode */
base . ParseSpriteTable ( y ) ;
}
else
{
if ( y < scanlineActiveDisplay | | y > = scanlineBottomBorder ) return ;
/* Determine coordinates in active display */
int activeDisplayY = ( y - scanlineActiveDisplay ) ;
/* Clear sprite list for current line */
for ( int i = 0 ; i < spriteBuffer [ activeDisplayY ] . Length ; i + + ) spriteBuffer [ activeDisplayY ] [ i ] = ( - 1 , 0 , 0 , 0 , 0 ) ;
/* Determine sprite size & get zoomed sprites adjustment */
int zoomShift = ( isZoomedSprites ? 1 : 0 ) ;
int spriteHeight = ( ( isLargeSprites ? 16 : 8 ) < < zoomShift ) ;
int numValidSprites = 0 ;
for ( int sprite = 0 ; sprite < NumSpritesMode4 ; sprite + + )
{
int yCoordinate = ReadVram ( ( ushort ) ( spriteAttribTableBaseAddress + sprite ) ) ;
/* Ignore following if Y coord is 208 in 192-line mode */
if ( yCoordinate = = 208 & & screenHeight = = NumActiveScanlinesLow )
{
/* Store first "illegal sprite" number in status register */
WriteSpriteNumberToStatus ( sprite ) ;
return ;
}
/* Modify Y coord as needed */
yCoordinate + + ;
if ( yCoordinate > screenHeight + 32 ) yCoordinate - = 256 ;
/* Ignore this sprite if on incorrect lines */
if ( activeDisplayY < yCoordinate | | activeDisplayY > = ( yCoordinate + spriteHeight ) ) continue ;
/* Check if maximum number of sprites per line is reached */
numValidSprites + + ;
if ( numValidSprites > NumSpritesPerLineMode4 ) return ;
/* Mark sprite for rendering */
int xCoordinate = ReadVram ( ( ushort ) ( spriteAttribTableBaseAddress + 0x80 + ( sprite * 2 ) ) ) ;
int patternNumber = ReadVram ( ( ushort ) ( spriteAttribTableBaseAddress + 0x80 + ( sprite * 2 ) + 1 ) ) ;
int unusedData = ReadVram ( ( ushort ) ( spriteAttribTableBaseAddress + 0x40 + ( sprite * 2 ) ) ) ;
spriteBuffer [ activeDisplayY ] [ numValidSprites - 1 ] = ( sprite , yCoordinate , xCoordinate , patternNumber , unusedData ) ;
}
/* Because we didn't bow out before already, store total number of sprites in status register */
WriteSpriteNumberToStatus ( NumSprites - 1 ) ;
}
}
private void RenderLineMode4Sprites ( int y )
{
if ( y < scanlineActiveDisplay | | y > = scanlineBottomBorder ) return ;
/* Determine coordinates in active display */
int activeDisplayY = ( y - scanlineActiveDisplay ) ;
/* Determine sprite size & get zoomed sprites adjustment */
int zoomShift = ( isZoomedSprites ? 1 : 0 ) ;
int spriteHeight = ( ( isLargeSprites ? 16 : 8 ) < < zoomShift ) ;
for ( int s = 0 ; s < spriteBuffer [ activeDisplayY ] . Length ; s + + )
{
var sprite = spriteBuffer [ activeDisplayY ] [ s ] ;
if ( sprite . Number = = - 1 ) continue ;
/ * VDPDIFF : Mk3 / SMS1 VDP zoomed sprites bug , only first four sprites ( same as max sprites / line on TMS99xxA ) can be zoomed horizontally
* Zoom works normally on SMS2 / GG * /
int spriteWidth = ( 8 < < zoomShift ) ;
if ( vdpType = = VDPTypes . Mk3SMS1 & & s > = NumSpritesPerLine ) spriteWidth = 8 ;
if ( ! isDisplayBlanked )
{
int yCoordinate = sprite . Y ;
int xCoordinate = sprite . X ;
int patternNumber = sprite . Pattern ;
int unusedData = sprite . Attribute ;
if ( isSpriteShiftLeft8 ) xCoordinate - = 8 ;
for ( int pixel = 0 ; pixel < spriteWidth ; pixel + + )
{
/* Ignore pixel if column 0 masking is enabled and sprite pixel is in column 0 */
if ( isColumn0MaskEnabled & & ( xCoordinate + pixel ) < 8 ) continue ;
/* Check if sprite is outside active display, else continue to next sprite */
if ( ( xCoordinate + pixel ) < 0 | | ( xCoordinate + pixel ) > = NumActivePixelsPerScanline ) continue ;
/* Determine coordinate inside sprite */
int inSpriteXCoord = ( pixel > > zoomShift ) % spriteWidth ;
int inSpriteYCoord = ( ( activeDisplayY - yCoordinate ) > > zoomShift ) % spriteHeight ;
/* Calculate address and fetch pixel data */
int tileIndex = patternNumber ;
if ( isLargeSprites ) tileIndex & = ~ 0x01 ;
ushort tileAddress = ( ushort ) ( spritePatternGenBaseAddress + ( tileIndex < < 5 ) + ( inSpriteYCoord < < 2 ) ) ;
/* Get color & check transparency and position */
int c = ( ( ( ReadVram ( ( ushort ) ( tileAddress + 0 ) ) > > ( 7 - inSpriteXCoord ) ) & 0x1 ) < < 0 ) ;
c | = ( ( ( ReadVram ( ( ushort ) ( tileAddress + 1 ) ) > > ( 7 - inSpriteXCoord ) ) & 0x1 ) < < 1 ) ;
c | = ( ( ( ReadVram ( ( ushort ) ( tileAddress + 2 ) ) > > ( 7 - inSpriteXCoord ) ) & 0x1 ) < < 2 ) ;
c | = ( ( ( ReadVram ( ( ushort ) ( tileAddress + 3 ) ) > > ( 7 - inSpriteXCoord ) ) & 0x1 ) < < 3 ) ;
if ( c = = 0 ) continue ;
int x = pixelActiveDisplay + ( xCoordinate + pixel ) ;
if ( IsScreenUsageFlagSet ( y , x , screenUsageSprite ) )
{
/* If sprite was already at this location, set sprite collision flag */
isSpriteCollision = true ;
}
else if ( ! IsScreenUsageFlagSet ( y , x , screenUsageBgHighPriority ) )
{
/* Draw if pixel isn't occupied by high-priority BG */
SetPixel ( y , x , 1 , c ) ;
}
/* Note that there is a sprite here regardless */
/ * VDPDIFF : Mk3 / SMS1 VDP zoomed sprites bug , horizontally zoomed area of sprite ( i . e . pixels 9 - 16 ) is ignored by collision ; do not mark location as containing a sprite
* https : //www.smspower.org/forums/post109677#109677
* TODO : verify behavior somehow ?
* /
if ( ( vdpType = = VDPTypes . Mk3SMS1 & & isZoomedSprites & & pixel < 8 ) | | ( vdpType = = VDPTypes . Mk3SMS1 & & ! isZoomedSprites ) | | vdpType = = VDPTypes . SMS2GG )
SetScreenUsageFlag ( y , x , screenUsageSprite ) ;
}
}
}
}
protected void UpdateResolution ( )
{
/* Check screenmode */
if ( isSMS240LineMode )
{
screenHeight = NumActiveScanlinesHigh ;
nametableHeight = 256 ;
vCounterTableIndex = ( isPalChip ? 5 : 2 ) ;
}
else if ( isSMS224LineMode )
{
screenHeight = NumActiveScanlinesMed ;
nametableHeight = 256 ;
vCounterTableIndex = ( isPalChip ? 4 : 1 ) ;
}
else
{
screenHeight = NumActiveScanlinesLow ;
nametableHeight = 224 ;
vCounterTableIndex = ( isPalChip ? 3 : 0 ) ;
}
/* Scanline parameters */
if ( ! isPalChip )
{
/* NTSC */
if ( screenHeight = = NumActiveScanlinesHigh )
{
/* 240 active lines, invalid on NTSC (dummy values); "Line Interrupt Test #1" mode 1 will show blue screen */
topBorderSize = 0 ;
verticalActiveDisplaySize = 0 ;
bottomBorderSize = 0 ;
}
else if ( screenHeight = = NumActiveScanlinesMed )
{
/* 224 active lines */
topBorderSize = 11 ;
verticalActiveDisplaySize = 224 ;
bottomBorderSize = 8 ;
}
else
{
/* 192 active lines */
topBorderSize = 27 ;
verticalActiveDisplaySize = 192 ;
bottomBorderSize = 24 ;
}
}
else
{
/* PAL */
if ( screenHeight = = NumActiveScanlinesHigh )
{
/* 240 active lines */
topBorderSize = 30 ;
verticalActiveDisplaySize = 240 ;
bottomBorderSize = 24 ;
}
else if ( screenHeight = = NumActiveScanlinesMed )
{
/* 224 active lines */
topBorderSize = 38 ;
verticalActiveDisplaySize = 224 ;
bottomBorderSize = 32 ;
}
else
{
/* 192 active lines */
topBorderSize = 54 ;
verticalActiveDisplaySize = 192 ;
bottomBorderSize = 48 ;
}
}
scanlineTopBorder = 0 ;
scanlineActiveDisplay = ( scanlineTopBorder + topBorderSize ) ;
scanlineBottomBorder = ( scanlineActiveDisplay + verticalActiveDisplaySize ) ;
numVisibleScanlines = ( topBorderSize + verticalActiveDisplaySize + bottomBorderSize ) ;
/* Pixel parameters */
leftBorderSize = 13 ;
horizontalActiveDisplaySize = 256 ;
rightBorderSize = 15 ;
pixelLeftBorder = 0 ;
pixelActiveDisplay = ( pixelLeftBorder + leftBorderSize ) ;
pixelRightBorder = ( pixelActiveDisplay + horizontalActiveDisplaySize ) ;
numVisiblePixels = ( leftBorderSize + horizontalActiveDisplaySize + rightBorderSize ) ;
}
protected virtual void WriteColorToFramebuffer ( int palette , int color , int address )
{
WriteColorToFramebuffer ( cram [ ( ( palette * 16 ) + color ) ] , address ) ;
}
protected override void WriteColorToFramebuffer ( ushort colorValue , int address )
{
/* If not in Master System video mode, color value is index into legacy colormap */
if ( ! isBitM4Set )
colorValue = ( legacyColorMap [ colorValue & 0x000F ] ) ;
RGB222toBGRA8888 ( colorValue , ref outputFramebuffer , address ) ;
}
protected override void WriteDataPort ( byte value )
{
isSecondControlWrite = false ;
readBuffer = value ;
switch ( codeRegister )
{
case 0x00 :
case 0x01 :
case 0x02 :
WriteVram ( addressRegister , value ) ;
break ;
case 0x03 :
cram [ ( addressRegister & 0x001F ) ] = value ;
break ;
}
addressRegister + + ;
}
protected override byte ReadControlPort ( )
{
byte statusCurrent = ( byte ) statusFlags ;
statusFlags = StatusFlags . None ;
isSecondControlWrite = false ;
isLineInterruptPending = false ;
InterruptLine = InterruptState . Clear ;
return statusCurrent ;
}
public override byte ReadPort ( byte port )
{
if ( ( port & 0x40 ) = = 0x40 )
if ( ( port & 0x01 ) = = 0 )
return ( byte ) vCounter ; /* V counter */
else
return ( byte ) hCounter ; /* H counter */
else
return base . ReadPort ( port ) ;
}
protected override void WriteRegister ( byte register , byte value )
{
if ( register < registers . Length )
registers [ register ] = value ;
if ( register = = 0x00 | | register = = 0x01 )
UpdateResolution ( ) ;
}
}
}