More video, DMA and interrupt work
This commit is contained in:
parent
875a7d808c
commit
e8a2093a74
|
@ -84,6 +84,8 @@ namespace BizHawk.Client.Common
|
||||||
["ChannelF"] = 234375.0 / 3872.0, // (NTSCCarrier * 8 / 7) / (256 * 264)
|
["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)
|
["ChannelF_PAL"] = 15625.0 / 312.0, // 4000000 / (256 * 312)
|
||||||
|
|
||||||
|
["SuperVision"] = 50.08012820512821
|
||||||
};
|
};
|
||||||
|
|
||||||
public static double GetFrameRate(string systemId, bool pal)
|
public static double GetFrameRate(string systemId, bool pal)
|
||||||
|
|
|
@ -34,7 +34,8 @@ namespace BizHawk.Emulation.Common
|
||||||
new(VSystemID.Raw.O2, "Odyssey2"),
|
new(VSystemID.Raw.O2, "Odyssey2"),
|
||||||
new(VSystemID.Raw.VEC, "Vectrex"),
|
new(VSystemID.Raw.VEC, "Vectrex"),
|
||||||
new(VSystemID.Raw.MSX, "MSX"),
|
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]
|
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_SYSTEM_CONTROL = 0x26;
|
||||||
public const int R_IRQ_STATUS = 0x27;
|
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];
|
private byte[] _regs = new byte[0x30];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The inbuilt LCD screen
|
||||||
|
/// </summary>
|
||||||
|
public LCD Screen;
|
||||||
|
|
||||||
public ASIC(SuperVision sv, SuperVision.SuperVisionSyncSettings ss)
|
public ASIC(SuperVision sv, SuperVision.SuperVisionSyncSettings ss)
|
||||||
{
|
{
|
||||||
_sv = sv;
|
_sv = sv;
|
||||||
_screen = new LCD(ss.ScreenType);
|
Screen = new LCD(ss.ScreenType);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool FrameStart;
|
public bool FrameStart;
|
||||||
private int _intTimer;
|
private int _intTimer;
|
||||||
|
private bool _intTimerEnabled;
|
||||||
|
private bool _intTimerChanged;
|
||||||
private int _nmiTimer;
|
private int _nmiTimer;
|
||||||
private bool _intFlag;
|
private bool _intFlag;
|
||||||
private bool _dmaInProgress;
|
private bool _dmaInProgress;
|
||||||
private int _dmaCounter;
|
private int _dmaCounter;
|
||||||
|
private int _seqCounter;
|
||||||
|
private int _byteCounter;
|
||||||
|
private int _lineCounter;
|
||||||
|
private int _field;
|
||||||
|
private ushort _vramByteBuffer;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// ASIC is clocked at the same rate as the CPU
|
/// ASIC is clocked at the same rate as the CPU
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void Clock()
|
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();
|
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();
|
AudioClock();
|
||||||
|
|
||||||
if (FrameStart)
|
if (FrameStart)
|
||||||
FrameStart = false;
|
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;
|
private int IntPrescaler => _regs[R_SYSTEM_CONTROL].Bit(4) ? 16384 : 256;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -88,16 +192,63 @@ namespace BizHawk.Emulation.Cores.Consoles.SuperVision
|
||||||
_sv._cpu.NMI = true;
|
_sv._cpu.NMI = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
_intTimer--;
|
if (_intTimerChanged)
|
||||||
|
|
||||||
if (_intTimer <= 0)
|
|
||||||
{
|
{
|
||||||
|
// 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>
|
/// <summary>
|
||||||
/// DMA Control
|
/// Check whether DMA needs to start
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void CheckDMA()
|
private void CheckDMA()
|
||||||
{
|
{
|
||||||
|
@ -105,22 +256,46 @@ namespace BizHawk.Emulation.Cores.Consoles.SuperVision
|
||||||
{
|
{
|
||||||
// DMA start requested
|
// DMA start requested
|
||||||
_dmaInProgress = true;
|
_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)
|
if (_dmaInProgress)
|
||||||
|
{
|
||||||
|
_dmaCounter++;
|
||||||
|
|
||||||
|
if (_dmaCounter == 4096 || _dmaCounter == _regs[R_DMA_LENGTH] * 16)
|
||||||
|
{
|
||||||
|
// wraparound or length reached
|
||||||
|
_dmaCounter = 0;
|
||||||
|
_dmaInProgress = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
ushort source = (ushort) (_regs[R_DMA_SOURCE_HIGH] << 8 | _regs[R_DMA_SOURCE_LOW]);
|
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]);
|
ushort dest = (ushort) (_regs[R_DMA_DEST_HIGH] << 8 | _regs[R_DMA_DEST_LOW]);
|
||||||
|
|
||||||
_dmaCounter++;
|
// transfer a byte from source to dest using DMA
|
||||||
|
_sv.WriteMemory(dest, _sv.ReadMemory(source));
|
||||||
|
|
||||||
if (_dmaCounter == 4096)
|
// source registers incremented
|
||||||
{
|
source++;
|
||||||
// wrap around
|
_regs[R_DMA_SOURCE_HIGH] = (byte) (source >> 8);
|
||||||
_dmaCounter = 0;
|
_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>
|
/// <summary>
|
||||||
|
@ -135,45 +310,83 @@ namespace BizHawk.Emulation.Cores.Consoles.SuperVision
|
||||||
// LCD_X_Size
|
// LCD_X_Size
|
||||||
case 0x00:
|
case 0x00:
|
||||||
case 0x04:
|
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;
|
break;
|
||||||
|
|
||||||
// LCD_Y_Size
|
// LCD_Y_Size
|
||||||
case 0x01:
|
case 0x01:
|
||||||
case 0x05:
|
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;
|
break;
|
||||||
|
|
||||||
// X_Scroll
|
// X_Scroll
|
||||||
case 0x02:
|
case 0x02:
|
||||||
case 0x06:
|
case 0x06:
|
||||||
|
|
||||||
|
_regs[R_X_SCROLL] = value;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Y_Scroll
|
// Y_Scroll
|
||||||
case 0x03:
|
case 0x03:
|
||||||
case 0x07:
|
case 0x07:
|
||||||
|
|
||||||
|
_regs[R_Y_SCROLL] = value;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// DMA Source low
|
// DMA Source low
|
||||||
case 0x08:
|
case 0x08:
|
||||||
|
|
||||||
|
_regs[R_DMA_SOURCE_LOW] = value;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// DMA Source high
|
// DMA Source high
|
||||||
case 0x09:
|
case 0x09:
|
||||||
|
|
||||||
|
_regs[R_DMA_SOURCE_HIGH] = value;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// DMA Destination low
|
// DMA Destination low
|
||||||
case 0x0A:
|
case 0x0A:
|
||||||
|
|
||||||
|
_regs[R_DMA_DEST_LOW] = value;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// DMA Destination high
|
// DMA Destination high
|
||||||
case 0x0B:
|
case 0x0B:
|
||||||
|
|
||||||
|
_regs[R_DMA_DEST_HIGH] = value;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// DMA Length
|
// DMA Length
|
||||||
case 0x0C:
|
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;
|
break;
|
||||||
|
|
||||||
// DMA Control
|
// DMA Control
|
||||||
case 0x0D:
|
case 0x0D:
|
||||||
|
|
||||||
|
// Start DMA when written with bit7 set
|
||||||
|
_regs[R_DMA_CONTROL] = value;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// CH1_Flow (right only)
|
// 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 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.
|
// This prescaler can divide the system clock by 256 or 16384.
|
||||||
// 8bits
|
// 8bits
|
||||||
|
_regs[R_IRQ_TIMER] = value;
|
||||||
|
|
||||||
|
_intTimerEnabled = true;
|
||||||
|
|
||||||
// reset prescaler?
|
// 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;
|
_intTimer = value * IntPrescaler;
|
||||||
|
|
||||||
|
@ -274,20 +490,16 @@ namespace BizHawk.Emulation.Cores.Consoles.SuperVision
|
||||||
_regs[regIndex] = value;
|
_regs[regIndex] = value;
|
||||||
|
|
||||||
// lcd displayenable
|
// lcd displayenable
|
||||||
_screen.DisplayEnable = value.Bit(3);
|
Screen.DisplayEnable = value.Bit(3);
|
||||||
|
|
||||||
// banking
|
// banking
|
||||||
_sv.BankSelect = value >> 5;
|
_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.
|
// 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;
|
break;
|
||||||
|
|
||||||
// IRQ status
|
|
||||||
case 0x27:
|
|
||||||
break;
|
|
||||||
|
|
||||||
// CH4_Freq_Vol (left and right)
|
// CH4_Freq_Vol (left and right)
|
||||||
case 0x28:
|
case 0x28:
|
||||||
case 0x2C:
|
case 0x2C:
|
||||||
|
@ -306,6 +518,7 @@ namespace BizHawk.Emulation.Cores.Consoles.SuperVision
|
||||||
|
|
||||||
// READONLY
|
// READONLY
|
||||||
case 0x20: // Controller
|
case 0x20: // Controller
|
||||||
|
case 0x27: // IRQ status
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// UNKNOWN
|
// UNKNOWN
|
||||||
|
@ -440,6 +653,7 @@ namespace BizHawk.Emulation.Cores.Consoles.SuperVision
|
||||||
|
|
||||||
// IRQ timer
|
// IRQ timer
|
||||||
case 0x23:
|
case 0x23:
|
||||||
|
result = _regs[R_IRQ_TIMER];
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Reset IRQ timer flag
|
// Reset IRQ timer flag
|
||||||
|
@ -457,6 +671,11 @@ namespace BizHawk.Emulation.Cores.Consoles.SuperVision
|
||||||
|
|
||||||
// IRQ status
|
// IRQ status
|
||||||
case 0x27:
|
case 0x27:
|
||||||
|
|
||||||
|
// bit0: DMA Audio System (1 == DMA audio finished)
|
||||||
|
// bit1: IRQ Timer expired (1 == expired)
|
||||||
|
result = _regs[regIndex];
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// CH4_Freq_Vol (left and right)
|
// 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(_regs), ref _regs, false);
|
||||||
ser.Sync(nameof(FrameStart), ref FrameStart);
|
ser.Sync(nameof(FrameStart), ref FrameStart);
|
||||||
ser.Sync(nameof(_intTimer), ref _intTimer);
|
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(_nmiTimer), ref _nmiTimer);
|
||||||
ser.Sync(nameof(_intFlag), ref _intFlag);
|
ser.Sync(nameof(_intFlag), ref _intFlag);
|
||||||
ser.Sync(nameof(_dmaInProgress), ref _dmaInProgress);
|
ser.Sync(nameof(_dmaInProgress), ref _dmaInProgress);
|
||||||
ser.Sync(nameof(_dmaCounter), ref _dmaCounter);
|
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(_field), ref _field);
|
||||||
ser.Sync(nameof(_latchedVRAM), ref _latchedVRAM);
|
ser.Sync(nameof(_vramByteBuffer), ref _vramByteBuffer);
|
||||||
ser.Sync(nameof(_currentVRAMPointer), ref _currentVRAMPointer);
|
Screen.SyncState(ser);
|
||||||
ser.Sync(nameof(_currY), ref _currY);
|
|
||||||
ser.Sync(nameof(_currX), ref _currX);
|
|
||||||
|
|
||||||
_screen.SyncState(ser);
|
|
||||||
|
|
||||||
ser.EndSection();
|
ser.EndSection();
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,10 +27,13 @@ namespace BizHawk.Emulation.Cores.Consoles.SuperVision
|
||||||
|
|
||||||
private readonly int[] _palette = new int[4];
|
private readonly int[] _palette = new int[4];
|
||||||
|
|
||||||
|
public const int PEN_BUFFER_WIDTH = 160 * 2;
|
||||||
|
public const int PEN_BUFFER_HEIGHT = 160;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The inbuilt screen is a 160*160 dot 2bpp monochrome LCD
|
/// The inbuilt screen is a 160*160 dot 2bpp monochrome LCD
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private int[] _penBuffer = new int[160 * 160 * 2];
|
private int[] _penBuffer = new int[PEN_BUFFER_WIDTH * PEN_BUFFER_HEIGHT];
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The output framebuffer
|
/// The output framebuffer
|
||||||
|
@ -90,13 +93,11 @@ namespace BizHawk.Emulation.Cores.Consoles.SuperVision
|
||||||
WritePixels(data, framePolarity);
|
WritePixels(data, framePolarity);
|
||||||
|
|
||||||
// setup for next frame
|
// setup for next frame
|
||||||
_vPos = 0;
|
ResetPosition();
|
||||||
_hPos = 0;
|
|
||||||
}
|
}
|
||||||
else if (lineLatch)
|
else if (lineLatch)
|
||||||
{
|
{
|
||||||
// end of scanline
|
// end of scanline
|
||||||
//
|
|
||||||
_hPos = 0;
|
_hPos = 0;
|
||||||
_vPos++;
|
_vPos++;
|
||||||
}
|
}
|
||||||
|
@ -115,13 +116,27 @@ namespace BizHawk.Emulation.Cores.Consoles.SuperVision
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 4; i++)
|
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;
|
_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 VirtualHeight => BufferHeight;
|
||||||
public int BufferWidth => 160;
|
public int BufferWidth => 160;
|
||||||
public int BufferHeight => 160;
|
public int BufferHeight => 160;
|
||||||
|
@ -144,6 +159,7 @@ namespace BizHawk.Emulation.Cores.Consoles.SuperVision
|
||||||
{
|
{
|
||||||
ser.BeginSection("LCD");
|
ser.BeginSection("LCD");
|
||||||
ser.Sync(nameof(_frameBuffer), ref _frameBuffer, false);
|
ser.Sync(nameof(_frameBuffer), ref _frameBuffer, false);
|
||||||
|
ser.Sync(nameof(_penBuffer), ref _penBuffer, false);
|
||||||
ser.Sync(nameof(_hPos), ref _hPos);
|
ser.Sync(nameof(_hPos), ref _hPos);
|
||||||
ser.Sync(nameof(_vPos), ref _vPos);
|
ser.Sync(nameof(_vPos), ref _vPos);
|
||||||
ser.Sync(nameof(DisplayEnable), ref DisplayEnable);
|
ser.Sync(nameof(DisplayEnable), ref DisplayEnable);
|
||||||
|
|
|
@ -13,8 +13,6 @@ namespace BizHawk.Emulation.Cores.Consoles.SuperVision
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public byte[] VRAM = new byte[0x2000];
|
public byte[] VRAM = new byte[0x2000];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Bank select index
|
/// Bank select index
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -30,53 +28,54 @@ namespace BizHawk.Emulation.Cores.Consoles.SuperVision
|
||||||
_cpuMemoryAccess = true;
|
_cpuMemoryAccess = true;
|
||||||
byte result = 0xFF;
|
byte result = 0xFF;
|
||||||
|
|
||||||
if (address < 0x2000)
|
var divider = address / 0x2000;
|
||||||
{
|
|
||||||
// RAM
|
|
||||||
return WRAM[address];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (address < 0x4000)
|
switch (divider)
|
||||||
{
|
{
|
||||||
// port access
|
case 0:
|
||||||
return ReadHardware(address);
|
// WRAM
|
||||||
}
|
result = WRAM[address];
|
||||||
|
break;
|
||||||
|
|
||||||
if (address < 0x6000)
|
case 1:
|
||||||
{
|
// IO address space
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
// VRAM
|
// VRAM
|
||||||
return VRAM[address - 0x4000];
|
result = VRAM[address - 0x4000];
|
||||||
}
|
break;
|
||||||
|
|
||||||
if (address < 0x8000)
|
case 3:
|
||||||
{
|
|
||||||
// nothing here
|
// nothing here
|
||||||
}
|
break;
|
||||||
|
|
||||||
if (address < 0xC000)
|
case 4:
|
||||||
{
|
|
||||||
// cartridge rom banking
|
// cartridge rom banking
|
||||||
// 0x8000 - 0xBFFF is selectable using the 3 bits from the SystemControl register
|
// 0x8000 - 0xBFFF is selectable using the 3 bits from the SystemControl register
|
||||||
switch (BankSelect)
|
switch (BankSelect)
|
||||||
{
|
{
|
||||||
// first 16k
|
// first 16k
|
||||||
case 0:
|
case 0:
|
||||||
return _cartridge.ReadByte((ushort)(address % 0x2000));
|
result = _cartridge.ReadByte((ushort) (address % 0x2000));
|
||||||
|
break;
|
||||||
|
|
||||||
// second 16k
|
// second 16k
|
||||||
case 1:
|
case 1:
|
||||||
return _cartridge.ReadByte((ushort)((address % 0x2000) + 0x2000));
|
result = _cartridge.ReadByte((ushort) ((address % 0x2000) + 0x2000));
|
||||||
|
break;
|
||||||
|
|
||||||
// third 16k
|
// third 16k
|
||||||
case 2:
|
case 2:
|
||||||
return _cartridge.ReadByte((ushort)((address % 0x2000) + 0x4000));
|
result = _cartridge.ReadByte((ushort) ((address % 0x2000) + 0x4000));
|
||||||
}
|
break;
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
if (address < 0xFFFF)
|
case 5:
|
||||||
{
|
|
||||||
// fixed to the last 16K in the cart address space
|
// fixed to the last 16K in the cart address space
|
||||||
return _cartridge.ReadByte((ushort)((address % 0x2000) + 0x6000));
|
result = _cartridge.ReadByte(address);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|
|
@ -18,7 +18,11 @@ namespace BizHawk.Emulation.Cores.Consoles.SuperVision
|
||||||
private int _frameClock;
|
private int _frameClock;
|
||||||
private int _frame;
|
private int _frame;
|
||||||
|
|
||||||
public int FrameClock => _frameClock;
|
public int FrameClock
|
||||||
|
{
|
||||||
|
get => _frameClock;
|
||||||
|
set => _frameClock = value;
|
||||||
|
}
|
||||||
public int Frame => _frame;
|
public int Frame => _frame;
|
||||||
|
|
||||||
private void CalcClock()
|
private void CalcClock()
|
||||||
|
@ -31,6 +35,10 @@ namespace BizHawk.Emulation.Cores.Consoles.SuperVision
|
||||||
2; // fields per frame
|
2; // fields per frame
|
||||||
|
|
||||||
double refreshRate = _cpuClocksPerSecond / _cpuClocksPerFrame; // 50.8130081300813
|
double refreshRate = _cpuClocksPerSecond / _cpuClocksPerFrame; // 50.8130081300813
|
||||||
|
|
||||||
|
_asic.Screen.SetRates(
|
||||||
|
(int) _cpuClocksPerSecond,
|
||||||
|
(int) _cpuClocksPerFrame);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool FrameAdvance(IController controller, bool render, bool renderSound)
|
public bool FrameAdvance(IController controller, bool render, bool renderSound)
|
||||||
|
|
|
@ -28,6 +28,9 @@ namespace BizHawk.Emulation.Cores.Consoles.SuperVision
|
||||||
_tracer = new TraceBuffer(_cpu.TraceHeader);
|
_tracer = new TraceBuffer(_cpu.TraceHeader);
|
||||||
_asic = new ASIC(this, _syncSettings);
|
_asic = new ASIC(this, _syncSettings);
|
||||||
|
|
||||||
|
CalcClock();
|
||||||
|
|
||||||
|
ser.Register<IVideoProvider>(_asic.Screen);
|
||||||
ser.Register<ITraceable>(_tracer);
|
ser.Register<ITraceable>(_tracer);
|
||||||
ser.Register<IDisassemblable>(_cpu);
|
ser.Register<IDisassemblable>(_cpu);
|
||||||
ser.Register<IStatable>(new StateSerializer(SyncState));
|
ser.Register<IStatable>(new StateSerializer(SyncState));
|
||||||
|
|
Loading…
Reference in New Issue