More video, DMA and interrupt work
This commit is contained in:
parent
875a7d808c
commit
e8a2093a74
|
@ -72,7 +72,7 @@ namespace BizHawk.Client.Common
|
|||
["INTV"] = 59.92,
|
||||
|
||||
["ZXSpectrum_PAL"] = 50.080128205,
|
||||
["AmstradCPC_PAL"] = 50.08012820512821, // = 1 / ((1024 * 312) / 16,000,000)
|
||||
["AmstradCPC_PAL"] = 50.08012820512821, // = 1 / ((1024 * 312) / 16,000,000)
|
||||
|
||||
["UZE"] = 1125000.0 / 18733.0, // = 8 * 315000000 / 88 / 1820 / 262 ≈ 60.05444936742646666
|
||||
["VEC"] = 50,
|
||||
|
@ -82,8 +82,10 @@ namespace BizHawk.Client.Common
|
|||
["TIC80"] = 60,
|
||||
|
||||
["ChannelF"] = 234375.0 / 3872.0, // (NTSCCarrier * 8 / 7) / (256 * 264)
|
||||
// note: ChannelF II PAL timings might be slightly different...
|
||||
// note: ChannelF II PAL timings might be slightly different...
|
||||
["ChannelF_PAL"] = 15625.0 / 312.0, // 4000000 / (256 * 312)
|
||||
|
||||
["SuperVision"] = 50.08012820512821
|
||||
};
|
||||
|
||||
public static double GetFrameRate(string systemId, bool pal)
|
||||
|
|
|
@ -34,7 +34,8 @@ namespace BizHawk.Emulation.Common
|
|||
new(VSystemID.Raw.O2, "Odyssey2"),
|
||||
new(VSystemID.Raw.VEC, "Vectrex"),
|
||||
new(VSystemID.Raw.MSX, "MSX"),
|
||||
new(VSystemID.Raw.NDS, "Nintendo DS")
|
||||
new(VSystemID.Raw.NDS, "Nintendo DS"),
|
||||
new(VSystemID.Raw.SuperVision, "Watara SuperVision")
|
||||
};
|
||||
|
||||
public SystemInfo this[string systemId]
|
||||
|
|
|
@ -1,63 +0,0 @@
|
|||
namespace BizHawk.Emulation.Cores.Consoles.SuperVision
|
||||
{
|
||||
public partial class ASIC
|
||||
{
|
||||
/// <summary>
|
||||
/// The inbuilt LCD screen
|
||||
/// </summary>
|
||||
private LCD _screen;
|
||||
|
||||
/// <summary>
|
||||
/// The current field being drawn (0 or 1)
|
||||
/// </summary>
|
||||
private int _field;
|
||||
|
||||
/// <summary>
|
||||
/// VRAM byte read every 6 CPU cycles
|
||||
/// </summary>
|
||||
private byte _latchedVRAM;
|
||||
|
||||
/// <summary>
|
||||
/// The current VRAM pointer - latched every 6 cpu cycles
|
||||
/// </summary>
|
||||
private int _currentVRAMPointer;
|
||||
|
||||
private int _currY;
|
||||
private int _currX;
|
||||
|
||||
private void SetupScreen(SuperVision.SuperVisionSyncSettings superVisionSyncSettings)
|
||||
{
|
||||
_screen = new LCD(superVisionSyncSettings.ScreenType);
|
||||
}
|
||||
|
||||
private void VideoClock()
|
||||
{
|
||||
if (FrameStart)
|
||||
{
|
||||
// initial V start value (limit to 8k size)
|
||||
_currentVRAMPointer = (_regs[R_Y_SCROLL] * 0x30) * 0x1FFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
_currentVRAMPointer++;
|
||||
|
||||
if (_currentVRAMPointer == 0x1FE0)
|
||||
{
|
||||
// wrap around
|
||||
_currentVRAMPointer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (_sv.FrameClock % 6 == 0)
|
||||
{
|
||||
// address lines are updated with a new VRAM address
|
||||
_latchedVRAM = _sv.VRAM[_currentVRAMPointer];
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -41,37 +41,141 @@ namespace BizHawk.Emulation.Cores.Consoles.SuperVision
|
|||
public const int R_SYSTEM_CONTROL = 0x26;
|
||||
public const int R_IRQ_STATUS = 0x27;
|
||||
|
||||
private SuperVision _sv;
|
||||
/// <summary>
|
||||
/// Scanline length in cpu clocks
|
||||
/// </summary>
|
||||
public int CLOCK_WIDTH =>
|
||||
((_regs[R_LCD_X_SIZE] & 0xFC) // topmost 6 bits of the X Size register
|
||||
+ 4) // line latch pulse
|
||||
* 6; // 6 clocks per pixel
|
||||
|
||||
/// <summary>
|
||||
/// Number of scanlines in a field
|
||||
/// </summary>
|
||||
public int LINE_HEIGHT => _regs[R_LCD_Y_SIZE];
|
||||
|
||||
private SuperVision _sv;
|
||||
private byte[] _regs = new byte[0x30];
|
||||
|
||||
/// <summary>
|
||||
/// The inbuilt LCD screen
|
||||
/// </summary>
|
||||
public LCD Screen;
|
||||
|
||||
public ASIC(SuperVision sv, SuperVision.SuperVisionSyncSettings ss)
|
||||
{
|
||||
_sv = sv;
|
||||
_screen = new LCD(ss.ScreenType);
|
||||
Screen = new LCD(ss.ScreenType);
|
||||
}
|
||||
|
||||
public bool FrameStart;
|
||||
private int _intTimer;
|
||||
private bool _intTimerEnabled;
|
||||
private bool _intTimerChanged;
|
||||
private int _nmiTimer;
|
||||
private bool _intFlag;
|
||||
private bool _dmaInProgress;
|
||||
private int _dmaCounter;
|
||||
private int _seqCounter;
|
||||
private int _byteCounter;
|
||||
private int _lineCounter;
|
||||
private int _field;
|
||||
private ushort _vramByteBuffer;
|
||||
|
||||
/// <summary>
|
||||
/// ASIC is clocked at the same rate as the CPU
|
||||
/// </summary>
|
||||
public void Clock()
|
||||
{
|
||||
CheckInterrupt();
|
||||
// According to the information presented in https://github.com/GrenderG/supervision_reveng_notes/blob/master/Supervision_Tech.txt
|
||||
// it can be surmised that the ASIC rigidly sticks to a 6-phase sequencer, which is as follows:
|
||||
// 0: CPU RDY line true / PixelCLK to LCD / Output 1/2 byte to LCD
|
||||
// 1: DMA byte transfer to VRAM (if DMA is active) / CPU RDY line false (if DMA is active)
|
||||
// 2: DMA byte transfer to VRAM (if DMA is active) / CPU RDY line false (if DMA is active)
|
||||
// 3: DMA byte transfer to VRAM (if DMA is active) / CPU RDY line false (if DMA is active)
|
||||
// 4: DMA byte transfer to VRAM (if DMA is active) / CPU RDY line false (if DMA is active)
|
||||
// 5: DMA byte transfer to VRAM (if DMA is active) / CPU RDY line false (if DMA is active)
|
||||
|
||||
CheckDMA();
|
||||
VideoClock();
|
||||
|
||||
// so DMA can transfer 5 bytes to VRAM every 6 clocks, the 6th clock being the 1/2 byte transfer to the LCD
|
||||
switch (_seqCounter)
|
||||
{
|
||||
case 0:
|
||||
// there is no DMA on this cycle so CPU can run freely
|
||||
_sv._cpu.RDY = true;
|
||||
|
||||
// ASIC reads a byte from VRAM
|
||||
byte data = 0xff; //todo
|
||||
|
||||
// shift the last read byte in the buffer and add the new byte to the start
|
||||
_vramByteBuffer = (ushort) ((_vramByteBuffer << 8) | data);
|
||||
|
||||
// get the correct byte data based on the X Scroll register lower 2 bits
|
||||
// this simulates a delay in the bits sent to the LCD
|
||||
byte b = (byte) ((_vramByteBuffer >> (_regs[R_X_SCROLL] & 0b0000_0011)) & 0xff);
|
||||
|
||||
// depending on the field, a 4 bit sequence is sent to the LCD
|
||||
// Field0: bits 0-2-4-6
|
||||
// Field1: bits 1-3-5-7
|
||||
byte lData = _field == 0
|
||||
? (byte) ((b & 0b0000_0001) | ((b & 0b0000_0100) >> 1) | ((b & 0b0001_0000) >> 2) | ((b & 0b0100_0000) >> 3))
|
||||
: (byte) ((b & 0b0000_0010) >> 1 | ((b & 0b0000_1000) >> 2) | ((b & 0b0010_0000) >> 3) | ((b & 0b1000_0000) >> 4));
|
||||
|
||||
bool lineEnd = _byteCounter == CLOCK_WIDTH - 1;
|
||||
bool frameEnd = _lineCounter == LINE_HEIGHT && lineEnd && _field == 1;
|
||||
|
||||
// send 1/2 byte to the LCD
|
||||
Screen.PixelClock(lData, _field, lineEnd, frameEnd);
|
||||
|
||||
_byteCounter++;
|
||||
|
||||
if (_byteCounter == CLOCK_WIDTH)
|
||||
{
|
||||
// end of scanline
|
||||
_byteCounter = 0;
|
||||
_lineCounter++;
|
||||
|
||||
if (_lineCounter == LINE_HEIGHT)
|
||||
{
|
||||
// end of field
|
||||
_lineCounter = 0;
|
||||
_field++;
|
||||
|
||||
if (_field == 2)
|
||||
_field = 0; // wraparound
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if (_dmaInProgress)
|
||||
{
|
||||
// perform DMA transfer
|
||||
DoDMA();
|
||||
|
||||
_sv._cpu.RDY = !_dmaInProgress;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
_seqCounter++;
|
||||
|
||||
if (_seqCounter == 7)
|
||||
_seqCounter = 0; // wraparound
|
||||
|
||||
CheckInterrupt();
|
||||
AudioClock();
|
||||
|
||||
if (FrameStart)
|
||||
FrameStart = false;
|
||||
|
||||
_sv.FrameClock++;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The current prescaler value for the IRQ timer
|
||||
/// </summary>
|
||||
private int IntPrescaler => _regs[R_SYSTEM_CONTROL].Bit(4) ? 16384 : 256;
|
||||
|
||||
/// <summary>
|
||||
|
@ -88,16 +192,63 @@ namespace BizHawk.Emulation.Cores.Consoles.SuperVision
|
|||
_sv._cpu.NMI = true;
|
||||
}
|
||||
|
||||
_intTimer--;
|
||||
|
||||
if (_intTimer <= 0)
|
||||
if (_intTimerChanged)
|
||||
{
|
||||
// IRQ timer register has just been modified
|
||||
_intTimerChanged = false;
|
||||
_intTimerEnabled = true;
|
||||
|
||||
// prescaler reset
|
||||
_intTimer = 0;
|
||||
|
||||
if (_regs[R_IRQ_TIMER] == 0)
|
||||
{
|
||||
if (_regs[R_SYSTEM_CONTROL].Bit(1))
|
||||
{
|
||||
// instant IRQ
|
||||
_intFlag = true;
|
||||
_intTimerEnabled = false;
|
||||
|
||||
// set IRQ Timer expired bit
|
||||
_regs[R_IRQ_STATUS] = (byte) (_regs[R_IRQ_STATUS] | 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (_regs[R_IRQ_TIMER] > 0)
|
||||
{
|
||||
// timer will be counting down clocked by the prescaler
|
||||
if (_intTimer++ == IntPrescaler)
|
||||
{
|
||||
// prescaler clock
|
||||
_intTimer = 0;
|
||||
|
||||
// decrement timer
|
||||
_regs[R_IRQ_TIMER]--;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// timer has expired
|
||||
if (_intTimerEnabled)
|
||||
{
|
||||
_intFlag = true;
|
||||
_intTimerEnabled = false;
|
||||
|
||||
// set IRQ Timer expired bit
|
||||
_regs[R_IRQ_STATUS] = (byte) (_regs[R_IRQ_STATUS] | 2);
|
||||
}
|
||||
}
|
||||
|
||||
if (_intFlag && _regs[R_SYSTEM_CONTROL].Bit(1))
|
||||
{
|
||||
// IRQ enabled
|
||||
_sv._cpu.IRQ = true;
|
||||
_intFlag = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// DMA Control
|
||||
/// Check whether DMA needs to start
|
||||
/// </summary>
|
||||
private void CheckDMA()
|
||||
{
|
||||
|
@ -105,22 +256,46 @@ namespace BizHawk.Emulation.Cores.Consoles.SuperVision
|
|||
{
|
||||
// DMA start requested
|
||||
_dmaInProgress = true;
|
||||
}
|
||||
|
||||
// Unset the DMA start bit
|
||||
_regs[R_DMA_CONTROL] = (byte) (_regs[R_DMA_CONTROL] & ~(1 << 7));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform a DMA transfer
|
||||
/// </summary>
|
||||
private void DoDMA()
|
||||
{
|
||||
if (_dmaInProgress)
|
||||
{
|
||||
ushort source = (ushort) (_regs[R_DMA_SOURCE_HIGH] << 8 | _regs[R_DMA_SOURCE_LOW]);
|
||||
ushort dest = (ushort) (_regs[R_DMA_DEST_HIGH] << 8 | _regs[R_DMA_DEST_LOW]);
|
||||
|
||||
_dmaCounter++;
|
||||
|
||||
if (_dmaCounter == 4096)
|
||||
if (_dmaCounter == 4096 || _dmaCounter == _regs[R_DMA_LENGTH] * 16)
|
||||
{
|
||||
// wrap around
|
||||
// wraparound or length reached
|
||||
_dmaCounter = 0;
|
||||
_dmaInProgress = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
ushort source = (ushort) (_regs[R_DMA_SOURCE_HIGH] << 8 | _regs[R_DMA_SOURCE_LOW]);
|
||||
ushort dest = (ushort) (_regs[R_DMA_DEST_HIGH] << 8 | _regs[R_DMA_DEST_LOW]);
|
||||
|
||||
// transfer a byte from source to dest using DMA
|
||||
_sv.WriteMemory(dest, _sv.ReadMemory(source));
|
||||
|
||||
// source registers incremented
|
||||
source++;
|
||||
_regs[R_DMA_SOURCE_HIGH] = (byte) (source >> 8);
|
||||
_regs[R_DMA_SOURCE_LOW] = (byte) source;
|
||||
|
||||
// destination registers incremeneted
|
||||
dest++;
|
||||
_regs[R_DMA_DEST_HIGH] = (byte) (dest >> 8);
|
||||
_regs[R_DMA_DEST_LOW] = (byte) dest;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -135,45 +310,83 @@ namespace BizHawk.Emulation.Cores.Consoles.SuperVision
|
|||
// LCD_X_Size
|
||||
case 0x00:
|
||||
case 0x04:
|
||||
|
||||
// Only the upper 6 bits of LCD_X_Size are usable. The lower 2 bits are ignored.
|
||||
// the LCD size can only be changed in 4 pixel increments
|
||||
_regs[R_LCD_X_SIZE] = value;
|
||||
|
||||
break;
|
||||
|
||||
// LCD_Y_Size
|
||||
case 0x01:
|
||||
case 0x05:
|
||||
|
||||
// LCD_Y_Size controls how many scanlines are shown in the field
|
||||
// After the requisite number of scanlines, the LCD frame latch signal is output and the frame polarity line is toggled
|
||||
_regs[R_LCD_Y_SIZE] = value;
|
||||
|
||||
break;
|
||||
|
||||
// X_Scroll
|
||||
case 0x02:
|
||||
case 0x06:
|
||||
|
||||
_regs[R_X_SCROLL] = value;
|
||||
|
||||
break;
|
||||
|
||||
// Y_Scroll
|
||||
case 0x03:
|
||||
case 0x07:
|
||||
|
||||
_regs[R_Y_SCROLL] = value;
|
||||
|
||||
break;
|
||||
|
||||
// DMA Source low
|
||||
case 0x08:
|
||||
|
||||
_regs[R_DMA_SOURCE_LOW] = value;
|
||||
|
||||
break;
|
||||
|
||||
// DMA Source high
|
||||
case 0x09:
|
||||
|
||||
_regs[R_DMA_SOURCE_HIGH] = value;
|
||||
|
||||
break;
|
||||
|
||||
// DMA Destination low
|
||||
case 0x0A:
|
||||
|
||||
_regs[R_DMA_DEST_LOW] = value;
|
||||
|
||||
break;
|
||||
|
||||
// DMA Destination high
|
||||
case 0x0B:
|
||||
|
||||
_regs[R_DMA_DEST_HIGH] = value;
|
||||
|
||||
break;
|
||||
|
||||
// DMA Length
|
||||
case 0x0C:
|
||||
|
||||
// 8bit register
|
||||
// This register selects how many bytes of data to move. The actual number of bytes to move is (L * 16).
|
||||
// If the register is loaded with 0, a full 4096 bytes is moved.
|
||||
_regs[R_DMA_LENGTH] = value;
|
||||
|
||||
break;
|
||||
|
||||
// DMA Control
|
||||
case 0x0D:
|
||||
|
||||
// Start DMA when written with bit7 set
|
||||
_regs[R_DMA_CONTROL] = value;
|
||||
|
||||
break;
|
||||
|
||||
// CH1_Flow (right only)
|
||||
|
@ -244,9 +457,12 @@ namespace BizHawk.Emulation.Cores.Consoles.SuperVision
|
|||
// This timer is clocked by a prescaler, which is reset when the timer is written to.
|
||||
// This prescaler can divide the system clock by 256 or 16384.
|
||||
// 8bits
|
||||
_regs[R_IRQ_TIMER] = value;
|
||||
|
||||
_intTimerEnabled = true;
|
||||
|
||||
// reset prescaler?
|
||||
_regs[R_SYSTEM_CONTROL] = (byte)(_regs[R_SYSTEM_CONTROL] & ~(1 << 4)); // Reset bit 4
|
||||
//_regs[R_SYSTEM_CONTROL] = (byte)(_regs[R_SYSTEM_CONTROL] & ~(1 << 4)); // Reset bit 4
|
||||
|
||||
_intTimer = value * IntPrescaler;
|
||||
|
||||
|
@ -274,20 +490,16 @@ namespace BizHawk.Emulation.Cores.Consoles.SuperVision
|
|||
_regs[regIndex] = value;
|
||||
|
||||
// lcd displayenable
|
||||
_screen.DisplayEnable = value.Bit(3);
|
||||
Screen.DisplayEnable = value.Bit(3);
|
||||
|
||||
// banking
|
||||
_sv.BankSelect = value >> 5;
|
||||
|
||||
// writing to this register resets the LCD rendering system and makes it start rendering from the upper left corner, regardless of the bit pattern.
|
||||
_screen.ResetPosition();
|
||||
Screen.ResetPosition();
|
||||
|
||||
break;
|
||||
|
||||
// IRQ status
|
||||
case 0x27:
|
||||
break;
|
||||
|
||||
// CH4_Freq_Vol (left and right)
|
||||
case 0x28:
|
||||
case 0x2C:
|
||||
|
@ -305,7 +517,8 @@ namespace BizHawk.Emulation.Cores.Consoles.SuperVision
|
|||
|
||||
|
||||
// READONLY
|
||||
case 0x20: // Controller
|
||||
case 0x20: // Controller
|
||||
case 0x27: // IRQ status
|
||||
break;
|
||||
|
||||
// UNKNOWN
|
||||
|
@ -440,6 +653,7 @@ namespace BizHawk.Emulation.Cores.Consoles.SuperVision
|
|||
|
||||
// IRQ timer
|
||||
case 0x23:
|
||||
result = _regs[R_IRQ_TIMER];
|
||||
break;
|
||||
|
||||
// Reset IRQ timer flag
|
||||
|
@ -457,6 +671,11 @@ namespace BizHawk.Emulation.Cores.Consoles.SuperVision
|
|||
|
||||
// IRQ status
|
||||
case 0x27:
|
||||
|
||||
// bit0: DMA Audio System (1 == DMA audio finished)
|
||||
// bit1: IRQ Timer expired (1 == expired)
|
||||
result = _regs[regIndex];
|
||||
|
||||
break;
|
||||
|
||||
// CH4_Freq_Vol (left and right)
|
||||
|
@ -500,18 +719,18 @@ namespace BizHawk.Emulation.Cores.Consoles.SuperVision
|
|||
ser.Sync(nameof(_regs), ref _regs, false);
|
||||
ser.Sync(nameof(FrameStart), ref FrameStart);
|
||||
ser.Sync(nameof(_intTimer), ref _intTimer);
|
||||
ser.Sync(nameof(_intTimerEnabled), ref _intTimerEnabled);
|
||||
ser.Sync(nameof(_intTimerChanged), ref _intTimerChanged);
|
||||
ser.Sync(nameof(_nmiTimer), ref _nmiTimer);
|
||||
ser.Sync(nameof(_intFlag), ref _intFlag);
|
||||
ser.Sync(nameof(_dmaInProgress), ref _dmaInProgress);
|
||||
ser.Sync(nameof(_dmaCounter), ref _dmaCounter);
|
||||
|
||||
ser.Sync(nameof(_seqCounter), ref _seqCounter);
|
||||
ser.Sync(nameof(_byteCounter), ref _byteCounter);
|
||||
ser.Sync(nameof(_lineCounter), ref _lineCounter);
|
||||
ser.Sync(nameof(_field), ref _field);
|
||||
ser.Sync(nameof(_latchedVRAM), ref _latchedVRAM);
|
||||
ser.Sync(nameof(_currentVRAMPointer), ref _currentVRAMPointer);
|
||||
ser.Sync(nameof(_currY), ref _currY);
|
||||
ser.Sync(nameof(_currX), ref _currX);
|
||||
|
||||
_screen.SyncState(ser);
|
||||
ser.Sync(nameof(_vramByteBuffer), ref _vramByteBuffer);
|
||||
Screen.SyncState(ser);
|
||||
|
||||
ser.EndSection();
|
||||
}
|
||||
|
|
|
@ -27,10 +27,13 @@ namespace BizHawk.Emulation.Cores.Consoles.SuperVision
|
|||
|
||||
private readonly int[] _palette = new int[4];
|
||||
|
||||
public const int PEN_BUFFER_WIDTH = 160 * 2;
|
||||
public const int PEN_BUFFER_HEIGHT = 160;
|
||||
|
||||
/// <summary>
|
||||
/// The inbuilt screen is a 160*160 dot 2bpp monochrome LCD
|
||||
/// </summary>
|
||||
private int[] _penBuffer = new int[160 * 160 * 2];
|
||||
private int[] _penBuffer = new int[PEN_BUFFER_WIDTH * PEN_BUFFER_HEIGHT];
|
||||
|
||||
/// <summary>
|
||||
/// The output framebuffer
|
||||
|
@ -90,13 +93,11 @@ namespace BizHawk.Emulation.Cores.Consoles.SuperVision
|
|||
WritePixels(data, framePolarity);
|
||||
|
||||
// setup for next frame
|
||||
_vPos = 0;
|
||||
_hPos = 0;
|
||||
ResetPosition();
|
||||
}
|
||||
else if (lineLatch)
|
||||
{
|
||||
// end of scanline
|
||||
//
|
||||
_hPos = 0;
|
||||
_vPos++;
|
||||
}
|
||||
|
@ -115,13 +116,27 @@ namespace BizHawk.Emulation.Cores.Consoles.SuperVision
|
|||
{
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
_frameBuffer[(_vPos * 160 * 2) + (_hPos + framePolarity)] = (data >> i) & 0x01;
|
||||
if (_hPos < PEN_BUFFER_WIDTH && _vPos < PEN_BUFFER_HEIGHT)
|
||||
{
|
||||
_penBuffer[(_vPos * 160 * 2) + (_hPos + framePolarity)] = (data >> i) & 0x01;
|
||||
}
|
||||
else
|
||||
{
|
||||
// bits out of bounds of the LCD screen
|
||||
// data is discarded
|
||||
}
|
||||
|
||||
_hPos += 2;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetRates(int num, int dom)
|
||||
{
|
||||
VsyncNumerator = num;
|
||||
VsyncDenominator = dom;
|
||||
}
|
||||
|
||||
public int VirtualWidth => BufferWidth;
|
||||
public int VirtualWidth => (int)(BufferWidth * 1.25);
|
||||
public int VirtualHeight => BufferHeight;
|
||||
public int BufferWidth => 160;
|
||||
public int BufferHeight => 160;
|
||||
|
@ -144,6 +159,7 @@ namespace BizHawk.Emulation.Cores.Consoles.SuperVision
|
|||
{
|
||||
ser.BeginSection("LCD");
|
||||
ser.Sync(nameof(_frameBuffer), ref _frameBuffer, false);
|
||||
ser.Sync(nameof(_penBuffer), ref _penBuffer, false);
|
||||
ser.Sync(nameof(_hPos), ref _hPos);
|
||||
ser.Sync(nameof(_vPos), ref _vPos);
|
||||
ser.Sync(nameof(DisplayEnable), ref DisplayEnable);
|
||||
|
|
|
@ -11,9 +11,7 @@ namespace BizHawk.Emulation.Cores.Consoles.SuperVision
|
|||
/// <summary>
|
||||
/// 8K of VRAM which the CPU can access with 0 wait states
|
||||
/// </summary>
|
||||
public byte[] VRAM = new byte[0x2000];
|
||||
|
||||
|
||||
public byte[] VRAM = new byte[0x2000];
|
||||
|
||||
/// <summary>
|
||||
/// Bank select index
|
||||
|
@ -30,53 +28,54 @@ namespace BizHawk.Emulation.Cores.Consoles.SuperVision
|
|||
_cpuMemoryAccess = true;
|
||||
byte result = 0xFF;
|
||||
|
||||
if (address < 0x2000)
|
||||
var divider = address / 0x2000;
|
||||
|
||||
switch (divider)
|
||||
{
|
||||
// RAM
|
||||
return WRAM[address];
|
||||
}
|
||||
case 0:
|
||||
// WRAM
|
||||
result = WRAM[address];
|
||||
break;
|
||||
|
||||
if (address < 0x4000)
|
||||
{
|
||||
// port access
|
||||
return ReadHardware(address);
|
||||
}
|
||||
case 1:
|
||||
// IO address space
|
||||
break;
|
||||
|
||||
if (address < 0x6000)
|
||||
{
|
||||
// VRAM
|
||||
return VRAM[address - 0x4000];
|
||||
}
|
||||
case 2:
|
||||
// VRAM
|
||||
result = VRAM[address - 0x4000];
|
||||
break;
|
||||
|
||||
if (address < 0x8000)
|
||||
{
|
||||
// nothing here
|
||||
}
|
||||
case 3:
|
||||
// nothing here
|
||||
break;
|
||||
|
||||
if (address < 0xC000)
|
||||
{
|
||||
// cartridge rom banking
|
||||
// 0x8000 - 0xBFFF is selectable using the 3 bits from the SystemControl register
|
||||
switch (BankSelect)
|
||||
{
|
||||
// first 16k
|
||||
case 0:
|
||||
return _cartridge.ReadByte((ushort)(address % 0x2000));
|
||||
case 4:
|
||||
// cartridge rom banking
|
||||
// 0x8000 - 0xBFFF is selectable using the 3 bits from the SystemControl register
|
||||
switch (BankSelect)
|
||||
{
|
||||
// first 16k
|
||||
case 0:
|
||||
result = _cartridge.ReadByte((ushort) (address % 0x2000));
|
||||
break;
|
||||
|
||||
// second 16k
|
||||
case 1:
|
||||
return _cartridge.ReadByte((ushort)((address % 0x2000) + 0x2000));
|
||||
// second 16k
|
||||
case 1:
|
||||
result = _cartridge.ReadByte((ushort) ((address % 0x2000) + 0x2000));
|
||||
break;
|
||||
|
||||
// third 16k
|
||||
case 2:
|
||||
return _cartridge.ReadByte((ushort)((address % 0x2000) + 0x4000));
|
||||
}
|
||||
}
|
||||
// third 16k
|
||||
case 2:
|
||||
result = _cartridge.ReadByte((ushort) ((address % 0x2000) + 0x4000));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
if (address < 0xFFFF)
|
||||
{
|
||||
// fixed to the last 16K in the cart address space
|
||||
return _cartridge.ReadByte((ushort)((address % 0x2000) + 0x6000));
|
||||
case 5:
|
||||
// fixed to the last 16K in the cart address space
|
||||
result = _cartridge.ReadByte(address);
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
|
@ -18,7 +18,11 @@ namespace BizHawk.Emulation.Cores.Consoles.SuperVision
|
|||
private int _frameClock;
|
||||
private int _frame;
|
||||
|
||||
public int FrameClock => _frameClock;
|
||||
public int FrameClock
|
||||
{
|
||||
get => _frameClock;
|
||||
set => _frameClock = value;
|
||||
}
|
||||
public int Frame => _frame;
|
||||
|
||||
private void CalcClock()
|
||||
|
@ -31,6 +35,10 @@ namespace BizHawk.Emulation.Cores.Consoles.SuperVision
|
|||
2; // fields per frame
|
||||
|
||||
double refreshRate = _cpuClocksPerSecond / _cpuClocksPerFrame; // 50.8130081300813
|
||||
|
||||
_asic.Screen.SetRates(
|
||||
(int) _cpuClocksPerSecond,
|
||||
(int) _cpuClocksPerFrame);
|
||||
}
|
||||
|
||||
public bool FrameAdvance(IController controller, bool render, bool renderSound)
|
||||
|
|
|
@ -28,6 +28,9 @@ namespace BizHawk.Emulation.Cores.Consoles.SuperVision
|
|||
_tracer = new TraceBuffer(_cpu.TraceHeader);
|
||||
_asic = new ASIC(this, _syncSettings);
|
||||
|
||||
CalcClock();
|
||||
|
||||
ser.Register<IVideoProvider>(_asic.Screen);
|
||||
ser.Register<ITraceable>(_tracer);
|
||||
ser.Register<IDisassemblable>(_cpu);
|
||||
ser.Register<IStatable>(new StateSerializer(SyncState));
|
||||
|
|
Loading…
Reference in New Issue