commodore64: new core with focus on low-level comm between chips and activity on both phases of the clock
This commit is contained in:
parent
76487941be
commit
95d228f413
|
@ -79,38 +79,31 @@
|
|||
<Link>VersionInfo.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="Buffer.cs" />
|
||||
<Compile Include="Computers\Commodore64\1541.cs" />
|
||||
<Compile Include="Computers\Commodore64\C64.core.cs" />
|
||||
<Compile Include="Computers\Commodore64\C64.Core.cs" />
|
||||
<Compile Include="Computers\Commodore64\C64.cs" />
|
||||
<Compile Include="Computers\Commodore64\C64.PeekPoke.cs" />
|
||||
<Compile Include="Computers\Commodore64\Cartridge.cs" />
|
||||
<Compile Include="Computers\Commodore64\CartridgeMappers.cs" />
|
||||
<Compile Include="Computers\Commodore64\Cia.cs" />
|
||||
<Compile Include="Computers\Commodore64\CiaState.cs" />
|
||||
<Compile Include="Computers\Commodore64\D64.cs" />
|
||||
<Compile Include="Computers\Commodore64\DataPort.cs" />
|
||||
<Compile Include="Computers\Commodore64\Disk.cs" />
|
||||
<Compile Include="Computers\Commodore64\G64.cs" />
|
||||
<Compile Include="Computers\Commodore64\IMedia.cs" />
|
||||
<Compile Include="Computers\Commodore64\Input.cs" />
|
||||
<Compile Include="Computers\Commodore64\PRGFile.cs" />
|
||||
<Compile Include="Computers\Commodore64\MemBus.cs" />
|
||||
<Compile Include="Computers\Commodore64\Savestate.cs" />
|
||||
<Compile Include="Computers\Commodore64\SerialCable.cs" />
|
||||
<Compile Include="Computers\Commodore64\Sid.cs" />
|
||||
<Compile Include="Computers\Commodore64\SidEnvelopeGenerator.cs" />
|
||||
<Compile Include="Computers\Commodore64\SidSoundProvider.cs" />
|
||||
<Compile Include="Computers\Commodore64\SidState.cs" />
|
||||
<Compile Include="Computers\Commodore64\SidWaveformCalculator.cs" />
|
||||
<Compile Include="Computers\Commodore64\SidWaveformGenerator.cs" />
|
||||
<Compile Include="Computers\Commodore64\Timing.cs" />
|
||||
<Compile Include="Computers\Commodore64\Via.cs" />
|
||||
<Compile Include="Computers\Commodore64\VicII.cs" />
|
||||
<Compile Include="Computers\Commodore64\VicIIPipeline.cs" />
|
||||
<Compile Include="Computers\Commodore64\VicIIPipelineBuilder.cs" />
|
||||
<Compile Include="Computers\Commodore64\VicIIVideoProvider.cs" />
|
||||
<Compile Include="Computers\Commodore64\VicIIRegs.cs" />
|
||||
<Compile Include="Computers\Commodore64\VicState.cs" />
|
||||
<Compile Include="Computers\Commodore64\Memory.cs" />
|
||||
<Compile Include="Computers\Commodore64\MOS\Chip2114.cs" />
|
||||
<Compile Include="Computers\Commodore64\MOS\Chip23XX.cs" />
|
||||
<Compile Include="Computers\Commodore64\MOS\Chip4864.cs" />
|
||||
<Compile Include="Computers\Commodore64\MOS\IStandardIO.cs" />
|
||||
<Compile Include="Computers\Commodore64\MOS\MOS6510.cs" />
|
||||
<Compile Include="Computers\Commodore64\MOS\MOS6522.cs" />
|
||||
<Compile Include="Computers\Commodore64\Media\D64.cs" />
|
||||
<Compile Include="Computers\Commodore64\Media\Disk.cs" />
|
||||
<Compile Include="Computers\Commodore64\Media\G64.cs" />
|
||||
<Compile Include="Computers\Commodore64\MOS\MOS6526.cs" />
|
||||
<Compile Include="Computers\Commodore64\MOS\MOS6567.cs" />
|
||||
<Compile Include="Computers\Commodore64\MOS\MOS6581.cs" />
|
||||
<Compile Include="Computers\Commodore64\MOS\MOS6569.cs" />
|
||||
<Compile Include="Computers\Commodore64\C64.Savestate.cs" />
|
||||
<Compile Include="Computers\Commodore64\MOS\MOSPLA.cs" />
|
||||
<Compile Include="Computers\Commodore64\MOS\Port.cs" />
|
||||
<Compile Include="Computers\Commodore64\MOS\Sid.cs" />
|
||||
<Compile Include="Computers\Commodore64\MOS\Sid.SoundProvider.cs" />
|
||||
<Compile Include="Computers\Commodore64\MOS\Sid.SyncSoundProvider.cs" />
|
||||
<Compile Include="Computers\Commodore64\MOS\Timer.cs" />
|
||||
<Compile Include="Computers\Commodore64\MOS\Vic.cs" />
|
||||
<Compile Include="Computers\Commodore64\MOS\Vic.VideoProvider.cs" />
|
||||
<Compile Include="Consoles\Atari\2600\Atari2600.cs" />
|
||||
<Compile Include="Consoles\Atari\2600\Atari2600.Core.cs" />
|
||||
<Compile Include="Consoles\Atari\2600\Mappers\m3Fe.cs" />
|
||||
|
@ -559,6 +552,7 @@
|
|||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Computers\Commodore64\docs\" />
|
||||
<Folder Include="Computers\Commodore64\Peripheral\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<ItemGroup>
|
||||
|
|
|
@ -1,210 +0,0 @@
|
|||
using BizHawk.Emulation.CPUs.M6502;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace BizHawk.Emulation.Computers.Commodore64
|
||||
{
|
||||
public class Drive1541
|
||||
{
|
||||
// the 1541 drive:
|
||||
//
|
||||
// 2kb ram, mapped 0000-07FF
|
||||
// two 6522 VIA chips, mapped at 1800 (communication to C64) and 1C00 (drive mechanics)
|
||||
// drive ROM, mapped C000-FFFF
|
||||
|
||||
// default 1800:
|
||||
// 07 00 1A FF 05 00 FF FF
|
||||
// 04 00 00 00 00 00 00 00
|
||||
// default 1C00:
|
||||
// 90 00 00 00 05 00 FF FF
|
||||
// 04 00 00 00 00 00 80 00
|
||||
|
||||
private Cia cia;
|
||||
|
||||
public MOS6502X cpu;
|
||||
public int cyclesPerRevolution;
|
||||
public int cyclesPerSecond;
|
||||
public Disk disk;
|
||||
public byte[] ram;
|
||||
public byte[] rom;
|
||||
public double rpm;
|
||||
public ChipSignals signal;
|
||||
public Via via0;
|
||||
public Via via1;
|
||||
|
||||
public Drive1541(byte[] driveRom, Region driveRegion, Cia ciaInterface)
|
||||
{
|
||||
rom = new byte[driveRom.Length];
|
||||
Array.Copy(driveRom, rom, driveRom.Length);
|
||||
|
||||
cia = ciaInterface;
|
||||
|
||||
switch (driveRegion)
|
||||
{
|
||||
case Region.NTSC:
|
||||
cyclesPerSecond = 14318181 / 14;
|
||||
break;
|
||||
case Region.PAL:
|
||||
cyclesPerSecond = 14318181 / 18;
|
||||
break;
|
||||
}
|
||||
|
||||
HardReset();
|
||||
}
|
||||
|
||||
public void Eject()
|
||||
{
|
||||
disk = null;
|
||||
}
|
||||
|
||||
public void HardReset()
|
||||
{
|
||||
cpu = new MOS6502X();
|
||||
cpu.PC = (ushort)(Read(0xFFFC) + (Read(0xFFFD) << 8));
|
||||
cpu.ReadMemory = Read;
|
||||
cpu.WriteMemory = Write;
|
||||
cpu.DummyReadMemory = Read;
|
||||
|
||||
ram = new byte[0x800];
|
||||
via0 = new Via();
|
||||
via1 = new Via();
|
||||
SetRPM(300.0);
|
||||
|
||||
// attach VIA/CIA
|
||||
via0.Connect(cia.ConnectSerialPort(1));
|
||||
|
||||
// set VIA values
|
||||
via0.Poke(0x0, 0x07);
|
||||
via0.Poke(0x2, 0x1A);
|
||||
via0.Poke(0x3, 0xFF);
|
||||
via0.Poke(0x4, 0x05);
|
||||
via0.Poke(0x6, 0xFF);
|
||||
via0.Poke(0x7, 0xFF);
|
||||
via0.Poke(0x8, 0x04);
|
||||
via0.Poke(0xE, 0x80);
|
||||
via1.Poke(0x0, 0x90);
|
||||
via1.Poke(0x4, 0x05);
|
||||
via1.Poke(0x6, 0xFF);
|
||||
via1.Poke(0x7, 0xFF);
|
||||
via1.Poke(0x8, 0x04);
|
||||
via1.Poke(0xE, 0x80);
|
||||
}
|
||||
|
||||
public void Insert(Disk newDisk)
|
||||
{
|
||||
disk = newDisk;
|
||||
}
|
||||
|
||||
public byte Peek(int addr)
|
||||
{
|
||||
addr &= 0xFFFF;
|
||||
if (addr < 0x0800)
|
||||
{
|
||||
return ram[addr];
|
||||
}
|
||||
else if (addr >= 0x1800 && addr < 0x1C00)
|
||||
{
|
||||
return via0.Peek(addr);
|
||||
}
|
||||
else if (addr >= 0x1C00 && addr < 0x2000)
|
||||
{
|
||||
return via1.Peek(addr);
|
||||
}
|
||||
else if (addr >= 0xC000)
|
||||
{
|
||||
return rom[addr & 0x3FFF];
|
||||
}
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
public byte PeekVia0(int addr)
|
||||
{
|
||||
return via0.Peek(addr);
|
||||
}
|
||||
|
||||
public byte PeekVia1(int addr)
|
||||
{
|
||||
return via1.Peek(addr);
|
||||
}
|
||||
|
||||
public void PerformCycle()
|
||||
{
|
||||
cpu.IRQ = via0.IRQ | via1.IRQ;
|
||||
cpu.ExecuteOne();
|
||||
via0.PerformCycle();
|
||||
via1.PerformCycle();
|
||||
}
|
||||
|
||||
public void Poke(int addr, byte val)
|
||||
{
|
||||
addr &= 0xFFFF;
|
||||
if (addr < 0x0800)
|
||||
{
|
||||
ram[addr] = val;
|
||||
}
|
||||
else if (addr >= 0x1800 && addr < 0x1C00)
|
||||
{
|
||||
via0.Poke(addr, val);
|
||||
}
|
||||
else if (addr >= 0x1C00 && addr < 0x2000)
|
||||
{
|
||||
via1.Poke(addr, val);
|
||||
}
|
||||
}
|
||||
|
||||
public void PokeVia0(int addr, byte val)
|
||||
{
|
||||
via0.Poke(addr, val);
|
||||
}
|
||||
|
||||
public void PokeVia1(int addr, byte val)
|
||||
{
|
||||
via1.Poke(addr, val);
|
||||
}
|
||||
|
||||
public byte Read(ushort addr)
|
||||
{
|
||||
if (addr < 0x0800)
|
||||
{
|
||||
return ram[addr];
|
||||
}
|
||||
else if (addr >= 0x1800 && addr < 0x1C00)
|
||||
{
|
||||
return via0.Read(addr);
|
||||
}
|
||||
else if (addr >= 0x1C00 && addr < 0x2000)
|
||||
{
|
||||
return via1.Read(addr);
|
||||
}
|
||||
else if (addr >= 0xC000)
|
||||
{
|
||||
return rom[addr & 0x3FFF];
|
||||
}
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
public void SetRPM(double newRPM)
|
||||
{
|
||||
rpm = newRPM;
|
||||
cyclesPerRevolution = (int)((double)cyclesPerSecond / newRPM / (double)60);
|
||||
}
|
||||
|
||||
public void Write(ushort addr, byte val)
|
||||
{
|
||||
if (addr < 0x0800)
|
||||
{
|
||||
ram[addr] = val;
|
||||
}
|
||||
else if (addr >= 0x1800 && addr < 0x1C00)
|
||||
{
|
||||
via0.Write(addr, val);
|
||||
}
|
||||
else if (addr >= 0x1C00 && addr < 0x2000)
|
||||
{
|
||||
via1.Write(addr, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,179 @@
|
|||
using BizHawk.Emulation.CPUs.M6502;
|
||||
using BizHawk.Emulation.Computers.Commodore64.MOS;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace BizHawk.Emulation.Computers.Commodore64
|
||||
{
|
||||
public enum Region
|
||||
{
|
||||
NTSC,
|
||||
PAL
|
||||
}
|
||||
|
||||
// emulated chips:
|
||||
// U1: 6526 CIA0
|
||||
// U2: 6526 CIA1
|
||||
// U4: KERNAL & BASIC ROM
|
||||
// U5: CHARACTER ROM
|
||||
// U6: 6510 CPU
|
||||
// U7: VIC 6567 (NTSC) or 6569 (PAL)
|
||||
// U8: Memory multiplexer
|
||||
// U9: SID 6581 or 8580
|
||||
// U10: RAM
|
||||
// U11: RAM
|
||||
// U19: 2114 color RAM
|
||||
|
||||
public partial class C64 : IEmulator
|
||||
{
|
||||
// ------------------------------------
|
||||
|
||||
C64Chips chips;
|
||||
|
||||
// ------------------------------------
|
||||
|
||||
private void Init(Region initRegion)
|
||||
{
|
||||
chips = new C64Chips(initRegion);
|
||||
InitRoms();
|
||||
|
||||
// configure video
|
||||
CoreOutputComm.VsyncDen = chips.vic.CyclesPerFrame;
|
||||
CoreOutputComm.VsyncNum = chips.vic.CyclesPerSecond;
|
||||
}
|
||||
|
||||
private void InitRoms()
|
||||
{
|
||||
string sourceFolder = CoreInputComm.C64_FirmwaresPath;
|
||||
if (sourceFolder == null)
|
||||
sourceFolder = @".\C64\Firmwares";
|
||||
|
||||
string basicFile = "basic";
|
||||
string charFile = "chargen";
|
||||
string kernalFile = "kernal";
|
||||
|
||||
string basicPath = Path.Combine(sourceFolder, basicFile);
|
||||
string charPath = Path.Combine(sourceFolder, charFile);
|
||||
string kernalPath = Path.Combine(sourceFolder, kernalFile);
|
||||
|
||||
if (!File.Exists(basicPath)) HandleFirmwareError(basicFile);
|
||||
if (!File.Exists(charPath)) HandleFirmwareError(charFile);
|
||||
if (!File.Exists(kernalPath)) HandleFirmwareError(kernalFile);
|
||||
|
||||
byte[] basicRom = File.ReadAllBytes(basicPath);
|
||||
byte[] charRom = File.ReadAllBytes(charPath);
|
||||
byte[] kernalRom = File.ReadAllBytes(kernalPath);
|
||||
|
||||
chips.basicRom = new Chip23XX(Chip23XXmodel.Chip2364, basicRom);
|
||||
chips.kernalRom = new Chip23XX(Chip23XXmodel.Chip2364, kernalRom);
|
||||
chips.charRom = new Chip23XX(Chip23XXmodel.Chip2332, charRom);
|
||||
}
|
||||
|
||||
// ------------------------------------
|
||||
|
||||
public bool DriveLED
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void Execute(uint count)
|
||||
{
|
||||
for (; count > 0; count--)
|
||||
{
|
||||
chips.ExecutePhase1();
|
||||
chips.ExecutePhase2();
|
||||
}
|
||||
}
|
||||
|
||||
public void HardReset()
|
||||
{
|
||||
chips.HardReset();
|
||||
}
|
||||
|
||||
private byte Peek(int addr)
|
||||
{
|
||||
return chips.cpu.Peek(addr);
|
||||
}
|
||||
|
||||
private void Poke(int addr, byte val)
|
||||
{
|
||||
chips.cpu.Poke(addr, val);
|
||||
}
|
||||
|
||||
// ------------------------------------
|
||||
}
|
||||
|
||||
public class C64Chips
|
||||
{
|
||||
public Chip23XX basicRom; //u4
|
||||
public Chip23XX charRom; //u5
|
||||
public MOS6526 cia0; //u1
|
||||
public MOS6526 cia1; //u2
|
||||
public Chip2114 colorRam; //u19
|
||||
public MOS6510 cpu; //u6
|
||||
public Chip23XX kernalRom; //u4
|
||||
public MOSPLA pla; //
|
||||
public Chip4864 ram; //u10+11
|
||||
public Sid sid; //u9
|
||||
public Vic vic; //u7
|
||||
|
||||
public C64Chips(Region initRegion)
|
||||
{
|
||||
cia0 = new MOS6526(initRegion);
|
||||
cia1 = new MOS6526(initRegion);
|
||||
pla = new MOSPLA(this, cia1.ReadPort0);
|
||||
switch (initRegion)
|
||||
{
|
||||
case Region.NTSC:
|
||||
vic = new MOS6567(this);
|
||||
break;
|
||||
case Region.PAL:
|
||||
vic = new MOS6569(this);
|
||||
break;
|
||||
}
|
||||
colorRam = new Chip2114();
|
||||
cpu = new MOS6510(this);
|
||||
ram = new Chip4864();
|
||||
sid = new MOS6581();
|
||||
}
|
||||
|
||||
public void ExecutePhase1()
|
||||
{
|
||||
cia0.ExecutePhase1();
|
||||
cia1.ExecutePhase1();
|
||||
sid.ExecutePhase1();
|
||||
vic.ExecutePhase1();
|
||||
cpu.ExecutePhase1();
|
||||
}
|
||||
|
||||
public void ExecutePhase2()
|
||||
{
|
||||
cia0.ExecutePhase2();
|
||||
cia1.ExecutePhase2();
|
||||
sid.ExecutePhase2();
|
||||
vic.ExecutePhase2();
|
||||
cpu.ExecutePhase2();
|
||||
}
|
||||
|
||||
public void HardReset()
|
||||
{
|
||||
// note about hard reset: NOT identical to cold start
|
||||
|
||||
// reset all chips
|
||||
cia0.HardReset();
|
||||
cia1.HardReset();
|
||||
colorRam.HardReset();
|
||||
cpu.HardReset();
|
||||
pla.HardReset();
|
||||
ram.HardReset();
|
||||
sid.HardReset();
|
||||
vic.HardReset();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,124 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace BizHawk.Emulation.Computers.Commodore64
|
||||
{
|
||||
public partial class C64 : IEmulator
|
||||
{
|
||||
public byte PeekCia0(int addr)
|
||||
{
|
||||
return cia0.Peek(addr);
|
||||
}
|
||||
|
||||
public byte PeekCia1(int addr)
|
||||
{
|
||||
return cia1.Peek(addr);
|
||||
}
|
||||
|
||||
public byte PeekColorRAM(int addr)
|
||||
{
|
||||
return (byte)(mem.colorRam[addr & 0x3FF] & 0xF);
|
||||
}
|
||||
|
||||
public byte PeekDiskDrive(int addr)
|
||||
{
|
||||
if (diskDriveAttached)
|
||||
return diskDrive.Peek(addr);
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
public byte PeekMemory(ushort addr)
|
||||
{
|
||||
return mem.Peek(addr);
|
||||
}
|
||||
|
||||
public byte PeekMemoryInt(int addr)
|
||||
{
|
||||
return mem.Peek((ushort)(addr & 0xFFFF));
|
||||
}
|
||||
|
||||
public byte PeekRAM(int addr)
|
||||
{
|
||||
return mem.ram[addr];
|
||||
}
|
||||
|
||||
public byte PeekSid(int addr)
|
||||
{
|
||||
return sid.regs[addr];
|
||||
}
|
||||
|
||||
public byte PeekVia0(int addr)
|
||||
{
|
||||
if (diskDriveAttached)
|
||||
return diskDrive.PeekVia0(addr);
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
public byte PeekVia1(int addr)
|
||||
{
|
||||
if (diskDriveAttached)
|
||||
return diskDrive.PeekVia1(addr);
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
public byte PeekVic(int addr)
|
||||
{
|
||||
return vic.Peek(addr);
|
||||
}
|
||||
|
||||
public void PokeCia0(int addr, byte val)
|
||||
{
|
||||
cia0.Poke(addr, val);
|
||||
}
|
||||
|
||||
public void PokeCia1(int addr, byte val)
|
||||
{
|
||||
cia1.Poke(addr, val);
|
||||
}
|
||||
|
||||
public void PokeColorRAM(int addr, byte val)
|
||||
{
|
||||
mem.colorRam[addr & 0x3FF] = (byte)(val & 0xF);
|
||||
}
|
||||
|
||||
public void PokeDiskDrive(int addr, byte val)
|
||||
{
|
||||
if (diskDriveAttached)
|
||||
diskDrive.Poke(addr, val);
|
||||
}
|
||||
|
||||
public void PokeMemoryInt(int addr, byte val)
|
||||
{
|
||||
mem.Poke((ushort)(addr & 0xFFFF), val);
|
||||
}
|
||||
|
||||
public void PokeRAM(int addr, byte val)
|
||||
{
|
||||
mem.PokeRam(addr, val);
|
||||
}
|
||||
|
||||
public void PokeSid(int addr, byte val)
|
||||
{
|
||||
sid.Poke(addr, val);
|
||||
}
|
||||
|
||||
public void PokeVia0(int addr, byte val)
|
||||
{
|
||||
if (diskDriveAttached)
|
||||
diskDrive.PokeVia0(addr, val);
|
||||
}
|
||||
|
||||
public void PokeVia1(int addr, byte val)
|
||||
{
|
||||
if (diskDriveAttached)
|
||||
diskDrive.PokeVia1(addr, val);
|
||||
}
|
||||
|
||||
public void PokeVic(int addr, byte val)
|
||||
{
|
||||
vic.Poke(addr, val);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -55,37 +55,6 @@ namespace BizHawk.Emulation.Computers.Commodore64
|
|||
|
||||
void SyncState(Serializer ser)
|
||||
{
|
||||
// global stuffs
|
||||
ser.BeginSection("GAME");
|
||||
ser.Sync("Lag", ref _lagcount);
|
||||
ser.Sync("Frame", ref _frame);
|
||||
ser.Sync("IsLag", ref _islag);
|
||||
ser.EndSection();
|
||||
|
||||
// cpu creates its own section..
|
||||
cpu.SyncState(ser);
|
||||
|
||||
ser.BeginSection("MEM");
|
||||
mem.SyncState(ser);
|
||||
ser.EndSection();
|
||||
|
||||
ser.BeginSection("VIC");
|
||||
vic.SyncState(ser);
|
||||
ser.EndSection();
|
||||
|
||||
ser.BeginSection("SID");
|
||||
sid.SyncState(ser);
|
||||
ser.EndSection();
|
||||
|
||||
ser.BeginSection("CIA0");
|
||||
cia0.SyncState(ser);
|
||||
ser.EndSection();
|
||||
|
||||
ser.BeginSection("CIA1");
|
||||
cia1.SyncState(ser);
|
||||
ser.EndSection();
|
||||
|
||||
// TODO: drive
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,201 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using BizHawk.Emulation.CPUs.M6502;
|
||||
|
||||
namespace BizHawk.Emulation.Computers.Commodore64
|
||||
{
|
||||
public enum Region
|
||||
{
|
||||
NTSC,
|
||||
PAL
|
||||
}
|
||||
|
||||
public partial class C64 : IEmulator
|
||||
{
|
||||
// input
|
||||
public Input input;
|
||||
|
||||
// source
|
||||
public Cartridge cart = null;
|
||||
public Drive1541 diskDrive = null;
|
||||
public bool diskDriveAttached = false;
|
||||
public string extension;
|
||||
public byte[] inputFile;
|
||||
public List<IMedia> mediaAttached = new List<IMedia>();
|
||||
|
||||
// chipset
|
||||
public Cia cia0;
|
||||
public Cia cia1;
|
||||
public MOS6502X cpu;
|
||||
public Memory mem;
|
||||
public Sid sid;
|
||||
public VicII vic;
|
||||
public ChipSignals signal;
|
||||
|
||||
// cpu
|
||||
private bool haltCPU;
|
||||
|
||||
public bool DriveLED
|
||||
{
|
||||
get
|
||||
{
|
||||
if (diskDriveAttached)
|
||||
{
|
||||
return (diskDrive.Peek(0x1C00) & 0x8) != 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void HardReset()
|
||||
{
|
||||
mem.HardReset();
|
||||
cia0.HardReset();
|
||||
cia1.HardReset();
|
||||
vic.HardReset();
|
||||
sid.HardReset();
|
||||
if (diskDriveAttached)
|
||||
diskDrive.HardReset();
|
||||
}
|
||||
|
||||
private void Init(Region initRegion)
|
||||
{
|
||||
// initalize cpu
|
||||
cpu = new MOS6502X();
|
||||
cpu.ReadMemory = ReadMemoryCPU;
|
||||
cpu.WriteMemory = WriteMemory;
|
||||
cpu.DummyReadMemory = PeekMemory;
|
||||
|
||||
// initialize cia timers
|
||||
cia0 = new Cia(initRegion);
|
||||
cia1 = new Cia(initRegion);
|
||||
|
||||
// initialize vic
|
||||
signal = new ChipSignals();
|
||||
vic = new VicII(signal, initRegion);
|
||||
|
||||
// set vsync rate
|
||||
switch (initRegion)
|
||||
{
|
||||
case Region.NTSC:
|
||||
CoreOutputComm.VsyncDen = vic.CyclesPerFrame * 14;
|
||||
CoreOutputComm.VsyncNum = 14318181;
|
||||
break;
|
||||
case Region.PAL:
|
||||
CoreOutputComm.VsyncDen = vic.CyclesPerFrame * 18;
|
||||
CoreOutputComm.VsyncNum = 17734472;
|
||||
break;
|
||||
}
|
||||
|
||||
// initialize sid
|
||||
sid = new Sid(initRegion, 44100); // we'll assume 44.1k for now until there's a better way
|
||||
|
||||
// initialize memory (this must be done AFTER all other chips are initialized)
|
||||
string romPath = CoreInputComm.C64_FirmwaresPath;
|
||||
if (romPath == null)
|
||||
{
|
||||
romPath = @".\C64\Firmwares";
|
||||
}
|
||||
mem = new Memory(romPath, vic, sid, cia0, cia1);
|
||||
vic.mem = mem;
|
||||
|
||||
// initialize cpu hard reset vector
|
||||
cpu.PC = (ushort)(ReadMemory(0xFFFC) + (ReadMemory(0xFFFD) << 8));
|
||||
cpu.BCD_Enabled = true;
|
||||
|
||||
// initailize input
|
||||
input = new Input(new DataPortConnector[] { cia0.ConnectPort(0), cia0.ConnectPort(1) });
|
||||
cia0.AttachWriteHook(0, input.WritePortA);
|
||||
cia0.AttachWriteHook(1, input.WritePortB);
|
||||
|
||||
// initialize media
|
||||
switch (extension.ToUpper())
|
||||
{
|
||||
case @".G64":
|
||||
diskDrive = new Drive1541(File.ReadAllBytes(Path.Combine(romPath, @"dos1541")), initRegion, cia1);
|
||||
diskDrive.Insert(G64.Read(inputFile));
|
||||
break;
|
||||
case @".D64":
|
||||
diskDrive = new Drive1541(File.ReadAllBytes(Path.Combine(romPath, @"dos1541")), initRegion, cia1);
|
||||
diskDrive.Insert(D64.Read(inputFile));
|
||||
break;
|
||||
case @".PRG":
|
||||
if (inputFile.Length > 2)
|
||||
mediaAttached.Add(new PRGFile(inputFile, mem, cpu));
|
||||
break;
|
||||
case @".CRT":
|
||||
Cartridge newCart = new Cartridge(inputFile, mem);
|
||||
if (newCart.valid)
|
||||
{
|
||||
cart = newCart;
|
||||
mediaAttached.Add(cart);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
diskDriveAttached = (diskDrive != null);
|
||||
}
|
||||
|
||||
public void PollInput()
|
||||
{
|
||||
input.Poll();
|
||||
signal.KeyboardNMI = input.restorePressed;
|
||||
}
|
||||
|
||||
public byte ReadMemory(ushort addr)
|
||||
{
|
||||
return mem.Read(addr);
|
||||
}
|
||||
|
||||
private byte ReadMemoryCPU(ushort addr)
|
||||
{
|
||||
if (!signal.CpuRDY || !signal.CpuAEC)
|
||||
haltCPU = true;
|
||||
return mem.Read(addr);
|
||||
}
|
||||
|
||||
public void WriteMemory(ushort addr, byte value)
|
||||
{
|
||||
mem.Write(addr, value);
|
||||
}
|
||||
|
||||
public void WriteMemoryCPU(ushort addr, byte value)
|
||||
{
|
||||
if (!signal.CpuAEC)
|
||||
haltCPU = true;
|
||||
mem.Write(addr, value);
|
||||
}
|
||||
}
|
||||
|
||||
public class ChipSignals
|
||||
{
|
||||
private bool[] _CiaSerialInput = new bool[2];
|
||||
private bool[] _CiaIRQOutput = new bool[2];
|
||||
private bool _KeyboardNMIOutput;
|
||||
private bool _VicAECOutput;
|
||||
private bool _VicBAOutput;
|
||||
private bool _VicIRQOutput;
|
||||
private bool _VicLPInput;
|
||||
|
||||
public bool CiaIRQ0 { get { return _CiaIRQOutput[0]; } set { _CiaIRQOutput[0] = value; } }
|
||||
public bool CiaIRQ1 { get { return _CiaIRQOutput[1]; } set { _CiaIRQOutput[1] = value; } }
|
||||
public bool CiaSerial0 { get { return _CiaSerialInput[0]; } }
|
||||
public bool CiaSerial1 { get { return _CiaSerialInput[1]; } }
|
||||
public bool CpuAEC { get { return _VicAECOutput; } }
|
||||
public bool CpuIRQ { get { return _VicIRQOutput | _CiaIRQOutput[0]; } }
|
||||
public bool CpuNMI { get { return _CiaIRQOutput[1] | _KeyboardNMIOutput; } }
|
||||
public bool CpuRDY { get { return !_VicBAOutput; } }
|
||||
public bool KeyboardNMI { get { return _KeyboardNMIOutput; } set { _KeyboardNMIOutput = value; } }
|
||||
public bool LPOutput { get { return _VicLPInput; } set { _VicLPInput = value; } }
|
||||
public bool VicAEC { get { return _VicAECOutput; } set { _VicAECOutput = value; } }
|
||||
public bool VicBA { get { return _VicBAOutput; } set { _VicBAOutput = value; } }
|
||||
public bool VicIRQ { get { return _VicIRQOutput; } set { _VicIRQOutput = value; } }
|
||||
public bool VicLP { get { return _VicLPInput; } }
|
||||
}
|
||||
}
|
|
@ -9,6 +9,10 @@ namespace BizHawk.Emulation.Computers.Commodore64
|
|||
{
|
||||
public partial class C64 : IEmulator
|
||||
{
|
||||
private uint cyclesPerFrame;
|
||||
private string extension;
|
||||
private byte[] inputFile;
|
||||
|
||||
public C64(GameInfo game, byte[] rom, string romextension)
|
||||
{
|
||||
inputFile = rom;
|
||||
|
@ -17,6 +21,7 @@ namespace BizHawk.Emulation.Computers.Commodore64
|
|||
CoreOutputComm = new CoreOutputComm();
|
||||
CoreInputComm = new CoreInputComm();
|
||||
Init(Region.PAL);
|
||||
cyclesPerFrame = (uint)chips.vic.CyclesPerFrame;
|
||||
CoreOutputComm.UsesDriveLed = true;
|
||||
}
|
||||
|
||||
|
@ -52,14 +57,14 @@ namespace BizHawk.Emulation.Computers.Commodore64
|
|||
|
||||
// audio/video
|
||||
public void EndAsyncSound() { } //TODO
|
||||
public ISoundProvider SoundProvider { get { return sid; } }
|
||||
public ISoundProvider SoundProvider { get { return chips.sid; } }
|
||||
public bool StartAsyncSound() { return true; } //TODO
|
||||
public ISyncSoundProvider SyncSoundProvider { get { return new SidSyncSoundProvider(sid); } }
|
||||
public IVideoProvider VideoProvider { get { return vic; } }
|
||||
public ISyncSoundProvider SyncSoundProvider { get { return chips.sid; } }
|
||||
public IVideoProvider VideoProvider { get { return chips.vic; } }
|
||||
|
||||
// controller
|
||||
public ControllerDefinition ControllerDefinition { get { return C64ControllerDefinition; } }
|
||||
public IController Controller { get { return input.controller; } set { input.controller = value; } }
|
||||
public IController Controller { get { return null; } set { } }
|
||||
public static readonly ControllerDefinition C64ControllerDefinition = new ControllerDefinition
|
||||
{
|
||||
Name = "Commodore 64 Controller", //TODO
|
||||
|
@ -84,59 +89,23 @@ namespace BizHawk.Emulation.Computers.Commodore64
|
|||
// process frame
|
||||
public void FrameAdvance(bool render, bool rendersound)
|
||||
{
|
||||
int cyclesPerFrame = vic.CyclesPerFrame;
|
||||
|
||||
// bizhawk interface setup
|
||||
_frame++;
|
||||
mem.inputWasRead = false;
|
||||
|
||||
// apply any media if needed
|
||||
foreach (IMedia media in mediaAttached)
|
||||
{
|
||||
if (!media.Loaded() && media.Ready())
|
||||
{
|
||||
media.Apply();
|
||||
}
|
||||
}
|
||||
|
||||
// refresh the input values
|
||||
PollInput();
|
||||
|
||||
// perform the cycle
|
||||
for (int i = 0; i < cyclesPerFrame; i++)
|
||||
{
|
||||
if (!haltCPU)
|
||||
{
|
||||
cpu.IRQ = signal.CpuIRQ;
|
||||
cpu.NMI = signal.CpuNMI;
|
||||
cpu.ExecuteOne();
|
||||
}
|
||||
|
||||
vic.PerformCycle();
|
||||
cia0.PerformCycle();
|
||||
signal.CiaIRQ0 = cia0.IRQ;
|
||||
cia1.PerformCycle();
|
||||
signal.CiaIRQ1 = cia1.IRQ;
|
||||
sid.PerformCycle();
|
||||
|
||||
if (diskDriveAttached)
|
||||
diskDrive.PerformCycle();
|
||||
|
||||
if (signal.CpuAEC)
|
||||
haltCPU = false;
|
||||
|
||||
}
|
||||
|
||||
_islag = !mem.inputWasRead;
|
||||
chips.pla.InputWasRead = false;
|
||||
Execute(cyclesPerFrame);
|
||||
_islag = !chips.pla.InputWasRead;
|
||||
|
||||
if (_islag)
|
||||
{
|
||||
LagCount++;
|
||||
}
|
||||
_frame++;
|
||||
|
||||
CoreOutputComm.DriveLED = DriveLED;
|
||||
}
|
||||
|
||||
private void HandleFirmwareError(string file)
|
||||
{
|
||||
System.Windows.Forms.MessageBox.Show("the C64 core is referencing a firmware file which could not be found. Please make sure it's in your configured C64 firmwares folder. The referenced filename is: " + file);
|
||||
throw new FileNotFoundException();
|
||||
}
|
||||
|
||||
public byte[] SaveStateBinary()
|
||||
{
|
||||
MemoryStream ms = new MemoryStream();
|
||||
|
@ -149,16 +118,7 @@ namespace BizHawk.Emulation.Computers.Commodore64
|
|||
private void SetupMemoryDomains()
|
||||
{
|
||||
var domains = new List<MemoryDomain>(1);
|
||||
domains.Add(new MemoryDomain("System Bus", 0x10000, Endian.Little, new Func<int, byte>(PeekMemoryInt), new Action<int, byte>(PokeMemoryInt)));
|
||||
domains.Add(new MemoryDomain("RAM", 0x10000, Endian.Little, new Func<int, byte>(PeekRAM), new Action<int, byte>(PokeRAM)));
|
||||
domains.Add(new MemoryDomain("CIA0", 0x10, Endian.Little, new Func<int, byte>(PeekCia0), new Action<int, byte>(PokeCia0)));
|
||||
domains.Add(new MemoryDomain("CIA1", 0x10, Endian.Little, new Func<int, byte>(PeekCia1), new Action<int, byte>(PokeCia1)));
|
||||
domains.Add(new MemoryDomain("SID", 0x20, Endian.Little, new Func<int, byte>(PeekSid), new Action<int, byte>(PokeSid)));
|
||||
domains.Add(new MemoryDomain("VIC", 0x40, Endian.Little, new Func<int, byte>(PeekVic), new Action<int, byte>(PokeVic)));
|
||||
domains.Add(new MemoryDomain("CRAM", 0x400, Endian.Little, new Func<int, byte>(PeekColorRAM), new Action<int, byte>(PokeColorRAM)));
|
||||
domains.Add(new MemoryDomain("DISKRAM", 0x10000, Endian.Little, new Func<int, byte>(PeekDiskDrive), new Action<int, byte>(PokeDiskDrive)));
|
||||
domains.Add(new MemoryDomain("DISKVIA0", 0x10, Endian.Little, new Func<int, byte>(PeekVia0), new Action<int, byte>(PokeVia0)));
|
||||
domains.Add(new MemoryDomain("DISKVIA1", 0x10, Endian.Little, new Func<int, byte>(PeekVia1), new Action<int, byte>(PokeVia1)));
|
||||
domains.Add(new MemoryDomain("System Bus", 0x10000, Endian.Little, new Func<int, byte>(Peek), new Action<int, byte>(Poke)));
|
||||
memoryDomains = domains.AsReadOnly();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,199 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace BizHawk.Emulation.Computers.Commodore64
|
||||
{
|
||||
public class CartridgeChip
|
||||
{
|
||||
public int address;
|
||||
public int bank;
|
||||
public byte[] data;
|
||||
public ushort romMask;
|
||||
public int type;
|
||||
}
|
||||
|
||||
public partial class Cartridge : IMedia
|
||||
{
|
||||
public Func<ushort, byte> Read;
|
||||
public Func<ushort, byte> ReadPort;
|
||||
public Action<ushort, byte> WritePort;
|
||||
|
||||
public List<CartridgeChip> chips;
|
||||
public bool exRomPin;
|
||||
public bool gamePin;
|
||||
public CartridgeChip selectedChip;
|
||||
public int type;
|
||||
public bool valid;
|
||||
public int version;
|
||||
|
||||
private bool loaded;
|
||||
private Memory mem;
|
||||
|
||||
public Cartridge(byte[] rom, Memory memory)
|
||||
{
|
||||
mem = memory;
|
||||
chips = new List<CartridgeChip>();
|
||||
|
||||
if (rom.Length >= 0x50)
|
||||
{
|
||||
MemoryStream source = new MemoryStream(rom);
|
||||
BinaryReader reader = new BinaryReader(source);
|
||||
string idString;
|
||||
|
||||
// note: cartridge files store values big-endian.
|
||||
|
||||
idString = new string(reader.ReadChars(16));
|
||||
if (idString == "C64 CARTRIDGE ")
|
||||
{
|
||||
int headerLength = 0;
|
||||
headerLength = reader.ReadByte();
|
||||
headerLength <<= 8;
|
||||
headerLength |= reader.ReadByte();
|
||||
headerLength <<= 8;
|
||||
headerLength |= reader.ReadByte();
|
||||
headerLength <<= 8;
|
||||
headerLength |= reader.ReadByte();
|
||||
|
||||
version = reader.ReadByte();
|
||||
version <<= 8;
|
||||
version |= reader.ReadByte();
|
||||
|
||||
type = reader.ReadByte();
|
||||
type <<= 8;
|
||||
type |= reader.ReadByte();
|
||||
|
||||
if (type != 0x0000)
|
||||
{
|
||||
// the emulator does not support anything other than type 0 right now
|
||||
valid = false;
|
||||
return;
|
||||
}
|
||||
|
||||
exRomPin = (reader.ReadByte() == 1);
|
||||
gamePin = (reader.ReadByte() == 1);
|
||||
|
||||
reader.ReadBytes(6); // reserved
|
||||
reader.ReadBytes(32); // name
|
||||
|
||||
// skip the rest, don't need this info
|
||||
if (headerLength > 0x40)
|
||||
{
|
||||
reader.ReadBytes(headerLength - 0x40);
|
||||
}
|
||||
|
||||
while (source.Position < rom.Length)
|
||||
{
|
||||
string chipID = new string(reader.ReadChars(4));
|
||||
|
||||
if (chipID == "CHIP")
|
||||
{
|
||||
CartridgeChip chip = new CartridgeChip();
|
||||
|
||||
int packetLength;
|
||||
packetLength = reader.ReadByte();
|
||||
packetLength <<= 8;
|
||||
packetLength |= reader.ReadByte();
|
||||
packetLength <<= 8;
|
||||
packetLength |= reader.ReadByte();
|
||||
packetLength <<= 8;
|
||||
packetLength |= reader.ReadByte();
|
||||
packetLength -= 16;
|
||||
|
||||
chip.type = reader.ReadByte();
|
||||
chip.type <<= 8;
|
||||
chip.type |= reader.ReadByte();
|
||||
|
||||
chip.bank = reader.ReadByte();
|
||||
chip.bank <<= 8;
|
||||
chip.bank |= reader.ReadByte();
|
||||
|
||||
chip.address = reader.ReadByte();
|
||||
chip.address <<= 8;
|
||||
chip.address |= reader.ReadByte();
|
||||
|
||||
int size;
|
||||
size = reader.ReadByte();
|
||||
size <<= 8;
|
||||
size |= reader.ReadByte();
|
||||
|
||||
chip.data = reader.ReadBytes(size);
|
||||
chip.romMask = (ushort)(size - 1);
|
||||
|
||||
packetLength -= size;
|
||||
if (packetLength > 0)
|
||||
{
|
||||
// discard extra bytes
|
||||
reader.ReadBytes(packetLength);
|
||||
}
|
||||
|
||||
chips.Add(chip);
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
valid = (chips.Count > 0);
|
||||
|
||||
if (valid)
|
||||
UpdateMapper();
|
||||
}
|
||||
reader.Close();
|
||||
source.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public void Apply()
|
||||
{
|
||||
mem.cart = this;
|
||||
UpdateRomPins();
|
||||
loaded = true;
|
||||
}
|
||||
|
||||
public bool Loaded()
|
||||
{
|
||||
return loaded;
|
||||
}
|
||||
|
||||
private byte ReadDummy(ushort addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public bool Ready()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
private void UpdateMapper()
|
||||
{
|
||||
Read = ReadDummy;
|
||||
ReadPort = ReadDummy;
|
||||
WritePort = WriteDummy;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case 0x0000:
|
||||
Read = Read0000;
|
||||
ReadPort = ReadPort0000;
|
||||
WritePort = WritePort0000;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateRomPins()
|
||||
{
|
||||
mem.exRomPin = exRomPin;
|
||||
mem.gamePin = gamePin;
|
||||
mem.UpdateLayout();
|
||||
}
|
||||
|
||||
private void WriteDummy(ushort addr, byte val)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,419 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace BizHawk.Emulation.Computers.Commodore64
|
||||
{
|
||||
public partial class Cartridge : IMedia
|
||||
{
|
||||
public int bank;
|
||||
|
||||
private byte Read0000(ushort addr)
|
||||
{
|
||||
// standard cart, no banking
|
||||
CartridgeChip currentChip = chips[0];
|
||||
return currentChip.data[addr & currentChip.romMask];
|
||||
}
|
||||
|
||||
private byte Read0001(ushort addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
private byte Read0002(ushort addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
private byte Read0003(ushort addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
private byte Read0004(ushort addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
private byte Read0005(ushort addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
private byte Read0006(ushort addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
private byte Read0007(ushort addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
private byte Read0008(ushort addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
private byte Read0009(ushort addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
private byte Read000A(ushort addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
private byte Read000B(ushort addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
private byte Read000C(ushort addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
private byte Read000D(ushort addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
private byte Read000E(ushort addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
private byte Read000F(ushort addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
private byte Read0010(ushort addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
private byte Read0011(ushort addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
private byte Read0012(ushort addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
private byte Read0013(ushort addr)
|
||||
{
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private byte Read0014(ushort addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
private byte Read0015(ushort addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
private byte Read0016(ushort addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
private byte Read0017(ushort addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
private byte Read0018(ushort addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
private byte Read0019(ushort addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
private byte Read001A(ushort addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
private byte Read001B(ushort addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
private byte ReadPort0000(ushort addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
private byte ReadPort0001(ushort addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
private byte ReadPort0002(ushort addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
private byte ReadPort0003(ushort addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
private byte ReadPort0004(ushort addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
private byte ReadPort0005(ushort addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
private byte ReadPort0006(ushort addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
private byte ReadPort0007(ushort addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
private byte ReadPort0008(ushort addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
private byte ReadPort0009(ushort addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
private byte ReadPort000A(ushort addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
private byte ReadPort000B(ushort addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
private byte ReadPort000C(ushort addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
private byte ReadPort000D(ushort addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
private byte ReadPort000E(ushort addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
private byte ReadPort000F(ushort addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
private byte ReadPort0010(ushort addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
private byte ReadPort0011(ushort addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
private byte ReadPort0012(ushort addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
private byte ReadPort0013(ushort addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
private byte ReadPort0014(ushort addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
private byte ReadPort0015(ushort addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
private byte ReadPort0016(ushort addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
private byte ReadPort0017(ushort addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
private byte ReadPort0018(ushort addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
private byte ReadPort0019(ushort addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
private byte ReadPort001A(ushort addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
private byte ReadPort001B(ushort addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
private void WritePort0000(ushort addr, byte val)
|
||||
{
|
||||
}
|
||||
|
||||
private void WritePort0001(ushort addr, byte val)
|
||||
{
|
||||
}
|
||||
|
||||
private void WritePort0002(ushort addr, byte val)
|
||||
{
|
||||
}
|
||||
|
||||
private void WritePort0003(ushort addr, byte val)
|
||||
{
|
||||
}
|
||||
|
||||
private void WritePort0004(ushort addr, byte val)
|
||||
{
|
||||
}
|
||||
|
||||
private void WritePort0005(ushort addr, byte val)
|
||||
{
|
||||
}
|
||||
|
||||
private void WritePort0006(ushort addr, byte val)
|
||||
{
|
||||
}
|
||||
|
||||
private void WritePort0007(ushort addr, byte val)
|
||||
{
|
||||
}
|
||||
|
||||
private void WritePort0008(ushort addr, byte val)
|
||||
{
|
||||
}
|
||||
|
||||
private void WritePort0009(ushort addr, byte val)
|
||||
{
|
||||
}
|
||||
|
||||
private void WritePort000A(ushort addr, byte val)
|
||||
{
|
||||
}
|
||||
|
||||
private void WritePort000B(ushort addr, byte val)
|
||||
{
|
||||
}
|
||||
|
||||
private void WritePort000C(ushort addr, byte val)
|
||||
{
|
||||
}
|
||||
|
||||
private void WritePort000D(ushort addr, byte val)
|
||||
{
|
||||
}
|
||||
|
||||
private void WritePort000E(ushort addr, byte val)
|
||||
{
|
||||
}
|
||||
|
||||
private void WritePort000F(ushort addr, byte val)
|
||||
{
|
||||
}
|
||||
|
||||
private void WritePort0010(ushort addr, byte val)
|
||||
{
|
||||
}
|
||||
|
||||
private void WritePort0011(ushort addr, byte val)
|
||||
{
|
||||
}
|
||||
|
||||
private void WritePort0012(ushort addr, byte val)
|
||||
{
|
||||
}
|
||||
|
||||
private void WritePort0013(ushort addr, byte val)
|
||||
{
|
||||
bank = (val & 0x7F) % chips.Count;
|
||||
if ((bank & 0x80) != 0x00)
|
||||
{
|
||||
exRomPin = false;
|
||||
gamePin = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
exRomPin = true;
|
||||
gamePin = false;
|
||||
}
|
||||
UpdateRomPins();
|
||||
}
|
||||
|
||||
private void WritePort0014(ushort addr, byte val)
|
||||
{
|
||||
}
|
||||
|
||||
private void WritePort0015(ushort addr, byte val)
|
||||
{
|
||||
}
|
||||
|
||||
private void WritePort0016(ushort addr, byte val)
|
||||
{
|
||||
}
|
||||
|
||||
private void WritePort0017(ushort addr, byte val)
|
||||
{
|
||||
}
|
||||
|
||||
private void WritePort0018(ushort addr, byte val)
|
||||
{
|
||||
}
|
||||
|
||||
private void WritePort0019(ushort addr, byte val)
|
||||
{
|
||||
}
|
||||
|
||||
private void WritePort001A(ushort addr, byte val)
|
||||
{
|
||||
}
|
||||
|
||||
private void WritePort001B(ushort addr, byte val)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,652 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace BizHawk.Emulation.Computers.Commodore64
|
||||
{
|
||||
public class CiaRegs
|
||||
{
|
||||
public bool ALARM; // alarm enabled
|
||||
public int ALARM10; // alarm 10ths of a second
|
||||
public int ALARMHR; // alarm hours
|
||||
public int ALARMMIN; // alarm minutes
|
||||
public bool ALARMPM; // alarm AM/PM
|
||||
public int ALARMSEC; // alarm seconds
|
||||
public bool CNT; // external counter bit input
|
||||
public bool EIALARM; // enable alarm interrupt (internal)
|
||||
public bool EIFLG; // enable flag pin interrupt (internal)
|
||||
public bool EISP; // enable shift register interrupt (internal)
|
||||
public bool[] EIT = new bool[2]; // enable timer interrupt (internal)
|
||||
public bool FLG; // external flag bit input
|
||||
public bool IALARM; // alarm interrupt triggered
|
||||
public bool IFLG; // interrupt triggered on FLAG pin
|
||||
public int[] INMODE = new int[2]; // timer input mode
|
||||
public bool IRQ; // interrupt triggered
|
||||
public bool ISP; // shift register interrupt
|
||||
public bool[] IT = new bool[2]; // timer interrupt
|
||||
public bool[] LOAD = new bool[2]; // force load timer
|
||||
public bool[] OUTMODE = new bool[2]; // timer output mode
|
||||
public bool[] PBON = new bool[2]; // port bit modify on
|
||||
public bool[] RUNMODE = new bool[2]; // running mode
|
||||
public int SDR; // serial shift register
|
||||
public int SDRCOUNT; // serial shift register bit count
|
||||
public bool SPMODE; // shift register mode
|
||||
public bool[] START = new bool[2]; // timer enabled
|
||||
public int[] T = new int[2]; // timer counter
|
||||
public bool[] TICK = new bool[2]; // execute timer tick
|
||||
public int[] TLATCH = new int[2]; // timer latch (internal)
|
||||
public int TOD10; // time of day 10ths of a second
|
||||
public bool TODIN; // time of day/alarm set
|
||||
public int TODHR; // time of day hour
|
||||
public int TODMIN; // time of day minute
|
||||
public bool TODPM; // time of day AM/PM
|
||||
public bool TODREADLATCH; // read latch (internal)
|
||||
public int TODREADLATCH10; // tod read latch (internal)
|
||||
public int TODREADLATCHSEC; // tod read latch (internal)
|
||||
public int TODREADLATCHMIN; // tod read latch (internal)
|
||||
public int TODREADLATCHHR; // tod read latch (internal)
|
||||
public int TODSEC; // time of day seconds
|
||||
|
||||
public DataPortBus[] ports;
|
||||
|
||||
public DataPortConnector[] connectors;
|
||||
|
||||
public CiaRegs()
|
||||
{
|
||||
ports = new DataPortBus[2];
|
||||
ports[0] = new DataPortBus();
|
||||
ports[1] = new DataPortBus();
|
||||
connectors = new DataPortConnector[2];
|
||||
connectors[0] = ports[0].Connect();
|
||||
connectors[1] = ports[1].Connect();
|
||||
HardReset();
|
||||
}
|
||||
|
||||
public byte this[int addr]
|
||||
{
|
||||
get
|
||||
{
|
||||
// value of open bits
|
||||
int result = 0x00;
|
||||
|
||||
addr &= 0x0F;
|
||||
switch (addr)
|
||||
{
|
||||
case 0x00:
|
||||
result = connectors[0].Data;
|
||||
break;
|
||||
case 0x01:
|
||||
result = connectors[1].Data;
|
||||
break;
|
||||
case 0x02:
|
||||
result = connectors[0].Direction;
|
||||
break;
|
||||
case 0x03:
|
||||
result = connectors[1].Direction;
|
||||
break;
|
||||
case 0x04:
|
||||
result = (T[0] & 0xFF);
|
||||
break;
|
||||
case 0x05:
|
||||
result = ((T[0] >> 8) & 0xFF);
|
||||
break;
|
||||
case 0x06:
|
||||
result = (T[1] & 0xFF);
|
||||
break;
|
||||
case 0x07:
|
||||
result = ((T[1] >> 8) & 0xFF);
|
||||
break;
|
||||
case 0x08:
|
||||
result |= (TOD10 & 0x0F);
|
||||
break;
|
||||
case 0x09:
|
||||
result &= 0x80;
|
||||
result |= (TODSEC & 0x7F);
|
||||
break;
|
||||
case 0x0A:
|
||||
result &= 0x80;
|
||||
result |= (TODMIN & 0x7F);
|
||||
break;
|
||||
case 0x0B:
|
||||
result &= 0x40;
|
||||
result |= ((TODHR & 0x3F) | (TODPM ? 0x80 : 0x00));
|
||||
break;
|
||||
case 0x0C:
|
||||
result = SDR;
|
||||
break;
|
||||
case 0x0D:
|
||||
result &= 0x9F;
|
||||
result |= (IT[0] ? 0x01 : 0x00);
|
||||
result |= (IT[1] ? 0x02 : 0x00);
|
||||
result |= (IALARM ? 0x04 : 0x00);
|
||||
result |= (ISP ? 0x08 : 0x00);
|
||||
result |= (IFLG ? 0x10 : 0x00);
|
||||
result |= (IRQ ? 0x80 : 0x00);
|
||||
break;
|
||||
case 0x0E:
|
||||
result = (START[0] ? 0x01 : 0x00);
|
||||
result = (PBON[0] ? 0x02 : 0x00);
|
||||
result = (OUTMODE[0] ? 0x04 : 0x00);
|
||||
result = (RUNMODE[0] ? 0x08 : 0x00);
|
||||
result = (LOAD[0] ? 0x10 : 0x00);
|
||||
result = ((INMODE[0] & 0x01) << 5);
|
||||
result = (SPMODE ? 0x40 : 0x00);
|
||||
result = (TODIN ? 0x80 : 0x00);
|
||||
break;
|
||||
case 0x0F:
|
||||
result = (START[1] ? 0x01 : 0x00);
|
||||
result = (PBON[1] ? 0x02 : 0x00);
|
||||
result = (OUTMODE[1] ? 0x04 : 0x00);
|
||||
result = (RUNMODE[1] ? 0x08 : 0x00);
|
||||
result = (LOAD[1] ? 0x10 : 0x00);
|
||||
result = ((INMODE[1] & 0x03) << 5);
|
||||
result = (ALARM ? 0x80 : 0x00);
|
||||
break;
|
||||
}
|
||||
|
||||
return (byte)(result & 0xFF);
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
byte val = value;
|
||||
addr &= 0x0F;
|
||||
switch (addr)
|
||||
{
|
||||
case 0x00:
|
||||
connectors[0].Data = val;
|
||||
break;
|
||||
case 0x01:
|
||||
connectors[1].Data = val;
|
||||
break;
|
||||
case 0x02:
|
||||
connectors[0].Direction = val;
|
||||
break;
|
||||
case 0x03:
|
||||
connectors[1].Direction = val;
|
||||
break;
|
||||
case 0x04:
|
||||
T[0] &= 0xFF00;
|
||||
T[0] |= val;
|
||||
break;
|
||||
case 0x05:
|
||||
T[0] &= 0x00FF;
|
||||
T[0] |= ((int)val << 8);
|
||||
break;
|
||||
case 0x06:
|
||||
T[1] &= 0xFF00;
|
||||
T[1] |= val;
|
||||
break;
|
||||
case 0x07:
|
||||
T[1] &= 0x00FF;
|
||||
T[1] |= ((int)val << 8);
|
||||
break;
|
||||
case 0x08:
|
||||
TOD10 = val & 0x0F;
|
||||
break;
|
||||
case 0x09:
|
||||
TODSEC = val & 0x7F;
|
||||
break;
|
||||
case 0x0A:
|
||||
TODMIN = val & 0x7F;
|
||||
break;
|
||||
case 0x0B:
|
||||
val &= 0x9F;
|
||||
TODHR = val;
|
||||
TODPM = ((val & 0x80) != 0x00);
|
||||
break;
|
||||
case 0x0C:
|
||||
SDR = val;
|
||||
break;
|
||||
case 0x0D:
|
||||
IT[0] = ((val & 0x01) != 0x00);
|
||||
IT[1] = ((val & 0x02) != 0x00);
|
||||
IALARM = ((val & 0x04) != 0x00);
|
||||
ISP = ((val & 0x08) != 0x00);
|
||||
IFLG = ((val & 0x10) != 0x00);
|
||||
IRQ = ((val & 0x80) != 0x00);
|
||||
break;
|
||||
case 0x0E:
|
||||
START[0] = ((val & 0x01) != 0x00);
|
||||
PBON[0] = ((val & 0x02) != 0x00);
|
||||
OUTMODE[0] = ((val & 0x04) != 0x00);
|
||||
RUNMODE[0] = ((val & 0x08) != 0x00);
|
||||
LOAD[0] = ((val & 0x10) != 0x00);
|
||||
INMODE[0] = ((val & 0x20) >> 5);
|
||||
SPMODE = ((val & 0x40) != 0x00);
|
||||
TODIN = ((val & 0x80) != 0x00);
|
||||
break;
|
||||
case 0x0F:
|
||||
START[1] = ((val & 0x01) != 0x00);
|
||||
PBON[1] = ((val & 0x02) != 0x00);
|
||||
OUTMODE[1] = ((val & 0x04) != 0x00);
|
||||
RUNMODE[1] = ((val & 0x08) != 0x00);
|
||||
LOAD[1] = ((val & 0x10) != 0x00);
|
||||
INMODE[1] = ((val & 0x60) >> 5);
|
||||
ALARM = ((val & 0x80) != 0x00);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void HardReset()
|
||||
{
|
||||
// power on state
|
||||
for (int i = 0; i < 0x10; i++)
|
||||
this[i] = 0x00;
|
||||
|
||||
TLATCH[0] = 0xFFFF;
|
||||
TLATCH[1] = 0xFFFF;
|
||||
T[0] = TLATCH[0];
|
||||
T[1] = TLATCH[1];
|
||||
|
||||
this[0x0B] = 0x01;
|
||||
|
||||
connectors[0].Latch = 0xFF;
|
||||
connectors[1].Latch = 0xFF;
|
||||
connectors[0].Direction = 0xFF;
|
||||
connectors[1].Direction = 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
public partial class Cia
|
||||
{
|
||||
public int intMask;
|
||||
public bool lastCNT;
|
||||
public byte[] outputBitMask = new byte[] { 0x40, 0x80 };
|
||||
private CiaRegs regs = new CiaRegs();
|
||||
public int todCounter;
|
||||
public int todFrequency;
|
||||
public bool[] underflow;
|
||||
|
||||
public Func<bool> ReadSerial;
|
||||
public Action<bool> WriteSerial;
|
||||
|
||||
public Cia(Region newRegion)
|
||||
{
|
||||
ReadSerial = ReadSerialDummy;
|
||||
WriteSerial = WriteSerialDummy;
|
||||
switch (newRegion)
|
||||
{
|
||||
case Region.NTSC:
|
||||
todFrequency = 14318181 / 14 / 10;
|
||||
break;
|
||||
case Region.PAL:
|
||||
todFrequency = 14318181 / 18 / 10;
|
||||
break;
|
||||
}
|
||||
HardReset();
|
||||
}
|
||||
|
||||
private void AdvanceTOD()
|
||||
{
|
||||
bool overflow;
|
||||
int tenths = regs.TOD10;
|
||||
int seconds = regs.TODSEC;
|
||||
int minutes = regs.TODMIN;
|
||||
int hours = regs.TODHR;
|
||||
bool ampm = regs.TODPM;
|
||||
todCounter = todFrequency;
|
||||
|
||||
tenths = BCDAdd(tenths, 1, out overflow);
|
||||
if (tenths >= 10)
|
||||
{
|
||||
tenths = 0;
|
||||
seconds = BCDAdd(seconds, 1, out overflow);
|
||||
if (overflow)
|
||||
{
|
||||
seconds = 0;
|
||||
minutes = BCDAdd(minutes, 1, out overflow);
|
||||
if (overflow)
|
||||
{
|
||||
minutes = 0;
|
||||
hours = BCDAdd(hours, 1, out overflow);
|
||||
if (hours > 12)
|
||||
{
|
||||
hours = 1;
|
||||
ampm = !ampm;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
regs.TOD10 = tenths;
|
||||
regs.TODSEC = seconds;
|
||||
regs.TODMIN = minutes;
|
||||
regs.TODHR = hours;
|
||||
regs.TODPM = ampm;
|
||||
}
|
||||
|
||||
public void AttachWriteHook(int index, Action act)
|
||||
{
|
||||
regs.ports[index].AttachWriteHook(act);
|
||||
}
|
||||
|
||||
private int BCDAdd(int i, int j, out bool overflow)
|
||||
{
|
||||
int lo;
|
||||
int hi;
|
||||
int result;
|
||||
|
||||
lo = (i & 0x0F) + (j & 0x0F);
|
||||
hi = (i & 0x70) + (j & 0x70);
|
||||
if (lo > 0x09)
|
||||
{
|
||||
hi += 0x10;
|
||||
lo += 0x06;
|
||||
}
|
||||
if (hi > 0x50)
|
||||
{
|
||||
hi += 0xA0;
|
||||
}
|
||||
overflow = hi >= 0x60;
|
||||
result = (hi & 0x70) + (lo & 0x0F);
|
||||
return result;
|
||||
}
|
||||
|
||||
public DataPortConnector ConnectPort(int index)
|
||||
{
|
||||
return regs.ports[index].Connect();
|
||||
}
|
||||
|
||||
public DataPortConnector ConnectSerialPort(int index)
|
||||
{
|
||||
DataPortConnector result = regs.ports[index].Connect();
|
||||
regs.ports[index].AttachInputConverter(result, new DataPortSerialInputConverter());
|
||||
regs.ports[index].AttachOutputConverter(result, new DataPortSerialOutputConverter());
|
||||
return result;
|
||||
}
|
||||
|
||||
public void HardReset()
|
||||
{
|
||||
regs.HardReset();
|
||||
underflow = new bool[2];
|
||||
todCounter = todFrequency;
|
||||
}
|
||||
|
||||
public bool IRQ
|
||||
{
|
||||
get
|
||||
{
|
||||
return regs.IRQ;
|
||||
}
|
||||
}
|
||||
|
||||
public byte Peek(int addr)
|
||||
{
|
||||
addr &= 0xF;
|
||||
return regs[addr];
|
||||
}
|
||||
|
||||
public void PerformCycle()
|
||||
{
|
||||
// process time of day counter
|
||||
todCounter--;
|
||||
if (todCounter <= 0)
|
||||
AdvanceTOD();
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
if (regs.START[i])
|
||||
{
|
||||
TimerTick(i);
|
||||
if (regs.PBON[i])
|
||||
{
|
||||
// output the clock data to port B
|
||||
|
||||
if (regs.OUTMODE[i])
|
||||
{
|
||||
// clear bit if set
|
||||
regs[0x01] &= (byte)~outputBitMask[i];
|
||||
}
|
||||
if (underflow[i])
|
||||
{
|
||||
if (regs.OUTMODE[i])
|
||||
{
|
||||
// toggle bit
|
||||
regs[0x01] ^= outputBitMask[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
// set for a cycle
|
||||
regs[0x01] |= outputBitMask[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lastCNT = regs.CNT;
|
||||
regs.CNT = false;
|
||||
UpdateInterrupt();
|
||||
}
|
||||
|
||||
public void Poke(int addr, byte val)
|
||||
{
|
||||
addr &= 0xF;
|
||||
regs[addr] = val;
|
||||
}
|
||||
|
||||
public byte Read(ushort addr)
|
||||
{
|
||||
byte result;
|
||||
addr &= 0xF;
|
||||
|
||||
switch (addr)
|
||||
{
|
||||
case 0x08:
|
||||
regs.TODREADLATCH = false;
|
||||
return (byte)regs.TODREADLATCH10;
|
||||
case 0x09:
|
||||
if (!regs.TODREADLATCH)
|
||||
return regs[addr];
|
||||
else
|
||||
return (byte)regs.TODREADLATCHSEC;
|
||||
case 0x0A:
|
||||
if (!regs.TODREADLATCH)
|
||||
return regs[addr];
|
||||
else
|
||||
return (byte)regs.TODREADLATCHMIN;
|
||||
case 0x0B:
|
||||
regs.TODREADLATCH = true;
|
||||
regs.TODREADLATCH10 = regs.TOD10;
|
||||
regs.TODREADLATCHSEC = regs.TODSEC;
|
||||
regs.TODREADLATCHMIN = regs.TODMIN;
|
||||
regs.TODREADLATCHHR = regs.TODHR;
|
||||
return (byte)regs.TODREADLATCHHR;
|
||||
case 0x0D:
|
||||
// reading this reg clears it
|
||||
result = regs[0x0D];
|
||||
regs[0x0D] = 0x00;
|
||||
UpdateInterrupt();
|
||||
return result;
|
||||
default:
|
||||
return regs[addr];
|
||||
}
|
||||
}
|
||||
|
||||
private bool ReadSerialDummy()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public void TimerDec(int index)
|
||||
{
|
||||
int timer = regs.T[index];
|
||||
timer--;
|
||||
if (timer < 0)
|
||||
{
|
||||
underflow[index] = true;
|
||||
if (regs.RUNMODE[index])
|
||||
{
|
||||
// one shot timer
|
||||
regs.START[index] = false;
|
||||
}
|
||||
timer = regs.TLATCH[index];
|
||||
}
|
||||
else
|
||||
{
|
||||
underflow[index] = false;
|
||||
}
|
||||
|
||||
regs.IT[index] |= underflow[index];
|
||||
regs.T[index] = timer & 0xFFFF;
|
||||
}
|
||||
|
||||
public void TimerTick(int index)
|
||||
{
|
||||
switch (regs.INMODE[index])
|
||||
{
|
||||
case 0:
|
||||
regs.TICK[index] = true;
|
||||
break;
|
||||
case 1:
|
||||
regs.TICK[index] |= (regs.CNT && !lastCNT);
|
||||
break;
|
||||
case 2:
|
||||
regs.TICK[index] |= underflow[0];
|
||||
break;
|
||||
case 3:
|
||||
regs.TICK[index] |= (regs.CNT && !lastCNT) || underflow[0];
|
||||
break;
|
||||
}
|
||||
if (regs.TICK[index])
|
||||
{
|
||||
TimerDec(index);
|
||||
regs.TICK[index] = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateInterrupt()
|
||||
{
|
||||
bool irq = false;
|
||||
irq |= (regs.EIT[0] & regs.IT[0]);
|
||||
irq |= (regs.EIT[1] & regs.IT[1]);
|
||||
irq |= (regs.EIFLG & regs.IFLG);
|
||||
irq |= (regs.EISP & regs.ISP);
|
||||
irq |= (regs.EIALARM & regs.IALARM);
|
||||
regs.IRQ = irq;
|
||||
}
|
||||
|
||||
public void Write(ushort addr, byte val)
|
||||
{
|
||||
addr &= 0xF;
|
||||
switch (addr)
|
||||
{
|
||||
case 0x04:
|
||||
regs.TLATCH[0] &= 0xFF00;
|
||||
regs.TLATCH[0] |= val;
|
||||
if (regs.LOAD[0])
|
||||
regs.T[0] = regs.TLATCH[0];
|
||||
break;
|
||||
case 0x05:
|
||||
regs.TLATCH[0] &= 0xFF;
|
||||
regs.TLATCH[0] |= (int)val << 8;
|
||||
if (regs.LOAD[0] || !regs.START[0])
|
||||
regs.T[0] = regs.TLATCH[0];
|
||||
break;
|
||||
case 0x06:
|
||||
regs.TLATCH[1] &= 0xFF00;
|
||||
regs.TLATCH[1] |= val;
|
||||
if (regs.LOAD[1])
|
||||
regs.T[1] = regs.TLATCH[1];
|
||||
break;
|
||||
case 0x07:
|
||||
regs.TLATCH[1] &= 0xFF;
|
||||
regs.TLATCH[1] |= (int)val << 8;
|
||||
if (regs.LOAD[1] || !regs.START[1])
|
||||
regs.T[1] = regs.TLATCH[1];
|
||||
break;
|
||||
case 0x08:
|
||||
if (regs.ALARM)
|
||||
regs.ALARM10 = val & 0x0F;
|
||||
else
|
||||
regs[addr] = val;
|
||||
break;
|
||||
case 0x09:
|
||||
if (regs.ALARM)
|
||||
regs.ALARMSEC = val & 0x7F;
|
||||
else
|
||||
regs[addr] = val;
|
||||
break;
|
||||
case 0x0A:
|
||||
if (regs.ALARM)
|
||||
regs.ALARMMIN = val & 0x7F;
|
||||
else
|
||||
regs[addr] = val;
|
||||
break;
|
||||
case 0x0B:
|
||||
if (regs.ALARM)
|
||||
{
|
||||
regs.ALARMHR = val & 0x1F;
|
||||
regs.ALARMPM = ((val & 0x80) != 0x00);
|
||||
}
|
||||
else
|
||||
{
|
||||
regs[addr] = val;
|
||||
}
|
||||
break;
|
||||
case 0x0D:
|
||||
intMask &= ~val;
|
||||
if ((val & 0x80) != 0x00)
|
||||
intMask ^= val;
|
||||
regs.EIT[0] = ((intMask & 0x01) != 0x00);
|
||||
regs.EIT[1] = ((intMask & 0x02) != 0x00);
|
||||
regs.EIALARM = ((intMask & 0x04) != 0x00);
|
||||
regs.EISP = ((intMask & 0x08) != 0x00);
|
||||
regs.EIFLG = ((intMask & 0x10) != 0x00);
|
||||
UpdateInterrupt();
|
||||
break;
|
||||
default:
|
||||
regs[addr] = val;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void WriteCNT(bool val)
|
||||
{
|
||||
if (!lastCNT && val)
|
||||
{
|
||||
if (!regs.SPMODE)
|
||||
{
|
||||
// read bit into shift register
|
||||
bool inputBit = ReadSerial();
|
||||
regs.SDR = ((regs.SDR << 1) | (inputBit ? 0x01 : 0x00)) & 0xFF;
|
||||
|
||||
regs.SDRCOUNT = (regs.SDRCOUNT - 1) & 0x7;
|
||||
if (regs.SDRCOUNT == 0)
|
||||
{
|
||||
regs.ISP = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// write bit out from shift register
|
||||
bool outputBit = ((regs.SDR & 0x01) != 0);
|
||||
regs.SDR >>= 1;
|
||||
WriteSerial(outputBit);
|
||||
|
||||
regs.SDRCOUNT = (regs.SDRCOUNT - 1) & 0x7;
|
||||
if (regs.SDRCOUNT == 0)
|
||||
{
|
||||
regs.ISP = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
regs.CNT = val;
|
||||
}
|
||||
|
||||
public void WriteFLG(bool val)
|
||||
{
|
||||
regs.FLG = val;
|
||||
regs.IFLG |= val;
|
||||
}
|
||||
|
||||
private void WriteSerialDummy(bool val)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,89 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace BizHawk.Emulation.Computers.Commodore64
|
||||
{
|
||||
public partial class Cia
|
||||
{
|
||||
public void SyncState(Serializer ser)
|
||||
{
|
||||
ser.Sync("ALARM", ref regs.ALARM);
|
||||
ser.Sync("ALARM10", ref regs.ALARM10);
|
||||
ser.Sync("ALARMHR", ref regs.ALARMHR);
|
||||
ser.Sync("ALARMMIN", ref regs.ALARMMIN);
|
||||
ser.Sync("ALARMPM", ref regs.ALARMPM);
|
||||
ser.Sync("ALARMSEC", ref regs.ALARMSEC);
|
||||
ser.Sync("CNT", ref regs.CNT);
|
||||
ser.Sync("EIALARM", ref regs.EIALARM);
|
||||
ser.Sync("EIFLG", ref regs.EIFLG);
|
||||
ser.Sync("EISP", ref regs.EISP);
|
||||
ser.Sync("EIT0", ref regs.EIT[0]);
|
||||
ser.Sync("EIT1", ref regs.EIT[1]);
|
||||
ser.Sync("FLG", ref regs.FLG);
|
||||
ser.Sync("IALARM", ref regs.IALARM);
|
||||
ser.Sync("IFLG", ref regs.IFLG);
|
||||
ser.Sync("INMODE0", ref regs.INMODE[0]);
|
||||
ser.Sync("INMODE1", ref regs.INMODE[1]);
|
||||
ser.Sync("IRQ", ref regs.IRQ);
|
||||
ser.Sync("ISP", ref regs.ISP);
|
||||
ser.Sync("IT0", ref regs.IT[0]);
|
||||
ser.Sync("IT1", ref regs.IT[1]);
|
||||
ser.Sync("LOAD0", ref regs.LOAD[0]);
|
||||
ser.Sync("LOAD1", ref regs.LOAD[1]);
|
||||
ser.Sync("OUTMODE0", ref regs.OUTMODE[0]);
|
||||
ser.Sync("OUTMODE1", ref regs.OUTMODE[1]);
|
||||
ser.Sync("PBON0", ref regs.PBON[0]);
|
||||
ser.Sync("PBON1", ref regs.PBON[1]);
|
||||
ser.Sync("RUNMODE0", ref regs.RUNMODE[0]);
|
||||
ser.Sync("RUNMODE1", ref regs.RUNMODE[1]);
|
||||
ser.Sync("SDR", ref regs.SDR);
|
||||
ser.Sync("SDRCOUNT", ref regs.SDRCOUNT);
|
||||
ser.Sync("SPMODE", ref regs.SPMODE);
|
||||
ser.Sync("START0", ref regs.START[0]);
|
||||
ser.Sync("START1", ref regs.START[1]);
|
||||
ser.Sync("T0", ref regs.T[0]);
|
||||
ser.Sync("T1", ref regs.T[1]);
|
||||
ser.Sync("TICK0", ref regs.TICK[0]);
|
||||
ser.Sync("TICK1", ref regs.TICK[1]);
|
||||
ser.Sync("TLATCH0", ref regs.TLATCH[0]);
|
||||
ser.Sync("TLATCH1", ref regs.TLATCH[1]);
|
||||
ser.Sync("TOD10", ref regs.TOD10);
|
||||
ser.Sync("TODHR", ref regs.TODHR);
|
||||
ser.Sync("TODMIN", ref regs.TODMIN);
|
||||
ser.Sync("TODPM", ref regs.TODPM);
|
||||
ser.Sync("TODREADLATCH", ref regs.TODREADLATCH);
|
||||
ser.Sync("TODREADLATCH10", ref regs.TODREADLATCH10);
|
||||
ser.Sync("TODREADLATCHSEC", ref regs.TODREADLATCHSEC);
|
||||
ser.Sync("TODREADLATCHMIN", ref regs.TODREADLATCHMIN);
|
||||
ser.Sync("TODREADLATCHHR", ref regs.TODREADLATCHHR);
|
||||
ser.Sync("TODSEC", ref regs.TODSEC);
|
||||
|
||||
// ports
|
||||
byte dir0 = regs.connectors[0].Direction;
|
||||
byte dir1 = regs.connectors[1].Direction;
|
||||
byte latch0 = regs.connectors[0].Latch;
|
||||
byte latch1 = regs.connectors[0].Latch;
|
||||
ser.Sync("DIR0", ref dir0);
|
||||
ser.Sync("DIR1", ref dir1);
|
||||
ser.Sync("PORT0", ref latch0);
|
||||
ser.Sync("PORT1", ref latch1);
|
||||
if (ser.IsReader)
|
||||
{
|
||||
regs.connectors[0].Direction = dir0;
|
||||
regs.connectors[0].Latch = latch0;
|
||||
regs.connectors[1].Direction = dir1;
|
||||
regs.connectors[1].Latch = latch1;
|
||||
}
|
||||
|
||||
// state
|
||||
ser.Sync("INTMASK", ref intMask);
|
||||
ser.Sync("LASTCNT", ref lastCNT);
|
||||
ser.Sync("TODCOUNTER", ref todCounter);
|
||||
ser.Sync("TODFREQUENCY", ref todFrequency);
|
||||
ser.Sync("UNDERFLOW0", ref underflow[0]);
|
||||
ser.Sync("UNDERFLOW1", ref underflow[1]);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,371 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace BizHawk.Emulation.Computers.Commodore64
|
||||
{
|
||||
public class DataPortBus
|
||||
{
|
||||
protected bool[] connected = new bool[2];
|
||||
protected DataPortConnector[] connectors;
|
||||
protected byte[] direction = new byte[2];
|
||||
protected DataPortConverter[] inputConverters;
|
||||
protected byte[] latch = new byte[2];
|
||||
protected DataPortConverter[] outputConverters;
|
||||
protected List<int> servingHooks = new List<int>();
|
||||
protected List<Action> writeHooks = new List<Action>();
|
||||
|
||||
public DataPortBus()
|
||||
{
|
||||
inputConverters = new DataPortConverter[2];
|
||||
inputConverters[0] = new DataPortConverter();
|
||||
inputConverters[1] = new DataPortConverter();
|
||||
outputConverters = new DataPortConverter[2];
|
||||
outputConverters[0] = new DataPortConverter();
|
||||
outputConverters[1] = new DataPortConverter();
|
||||
connectors = new DataPortConnector[2];
|
||||
connectors[0] = new DataPortConnector(ReadData0, ReadDirection0, ReadLatch0, ReadRemoteLatch0, WriteData0, WriteDirection0, WriteLatch0);
|
||||
connectors[1] = new DataPortConnector(ReadData1, ReadDirection1, ReadLatch1, ReadRemoteLatch1, WriteData1, WriteDirection1, WriteLatch1);
|
||||
connected[0] = false;
|
||||
connected[1] = false;
|
||||
direction[0] = 0x00;
|
||||
direction[1] = 0x00;
|
||||
latch[0] = 0x00;
|
||||
latch[1] = 0x00;
|
||||
}
|
||||
|
||||
public void AttachInputConverter(DataPortConnector connector, DataPortConverter converter)
|
||||
{
|
||||
if (connector.Equals(connectors[0]))
|
||||
{
|
||||
inputConverters[0] = converter;
|
||||
}
|
||||
else if (connector.Equals(connectors[1]))
|
||||
{
|
||||
inputConverters[1] = converter;
|
||||
}
|
||||
}
|
||||
|
||||
public void AttachOutputConverter(DataPortConnector connector, DataPortConverter converter)
|
||||
{
|
||||
if (connector.Equals(connectors[0]))
|
||||
{
|
||||
outputConverters[0] = converter;
|
||||
}
|
||||
else if (connector.Equals(connectors[1]))
|
||||
{
|
||||
outputConverters[1] = converter;
|
||||
}
|
||||
}
|
||||
|
||||
public void AttachWriteHook(Action act)
|
||||
{
|
||||
writeHooks.Add(act);
|
||||
servingHooks.Add(0);
|
||||
}
|
||||
|
||||
protected void ClearHooks()
|
||||
{
|
||||
int count = servingHooks.Count;
|
||||
for (int i = 0; i < count; i++)
|
||||
servingHooks[i]--;
|
||||
}
|
||||
|
||||
public DataPortConnector Connect()
|
||||
{
|
||||
if (!connected[0])
|
||||
{
|
||||
connected[0] = true;
|
||||
direction[0] = 0xFF;
|
||||
return connectors[0];
|
||||
}
|
||||
else if (!connected[1])
|
||||
{
|
||||
connected[1] = true;
|
||||
direction[1] = 0xFF;
|
||||
return connectors[1];
|
||||
}
|
||||
throw new Exception("Two connections to this bus have already been established..");
|
||||
}
|
||||
|
||||
public void Connect(DataPortConnector connection)
|
||||
{
|
||||
if (!connected[0])
|
||||
{
|
||||
connected[0] = true;
|
||||
connectors[0] = connection;
|
||||
}
|
||||
else if (!connected[1])
|
||||
{
|
||||
connected[1] = true;
|
||||
connectors[1] = connection;
|
||||
}
|
||||
throw new Exception("Two connections to this bus have already been established..");
|
||||
}
|
||||
|
||||
public void Disconnect(DataPortConnector connector)
|
||||
{
|
||||
if (connector.Equals(connectors[0]))
|
||||
{
|
||||
connected[0] = false;
|
||||
latch[0] = 0;
|
||||
direction[0] = 0;
|
||||
}
|
||||
else if (connector.Equals(connectors[1]))
|
||||
{
|
||||
connected[1] = false;
|
||||
latch[1] = 0;
|
||||
direction[1] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
protected void ExecuteWriteHooks()
|
||||
{
|
||||
int count = servingHooks.Count;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
if (servingHooks[i] == 0)
|
||||
{
|
||||
servingHooks[i]++;
|
||||
writeHooks[i]();
|
||||
}
|
||||
else
|
||||
{
|
||||
servingHooks[i]++;
|
||||
}
|
||||
}
|
||||
ClearHooks();
|
||||
}
|
||||
|
||||
protected virtual byte ReadData0()
|
||||
{
|
||||
byte result;
|
||||
if (connected[1])
|
||||
result = (byte)((~direction[0] & latch[1]) | (direction[0] & latch[0]));
|
||||
else
|
||||
result = latch[0];
|
||||
return inputConverters[0].Convert(result, latch[1]);
|
||||
}
|
||||
|
||||
protected virtual byte ReadData1()
|
||||
{
|
||||
byte result;
|
||||
if (connected[0])
|
||||
result = (byte)((~direction[1] & latch[0]) | (direction[1] & latch[1]));
|
||||
else
|
||||
result = latch[1];
|
||||
return inputConverters[1].Convert(result, latch[0]);
|
||||
}
|
||||
|
||||
protected virtual byte ReadDirection0()
|
||||
{
|
||||
return direction[0];
|
||||
}
|
||||
|
||||
protected virtual byte ReadDirection1()
|
||||
{
|
||||
return direction[1];
|
||||
}
|
||||
|
||||
protected virtual byte ReadLatch0()
|
||||
{
|
||||
return latch[0];
|
||||
}
|
||||
|
||||
protected virtual byte ReadLatch1()
|
||||
{
|
||||
return latch[1];
|
||||
}
|
||||
|
||||
protected virtual byte ReadRemoteLatch0()
|
||||
{
|
||||
return latch[1];
|
||||
}
|
||||
|
||||
protected virtual byte ReadRemoteLatch1()
|
||||
{
|
||||
return latch[0];
|
||||
}
|
||||
|
||||
protected virtual void WriteData0(byte val)
|
||||
{
|
||||
byte result = latch[0];
|
||||
result &= (byte)~direction[0];
|
||||
result |= (byte)(val & direction[0]);
|
||||
latch[0] = outputConverters[0].Convert(result, latch[1]);
|
||||
ExecuteWriteHooks();
|
||||
}
|
||||
|
||||
protected virtual void WriteData1(byte val)
|
||||
{
|
||||
byte result = latch[1];
|
||||
result &= (byte)~direction[1];
|
||||
result |= (byte)(val & direction[1]);
|
||||
latch[1] = outputConverters[1].Convert(result, latch[0]);
|
||||
ExecuteWriteHooks();
|
||||
}
|
||||
|
||||
protected virtual void WriteDirection0(byte val)
|
||||
{
|
||||
direction[0] = val;
|
||||
ExecuteWriteHooks();
|
||||
}
|
||||
|
||||
protected virtual void WriteDirection1(byte val)
|
||||
{
|
||||
direction[1] = val;
|
||||
ExecuteWriteHooks();
|
||||
}
|
||||
|
||||
protected virtual void WriteLatch0(byte val)
|
||||
{
|
||||
latch[0] = val;
|
||||
}
|
||||
|
||||
protected virtual void WriteLatch1(byte val)
|
||||
{
|
||||
latch[1] = val;
|
||||
}
|
||||
}
|
||||
|
||||
public class DataPortConnector
|
||||
{
|
||||
private Func<byte> ReadData;
|
||||
private Func<byte> ReadDirection;
|
||||
private Func<byte> ReadLatch;
|
||||
private Func<byte> ReadRemoteLatch;
|
||||
private Action<byte> WriteData;
|
||||
private Action<byte> WriteDirection;
|
||||
private Action<byte> WriteLatch;
|
||||
|
||||
public DataPortConnector()
|
||||
{
|
||||
ReadData = ReadDataDummy;
|
||||
ReadDirection = ReadDataDummy;
|
||||
ReadLatch = ReadDataDummy;
|
||||
ReadRemoteLatch = ReadDataDummy;
|
||||
WriteData = WriteDataDummy;
|
||||
WriteDirection = WriteDataDummy;
|
||||
WriteLatch = WriteDataDummy;
|
||||
}
|
||||
|
||||
public DataPortConnector(DataPortConnector source)
|
||||
{
|
||||
ReadData = source.ReadData;
|
||||
ReadDirection = source.ReadDirection;
|
||||
ReadLatch = source.ReadLatch;
|
||||
ReadRemoteLatch = source.ReadRemoteLatch;
|
||||
WriteData = source.WriteData;
|
||||
WriteDirection = source.WriteDirection;
|
||||
WriteLatch = source.WriteLatch;
|
||||
}
|
||||
|
||||
public DataPortConnector(Func<byte> newReadData, Func<byte> newReadDirection, Func<byte> newReadLatch, Func<byte> newReadRemoteLatch, Action<byte> newWriteData, Action<byte> newWriteDirection, Action<byte> newWriteLatch)
|
||||
{
|
||||
ReadData = newReadData;
|
||||
ReadDirection = newReadDirection;
|
||||
ReadLatch = newReadLatch;
|
||||
ReadRemoteLatch = newReadRemoteLatch;
|
||||
WriteData = newWriteData;
|
||||
WriteDirection = newWriteDirection;
|
||||
WriteLatch = newWriteLatch;
|
||||
}
|
||||
|
||||
public byte Data
|
||||
{
|
||||
get
|
||||
{
|
||||
return ReadData();
|
||||
}
|
||||
set
|
||||
{
|
||||
WriteData(value);
|
||||
}
|
||||
}
|
||||
|
||||
public byte Direction
|
||||
{
|
||||
get
|
||||
{
|
||||
return ReadDirection();
|
||||
}
|
||||
set
|
||||
{
|
||||
WriteDirection(value);
|
||||
}
|
||||
}
|
||||
|
||||
public byte Latch
|
||||
{
|
||||
get
|
||||
{
|
||||
return ReadLatch();
|
||||
}
|
||||
set
|
||||
{
|
||||
WriteLatch(value);
|
||||
}
|
||||
}
|
||||
|
||||
public DataPortListener Listener()
|
||||
{
|
||||
return new DataPortListener(ReadData, ReadDirection);
|
||||
}
|
||||
|
||||
private byte ReadDataDummy()
|
||||
{
|
||||
return 0x00;
|
||||
}
|
||||
|
||||
public byte RemoteLatch
|
||||
{
|
||||
get
|
||||
{
|
||||
return ReadRemoteLatch();
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteDataDummy(byte val)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public class DataPortConverter
|
||||
{
|
||||
public virtual byte Convert(byte input, byte remote)
|
||||
{
|
||||
// the base converter transfers the values directly
|
||||
return input;
|
||||
}
|
||||
}
|
||||
|
||||
public class DataPortListener
|
||||
{
|
||||
private Func<byte> ReadData;
|
||||
private Func<byte> ReadDirection;
|
||||
|
||||
public DataPortListener(Func<byte> newReadData, Func<byte> newReadDirection)
|
||||
{
|
||||
ReadData = newReadData;
|
||||
ReadDirection = newReadDirection;
|
||||
}
|
||||
|
||||
public byte Data
|
||||
{
|
||||
get
|
||||
{
|
||||
return ReadData();
|
||||
}
|
||||
}
|
||||
|
||||
public byte Direction
|
||||
{
|
||||
get
|
||||
{
|
||||
return ReadDirection();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,118 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace BizHawk.Emulation.Computers.Commodore64
|
||||
{
|
||||
public class Input
|
||||
{
|
||||
static string[,] keyboardMatrix = new string[,]
|
||||
{
|
||||
{"Key Insert/Delete", "Key Return", "Key Cursor Left/Right", "Key F7", "Key F1", "Key F3", "Key F5", "Key Cursor Up/Down"},
|
||||
{"Key 3", "Key W", "Key A", "Key 4", "Key Z", "Key S", "Key E", "Key Left Shift"},
|
||||
{"Key 5", "Key R", "Key D", "Key 6", "Key C", "Key F", "Key T", "Key X"},
|
||||
{"Key 7", "Key Y", "Key G", "Key 8", "Key B", "Key H", "Key U", "Key V"},
|
||||
{"Key 9", "Key I", "Key J", "Key 0", "Key M", "Key K", "Key O", "Key N"},
|
||||
{"Key Plus", "Key P", "Key L", "Key Minus", "Key Period", "Key Colon", "Key At", "Key Comma"},
|
||||
{"Key Pound", "Key Asterisk", "Key Semicolon", "Key Clear/Home", "Key Right Shift", "Key Equal", "Key Up Arrow", "Key Slash"},
|
||||
{"Key 1", "Key Left Arrow", "Key Control", "Key 2", "Key Space", "Key Commodore", "Key Q", "Key Run/Stop"}
|
||||
};
|
||||
|
||||
static string[,] joystickMatrix = new string[,]
|
||||
{
|
||||
{"P1 Up", "P1 Down", "P1 Left", "P1 Right", "P1 Button"},
|
||||
{"P2 Up", "P2 Down", "P2 Left", "P2 Right", "P2 Button"}
|
||||
};
|
||||
|
||||
public IController controller;
|
||||
public bool restorePressed;
|
||||
|
||||
private byte[] joystickLatch = new byte[2];
|
||||
private byte keyboardColumnData = 0xFF;
|
||||
private byte[] keyboardLatch = new byte[8];
|
||||
private byte keyboardRowData = 0xFF;
|
||||
private DataPortConnector[] ports;
|
||||
|
||||
public Input(DataPortConnector[] newPorts)
|
||||
{
|
||||
ports = newPorts;
|
||||
|
||||
// set full output
|
||||
ports[0].Direction = 0xFF;
|
||||
ports[1].Direction = 0xFF;
|
||||
}
|
||||
|
||||
private byte GetJoystickBits(int index)
|
||||
{
|
||||
byte result = 0xE0;
|
||||
result |= controller[joystickMatrix[index, 0]] ? (byte)0x00 : (byte)0x01;
|
||||
result |= controller[joystickMatrix[index, 1]] ? (byte)0x00 : (byte)0x02;
|
||||
result |= controller[joystickMatrix[index, 2]] ? (byte)0x00 : (byte)0x04;
|
||||
result |= controller[joystickMatrix[index, 3]] ? (byte)0x00 : (byte)0x08;
|
||||
result |= controller[joystickMatrix[index, 4]] ? (byte)0x00 : (byte)0x10;
|
||||
return result;
|
||||
}
|
||||
|
||||
private byte GetKeyboardBits(int row)
|
||||
{
|
||||
byte result;
|
||||
result = controller[keyboardMatrix[row, 0]] ? (byte)0x00 : (byte)0x01;
|
||||
result |= controller[keyboardMatrix[row, 1]] ? (byte)0x00 : (byte)0x02;
|
||||
result |= controller[keyboardMatrix[row, 2]] ? (byte)0x00 : (byte)0x04;
|
||||
result |= controller[keyboardMatrix[row, 3]] ? (byte)0x00 : (byte)0x08;
|
||||
result |= controller[keyboardMatrix[row, 4]] ? (byte)0x00 : (byte)0x10;
|
||||
result |= controller[keyboardMatrix[row, 5]] ? (byte)0x00 : (byte)0x20;
|
||||
result |= controller[keyboardMatrix[row, 6]] ? (byte)0x00 : (byte)0x40;
|
||||
result |= controller[keyboardMatrix[row, 7]] ? (byte)0x00 : (byte)0x80;
|
||||
return result;
|
||||
}
|
||||
|
||||
public void Poll()
|
||||
{
|
||||
restorePressed = controller["Key Restore"];
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
joystickLatch[i] = GetJoystickBits(i);
|
||||
for (int i = 0; i < 8; i++)
|
||||
keyboardLatch[i] = GetKeyboardBits(i);
|
||||
UpdatePortData();
|
||||
}
|
||||
|
||||
private void UpdatePortData()
|
||||
{
|
||||
int keyboardShift = keyboardColumnData;
|
||||
byte port0result = 0xFF;
|
||||
byte port1result = 0xFF;
|
||||
|
||||
port0result = (byte)(joystickLatch[1]);
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
if ((keyboardShift & 0x01) == 0x00)
|
||||
{
|
||||
port1result &= keyboardLatch[i];
|
||||
}
|
||||
keyboardShift >>= 1;
|
||||
}
|
||||
port1result &= joystickLatch[0];
|
||||
|
||||
ports[0].Data = port0result;
|
||||
ports[1].Data = port1result;
|
||||
}
|
||||
|
||||
public void WritePortA()
|
||||
{
|
||||
// keyboard matrix column select
|
||||
keyboardColumnData = ports[0].RemoteLatch;
|
||||
UpdatePortData();
|
||||
}
|
||||
|
||||
public void WritePortB()
|
||||
{
|
||||
// keyboard matrix row select
|
||||
keyboardRowData = ports[1].RemoteLatch;
|
||||
UpdatePortData();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace BizHawk.Emulation.Computers.Commodore64.MOS
|
||||
{
|
||||
// used as Color RAM in C64
|
||||
|
||||
public class Chip2114 : IStandardIO
|
||||
{
|
||||
private byte[] ram;
|
||||
|
||||
public Chip2114()
|
||||
{
|
||||
HardReset();
|
||||
}
|
||||
|
||||
public void HardReset()
|
||||
{
|
||||
ram = new byte[0x400];
|
||||
}
|
||||
|
||||
public byte Peek(int addr)
|
||||
{
|
||||
return ram[addr & 0x3FF];
|
||||
}
|
||||
|
||||
public byte Peek(int addr, byte bus)
|
||||
{
|
||||
return (byte)(ram[addr & 0x3FF] | (bus & 0xF0));
|
||||
}
|
||||
|
||||
public void Poke(int addr, byte val)
|
||||
{
|
||||
ram[addr & 0x3FF] = (byte)(val & 0xF);
|
||||
}
|
||||
|
||||
public byte Read(ushort addr)
|
||||
{
|
||||
return (byte)(ram[addr & 0x3FF]);
|
||||
}
|
||||
|
||||
public byte Read(ushort addr, byte bus)
|
||||
{
|
||||
return (byte)(ram[addr & 0x3FF] | (bus & 0xF0));
|
||||
}
|
||||
|
||||
public void Write(ushort addr, byte val)
|
||||
{
|
||||
ram[addr & 0x3FF] = (byte)(val & 0xF);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace BizHawk.Emulation.Computers.Commodore64.MOS
|
||||
{
|
||||
// ROM chips
|
||||
// 2332: 32 kbit (4kbyte)
|
||||
// 2364: 64 kbit (8kbyte)
|
||||
// 23128: 128 kbit (16kbyte)
|
||||
|
||||
public enum Chip23XXmodel
|
||||
{
|
||||
Chip2332,
|
||||
Chip2364,
|
||||
Chip23128
|
||||
}
|
||||
|
||||
public class Chip23XX : IStandardIO
|
||||
{
|
||||
private uint addrMask;
|
||||
private byte[] rom;
|
||||
|
||||
public Chip23XX(Chip23XXmodel model, byte[] data)
|
||||
{
|
||||
switch (model)
|
||||
{
|
||||
case Chip23XXmodel.Chip2332:
|
||||
rom = new byte[0x1000];
|
||||
addrMask = 0xFFF;
|
||||
break;
|
||||
case Chip23XXmodel.Chip2364:
|
||||
rom = new byte[0x2000];
|
||||
addrMask = 0x1FFF;
|
||||
break;
|
||||
case Chip23XXmodel.Chip23128:
|
||||
rom = new byte[0x4000];
|
||||
addrMask = 0x3FFF;
|
||||
break;
|
||||
default:
|
||||
throw new Exception("Invalid chip model.");
|
||||
}
|
||||
Array.Copy(data, rom, rom.Length);
|
||||
}
|
||||
|
||||
public byte Peek(int addr)
|
||||
{
|
||||
return rom[addr & (int)addrMask];
|
||||
}
|
||||
|
||||
public void Poke(int addr, byte val)
|
||||
{
|
||||
// do nothing (this is rom)
|
||||
}
|
||||
|
||||
public byte Read(ushort addr)
|
||||
{
|
||||
return rom[addr & addrMask];
|
||||
}
|
||||
|
||||
public void Write(ushort addr, byte val)
|
||||
{
|
||||
// do nothing (this is rom)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace BizHawk.Emulation.Computers.Commodore64.MOS
|
||||
{
|
||||
// DRAM for the c64
|
||||
// 4164 = 64 kbit
|
||||
// 4464 = 256 kbit
|
||||
// 4864 = 512 kbit
|
||||
|
||||
// for purposes of simplification we'll just
|
||||
// use one 4864, the C64 can use sets of 4164 or
|
||||
// 4464 typically
|
||||
|
||||
public class Chip4864 : IStandardIO
|
||||
{
|
||||
private byte[] ram;
|
||||
|
||||
public Chip4864()
|
||||
{
|
||||
HardReset();
|
||||
}
|
||||
|
||||
public void HardReset()
|
||||
{
|
||||
ram = new byte[0x10000];
|
||||
}
|
||||
|
||||
public byte Peek(int addr)
|
||||
{
|
||||
return ram[addr & 0xFFFF];
|
||||
}
|
||||
|
||||
public void Poke(int addr, byte val)
|
||||
{
|
||||
ram[addr & 0xFFFF] = val;
|
||||
}
|
||||
|
||||
public byte Read(ushort addr)
|
||||
{
|
||||
return ram[addr];
|
||||
}
|
||||
|
||||
public void Write(ushort addr, byte val)
|
||||
{
|
||||
ram[addr] = val;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace BizHawk.Emulation.Computers.Commodore64.MOS
|
||||
{
|
||||
public interface IStandardIO
|
||||
{
|
||||
byte Peek(int addr);
|
||||
void Poke(int addr, byte val);
|
||||
byte Read(ushort addr);
|
||||
void Write(ushort addr, byte val);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,202 @@
|
|||
using BizHawk.Emulation.CPUs.M6502;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace BizHawk.Emulation.Computers.Commodore64.MOS
|
||||
{
|
||||
// an extension of the 6502 processor
|
||||
|
||||
public class MOS6510 : IStandardIO
|
||||
{
|
||||
// ------------------------------------
|
||||
|
||||
private C64Chips chips;
|
||||
private MOS6502X cpu;
|
||||
private bool freezeCpu;
|
||||
private bool pinAEC;
|
||||
private bool pinCassetteButton;
|
||||
private bool pinCassetteMotor;
|
||||
private bool pinCassetteOutput;
|
||||
private bool pinCharen;
|
||||
private bool pinIRQ;
|
||||
private bool pinLoram;
|
||||
private bool pinHiram;
|
||||
private bool pinNMI;
|
||||
private bool pinRDY;
|
||||
private byte portDir;
|
||||
|
||||
// ------------------------------------
|
||||
|
||||
public MOS6510(C64Chips newChips)
|
||||
{
|
||||
chips = newChips;
|
||||
cpu = new MOS6502X();
|
||||
|
||||
// configure cpu r/w
|
||||
cpu.DummyReadMemory = Read;
|
||||
cpu.ReadMemory = Read;
|
||||
cpu.WriteMemory = Write;
|
||||
|
||||
// configure data port defaults
|
||||
portDir = 0x2F;
|
||||
SetPortData(0x37);
|
||||
}
|
||||
|
||||
public void HardReset()
|
||||
{
|
||||
cpu.Reset();
|
||||
cpu.FlagI = true;
|
||||
cpu.BCD_Enabled = true;
|
||||
cpu.PC = (ushort)(chips.pla.Read(0xFFFC) | (chips.pla.Read(0xFFFD) << 8));
|
||||
}
|
||||
|
||||
// ------------------------------------
|
||||
|
||||
public void ExecutePhase1()
|
||||
{
|
||||
UpdatePins();
|
||||
}
|
||||
|
||||
public void ExecutePhase2()
|
||||
{
|
||||
UpdatePins();
|
||||
|
||||
if (pinAEC && !freezeCpu)
|
||||
{
|
||||
// the 6502 core expects active high
|
||||
// so we reverse the polarity here
|
||||
cpu.NMI = !pinNMI;
|
||||
cpu.IRQ = !pinIRQ;
|
||||
cpu.ExecuteOne();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdatePins()
|
||||
{
|
||||
pinAEC = chips.vic.AEC;
|
||||
pinIRQ = chips.vic.IRQ && chips.cia0.IRQ;
|
||||
pinNMI = chips.cia1.IRQ;
|
||||
pinRDY = chips.vic.BA;
|
||||
|
||||
if (pinRDY)
|
||||
freezeCpu = false;
|
||||
}
|
||||
|
||||
// ------------------------------------
|
||||
|
||||
public byte Peek(int addr)
|
||||
{
|
||||
if (addr == 0x0000)
|
||||
return PortDirection;
|
||||
else if (addr == 0x0001)
|
||||
return PortData;
|
||||
else
|
||||
return chips.pla.Peek(addr);
|
||||
}
|
||||
|
||||
public void Poke(int addr, byte val)
|
||||
{
|
||||
if (addr == 0x0000)
|
||||
portDir = val;
|
||||
else if (addr == 0x0001)
|
||||
SetPortData(val);
|
||||
else
|
||||
chips.pla.Poke(addr, val);
|
||||
}
|
||||
|
||||
public byte Read(ushort addr)
|
||||
{
|
||||
// cpu freezes after first read when RDY is low
|
||||
if (!pinRDY)
|
||||
freezeCpu = true;
|
||||
|
||||
if (addr == 0x0000)
|
||||
return PortDirection;
|
||||
else if (addr == 0x0001)
|
||||
return PortData;
|
||||
else
|
||||
return chips.pla.Read(addr);
|
||||
}
|
||||
|
||||
public void Write(ushort addr, byte val)
|
||||
{
|
||||
if (addr == 0x0000)
|
||||
PortDirection = val;
|
||||
else if (addr == 0x0001)
|
||||
PortData = val;
|
||||
else
|
||||
chips.pla.Write(addr, val);
|
||||
}
|
||||
|
||||
// ------------------------------------
|
||||
|
||||
public bool AEC
|
||||
{
|
||||
get { return pinAEC; }
|
||||
}
|
||||
|
||||
public bool IRQ
|
||||
{
|
||||
get { return pinIRQ; }
|
||||
}
|
||||
|
||||
public bool NMI
|
||||
{
|
||||
get { return pinNMI; }
|
||||
}
|
||||
|
||||
public bool RDY
|
||||
{
|
||||
get { return pinRDY; }
|
||||
}
|
||||
|
||||
public byte PortData
|
||||
{
|
||||
get
|
||||
{
|
||||
byte result = 0x00;
|
||||
|
||||
result |= pinLoram ? (byte)0x01 : (byte)0x00;
|
||||
result |= pinHiram ? (byte)0x02 : (byte)0x00;
|
||||
result |= pinCharen ? (byte)0x04 : (byte)0x00;
|
||||
result |= pinCassetteOutput ? (byte)0x08 : (byte)0x00;
|
||||
result |= pinCassetteButton ? (byte)0x10 : (byte)0x00;
|
||||
result |= pinCassetteMotor ? (byte)0x20 : (byte)0x00;
|
||||
|
||||
return result;
|
||||
}
|
||||
set
|
||||
{
|
||||
byte val = Port.CPUWrite(PortData, value, portDir);
|
||||
SetPortData(val);
|
||||
}
|
||||
}
|
||||
|
||||
public byte PortDirection
|
||||
{
|
||||
get { return portDir; }
|
||||
set { portDir = value; }
|
||||
}
|
||||
|
||||
private void SetPortData(byte val)
|
||||
{
|
||||
pinCassetteOutput = ((val & 0x08) != 0);
|
||||
pinCassetteButton = ((val & 0x10) != 0);
|
||||
pinCassetteMotor = ((val & 0x20) != 0);
|
||||
|
||||
if (!chips.pla.UltimaxMode)
|
||||
{
|
||||
pinLoram = ((val & 0x01) != 0);
|
||||
pinHiram = ((val & 0x02) != 0);
|
||||
pinCharen = ((val & 0x04) != 0);
|
||||
chips.pla.LoRam = pinLoram;
|
||||
chips.pla.HiRam = pinHiram;
|
||||
chips.pla.Charen = pinCharen;
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace BizHawk.Emulation.Computers.Commodore64.MOS
|
||||
{
|
||||
// via
|
||||
public class MOS6522 : Timer, IStandardIO
|
||||
{
|
||||
// ------------------------------------
|
||||
|
||||
public void ExecutePhase1()
|
||||
{
|
||||
}
|
||||
|
||||
public void ExecutePhase2()
|
||||
{
|
||||
}
|
||||
|
||||
// ------------------------------------
|
||||
|
||||
public byte Peek(int addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void Poke(int addr, byte val)
|
||||
{
|
||||
}
|
||||
|
||||
public byte Read(ushort addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void Write(ushort addr, byte val)
|
||||
{
|
||||
}
|
||||
|
||||
// ------------------------------------
|
||||
}
|
||||
}
|
|
@ -0,0 +1,342 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace BizHawk.Emulation.Computers.Commodore64.MOS
|
||||
{
|
||||
// MOS technology 6526 "CIA"
|
||||
//
|
||||
// emulation notes:
|
||||
// * CS, R/W and RS# pins are not emulated. (not needed)
|
||||
// * A low RES pin is emulated via HardReset().
|
||||
|
||||
public class MOS6526 : Timer, IStandardIO
|
||||
{
|
||||
// ------------------------------------
|
||||
|
||||
private enum InMode
|
||||
{
|
||||
Phase2,
|
||||
CNT,
|
||||
TimerAUnderflow,
|
||||
TimerAUnderflowCNT
|
||||
}
|
||||
|
||||
private enum OutMode
|
||||
{
|
||||
Pulse,
|
||||
Toggle
|
||||
}
|
||||
|
||||
private enum RunMode
|
||||
{
|
||||
Continuous,
|
||||
Oneshot
|
||||
}
|
||||
|
||||
private enum SPMode
|
||||
{
|
||||
Input,
|
||||
Output
|
||||
}
|
||||
|
||||
// ------------------------------------
|
||||
|
||||
private bool intAlarm;
|
||||
private bool intFlag;
|
||||
private bool intSP;
|
||||
private bool[] intTimer;
|
||||
private bool pinCnt;
|
||||
private bool pinFlag;
|
||||
private bool pinPC;
|
||||
private InMode[] timerInMode;
|
||||
private OutMode[] timerOutMode;
|
||||
private bool[] timerPortEnable;
|
||||
private byte[] tod;
|
||||
private byte[] todAlarm;
|
||||
private bool todAlarmPM;
|
||||
private bool todPM;
|
||||
private uint todCounter;
|
||||
private uint todCounterLatch;
|
||||
|
||||
// ------------------------------------
|
||||
|
||||
public MOS6526(Region region)
|
||||
{
|
||||
intTimer = new bool[2];
|
||||
timerInMode = new InMode[2];
|
||||
timerOutMode = new OutMode[2];
|
||||
timerPortEnable = new bool[2];
|
||||
tod = new byte[4];
|
||||
todAlarm = new byte[4];
|
||||
switch (region)
|
||||
{
|
||||
case Region.NTSC:
|
||||
todCounterLatch = 14318181 / 140;
|
||||
break;
|
||||
case Region.PAL:
|
||||
todCounterLatch = 17734472 / 180;
|
||||
break;
|
||||
}
|
||||
HardReset();
|
||||
}
|
||||
|
||||
// ------------------------------------
|
||||
|
||||
public void ExecutePhase1()
|
||||
{
|
||||
// unsure if the timer actually operates in ph1
|
||||
}
|
||||
|
||||
public void ExecutePhase2()
|
||||
{
|
||||
pinPC = true;
|
||||
TimerRun(0);
|
||||
TimerRun(1);
|
||||
}
|
||||
|
||||
public void HardReset()
|
||||
{
|
||||
HardResetInternal();
|
||||
intTimer[0] = false;
|
||||
intTimer[1] = false;
|
||||
timerPortEnable[0] = false;
|
||||
timerPortEnable[1] = false;
|
||||
timerInMode[0] = InMode.Phase2;
|
||||
timerInMode[1] = InMode.Phase2;
|
||||
timerOn[0] = false;
|
||||
timerOn[1] = false;
|
||||
timerOutMode[0] = OutMode.Pulse;
|
||||
timerOutMode[1] = OutMode.Pulse;
|
||||
tod[0] = 0;
|
||||
tod[1] = 0;
|
||||
tod[2] = 0;
|
||||
tod[3] = 0x12;
|
||||
todAlarm[0] = 0;
|
||||
todAlarm[1] = 0;
|
||||
todAlarm[2] = 0;
|
||||
todAlarm[3] = 0;
|
||||
todCounter = todCounterLatch;
|
||||
pinCnt = false;
|
||||
pinFlag = true;
|
||||
pinPC = true;
|
||||
}
|
||||
|
||||
// ------------------------------------
|
||||
|
||||
private byte BCDAdd(byte i, byte j, out bool overflow)
|
||||
{
|
||||
uint lo;
|
||||
uint hi;
|
||||
uint result;
|
||||
|
||||
lo = (i & (uint)0x0F) + (j & (uint)0x0F);
|
||||
hi = (i & (uint)0x70) + (j & (uint)0x70);
|
||||
if (lo > 0x09)
|
||||
{
|
||||
hi += 0x10;
|
||||
lo += 0x06;
|
||||
}
|
||||
if (hi > 0x50)
|
||||
{
|
||||
hi += 0xA0;
|
||||
}
|
||||
overflow = hi >= 0x60;
|
||||
result = (hi & 0x70) + (lo & 0x0F);
|
||||
return (byte)(result & 0xFF);
|
||||
}
|
||||
|
||||
private void TimerRun(uint index)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private void TODRun()
|
||||
{
|
||||
bool todV;
|
||||
|
||||
if (todCounter == 0)
|
||||
{
|
||||
todCounter = todCounterLatch;
|
||||
tod[0] = BCDAdd(tod[0], 1, out todV);
|
||||
if (tod[0] >= 10)
|
||||
{
|
||||
tod[0] = 0;
|
||||
tod[1] = BCDAdd(tod[1], 1, out todV);
|
||||
if (todV)
|
||||
{
|
||||
tod[1] = 0;
|
||||
tod[2] = BCDAdd(tod[2], 1, out todV);
|
||||
if (todV)
|
||||
{
|
||||
tod[2] = 0;
|
||||
tod[3] = BCDAdd(tod[3], 1, out todV);
|
||||
if (tod[3] > 12)
|
||||
{
|
||||
tod[3] = 1;
|
||||
}
|
||||
else if (tod[3] == 12)
|
||||
{
|
||||
todPM = !todPM;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------
|
||||
|
||||
public bool CNT
|
||||
{
|
||||
get { return pinCnt; }
|
||||
set { pinCnt = value; }
|
||||
}
|
||||
|
||||
public bool FLAG
|
||||
{
|
||||
get { return pinFlag; }
|
||||
set
|
||||
{
|
||||
if (pinFlag && !value)
|
||||
intFlag = true;
|
||||
pinFlag = value;
|
||||
}
|
||||
}
|
||||
|
||||
public bool PC
|
||||
{
|
||||
get { return pinPC; }
|
||||
}
|
||||
|
||||
public byte Peek(int addr)
|
||||
{
|
||||
return ReadRegister((ushort)(addr & 0xF));
|
||||
}
|
||||
|
||||
public void Poke(int addr, byte val)
|
||||
{
|
||||
WriteRegister((ushort)(addr & 0xF), val);
|
||||
}
|
||||
|
||||
public byte Read(ushort addr)
|
||||
{
|
||||
addr &= 0xF;
|
||||
byte val;
|
||||
|
||||
switch (addr)
|
||||
{
|
||||
case 0x01:
|
||||
val = ReadRegister(addr);
|
||||
pinPC = false;
|
||||
break;
|
||||
default:
|
||||
val = ReadRegister(addr);
|
||||
break;
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
private byte ReadRegister(ushort addr)
|
||||
{
|
||||
byte val = 0x00; //unused pin value
|
||||
|
||||
switch (addr)
|
||||
{
|
||||
case 0x0:
|
||||
break;
|
||||
case 0x1:
|
||||
break;
|
||||
case 0x2:
|
||||
break;
|
||||
case 0x3:
|
||||
break;
|
||||
case 0x4:
|
||||
break;
|
||||
case 0x5:
|
||||
break;
|
||||
case 0x6:
|
||||
break;
|
||||
case 0x7:
|
||||
break;
|
||||
case 0x8:
|
||||
break;
|
||||
case 0x9:
|
||||
break;
|
||||
case 0xA:
|
||||
break;
|
||||
case 0xB:
|
||||
break;
|
||||
case 0xC:
|
||||
break;
|
||||
case 0xD:
|
||||
break;
|
||||
case 0xE:
|
||||
break;
|
||||
case 0xF:
|
||||
break;
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
public void Write(ushort addr, byte val)
|
||||
{
|
||||
addr &= 0xF;
|
||||
|
||||
switch (addr)
|
||||
{
|
||||
case 0x1:
|
||||
WriteRegister(addr, val);
|
||||
pinPC = false;
|
||||
break;
|
||||
default:
|
||||
WriteRegister(addr, val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void WriteRegister(ushort addr, byte val)
|
||||
{
|
||||
switch (addr)
|
||||
{
|
||||
case 0x0:
|
||||
break;
|
||||
case 0x1:
|
||||
break;
|
||||
case 0x2:
|
||||
break;
|
||||
case 0x3:
|
||||
break;
|
||||
case 0x4:
|
||||
break;
|
||||
case 0x5:
|
||||
break;
|
||||
case 0x6:
|
||||
break;
|
||||
case 0x7:
|
||||
break;
|
||||
case 0x8:
|
||||
break;
|
||||
case 0x9:
|
||||
break;
|
||||
case 0xA:
|
||||
break;
|
||||
case 0xB:
|
||||
break;
|
||||
case 0xC:
|
||||
break;
|
||||
case 0xD:
|
||||
break;
|
||||
case 0xE:
|
||||
break;
|
||||
case 0xF:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace BizHawk.Emulation.Computers.Commodore64.MOS
|
||||
{
|
||||
// vic ntsc
|
||||
public class MOS6567 : Vic, IStandardIO
|
||||
{
|
||||
static uint[][] pipeline = new uint[5][];
|
||||
|
||||
public MOS6567(C64Chips newChips)
|
||||
: base(newChips, 65, 263, pipeline, 14318181 / 14)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,332 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace BizHawk.Emulation.Computers.Commodore64.MOS
|
||||
{
|
||||
// vic pal
|
||||
public class MOS6569 : Vic, IStandardIO
|
||||
{
|
||||
static uint[][] pipeline = new uint[][]
|
||||
{
|
||||
new uint[] // xposition
|
||||
{
|
||||
0x0194, 0x0198,
|
||||
0x019C, 0x01A0,
|
||||
0x01A4, 0x01A8,
|
||||
0x01AC, 0x01B0,
|
||||
0x01B4, 0x01B8,
|
||||
|
||||
0x01BC, 0x01C0,
|
||||
0x01C4, 0x01C8,
|
||||
0x01CC, 0x01D0,
|
||||
0x01D4, 0x01D8,
|
||||
0x01DC, 0x01E0,
|
||||
|
||||
0x01E4, 0x01E8,
|
||||
0x01EC, 0x01F0,
|
||||
0x01F4, 0x0000,
|
||||
0x0004, 0x0008,
|
||||
0x000C, 0x0010,
|
||||
|
||||
0x0014, 0x0018,
|
||||
0x001C, 0x0020,
|
||||
0x0024, 0x0028,
|
||||
0x002C, 0x0030,
|
||||
0x0034, 0x0038,
|
||||
|
||||
0x003C, 0x0040,
|
||||
0x0044, 0x0048,
|
||||
0x004C, 0x0050,
|
||||
0x0054, 0x0058,
|
||||
0x005C, 0x0060,
|
||||
|
||||
0x0064, 0x0068,
|
||||
0x006C, 0x0070,
|
||||
0x0074, 0x0078,
|
||||
0x007C, 0x0080,
|
||||
0x0084, 0x0088,
|
||||
|
||||
0x008C, 0x0090,
|
||||
0x0094, 0x0098,
|
||||
0x009C, 0x00A0,
|
||||
0x00A4, 0x00A8,
|
||||
0x00AC, 0x00B0,
|
||||
|
||||
0x00B4, 0x00B8,
|
||||
0x00BC, 0x00C0,
|
||||
0x00C4, 0x00C8,
|
||||
0x00CC, 0x00D0,
|
||||
0x00D4, 0x00D8,
|
||||
|
||||
0x00DC, 0x00E0,
|
||||
0x00E4, 0x00E8,
|
||||
0x00EC, 0x00F0,
|
||||
0x00F4, 0x00F8,
|
||||
0x00FC, 0x0100,
|
||||
|
||||
0x0104, 0x0108,
|
||||
0x010C, 0x0110,
|
||||
0x0114, 0x0118,
|
||||
0x011C, 0x0120,
|
||||
0x0124, 0x0128,
|
||||
|
||||
0x012C, 0x0130,
|
||||
0x0134, 0x0138,
|
||||
0x013C, 0x0140,
|
||||
0x0144, 0x0148,
|
||||
0x014C, 0x0150,
|
||||
|
||||
0x0154, 0x0158,
|
||||
0x015C, 0x0160,
|
||||
0x0164, 0x0168,
|
||||
0x016C, 0x0170,
|
||||
0x0174, 0x0178,
|
||||
|
||||
0x017C, 0x0180,
|
||||
0x0184, 0x0188,
|
||||
0x018C, 0x0190
|
||||
},
|
||||
new uint[] // fetch (100=ref 200=c 300=g 400=i 500=none)
|
||||
{
|
||||
0x0003, 0x0013,
|
||||
0x0023, 0x0033,
|
||||
0x0004, 0x0014,
|
||||
0x0024, 0x0034,
|
||||
0x0005, 0x0015,
|
||||
|
||||
0x0025, 0x0035,
|
||||
0x0006, 0x0016,
|
||||
0x0026, 0x0036,
|
||||
0x0007, 0x0017,
|
||||
0x0027, 0x0037,
|
||||
|
||||
0x0100, 0x0500,
|
||||
0x0100, 0x0500,
|
||||
0x0100, 0x0500,
|
||||
0x0100, 0x0500,
|
||||
0x0100, 0x0200,
|
||||
|
||||
0x0300, 0x0200,
|
||||
0x0300, 0x0200,
|
||||
0x0300, 0x0200,
|
||||
0x0300, 0x0200,
|
||||
0x0300, 0x0200,
|
||||
|
||||
0x0300, 0x0200,
|
||||
0x0300, 0x0200,
|
||||
0x0300, 0x0200,
|
||||
0x0300, 0x0200,
|
||||
0x0300, 0x0200,
|
||||
|
||||
0x0300, 0x0200,
|
||||
0x0300, 0x0200,
|
||||
0x0300, 0x0200,
|
||||
0x0300, 0x0200,
|
||||
0x0300, 0x0200,
|
||||
|
||||
0x0300, 0x0200,
|
||||
0x0300, 0x0200,
|
||||
0x0300, 0x0200,
|
||||
0x0300, 0x0200,
|
||||
0x0300, 0x0200,
|
||||
|
||||
0x0300, 0x0200,
|
||||
0x0300, 0x0200,
|
||||
0x0300, 0x0200,
|
||||
0x0300, 0x0200,
|
||||
0x0300, 0x0200,
|
||||
|
||||
0x0300, 0x0200,
|
||||
0x0300, 0x0200,
|
||||
0x0300, 0x0200,
|
||||
0x0300, 0x0200,
|
||||
0x0300, 0x0200,
|
||||
|
||||
0x0300, 0x0200,
|
||||
0x0300, 0x0200,
|
||||
0x0300, 0x0200,
|
||||
0x0300, 0x0200,
|
||||
0x0300, 0x0200,
|
||||
|
||||
0x0300, 0x0200,
|
||||
0x0300, 0x0200,
|
||||
0x0300, 0x0200,
|
||||
0x0300, 0x0200,
|
||||
0x0300, 0x0500,
|
||||
|
||||
0x0400, 0x0500,
|
||||
0x0400, 0x0500,
|
||||
0x0000, 0x0010,
|
||||
0x0020, 0x0030,
|
||||
0x0001, 0x0011,
|
||||
|
||||
0x0021, 0x0031,
|
||||
0x0002, 0x0012,
|
||||
0x0022, 0x0032
|
||||
},
|
||||
new uint[] // BA
|
||||
{
|
||||
0x0834, 0x0834,
|
||||
0x0345, 0x0345,
|
||||
0x0845, 0x0845,
|
||||
0x0456, 0x0456,
|
||||
0x0856, 0x0856,
|
||||
|
||||
0x0567, 0x0567,
|
||||
0x0867, 0x0867,
|
||||
0x0887, 0x0887,
|
||||
0x0887, 0x0887,
|
||||
0x0000, 0x0000,
|
||||
|
||||
0x1000, 0x1000,
|
||||
0x1000, 0x1000,
|
||||
0x1000, 0x1000,
|
||||
0x1000, 0x1000,
|
||||
0x1000, 0x1000,
|
||||
|
||||
0x1000, 0x1000,
|
||||
0x1000, 0x1000,
|
||||
0x1000, 0x1000,
|
||||
0x1000, 0x1000,
|
||||
0x1000, 0x1000,
|
||||
|
||||
0x1000, 0x1000,
|
||||
0x1000, 0x1000,
|
||||
0x1000, 0x1000,
|
||||
0x1000, 0x1000,
|
||||
0x1000, 0x1000,
|
||||
|
||||
0x1000, 0x1000,
|
||||
0x1000, 0x1000,
|
||||
0x1000, 0x1000,
|
||||
0x1000, 0x1000,
|
||||
0x1000, 0x1000,
|
||||
|
||||
0x1000, 0x1000,
|
||||
0x1000, 0x1000,
|
||||
0x1000, 0x1000,
|
||||
0x1000, 0x1000,
|
||||
0x1000, 0x1000,
|
||||
|
||||
0x1000, 0x1000,
|
||||
0x1000, 0x1000,
|
||||
0x1000, 0x1000,
|
||||
0x1000, 0x1000,
|
||||
0x1000, 0x1000,
|
||||
|
||||
0x1000, 0x1000,
|
||||
0x1000, 0x1000,
|
||||
0x1000, 0x1000,
|
||||
0x1000, 0x1000,
|
||||
0x1000, 0x1000,
|
||||
|
||||
0x1000, 0x1000,
|
||||
0x1000, 0x1000,
|
||||
0x1000, 0x1000,
|
||||
0x1000, 0x1000,
|
||||
0x1000, 0x1000,
|
||||
|
||||
0x1000, 0x1000,
|
||||
0x1000, 0x1000,
|
||||
0x1000, 0x1000,
|
||||
0x1000, 0x1000,
|
||||
0x1000, 0x1000,
|
||||
|
||||
0x0880, 0x0880,
|
||||
0x0880, 0x0880,
|
||||
0x0801, 0x0801,
|
||||
0x0012, 0x0012,
|
||||
0x0812, 0x0812,
|
||||
|
||||
0x0123, 0x0123,
|
||||
0x0823, 0x0823,
|
||||
0x0234, 0x0234
|
||||
},
|
||||
new uint[] // actions
|
||||
{
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, pipelineUpdateVc,
|
||||
0, pipelineChkSprChunch,
|
||||
|
||||
0, pipelineUpdateMcBase,
|
||||
0, pipelineChkBrdL1,
|
||||
0, pipelineChkBrdL0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
pipelineChkSprDma, 0,
|
||||
|
||||
pipelineChkSprDma, pipelineChkBrdR0 | pipelineChkSprExp,
|
||||
0, pipelineChkBrdR1,
|
||||
pipelineChkSprDisp, pipelineUpdateRc,
|
||||
0, 0,
|
||||
0, 0,
|
||||
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0
|
||||
}
|
||||
};
|
||||
|
||||
public MOS6569(C64Chips newChips)
|
||||
: base(newChips, 63, 312, pipeline, 17734472 / 18)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace BizHawk.Emulation.Computers.Commodore64.MOS
|
||||
{
|
||||
// sid
|
||||
public class MOS6581 : Sid, IStandardIO
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,557 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace BizHawk.Emulation.Computers.Commodore64.MOS
|
||||
{
|
||||
// emulates the PLA
|
||||
// which handles all bank switching
|
||||
|
||||
public class MOSPLA : IStandardIO
|
||||
{
|
||||
// ------------------------------------
|
||||
|
||||
private enum PLABank
|
||||
{
|
||||
None,
|
||||
RAM,
|
||||
BasicROM,
|
||||
KernalROM,
|
||||
CharROM,
|
||||
IO,
|
||||
CartridgeLo,
|
||||
CartridgeHi,
|
||||
Vic,
|
||||
Sid,
|
||||
ColorRam,
|
||||
Cia0,
|
||||
Cia1,
|
||||
Expansion0,
|
||||
Expansion1
|
||||
}
|
||||
|
||||
private struct PLACpuMap
|
||||
{
|
||||
public PLABank layout1000;
|
||||
public PLABank layout8000;
|
||||
public PLABank layoutA000;
|
||||
public PLABank layoutC000;
|
||||
public PLABank layoutD000;
|
||||
public PLABank layoutE000;
|
||||
}
|
||||
|
||||
// ------------------------------------
|
||||
|
||||
private byte bus;
|
||||
private C64Chips chips;
|
||||
private bool cia0portRead;
|
||||
private PLACpuMap map;
|
||||
private bool pinCharen;
|
||||
private bool pinExRom;
|
||||
private bool pinGame;
|
||||
private bool pinHiRam;
|
||||
private bool pinLoRam;
|
||||
private bool ultimax;
|
||||
private Func<byte> vicBankPortRead;
|
||||
|
||||
public MOSPLA(C64Chips newChips, Func<byte>newVicBankPortRead)
|
||||
{
|
||||
chips = newChips;
|
||||
vicBankPortRead = newVicBankPortRead;
|
||||
}
|
||||
|
||||
public void HardReset()
|
||||
{
|
||||
pinCharen = true;
|
||||
pinExRom = true;
|
||||
pinGame = true;
|
||||
pinHiRam = true;
|
||||
pinLoRam = true;
|
||||
UpdateMap();
|
||||
}
|
||||
|
||||
// ------------------------------------
|
||||
|
||||
public bool Charen
|
||||
{
|
||||
get { return pinCharen; }
|
||||
set { pinCharen = value; UpdateMap(); }
|
||||
}
|
||||
|
||||
public bool ExRom
|
||||
{
|
||||
get { return pinExRom; }
|
||||
set { pinExRom = value; UpdateMap(); }
|
||||
}
|
||||
|
||||
public bool Game
|
||||
{
|
||||
get { return pinGame; }
|
||||
set { pinGame = value; UpdateMap(); }
|
||||
}
|
||||
|
||||
public bool HiRam
|
||||
{
|
||||
get { return pinHiRam; }
|
||||
set { pinHiRam = value; UpdateMap(); }
|
||||
}
|
||||
|
||||
public bool InputWasRead
|
||||
{
|
||||
get { return cia0portRead; }
|
||||
set { cia0portRead = value; }
|
||||
}
|
||||
|
||||
public bool LoRam
|
||||
{
|
||||
get { return pinLoRam; }
|
||||
set { pinLoRam = value; UpdateMap(); }
|
||||
}
|
||||
|
||||
public bool UltimaxMode
|
||||
{
|
||||
get { return ultimax; }
|
||||
}
|
||||
|
||||
private void UpdateMap()
|
||||
{
|
||||
if (ultimax)
|
||||
return;
|
||||
|
||||
if (pinCharen && pinHiRam && pinLoRam && pinGame && pinExRom)
|
||||
{
|
||||
// 11111
|
||||
map.layout1000 = PLABank.RAM;
|
||||
map.layout8000 = PLABank.RAM;
|
||||
map.layoutA000 = PLABank.BasicROM;
|
||||
map.layoutC000 = PLABank.RAM;
|
||||
map.layoutD000 = PLABank.IO;
|
||||
map.layoutE000 = PLABank.KernalROM;
|
||||
}
|
||||
else if (!pinCharen && pinHiRam && pinLoRam && pinGame && pinExRom)
|
||||
{
|
||||
// 01111
|
||||
map.layout1000 = PLABank.RAM;
|
||||
map.layout8000 = PLABank.RAM;
|
||||
map.layoutA000 = PLABank.BasicROM;
|
||||
map.layoutC000 = PLABank.RAM;
|
||||
map.layoutD000 = PLABank.CharROM;
|
||||
map.layoutE000 = PLABank.KernalROM;
|
||||
}
|
||||
else if (pinCharen && !pinHiRam && pinLoRam && pinGame)
|
||||
{
|
||||
// 1011X
|
||||
map.layout1000 = PLABank.RAM;
|
||||
map.layout8000 = PLABank.RAM;
|
||||
map.layoutA000 = PLABank.RAM;
|
||||
map.layoutC000 = PLABank.RAM;
|
||||
map.layoutD000 = PLABank.IO;
|
||||
map.layoutE000 = PLABank.RAM;
|
||||
}
|
||||
else if (pinCharen && !pinHiRam && pinLoRam && !pinGame && !pinExRom)
|
||||
{
|
||||
// 10100
|
||||
map.layout1000 = PLABank.RAM;
|
||||
map.layout8000 = PLABank.RAM;
|
||||
map.layoutA000 = PLABank.RAM;
|
||||
map.layoutC000 = PLABank.RAM;
|
||||
map.layoutD000 = PLABank.IO;
|
||||
map.layoutE000 = PLABank.RAM;
|
||||
}
|
||||
else if (!pinCharen && !pinHiRam && pinLoRam && pinGame)
|
||||
{
|
||||
// 0011X
|
||||
map.layout1000 = PLABank.RAM;
|
||||
map.layout8000 = PLABank.RAM;
|
||||
map.layoutA000 = PLABank.RAM;
|
||||
map.layoutC000 = PLABank.RAM;
|
||||
map.layoutD000 = PLABank.CharROM;
|
||||
map.layoutE000 = PLABank.RAM;
|
||||
}
|
||||
else if (!pinCharen && !pinHiRam && pinLoRam && !pinGame && !pinExRom)
|
||||
{
|
||||
// 00100
|
||||
map.layout1000 = PLABank.RAM;
|
||||
map.layout8000 = PLABank.RAM;
|
||||
map.layoutA000 = PLABank.RAM;
|
||||
map.layoutC000 = PLABank.RAM;
|
||||
map.layoutD000 = PLABank.RAM;
|
||||
map.layoutE000 = PLABank.RAM;
|
||||
}
|
||||
else if (!pinHiRam && !pinLoRam && pinGame)
|
||||
{
|
||||
// X001X
|
||||
map.layout1000 = PLABank.RAM;
|
||||
map.layout8000 = PLABank.RAM;
|
||||
map.layoutA000 = PLABank.RAM;
|
||||
map.layoutC000 = PLABank.RAM;
|
||||
map.layoutD000 = PLABank.RAM;
|
||||
map.layoutE000 = PLABank.RAM;
|
||||
}
|
||||
else if (pinCharen && pinHiRam && !pinLoRam && pinGame)
|
||||
{
|
||||
// 1101X
|
||||
map.layout1000 = PLABank.RAM;
|
||||
map.layout8000 = PLABank.RAM;
|
||||
map.layoutA000 = PLABank.RAM;
|
||||
map.layoutC000 = PLABank.RAM;
|
||||
map.layoutD000 = PLABank.IO;
|
||||
map.layoutE000 = PLABank.KernalROM;
|
||||
}
|
||||
else if (pinCharen && !pinHiRam && !pinLoRam && !pinExRom)
|
||||
{
|
||||
// 100X0
|
||||
map.layout1000 = PLABank.RAM;
|
||||
map.layout8000 = PLABank.RAM;
|
||||
map.layoutA000 = PLABank.RAM;
|
||||
map.layoutC000 = PLABank.RAM;
|
||||
map.layoutD000 = PLABank.IO;
|
||||
map.layoutE000 = PLABank.KernalROM;
|
||||
}
|
||||
else if (!pinCharen && pinHiRam && !pinLoRam && pinGame)
|
||||
{
|
||||
// 0101X
|
||||
map.layout1000 = PLABank.RAM;
|
||||
map.layout8000 = PLABank.RAM;
|
||||
map.layoutA000 = PLABank.RAM;
|
||||
map.layoutC000 = PLABank.RAM;
|
||||
map.layoutD000 = PLABank.CharROM;
|
||||
map.layoutE000 = PLABank.KernalROM;
|
||||
}
|
||||
else if (!pinCharen && !pinHiRam && !pinLoRam && !pinExRom)
|
||||
{
|
||||
// 000X0
|
||||
map.layout1000 = PLABank.RAM;
|
||||
map.layout8000 = PLABank.RAM;
|
||||
map.layoutA000 = PLABank.RAM;
|
||||
map.layoutC000 = PLABank.RAM;
|
||||
map.layoutD000 = PLABank.CharROM;
|
||||
map.layoutE000 = PLABank.KernalROM;
|
||||
}
|
||||
else if (pinCharen && pinHiRam && pinLoRam && pinGame && !pinExRom)
|
||||
{
|
||||
// 11110
|
||||
map.layout1000 = PLABank.RAM;
|
||||
map.layout8000 = PLABank.CartridgeLo;
|
||||
map.layoutA000 = PLABank.BasicROM;
|
||||
map.layoutC000 = PLABank.RAM;
|
||||
map.layoutD000 = PLABank.IO;
|
||||
map.layoutE000 = PLABank.KernalROM;
|
||||
}
|
||||
else if (!pinCharen && pinHiRam && pinLoRam && pinGame && !pinExRom)
|
||||
{
|
||||
// 01110
|
||||
map.layout1000 = PLABank.RAM;
|
||||
map.layout8000 = PLABank.CartridgeLo;
|
||||
map.layoutA000 = PLABank.BasicROM;
|
||||
map.layoutC000 = PLABank.RAM;
|
||||
map.layoutD000 = PLABank.CharROM;
|
||||
map.layoutE000 = PLABank.KernalROM;
|
||||
}
|
||||
else if (pinCharen && pinHiRam && !pinLoRam && !pinGame && !pinExRom)
|
||||
{
|
||||
// 11000
|
||||
map.layout1000 = PLABank.RAM;
|
||||
map.layout8000 = PLABank.RAM;
|
||||
map.layoutA000 = PLABank.CartridgeHi;
|
||||
map.layoutC000 = PLABank.RAM;
|
||||
map.layoutD000 = PLABank.IO;
|
||||
map.layoutE000 = PLABank.KernalROM;
|
||||
}
|
||||
else if (!pinCharen && pinHiRam && !pinLoRam && !pinGame && !pinExRom)
|
||||
{
|
||||
// 01000
|
||||
map.layout1000 = PLABank.RAM;
|
||||
map.layout8000 = PLABank.RAM;
|
||||
map.layoutA000 = PLABank.CartridgeHi;
|
||||
map.layoutC000 = PLABank.RAM;
|
||||
map.layoutD000 = PLABank.CharROM;
|
||||
map.layoutE000 = PLABank.KernalROM;
|
||||
}
|
||||
else if (pinCharen && pinHiRam && pinLoRam && !pinGame && !pinExRom)
|
||||
{
|
||||
// 11100
|
||||
map.layout1000 = PLABank.RAM;
|
||||
map.layout8000 = PLABank.CartridgeLo;
|
||||
map.layoutA000 = PLABank.CartridgeHi;
|
||||
map.layoutC000 = PLABank.RAM;
|
||||
map.layoutD000 = PLABank.IO;
|
||||
map.layoutE000 = PLABank.KernalROM;
|
||||
}
|
||||
else if (!pinCharen && pinHiRam && pinLoRam && !pinGame && !pinExRom)
|
||||
{
|
||||
// 01100
|
||||
map.layout1000 = PLABank.RAM;
|
||||
map.layout8000 = PLABank.CartridgeLo;
|
||||
map.layoutA000 = PLABank.CartridgeHi;
|
||||
map.layoutC000 = PLABank.RAM;
|
||||
map.layoutD000 = PLABank.CharROM;
|
||||
map.layoutE000 = PLABank.KernalROM;
|
||||
}
|
||||
else if (!pinGame && pinExRom)
|
||||
{
|
||||
// XXX01 (ultimax)
|
||||
// once in this mode, it is not supposed to change
|
||||
map.layout1000 = PLABank.None;
|
||||
map.layout8000 = PLABank.CartridgeLo;
|
||||
map.layoutA000 = PLABank.None;
|
||||
map.layoutC000 = PLABank.None;
|
||||
map.layoutD000 = PLABank.IO;
|
||||
map.layoutE000 = PLABank.CartridgeHi;
|
||||
ultimax = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("Memory configuration missing from PLA, fix this!");
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------
|
||||
|
||||
private PLABank Bank(ushort addr)
|
||||
{
|
||||
if (addr < 0x1000)
|
||||
return PLABank.RAM;
|
||||
else if (addr >= 0x1000 && addr < 0x8000)
|
||||
return map.layout1000;
|
||||
else if (addr >= 0x8000 && addr < 0xA000)
|
||||
return map.layout8000;
|
||||
else if (addr >= 0xA000 && addr < 0xC000)
|
||||
return map.layoutA000;
|
||||
else if (addr >= 0xC000 && addr < 0xD000)
|
||||
return map.layoutC000;
|
||||
else if (addr >= 0xD000 && addr < 0xE000)
|
||||
{
|
||||
if (map.layoutD000 == PLABank.IO)
|
||||
{
|
||||
if (addr >= 0xD000 && addr < 0xD400)
|
||||
return PLABank.Vic;
|
||||
else if (addr >= 0xD400 && addr < 0xD800)
|
||||
return PLABank.Sid;
|
||||
else if (addr >= 0xD800 && addr < 0xDC00)
|
||||
return PLABank.ColorRam;
|
||||
else if (addr >= 0xDC00 && addr < 0xDD00)
|
||||
return PLABank.Cia0;
|
||||
else if (addr >= 0xDD00 && addr < 0xDE00)
|
||||
return PLABank.Cia1;
|
||||
else if (addr >= 0xDE00 && addr < 0xDF00)
|
||||
return PLABank.Expansion0;
|
||||
else
|
||||
return PLABank.Expansion1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return map.layoutD000;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return map.layoutE000;
|
||||
}
|
||||
}
|
||||
|
||||
public byte Peek(int addr)
|
||||
{
|
||||
switch (Bank((ushort)(addr & 0xFFFF)))
|
||||
{
|
||||
case PLABank.BasicROM:
|
||||
return chips.basicRom.Peek(addr);
|
||||
case PLABank.CartridgeHi:
|
||||
return 0;
|
||||
case PLABank.CartridgeLo:
|
||||
return 0;
|
||||
case PLABank.CharROM:
|
||||
return chips.charRom.Peek(addr);
|
||||
case PLABank.Cia0:
|
||||
return chips.cia0.Peek(addr);
|
||||
case PLABank.Cia1:
|
||||
return chips.cia1.Peek(addr);
|
||||
case PLABank.ColorRam:
|
||||
return chips.colorRam.Peek(addr, bus);
|
||||
case PLABank.Expansion0:
|
||||
return 0;
|
||||
case PLABank.Expansion1:
|
||||
return 0;
|
||||
case PLABank.KernalROM:
|
||||
return chips.kernalRom.Peek(addr);
|
||||
case PLABank.None:
|
||||
return 0xFF;
|
||||
case PLABank.RAM:
|
||||
return chips.ram.Peek(addr);
|
||||
case PLABank.Sid:
|
||||
return chips.sid.Peek(addr);
|
||||
case PLABank.Vic:
|
||||
return chips.vic.Peek(addr);
|
||||
}
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
public void Poke(int addr, byte val)
|
||||
{
|
||||
switch (Bank((ushort)(addr & 0xFFFF)))
|
||||
{
|
||||
case PLABank.BasicROM:
|
||||
chips.basicRom.Poke(addr, val);
|
||||
break;
|
||||
case PLABank.CartridgeHi:
|
||||
break;
|
||||
case PLABank.CartridgeLo:
|
||||
break;
|
||||
case PLABank.CharROM:
|
||||
chips.charRom.Poke(addr, val);
|
||||
break;
|
||||
case PLABank.Cia0:
|
||||
chips.cia0.Poke(addr, val);
|
||||
break;
|
||||
case PLABank.Cia1:
|
||||
chips.cia1.Poke(addr, val);
|
||||
break;
|
||||
case PLABank.ColorRam:
|
||||
chips.colorRam.Poke(addr, val);
|
||||
break;
|
||||
case PLABank.Expansion0:
|
||||
break;
|
||||
case PLABank.Expansion1:
|
||||
break;
|
||||
case PLABank.KernalROM:
|
||||
chips.kernalRom.Poke(addr, val);
|
||||
break;
|
||||
case PLABank.None:
|
||||
break;
|
||||
case PLABank.RAM:
|
||||
chips.ram.Poke(addr, val);
|
||||
break;
|
||||
case PLABank.Sid:
|
||||
chips.sid.Poke(addr, val);
|
||||
break;
|
||||
case PLABank.Vic:
|
||||
chips.vic.Poke(addr, val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public byte Read(ushort addr)
|
||||
{
|
||||
switch (Bank(addr))
|
||||
{
|
||||
case PLABank.BasicROM:
|
||||
bus = chips.basicRom.Read(addr);
|
||||
break;
|
||||
case PLABank.CartridgeHi:
|
||||
bus = 0;
|
||||
break;
|
||||
case PLABank.CartridgeLo:
|
||||
bus = 0;
|
||||
break;
|
||||
case PLABank.CharROM:
|
||||
bus = chips.charRom.Read(addr);
|
||||
break;
|
||||
case PLABank.Cia0:
|
||||
if (addr == 0xDC00 || addr == 0xDC01)
|
||||
cia0portRead = true;
|
||||
bus = chips.cia0.Read(addr);
|
||||
break;
|
||||
case PLABank.Cia1:
|
||||
bus = chips.cia1.Read(addr);
|
||||
break;
|
||||
case PLABank.ColorRam:
|
||||
bus = chips.colorRam.Read(addr, bus);
|
||||
break;
|
||||
case PLABank.Expansion0:
|
||||
bus = 0;
|
||||
break;
|
||||
case PLABank.Expansion1:
|
||||
bus = 0;
|
||||
break;
|
||||
case PLABank.KernalROM:
|
||||
bus = chips.kernalRom.Read(addr);
|
||||
break;
|
||||
case PLABank.None:
|
||||
bus = 0xFF;
|
||||
break;
|
||||
case PLABank.RAM:
|
||||
bus = chips.ram.Read(addr);
|
||||
break;
|
||||
case PLABank.Sid:
|
||||
bus = chips.sid.Read(addr);
|
||||
break;
|
||||
case PLABank.Vic:
|
||||
bus = chips.vic.Read(addr);
|
||||
break;
|
||||
}
|
||||
return bus;
|
||||
}
|
||||
|
||||
public byte ReadVic(ushort addr)
|
||||
{
|
||||
addr &= 0x3FFF;
|
||||
|
||||
if (ultimax)
|
||||
{
|
||||
if (addr >= 0x3000)
|
||||
return 0; //todo: change to ROMHI
|
||||
else
|
||||
return chips.ram.Read(addr);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((addr & 0x7000) == 0x1000)
|
||||
{
|
||||
return chips.charRom.Read(addr);
|
||||
}
|
||||
else
|
||||
{
|
||||
ushort bank = (ushort)(vicBankPortRead() & 0x3);
|
||||
bank <<= 14;
|
||||
addr |= bank;
|
||||
return chips.ram.Read(addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Write(ushort addr, byte val)
|
||||
{
|
||||
switch (Bank(addr))
|
||||
{
|
||||
case PLABank.BasicROM:
|
||||
chips.basicRom.Write(addr, val);
|
||||
break;
|
||||
case PLABank.CartridgeHi:
|
||||
break;
|
||||
case PLABank.CartridgeLo:
|
||||
break;
|
||||
case PLABank.CharROM:
|
||||
chips.charRom.Write(addr, val);
|
||||
break;
|
||||
case PLABank.Cia0:
|
||||
chips.cia0.Write(addr, val);
|
||||
break;
|
||||
case PLABank.Cia1:
|
||||
chips.cia1.Write(addr, val);
|
||||
break;
|
||||
case PLABank.ColorRam:
|
||||
chips.colorRam.Write(addr, val);
|
||||
break;
|
||||
case PLABank.Expansion0:
|
||||
break;
|
||||
case PLABank.Expansion1:
|
||||
break;
|
||||
case PLABank.KernalROM:
|
||||
chips.kernalRom.Write(addr, val);
|
||||
break;
|
||||
case PLABank.None:
|
||||
break;
|
||||
case PLABank.RAM:
|
||||
chips.ram.Write(addr, val);
|
||||
break;
|
||||
case PLABank.Sid:
|
||||
chips.sid.Write(addr, val);
|
||||
break;
|
||||
case PLABank.Vic:
|
||||
chips.vic.Write(addr, val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace BizHawk.Emulation.Computers.Commodore64.MOS
|
||||
{
|
||||
static public class Port
|
||||
{
|
||||
static public byte CPUWrite(byte latch, byte val, byte dir)
|
||||
{
|
||||
byte result;
|
||||
result = (byte)(latch & ~dir);
|
||||
result |= (byte)(val & dir);
|
||||
return result;
|
||||
}
|
||||
|
||||
static public byte ExternalWrite(byte latch, byte val, byte dir)
|
||||
{
|
||||
byte result;
|
||||
result = (byte)(latch & dir);
|
||||
result |= (byte)(val & ~dir);
|
||||
return result;
|
||||
}
|
||||
|
||||
static public PortAdapter GetAdapter(Func<byte> newRead, Action<byte> newWrite)
|
||||
{
|
||||
return new PortAdapter(newRead, newWrite);
|
||||
}
|
||||
}
|
||||
|
||||
public class PortAdapter
|
||||
{
|
||||
private Action<byte> actWrite;
|
||||
private Func<byte> funcRead;
|
||||
|
||||
public PortAdapter(Func<byte> newRead, Action<byte> newWrite)
|
||||
{
|
||||
funcRead = newRead;
|
||||
actWrite = newWrite;
|
||||
}
|
||||
|
||||
public byte Data
|
||||
{
|
||||
get
|
||||
{
|
||||
return funcRead();
|
||||
}
|
||||
set
|
||||
{
|
||||
actWrite(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace BizHawk.Emulation.Computers.Commodore64.MOS
|
||||
{
|
||||
public abstract partial class Sid : ISoundProvider
|
||||
{
|
||||
public void GetSamples(short[] samples)
|
||||
{
|
||||
// produce no samples for now
|
||||
}
|
||||
|
||||
public int MaxVolume
|
||||
{
|
||||
get
|
||||
{
|
||||
return 255;
|
||||
}
|
||||
set
|
||||
{
|
||||
// no change in volume
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace BizHawk.Emulation.Computers.Commodore64.MOS
|
||||
{
|
||||
public abstract partial class Sid : ISyncSoundProvider
|
||||
{
|
||||
public void GetSamples(out short[] samples, out int nsamp)
|
||||
{
|
||||
samples = new short[0];
|
||||
nsamp = 0;
|
||||
}
|
||||
|
||||
public void DiscardSamples()
|
||||
{
|
||||
// todo
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace BizHawk.Emulation.Computers.Commodore64.MOS
|
||||
{
|
||||
public abstract partial class Sid : IStandardIO
|
||||
{
|
||||
// ------------------------------------
|
||||
|
||||
private class Envelope
|
||||
{
|
||||
}
|
||||
|
||||
private class Voice
|
||||
{
|
||||
}
|
||||
|
||||
// ------------------------------------
|
||||
|
||||
public void HardReset()
|
||||
{
|
||||
}
|
||||
|
||||
// ------------------------------------
|
||||
|
||||
public void ExecutePhase1()
|
||||
{
|
||||
}
|
||||
|
||||
public void ExecutePhase2()
|
||||
{
|
||||
}
|
||||
|
||||
// ------------------------------------
|
||||
|
||||
public byte Peek(int addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void Poke(int addr, byte val)
|
||||
{
|
||||
}
|
||||
|
||||
public byte Read(ushort addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void Write(ushort addr, byte val)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace BizHawk.Emulation.Computers.Commodore64.MOS
|
||||
{
|
||||
// base class for MOS Technology timer chips
|
||||
|
||||
public abstract class Timer
|
||||
{
|
||||
protected bool pinIRQ;
|
||||
protected byte[] portData;
|
||||
protected byte[] portDir;
|
||||
protected uint[] timer;
|
||||
protected uint[] timerLatch;
|
||||
protected bool[] timerOn;
|
||||
|
||||
public Timer()
|
||||
{
|
||||
portData = new byte[2];
|
||||
portDir = new byte[2];
|
||||
timer = new uint[2];
|
||||
timerLatch = new uint[2];
|
||||
timerOn = new bool[2];
|
||||
}
|
||||
|
||||
public PortAdapter Adapter0()
|
||||
{
|
||||
return Port.GetAdapter(ReadPort0, ExternalWritePort0);
|
||||
}
|
||||
|
||||
public PortAdapter Adapter1()
|
||||
{
|
||||
return Port.GetAdapter(ReadPort1, ExternalWritePort1);
|
||||
}
|
||||
|
||||
private void ExternalWritePort(uint index, byte data)
|
||||
{
|
||||
portData[index] = Port.ExternalWrite(portData[index], data, portDir[index]);
|
||||
}
|
||||
|
||||
private void ExternalWritePort0(byte data)
|
||||
{
|
||||
ExternalWritePort(0, data);
|
||||
}
|
||||
|
||||
private void ExternalWritePort1(byte data)
|
||||
{
|
||||
ExternalWritePort(1, data);
|
||||
}
|
||||
|
||||
protected void HardResetInternal()
|
||||
{
|
||||
timer[0] = 0xFFFF;
|
||||
timer[1] = 0xFFFF;
|
||||
timerLatch[0] = timer[0];
|
||||
timerLatch[1] = timer[1];
|
||||
pinIRQ = true;
|
||||
portDir[0] = 0x00;
|
||||
portDir[1] = 0x00;
|
||||
}
|
||||
|
||||
public bool IRQ
|
||||
{
|
||||
get
|
||||
{
|
||||
return pinIRQ;
|
||||
}
|
||||
}
|
||||
|
||||
public byte ReadPort0()
|
||||
{
|
||||
return portData[0];
|
||||
}
|
||||
|
||||
public byte ReadPort1()
|
||||
{
|
||||
return portData[1];
|
||||
}
|
||||
|
||||
protected void SyncInternal(Serializer ser)
|
||||
{
|
||||
}
|
||||
|
||||
private void WritePort(uint index, byte data)
|
||||
{
|
||||
portData[index] = Port.CPUWrite(portData[index], data, portDir[index]);
|
||||
}
|
||||
|
||||
public void WritePort0(byte data)
|
||||
{
|
||||
WritePort(0, data);
|
||||
}
|
||||
|
||||
public void WritePort1(byte data)
|
||||
{
|
||||
WritePort(1, data);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace BizHawk.Emulation.Computers.Commodore64.MOS
|
||||
{
|
||||
public abstract partial class Vic : IVideoProvider
|
||||
{
|
||||
private int[] buf;
|
||||
private int bufHeight;
|
||||
private uint bufLength;
|
||||
private uint bufOffset;
|
||||
private int bufWidth;
|
||||
|
||||
// palette
|
||||
private int[] palette =
|
||||
{
|
||||
Colors.ARGB(0x00, 0x00, 0x00),
|
||||
Colors.ARGB(0xFF, 0xFF, 0xFF),
|
||||
Colors.ARGB(0x68, 0x37, 0x2B),
|
||||
Colors.ARGB(0x70, 0xA4, 0xB2),
|
||||
Colors.ARGB(0x6F, 0x3D, 0x86),
|
||||
Colors.ARGB(0x58, 0x8D, 0x43),
|
||||
Colors.ARGB(0x35, 0x28, 0x79),
|
||||
Colors.ARGB(0xB8, 0xC7, 0x6F),
|
||||
Colors.ARGB(0x6F, 0x4F, 0x25),
|
||||
Colors.ARGB(0x43, 0x39, 0x00),
|
||||
Colors.ARGB(0x9A, 0x67, 0x59),
|
||||
Colors.ARGB(0x44, 0x44, 0x44),
|
||||
Colors.ARGB(0x6C, 0x6C, 0x6C),
|
||||
Colors.ARGB(0x9A, 0xD2, 0x84),
|
||||
Colors.ARGB(0x6C, 0x5E, 0xB5),
|
||||
Colors.ARGB(0x95, 0x95, 0x95)
|
||||
};
|
||||
|
||||
public int BackgroundColor
|
||||
{
|
||||
get { return Colors.ARGB(0, 0, 0); }
|
||||
}
|
||||
|
||||
public int BufferHeight
|
||||
{
|
||||
get { return bufHeight; }
|
||||
}
|
||||
|
||||
public int BufferWidth
|
||||
{
|
||||
get { return bufWidth; }
|
||||
}
|
||||
|
||||
public int[] GetVideoBuffer()
|
||||
{
|
||||
return buf;
|
||||
}
|
||||
|
||||
public int VirtualWidth
|
||||
{
|
||||
get { return bufWidth; }
|
||||
}
|
||||
|
||||
private void WritePixel(uint pixel)
|
||||
{
|
||||
if (borderOnMain || borderOnVertical)
|
||||
buf[bufOffset] = palette[borderColor];
|
||||
else
|
||||
buf[bufOffset] = palette[pixel];
|
||||
|
||||
bufOffset++;
|
||||
if (bufOffset == bufLength)
|
||||
bufOffset = 0;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,975 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace BizHawk.Emulation.Computers.Commodore64.MOS
|
||||
{
|
||||
public abstract partial class Vic : IStandardIO
|
||||
{
|
||||
// ------------------------------------
|
||||
|
||||
private class Sprite
|
||||
{
|
||||
public bool collideData;
|
||||
public bool collideSprite;
|
||||
public uint color;
|
||||
public bool enable;
|
||||
public uint mc;
|
||||
public uint mcbase;
|
||||
public bool multicolor;
|
||||
public uint pointer;
|
||||
public bool priority;
|
||||
public uint sr;
|
||||
public uint x;
|
||||
public bool xExpand;
|
||||
public uint y;
|
||||
public bool yExpand;
|
||||
|
||||
public void HardReset()
|
||||
{
|
||||
collideData = false;
|
||||
collideSprite = false;
|
||||
color = 0;
|
||||
enable = false;
|
||||
mc = 0;
|
||||
mcbase = 0;
|
||||
multicolor = false;
|
||||
pointer = 0;
|
||||
priority = false;
|
||||
sr = 0;
|
||||
x = 0;
|
||||
xExpand = false;
|
||||
y = 0;
|
||||
yExpand = false;
|
||||
}
|
||||
}
|
||||
private Sprite[] sprites;
|
||||
|
||||
private uint backgroundColor0;
|
||||
private uint backgroundColor1;
|
||||
private uint backgroundColor2;
|
||||
private uint backgroundColor3;
|
||||
private uint baCount;
|
||||
private bool badline;
|
||||
private bool badlineEnable;
|
||||
private bool bitmapMode;
|
||||
private bool borderCheckLEnable;
|
||||
private bool borderCheckREnable;
|
||||
private uint borderColor;
|
||||
private bool borderOnMain;
|
||||
private bool borderOnVertical;
|
||||
private uint[] bufferC;
|
||||
private uint[] bufferG;
|
||||
private byte bus;
|
||||
private bool columnSelect;
|
||||
private uint cycle;
|
||||
private uint cycleIndex;
|
||||
private uint dataC;
|
||||
private uint dataG;
|
||||
private bool displayEnable;
|
||||
private bool enableIntLightPen;
|
||||
private bool enableIntRaster;
|
||||
private bool enableIntSpriteCollision;
|
||||
private bool enableIntSpriteDataCollision;
|
||||
private bool extraColorMode;
|
||||
private bool idle;
|
||||
private bool intLightPen;
|
||||
private bool intRaster;
|
||||
private bool intSpriteCollision;
|
||||
private bool intSpriteDataCollision;
|
||||
private uint lastRasterLine;
|
||||
private uint lightPenX;
|
||||
private uint lightPenY;
|
||||
private bool multicolorMode;
|
||||
private uint pointerCB;
|
||||
private uint pointerVM;
|
||||
private uint rasterInterruptLine;
|
||||
private uint rasterLine;
|
||||
private uint rasterX;
|
||||
private uint rc;
|
||||
private uint refreshCounter;
|
||||
private bool rowSelect;
|
||||
private uint spriteMulticolor0;
|
||||
private uint spriteMulticolor1;
|
||||
private uint sr;
|
||||
private uint srCount;
|
||||
private uint vc;
|
||||
private uint vcbase;
|
||||
private uint vmli;
|
||||
private uint xScroll;
|
||||
private uint yScroll;
|
||||
|
||||
// ------------------------------------
|
||||
|
||||
private C64Chips chips;
|
||||
private int cyclesPerSec;
|
||||
private bool pinAEC;
|
||||
private bool pinBA;
|
||||
private bool pinIRQ;
|
||||
private uint[][] pipeline;
|
||||
private uint totalCycles;
|
||||
private uint totalLines;
|
||||
|
||||
// ------------------------------------
|
||||
|
||||
public Vic(C64Chips newChips, uint newCycles, uint newLines, uint[][] newPipeline, int newCyclesPerSec)
|
||||
{
|
||||
chips = newChips;
|
||||
totalCycles = newCycles;
|
||||
totalLines = newLines;
|
||||
pipeline = newPipeline;
|
||||
cyclesPerSec = newCyclesPerSec;
|
||||
|
||||
bufWidth = (int)(totalCycles * 8);
|
||||
bufHeight = (int)(totalLines);
|
||||
buf = new int[bufWidth * bufHeight];
|
||||
bufLength = (uint)buf.Length;
|
||||
|
||||
sprites = new Sprite[8];
|
||||
for (uint i = 0; i < 8; i++)
|
||||
sprites[i] = new Sprite();
|
||||
|
||||
bufferC = new uint[40];
|
||||
bufferG = new uint[40];
|
||||
}
|
||||
|
||||
public void HardReset()
|
||||
{
|
||||
pinAEC = true;
|
||||
pinBA = true;
|
||||
pinIRQ = false;
|
||||
|
||||
bufOffset = 0;
|
||||
|
||||
backgroundColor0 = 0;
|
||||
backgroundColor1 = 0;
|
||||
backgroundColor2 = 0;
|
||||
backgroundColor3 = 0;
|
||||
baCount = 4;
|
||||
badline = false;
|
||||
badlineEnable = false;
|
||||
bitmapMode = false;
|
||||
borderCheckLEnable = false;
|
||||
borderCheckREnable = false;
|
||||
borderColor = 0;
|
||||
borderOnMain = true;
|
||||
borderOnVertical = true;
|
||||
columnSelect = false;
|
||||
displayEnable = false;
|
||||
enableIntLightPen = false;
|
||||
enableIntRaster = false;
|
||||
enableIntSpriteCollision = false;
|
||||
enableIntSpriteDataCollision = false;
|
||||
extraColorMode = false;
|
||||
idle = true;
|
||||
intLightPen = false;
|
||||
intRaster = false;
|
||||
intSpriteCollision = false;
|
||||
intSpriteDataCollision = false;
|
||||
lastRasterLine = 0;
|
||||
lightPenX = 0;
|
||||
lightPenY = 0;
|
||||
multicolorMode = false;
|
||||
pointerCB = 0;
|
||||
pointerVM = 0;
|
||||
rasterInterruptLine = 0;
|
||||
rasterLine = 0;
|
||||
rasterX = 0;
|
||||
rc = 7;
|
||||
refreshCounter = 0xFF;
|
||||
rowSelect = false;
|
||||
spriteMulticolor0 = 0;
|
||||
spriteMulticolor1 = 0;
|
||||
sr = 0;
|
||||
srCount = 0;
|
||||
vc = 0;
|
||||
vcbase = 0;
|
||||
vmli = 0;
|
||||
xScroll = 0;
|
||||
yScroll = 0;
|
||||
|
||||
for (uint i = 0; i < 8; i++)
|
||||
sprites[i].HardReset();
|
||||
|
||||
for (uint i = 0; i < 40; i++)
|
||||
{
|
||||
bufferC[i] = 0;
|
||||
bufferG[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateBA()
|
||||
{
|
||||
if (pinBA)
|
||||
baCount = 4;
|
||||
else if (baCount > 0)
|
||||
baCount--;
|
||||
}
|
||||
|
||||
private void UpdatePins()
|
||||
{
|
||||
pinIRQ = !(
|
||||
(enableIntRaster & intRaster) |
|
||||
(enableIntSpriteDataCollision & intSpriteDataCollision) |
|
||||
(enableIntSpriteCollision & intSpriteCollision) |
|
||||
(enableIntLightPen & intLightPen));
|
||||
}
|
||||
|
||||
// ------------------------------------
|
||||
|
||||
protected const uint pipelineUpdateVc = 1;
|
||||
protected const uint pipelineChkSprChunch = 2;
|
||||
protected const uint pipelineUpdateMcBase = 4;
|
||||
protected const uint pipelineChkBrdL1 = 8;
|
||||
protected const uint pipelineChkBrdL0 = 16;
|
||||
protected const uint pipelineChkSprDma = 32;
|
||||
protected const uint pipelineChkBrdR0 = 64;
|
||||
protected const uint pipelineChkSprExp = 128;
|
||||
protected const uint pipelineChkBrdR1 = 256;
|
||||
protected const uint pipelineChkSprDisp = 512;
|
||||
protected const uint pipelineUpdateRc = 1024;
|
||||
|
||||
// ------------------------------------
|
||||
|
||||
public void ExecutePhase1()
|
||||
{
|
||||
// always assert access over the bus in ph1
|
||||
pinAEC = false;
|
||||
|
||||
// raster IRQ compare
|
||||
if ((cycle == 0 && rasterLine > 0) || (cycle == 1 && rasterLine == 0))
|
||||
{
|
||||
if (rasterLine != lastRasterLine)
|
||||
if (rasterLine == rasterInterruptLine)
|
||||
intRaster = true;
|
||||
lastRasterLine = rasterLine;
|
||||
}
|
||||
|
||||
// display enable compare
|
||||
if (rasterLine == 0x030)
|
||||
badlineEnable |= displayEnable;
|
||||
|
||||
// badline compare
|
||||
if (badlineEnable && rasterLine >= 0x030 && rasterLine < 0x0F7 && ((rasterLine & 0x7) == yScroll))
|
||||
badline = true;
|
||||
|
||||
if (badline)
|
||||
idle = false;
|
||||
|
||||
ParseCycle();
|
||||
|
||||
Render();
|
||||
|
||||
// must always come last
|
||||
UpdatePins();
|
||||
}
|
||||
|
||||
public void ExecutePhase2()
|
||||
{
|
||||
ParseCycle();
|
||||
|
||||
// advance cycle and optionally raster line
|
||||
cycle++;
|
||||
if (cycle == totalCycles)
|
||||
{
|
||||
vcbase = 0;
|
||||
cycleIndex = 0;
|
||||
cycle = 0;
|
||||
rasterLine++;
|
||||
if (rasterLine == totalLines)
|
||||
rasterLine = 0;
|
||||
}
|
||||
|
||||
// if the BA counter is nonzero, allow CPU bus access
|
||||
UpdateBA();
|
||||
if (baCount > 0)
|
||||
pinAEC = true;
|
||||
|
||||
Render();
|
||||
|
||||
// must always come last
|
||||
UpdatePins();
|
||||
}
|
||||
|
||||
private void ParseCycle()
|
||||
{
|
||||
ushort addr = 0x3FFF;
|
||||
uint cycleBAsprite0;
|
||||
uint cycleBAsprite1;
|
||||
uint cycleBAsprite2;
|
||||
uint cycleFetchSpriteIndex;
|
||||
uint fetch = pipeline[1][cycleIndex];
|
||||
uint ba = pipeline[2][cycleIndex];
|
||||
uint act = pipeline[3][cycleIndex];
|
||||
|
||||
// apply X location
|
||||
rasterX = pipeline[0][cycleIndex];
|
||||
|
||||
// perform fetch
|
||||
switch (fetch & 0xFF00)
|
||||
{
|
||||
case 0x0100:
|
||||
// fetch R
|
||||
refreshCounter = (refreshCounter - 1) & 0xFF;
|
||||
addr = (ushort)(0x3F00 | refreshCounter);
|
||||
bus = chips.pla.ReadVic(addr);
|
||||
break;
|
||||
case 0x0200:
|
||||
// fetch C
|
||||
if (!idle)
|
||||
{
|
||||
if (badline)
|
||||
{
|
||||
addr = (ushort)((pointerVM << 10) | vc);
|
||||
bus = chips.pla.ReadVic(addr);
|
||||
dataC = bus;
|
||||
}
|
||||
else
|
||||
{
|
||||
dataC = bufferC[vmli];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dataC = 0;
|
||||
}
|
||||
break;
|
||||
case 0x0300:
|
||||
// fetch G
|
||||
if (idle)
|
||||
addr = 0x3FFF;
|
||||
else
|
||||
{
|
||||
if (bitmapMode)
|
||||
addr = (ushort)(rc | (vc << 3) | ((pointerCB & 0x4) << 11));
|
||||
else
|
||||
addr = (ushort)(rc | (dataG << 3) | (pointerCB << 9));
|
||||
}
|
||||
if (extraColorMode)
|
||||
addr &= 0x39FF;
|
||||
bus = chips.pla.ReadVic(addr);
|
||||
dataG = bus;
|
||||
vmli++;
|
||||
vc++;
|
||||
break;
|
||||
case 0x0400:
|
||||
// fetch I
|
||||
addr = (extraColorMode ? (ushort)0x39FF : (ushort)0x3FFF);
|
||||
bus = chips.pla.ReadVic(addr);
|
||||
dataG = bus;
|
||||
dataC = 0;
|
||||
break;
|
||||
case 0x0500:
|
||||
// no fetch
|
||||
break;
|
||||
default:
|
||||
cycleFetchSpriteIndex = (fetch & 0x7);
|
||||
switch (fetch & 0xF0)
|
||||
{
|
||||
case 0x00:
|
||||
// fetch P
|
||||
addr = (ushort)(0x1F8 | (pointerVM << 10) | cycleFetchSpriteIndex);
|
||||
bus = chips.pla.ReadVic(addr);
|
||||
sprites[cycleFetchSpriteIndex].pointer = bus;
|
||||
break;
|
||||
case 0x10:
|
||||
case 0x20:
|
||||
case 0x30:
|
||||
// fetch S
|
||||
addr = (ushort)(sprites[cycleFetchSpriteIndex].mc | sprites[cycleFetchSpriteIndex].pointer << 6);
|
||||
bus = chips.pla.ReadVic(addr);
|
||||
sprites[cycleFetchSpriteIndex].sr <<= 8;
|
||||
sprites[cycleFetchSpriteIndex].sr |= bus;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// perform BA flag manipulation
|
||||
switch (ba)
|
||||
{
|
||||
case 0x0000:
|
||||
pinBA = true;
|
||||
break;
|
||||
case 0x1000:
|
||||
pinBA = !badline;
|
||||
break;
|
||||
default:
|
||||
cycleBAsprite0 = ba & 0x000F;
|
||||
cycleBAsprite1 = ba & 0x00F0;
|
||||
cycleBAsprite2 = ba & 0x0F00;
|
||||
if ((cycleBAsprite0 < 8 && sprites[cycleBAsprite0].enable) ||
|
||||
(cycleBAsprite1 < 8 && sprites[cycleBAsprite1].enable) ||
|
||||
(cycleBAsprite2 < 8 && sprites[cycleBAsprite2].enable))
|
||||
pinBA = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// perform actions
|
||||
borderCheckLEnable = false;
|
||||
borderCheckREnable = false;
|
||||
|
||||
if (!columnSelect && (act & pipelineChkBrdL0) != 0)
|
||||
borderCheckLEnable = true;
|
||||
if (columnSelect && (act & pipelineChkBrdL1) != 0)
|
||||
borderCheckLEnable = true;
|
||||
if (!columnSelect && (act & pipelineChkBrdR0) != 0)
|
||||
borderCheckREnable = true;
|
||||
if (columnSelect && (act & pipelineChkBrdR1) != 0)
|
||||
borderCheckREnable = true;
|
||||
if ((act & pipelineChkSprChunch) != 0)
|
||||
{
|
||||
}
|
||||
if ((act & pipelineChkSprDisp) != 0)
|
||||
{
|
||||
}
|
||||
if ((act & pipelineChkSprDma) != 0)
|
||||
{
|
||||
}
|
||||
if ((act & pipelineChkSprExp) != 0)
|
||||
{
|
||||
}
|
||||
if ((act & pipelineUpdateMcBase) != 0)
|
||||
{
|
||||
}
|
||||
if ((act & pipelineUpdateRc) != 0)
|
||||
{
|
||||
if (rc == 7)
|
||||
{
|
||||
idle = true;
|
||||
vcbase = vc;
|
||||
}
|
||||
if (!idle)
|
||||
rc++;
|
||||
}
|
||||
if ((act & pipelineUpdateVc) != 0)
|
||||
{
|
||||
vc = vcbase;
|
||||
vmli = 0;
|
||||
if (badline)
|
||||
rc = 0;
|
||||
}
|
||||
|
||||
cycleIndex++;
|
||||
}
|
||||
|
||||
private void Render()
|
||||
{
|
||||
if (borderCheckLEnable)
|
||||
{
|
||||
}
|
||||
if (borderCheckREnable)
|
||||
{
|
||||
}
|
||||
WritePixel(0);
|
||||
WritePixel(0);
|
||||
WritePixel(0);
|
||||
WritePixel(0);
|
||||
}
|
||||
|
||||
// ------------------------------------
|
||||
|
||||
public bool AEC { get { return pinAEC; } }
|
||||
public bool BA { get { return pinBA; } }
|
||||
public bool IRQ { get { return pinIRQ; } }
|
||||
|
||||
// ------------------------------------
|
||||
|
||||
public int CyclesPerFrame
|
||||
{
|
||||
get
|
||||
{
|
||||
return (int)(totalCycles * totalLines);
|
||||
}
|
||||
}
|
||||
|
||||
public int CyclesPerSecond
|
||||
{
|
||||
get
|
||||
{
|
||||
return cyclesPerSec;
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------
|
||||
|
||||
public byte Peek(int addr)
|
||||
{
|
||||
return ReadRegister((ushort)(addr & 0x3F));
|
||||
}
|
||||
|
||||
public void Poke(int addr, byte val)
|
||||
{
|
||||
WriteRegister((ushort)(addr & 0x3F), val);
|
||||
}
|
||||
|
||||
public byte Read(ushort addr)
|
||||
{
|
||||
byte result;
|
||||
addr &= 0x3F;
|
||||
|
||||
switch (addr)
|
||||
{
|
||||
case 0x1E:
|
||||
case 0x1F:
|
||||
// reading clears these
|
||||
result = ReadRegister(addr);
|
||||
WriteRegister(addr, 0);
|
||||
break;
|
||||
default:
|
||||
result = ReadRegister((ushort)(addr & 0x3F));
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private byte ReadRegister(ushort addr)
|
||||
{
|
||||
byte result = 0xFF; //unused bit value
|
||||
|
||||
switch (addr)
|
||||
{
|
||||
case 0x00:
|
||||
case 0x02:
|
||||
case 0x04:
|
||||
case 0x06:
|
||||
case 0x08:
|
||||
case 0x0A:
|
||||
case 0x0C:
|
||||
case 0x0E:
|
||||
result = (byte)(sprites[addr >> 1].x & 0xFF);
|
||||
break;
|
||||
case 0x01:
|
||||
case 0x03:
|
||||
case 0x05:
|
||||
case 0x07:
|
||||
case 0x09:
|
||||
case 0x0B:
|
||||
case 0x0D:
|
||||
case 0x0F:
|
||||
result = (byte)(sprites[addr >> 1].y & 0xFF);
|
||||
break;
|
||||
case 0x10:
|
||||
result = (byte)(
|
||||
((sprites[0].x >> 8) & 0x01) |
|
||||
((sprites[1].x >> 7) & 0x02) |
|
||||
((sprites[2].x >> 6) & 0x04) |
|
||||
((sprites[3].x >> 5) & 0x08) |
|
||||
((sprites[4].x >> 4) & 0x10) |
|
||||
((sprites[5].x >> 3) & 0x20) |
|
||||
((sprites[6].x >> 2) & 0x40) |
|
||||
((sprites[7].x >> 1) & 0x80)
|
||||
);
|
||||
break;
|
||||
case 0x11:
|
||||
result = (byte)(
|
||||
(byte)(yScroll & 0x7) |
|
||||
(rowSelect ? (byte)0x08 : (byte)0x00) |
|
||||
(displayEnable ? (byte)0x10 : (byte)0x00) |
|
||||
(bitmapMode ? (byte)0x20 : (byte)0x00) |
|
||||
(extraColorMode ? (byte)0x40 : (byte)0x00) |
|
||||
(byte)((rasterLine & 0x100) >> 1)
|
||||
);
|
||||
break;
|
||||
case 0x12:
|
||||
result = (byte)(rasterLine & 0xFF);
|
||||
break;
|
||||
case 0x13:
|
||||
result = (byte)(lightPenX & 0xFF);
|
||||
break;
|
||||
case 0x14:
|
||||
result = (byte)(lightPenY & 0xFF);
|
||||
break;
|
||||
case 0x15:
|
||||
result = (byte)(
|
||||
(sprites[0].enable ? 0x01 : 0x00) |
|
||||
(sprites[1].enable ? 0x02 : 0x00) |
|
||||
(sprites[2].enable ? 0x04 : 0x00) |
|
||||
(sprites[3].enable ? 0x08 : 0x00) |
|
||||
(sprites[4].enable ? 0x10 : 0x00) |
|
||||
(sprites[5].enable ? 0x20 : 0x00) |
|
||||
(sprites[6].enable ? 0x40 : 0x00) |
|
||||
(sprites[7].enable ? 0x80 : 0x00)
|
||||
);
|
||||
break;
|
||||
case 0x16:
|
||||
result &= 0xBF;
|
||||
result |= (byte)(
|
||||
(byte)(xScroll & 0x7) |
|
||||
(columnSelect ? (byte)0x08 : (byte)0x00) |
|
||||
(multicolorMode ? (byte)0x10 : (byte)0x00)
|
||||
);
|
||||
break;
|
||||
case 0x17:
|
||||
result = (byte)(
|
||||
(sprites[0].yExpand ? 0x01 : 0x00) |
|
||||
(sprites[1].yExpand ? 0x02 : 0x00) |
|
||||
(sprites[2].yExpand ? 0x04 : 0x00) |
|
||||
(sprites[3].yExpand ? 0x08 : 0x00) |
|
||||
(sprites[4].yExpand ? 0x10 : 0x00) |
|
||||
(sprites[5].yExpand ? 0x20 : 0x00) |
|
||||
(sprites[6].yExpand ? 0x40 : 0x00) |
|
||||
(sprites[7].yExpand ? 0x80 : 0x00)
|
||||
);
|
||||
break;
|
||||
case 0x18:
|
||||
result &= 0xFE;
|
||||
result |= (byte)(
|
||||
((pointerVM & 0xF) << 4) |
|
||||
((pointerCB & 0x7) << 1)
|
||||
);
|
||||
break;
|
||||
case 0x19:
|
||||
result &= 0x70;
|
||||
result |= (byte)(
|
||||
(intRaster ? 0x01 : 0x00) |
|
||||
(intSpriteDataCollision ? 0x02 : 0x00) |
|
||||
(intSpriteCollision ? 0x04 : 0x00) |
|
||||
(intLightPen ? 0x08 : 0x00) |
|
||||
(pinIRQ ? 0x00 : 0x80)
|
||||
);
|
||||
break;
|
||||
case 0x1A:
|
||||
result &= 0xF0;
|
||||
result |= (byte)(
|
||||
(enableIntRaster ? 0x01 : 0x00) |
|
||||
(enableIntSpriteDataCollision ? 0x02 : 0x00) |
|
||||
(enableIntSpriteCollision ? 0x04 : 0x00) |
|
||||
(enableIntLightPen ? 0x08 : 0x00)
|
||||
);
|
||||
break;
|
||||
case 0x1B:
|
||||
result = (byte)(
|
||||
(sprites[0].priority ? 0x01 : 0x00) |
|
||||
(sprites[1].priority ? 0x02 : 0x00) |
|
||||
(sprites[2].priority ? 0x04 : 0x00) |
|
||||
(sprites[3].priority ? 0x08 : 0x00) |
|
||||
(sprites[4].priority ? 0x10 : 0x00) |
|
||||
(sprites[5].priority ? 0x20 : 0x00) |
|
||||
(sprites[6].priority ? 0x40 : 0x00) |
|
||||
(sprites[7].priority ? 0x80 : 0x00)
|
||||
);
|
||||
break;
|
||||
case 0x1C:
|
||||
result = (byte)(
|
||||
(sprites[0].multicolor ? 0x01 : 0x00) |
|
||||
(sprites[1].multicolor ? 0x02 : 0x00) |
|
||||
(sprites[2].multicolor ? 0x04 : 0x00) |
|
||||
(sprites[3].multicolor ? 0x08 : 0x00) |
|
||||
(sprites[4].multicolor ? 0x10 : 0x00) |
|
||||
(sprites[5].multicolor ? 0x20 : 0x00) |
|
||||
(sprites[6].multicolor ? 0x40 : 0x00) |
|
||||
(sprites[7].multicolor ? 0x80 : 0x00)
|
||||
);
|
||||
break;
|
||||
case 0x1D:
|
||||
result = (byte)(
|
||||
(sprites[0].xExpand ? 0x01 : 0x00) |
|
||||
(sprites[1].xExpand ? 0x02 : 0x00) |
|
||||
(sprites[2].xExpand ? 0x04 : 0x00) |
|
||||
(sprites[3].xExpand ? 0x08 : 0x00) |
|
||||
(sprites[4].xExpand ? 0x10 : 0x00) |
|
||||
(sprites[5].xExpand ? 0x20 : 0x00) |
|
||||
(sprites[6].xExpand ? 0x40 : 0x00) |
|
||||
(sprites[7].xExpand ? 0x80 : 0x00)
|
||||
);
|
||||
break;
|
||||
case 0x1E:
|
||||
result = (byte)(
|
||||
(sprites[0].collideSprite ? 0x01 : 0x00) |
|
||||
(sprites[1].collideSprite ? 0x02 : 0x00) |
|
||||
(sprites[2].collideSprite ? 0x04 : 0x00) |
|
||||
(sprites[3].collideSprite ? 0x08 : 0x00) |
|
||||
(sprites[4].collideSprite ? 0x10 : 0x00) |
|
||||
(sprites[5].collideSprite ? 0x20 : 0x00) |
|
||||
(sprites[6].collideSprite ? 0x40 : 0x00) |
|
||||
(sprites[7].collideSprite ? 0x80 : 0x00)
|
||||
);
|
||||
break;
|
||||
case 0x1F:
|
||||
result = (byte)(
|
||||
(sprites[0].collideData ? 0x01 : 0x00) |
|
||||
(sprites[1].collideData ? 0x02 : 0x00) |
|
||||
(sprites[2].collideData ? 0x04 : 0x00) |
|
||||
(sprites[3].collideData ? 0x08 : 0x00) |
|
||||
(sprites[4].collideData ? 0x10 : 0x00) |
|
||||
(sprites[5].collideData ? 0x20 : 0x00) |
|
||||
(sprites[6].collideData ? 0x40 : 0x00) |
|
||||
(sprites[7].collideData ? 0x80 : 0x00)
|
||||
);
|
||||
break;
|
||||
case 0x20:
|
||||
result &= 0xF0;
|
||||
result |= (byte)(borderColor & 0x0F);
|
||||
break;
|
||||
case 0x21:
|
||||
result &= 0xF0;
|
||||
result |= (byte)(backgroundColor0 & 0x0F);
|
||||
break;
|
||||
case 0x22:
|
||||
result &= 0xF0;
|
||||
result |= (byte)(backgroundColor1 & 0x0F);
|
||||
break;
|
||||
case 0x23:
|
||||
result &= 0xF0;
|
||||
result |= (byte)(backgroundColor2 & 0x0F);
|
||||
break;
|
||||
case 0x24:
|
||||
result &= 0xF0;
|
||||
result |= (byte)(backgroundColor3 & 0x0F);
|
||||
break;
|
||||
case 0x25:
|
||||
result &= 0xF0;
|
||||
result |= (byte)(spriteMulticolor0 & 0x0F);
|
||||
break;
|
||||
case 0x26:
|
||||
result &= 0xF0;
|
||||
result |= (byte)(spriteMulticolor1 & 0x0F);
|
||||
break;
|
||||
case 0x27:
|
||||
case 0x28:
|
||||
case 0x29:
|
||||
case 0x2A:
|
||||
case 0x2B:
|
||||
case 0x2C:
|
||||
case 0x2D:
|
||||
case 0x2E:
|
||||
result &= 0xF0;
|
||||
result |= (byte)(sprites[addr - 0x27].color & 0xF);
|
||||
break;
|
||||
default:
|
||||
// not connected
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public void Write(ushort addr, byte val)
|
||||
{
|
||||
addr &= 0x3F;
|
||||
switch (addr)
|
||||
{
|
||||
case 0x19:
|
||||
// interrupts are cleared by writing a 1
|
||||
if ((val & 0x01) != 0)
|
||||
intRaster = false;
|
||||
if ((val & 0x02) != 0)
|
||||
intSpriteDataCollision = false;
|
||||
if ((val & 0x04) != 0)
|
||||
intSpriteCollision = false;
|
||||
if ((val & 0x08) != 0)
|
||||
intLightPen = false;
|
||||
break;
|
||||
case 0x1E:
|
||||
case 0x1F:
|
||||
// can't write to these
|
||||
break;
|
||||
case 0x2F:
|
||||
case 0x30:
|
||||
case 0x31:
|
||||
case 0x32:
|
||||
case 0x33:
|
||||
case 0x34:
|
||||
case 0x35:
|
||||
case 0x36:
|
||||
case 0x37:
|
||||
case 0x38:
|
||||
case 0x39:
|
||||
case 0x3A:
|
||||
case 0x3B:
|
||||
case 0x3C:
|
||||
case 0x3D:
|
||||
case 0x3E:
|
||||
case 0x3F:
|
||||
// not connected
|
||||
break;
|
||||
default:
|
||||
WriteRegister(addr, val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteRegister(ushort addr, byte val)
|
||||
{
|
||||
switch (addr)
|
||||
{
|
||||
case 0x00:
|
||||
case 0x02:
|
||||
case 0x04:
|
||||
case 0x06:
|
||||
case 0x08:
|
||||
case 0x0A:
|
||||
case 0x0C:
|
||||
case 0x0E:
|
||||
sprites[addr >> 1].x &= 0x100;
|
||||
sprites[addr >> 1].x |= val;
|
||||
break;
|
||||
case 0x01:
|
||||
case 0x03:
|
||||
case 0x05:
|
||||
case 0x07:
|
||||
case 0x09:
|
||||
case 0x0B:
|
||||
case 0x0D:
|
||||
case 0x0F:
|
||||
sprites[addr >> 1].y = val;
|
||||
break;
|
||||
case 0x10:
|
||||
sprites[0].x = (sprites[0].x & 0xFF) | ((uint)(val & 0x01) << 8);
|
||||
sprites[1].x = (sprites[1].x & 0xFF) | ((uint)(val & 0x02) << 7);
|
||||
sprites[2].x = (sprites[2].x & 0xFF) | ((uint)(val & 0x04) << 6);
|
||||
sprites[3].x = (sprites[3].x & 0xFF) | ((uint)(val & 0x08) << 5);
|
||||
sprites[4].x = (sprites[4].x & 0xFF) | ((uint)(val & 0x10) << 4);
|
||||
sprites[5].x = (sprites[5].x & 0xFF) | ((uint)(val & 0x20) << 3);
|
||||
sprites[6].x = (sprites[6].x & 0xFF) | ((uint)(val & 0x40) << 2);
|
||||
sprites[7].x = (sprites[7].x & 0xFF) | ((uint)(val & 0x80) << 1);
|
||||
break;
|
||||
case 0x11:
|
||||
yScroll = (val & (uint)0x07);
|
||||
rowSelect = ((val & 0x08) != 0);
|
||||
displayEnable = ((val & 0x10) != 0);
|
||||
bitmapMode = ((val & 0x20) != 0);
|
||||
extraColorMode = ((val & 0x40) != 0);
|
||||
rasterInterruptLine &= 0xFF;
|
||||
rasterInterruptLine |= (uint)(val & 0x80) << 1;
|
||||
break;
|
||||
case 0x12:
|
||||
rasterInterruptLine &= 0x100;
|
||||
rasterInterruptLine |= val;
|
||||
break;
|
||||
case 0x13:
|
||||
lightPenX = val;
|
||||
break;
|
||||
case 0x14:
|
||||
lightPenY = val;
|
||||
break;
|
||||
case 0x15:
|
||||
sprites[0].enable = ((val & 0x01) != 0);
|
||||
sprites[1].enable = ((val & 0x02) != 0);
|
||||
sprites[2].enable = ((val & 0x04) != 0);
|
||||
sprites[3].enable = ((val & 0x08) != 0);
|
||||
sprites[4].enable = ((val & 0x10) != 0);
|
||||
sprites[5].enable = ((val & 0x20) != 0);
|
||||
sprites[6].enable = ((val & 0x40) != 0);
|
||||
sprites[7].enable = ((val & 0x80) != 0);
|
||||
break;
|
||||
case 0x16:
|
||||
xScroll = (val & (uint)0x07);
|
||||
columnSelect = ((val & 0x08) != 0);
|
||||
multicolorMode = ((val & 0x10) != 0);
|
||||
break;
|
||||
case 0x17:
|
||||
sprites[0].yExpand = ((val & 0x01) != 0);
|
||||
sprites[1].yExpand = ((val & 0x02) != 0);
|
||||
sprites[2].yExpand = ((val & 0x04) != 0);
|
||||
sprites[3].yExpand = ((val & 0x08) != 0);
|
||||
sprites[4].yExpand = ((val & 0x10) != 0);
|
||||
sprites[5].yExpand = ((val & 0x20) != 0);
|
||||
sprites[6].yExpand = ((val & 0x40) != 0);
|
||||
sprites[7].yExpand = ((val & 0x80) != 0);
|
||||
break;
|
||||
case 0x18:
|
||||
pointerVM = (uint)((val >> 4) & 0xF);
|
||||
pointerCB = (uint)((val >> 1) & 0x7);
|
||||
break;
|
||||
case 0x19:
|
||||
intRaster = ((val & 0x01) != 0);
|
||||
intSpriteDataCollision = ((val & 0x02) != 0);
|
||||
intSpriteCollision = ((val & 0x04) != 0);
|
||||
intLightPen = ((val & 0x08) != 0);
|
||||
break;
|
||||
case 0x1A:
|
||||
enableIntRaster = ((val & 0x01) != 0);
|
||||
enableIntSpriteDataCollision = ((val & 0x02) != 0);
|
||||
enableIntSpriteCollision = ((val & 0x04) != 0);
|
||||
enableIntLightPen = ((val & 0x08) != 0);
|
||||
break;
|
||||
case 0x1B:
|
||||
sprites[0].priority = ((val & 0x01) != 0);
|
||||
sprites[1].priority = ((val & 0x02) != 0);
|
||||
sprites[2].priority = ((val & 0x04) != 0);
|
||||
sprites[3].priority = ((val & 0x08) != 0);
|
||||
sprites[4].priority = ((val & 0x10) != 0);
|
||||
sprites[5].priority = ((val & 0x20) != 0);
|
||||
sprites[6].priority = ((val & 0x40) != 0);
|
||||
sprites[7].priority = ((val & 0x80) != 0);
|
||||
break;
|
||||
case 0x1C:
|
||||
sprites[0].multicolor = ((val & 0x01) != 0);
|
||||
sprites[1].multicolor = ((val & 0x02) != 0);
|
||||
sprites[2].multicolor = ((val & 0x04) != 0);
|
||||
sprites[3].multicolor = ((val & 0x08) != 0);
|
||||
sprites[4].multicolor = ((val & 0x10) != 0);
|
||||
sprites[5].multicolor = ((val & 0x20) != 0);
|
||||
sprites[6].multicolor = ((val & 0x40) != 0);
|
||||
sprites[7].multicolor = ((val & 0x80) != 0);
|
||||
break;
|
||||
case 0x1D:
|
||||
sprites[0].xExpand = ((val & 0x01) != 0);
|
||||
sprites[1].xExpand = ((val & 0x02) != 0);
|
||||
sprites[2].xExpand = ((val & 0x04) != 0);
|
||||
sprites[3].xExpand = ((val & 0x08) != 0);
|
||||
sprites[4].xExpand = ((val & 0x10) != 0);
|
||||
sprites[5].xExpand = ((val & 0x20) != 0);
|
||||
sprites[6].xExpand = ((val & 0x40) != 0);
|
||||
sprites[7].xExpand = ((val & 0x80) != 0);
|
||||
break;
|
||||
case 0x1E:
|
||||
sprites[0].collideSprite = ((val & 0x01) != 0);
|
||||
sprites[1].collideSprite = ((val & 0x02) != 0);
|
||||
sprites[2].collideSprite = ((val & 0x04) != 0);
|
||||
sprites[3].collideSprite = ((val & 0x08) != 0);
|
||||
sprites[4].collideSprite = ((val & 0x10) != 0);
|
||||
sprites[5].collideSprite = ((val & 0x20) != 0);
|
||||
sprites[6].collideSprite = ((val & 0x40) != 0);
|
||||
sprites[7].collideSprite = ((val & 0x80) != 0);
|
||||
break;
|
||||
case 0x1F:
|
||||
sprites[0].collideData = ((val & 0x01) != 0);
|
||||
sprites[1].collideData = ((val & 0x02) != 0);
|
||||
sprites[2].collideData = ((val & 0x04) != 0);
|
||||
sprites[3].collideData = ((val & 0x08) != 0);
|
||||
sprites[4].collideData = ((val & 0x10) != 0);
|
||||
sprites[5].collideData = ((val & 0x20) != 0);
|
||||
sprites[6].collideData = ((val & 0x40) != 0);
|
||||
sprites[7].collideData = ((val & 0x80) != 0);
|
||||
break;
|
||||
case 0x20:
|
||||
borderColor = (uint)(val & 0xF);
|
||||
break;
|
||||
case 0x21:
|
||||
backgroundColor0 = (uint)(val & 0xF);
|
||||
break;
|
||||
case 0x22:
|
||||
backgroundColor1 = (uint)(val & 0xF);
|
||||
break;
|
||||
case 0x23:
|
||||
backgroundColor2 = (uint)(val & 0xF);
|
||||
break;
|
||||
case 0x24:
|
||||
backgroundColor3 = (uint)(val & 0xF);
|
||||
break;
|
||||
case 0x25:
|
||||
spriteMulticolor0 = (uint)(val & 0xF);
|
||||
break;
|
||||
case 0x26:
|
||||
spriteMulticolor1 = (uint)(val & 0xF);
|
||||
break;
|
||||
case 0x27:
|
||||
case 0x28:
|
||||
case 0x29:
|
||||
case 0x2A:
|
||||
case 0x2B:
|
||||
case 0x2C:
|
||||
case 0x2D:
|
||||
case 0x2E:
|
||||
sprites[addr - 0x27].color = (uint)(val & 0xF);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,635 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace BizHawk.Emulation.Computers.Commodore64
|
||||
{
|
||||
public enum MemoryDesignation
|
||||
{
|
||||
Disabled,
|
||||
RAM,
|
||||
Basic,
|
||||
Kernal,
|
||||
IO,
|
||||
Character,
|
||||
ROMLo,
|
||||
ROMHi,
|
||||
Vic,
|
||||
Sid,
|
||||
ColorRam,
|
||||
Cia0,
|
||||
Cia1,
|
||||
Expansion0,
|
||||
Expansion1
|
||||
}
|
||||
|
||||
public class MemoryLayout
|
||||
{
|
||||
public MemoryDesignation Mem1000 = MemoryDesignation.RAM;
|
||||
public MemoryDesignation Mem8000 = MemoryDesignation.RAM;
|
||||
public MemoryDesignation MemA000 = MemoryDesignation.RAM;
|
||||
public MemoryDesignation MemC000 = MemoryDesignation.RAM;
|
||||
public MemoryDesignation MemD000 = MemoryDesignation.RAM;
|
||||
public MemoryDesignation MemE000 = MemoryDesignation.RAM;
|
||||
}
|
||||
|
||||
public class Memory
|
||||
{
|
||||
// chips
|
||||
public Cia cia0;
|
||||
public Cia cia1;
|
||||
public VicII vic;
|
||||
public Sid sid;
|
||||
|
||||
// storage
|
||||
public Cartridge cart;
|
||||
|
||||
// roms
|
||||
public byte[] basicRom;
|
||||
public byte[] charRom;
|
||||
public bool exRomPin = true;
|
||||
public bool gamePin = true;
|
||||
public byte[] kernalRom;
|
||||
public MemoryLayout layout;
|
||||
|
||||
// ram
|
||||
public byte[] colorRam;
|
||||
public byte[] ram;
|
||||
public bool vicCharEnabled;
|
||||
public ushort vicOffset;
|
||||
|
||||
// registers
|
||||
public byte busData;
|
||||
public bool inputWasRead;
|
||||
public bool readTrigger = true;
|
||||
public bool writeTrigger = true;
|
||||
|
||||
// ports
|
||||
public DataPortConnector cpuIO;
|
||||
public DataPortConnector cpuPort;
|
||||
public DataPortBus cpuPortBus = new DataPortBus();
|
||||
|
||||
void HandleFirmwareError(string file)
|
||||
{
|
||||
System.Windows.Forms.MessageBox.Show("the C64 core is referencing a firmware file which could not be found. Please make sure it's in your configured C64 firmwares folder. The referenced filename is: " + file);
|
||||
throw new FileNotFoundException();
|
||||
}
|
||||
|
||||
public Memory(string sourceFolder, VicII newVic, Sid newSid, Cia newCia0, Cia newCia1)
|
||||
{
|
||||
string basicFile = "basic";
|
||||
string charFile = "chargen";
|
||||
string kernalFile = "kernal";
|
||||
|
||||
string basicPath = Path.Combine(sourceFolder, basicFile);
|
||||
string charPath = Path.Combine(sourceFolder, charFile);
|
||||
string kernalPath = Path.Combine(sourceFolder, kernalFile);
|
||||
|
||||
if (!File.Exists(basicPath)) HandleFirmwareError(basicFile);
|
||||
if (!File.Exists(charPath)) HandleFirmwareError(charFile);
|
||||
if (!File.Exists(kernalPath)) HandleFirmwareError(kernalFile);
|
||||
|
||||
basicRom = File.ReadAllBytes(basicPath);
|
||||
charRom = File.ReadAllBytes(charPath);
|
||||
kernalRom = File.ReadAllBytes(kernalPath);
|
||||
|
||||
vic = newVic;
|
||||
sid = newSid;
|
||||
cia0 = newCia0;
|
||||
cia1 = newCia1;
|
||||
|
||||
cpuPort = cpuPortBus.Connect();
|
||||
cpuPort.Latch = 0x00;
|
||||
cpuPort.Direction = 0x1F;
|
||||
cpuPortBus.AttachWriteHook(UpdateLayout);
|
||||
|
||||
cpuIO = cpuPortBus.Connect();
|
||||
cpuIO.Latch = 0x17;
|
||||
|
||||
cia1.AttachWriteHook(0, UpdateVicOffset);
|
||||
HardReset();
|
||||
}
|
||||
|
||||
public byte this[ushort index]
|
||||
{
|
||||
get
|
||||
{
|
||||
return ram[index];
|
||||
}
|
||||
set
|
||||
{
|
||||
ram[index] = value;
|
||||
}
|
||||
}
|
||||
|
||||
public MemoryDesignation GetDesignation(ushort addr)
|
||||
{
|
||||
MemoryDesignation result;
|
||||
|
||||
if (addr < 0x1000)
|
||||
{
|
||||
result = MemoryDesignation.RAM;
|
||||
}
|
||||
else if (addr < 0x8000)
|
||||
{
|
||||
result = layout.Mem1000;
|
||||
}
|
||||
else if (addr < 0xA000)
|
||||
{
|
||||
result = layout.Mem8000;
|
||||
}
|
||||
else if (addr < 0xC000)
|
||||
{
|
||||
result = layout.MemA000;
|
||||
}
|
||||
else if (addr < 0xD000)
|
||||
{
|
||||
result = layout.MemC000;
|
||||
}
|
||||
else if (addr < 0xE000)
|
||||
{
|
||||
result = layout.MemD000;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = layout.MemE000;
|
||||
}
|
||||
|
||||
if (result == MemoryDesignation.IO)
|
||||
{
|
||||
addr &= 0x0FFF;
|
||||
if (addr < 0x0400)
|
||||
{
|
||||
result = MemoryDesignation.Vic;
|
||||
}
|
||||
else if (addr < 0x0800)
|
||||
{
|
||||
result = MemoryDesignation.Sid;
|
||||
}
|
||||
else if (addr < 0x0C00)
|
||||
{
|
||||
result = MemoryDesignation.ColorRam;
|
||||
}
|
||||
else if (addr < 0x0D00)
|
||||
{
|
||||
result = MemoryDesignation.Cia0;
|
||||
}
|
||||
else if (addr < 0x0E00)
|
||||
{
|
||||
result = MemoryDesignation.Cia1;
|
||||
}
|
||||
else if (addr < 0x0F00)
|
||||
{
|
||||
result = MemoryDesignation.Expansion0;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = MemoryDesignation.Expansion1;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public void HardReset()
|
||||
{
|
||||
layout = new MemoryLayout();
|
||||
|
||||
ram = new byte[0x10000];
|
||||
colorRam = new byte[0x1000];
|
||||
WipeMemory();
|
||||
|
||||
cpuPort.Direction = 0x2F;
|
||||
cpuPort.Data = 0x37;
|
||||
|
||||
UpdateVicOffset();
|
||||
}
|
||||
|
||||
public byte Peek(ushort addr)
|
||||
{
|
||||
byte result;
|
||||
|
||||
if (addr == 0x0000)
|
||||
{
|
||||
result = cpuPort.Direction;
|
||||
}
|
||||
else if (addr == 0x0001)
|
||||
{
|
||||
result = cpuPort.Data;
|
||||
}
|
||||
else
|
||||
{
|
||||
MemoryDesignation des = GetDesignation(addr);
|
||||
|
||||
switch (des)
|
||||
{
|
||||
case MemoryDesignation.Basic:
|
||||
result = basicRom[addr & 0x1FFF];
|
||||
break;
|
||||
case MemoryDesignation.Character:
|
||||
result = charRom[addr & 0x0FFF];
|
||||
break;
|
||||
case MemoryDesignation.Vic:
|
||||
result = vic.Peek(addr & 0x3F);
|
||||
break;
|
||||
case MemoryDesignation.Sid:
|
||||
result = sid.Peek(addr & 0x1F);
|
||||
break;
|
||||
case MemoryDesignation.ColorRam:
|
||||
result = (byte)(colorRam[addr & 0x03FF] | (busData & 0xF0));
|
||||
break;
|
||||
case MemoryDesignation.Cia0:
|
||||
result = cia0.Peek(addr & 0x0F);
|
||||
break;
|
||||
case MemoryDesignation.Cia1:
|
||||
result = cia1.Peek(addr & 0x0F);
|
||||
break;
|
||||
case MemoryDesignation.Expansion0:
|
||||
result = 0;
|
||||
break;
|
||||
case MemoryDesignation.Expansion1:
|
||||
result = 0;
|
||||
break;
|
||||
case MemoryDesignation.Kernal:
|
||||
result = kernalRom[addr & 0x1FFF];
|
||||
break;
|
||||
case MemoryDesignation.RAM:
|
||||
result = ram[addr];
|
||||
break;
|
||||
case MemoryDesignation.ROMHi:
|
||||
result = cart.chips[cart.bank].data[addr & cart.chips[cart.bank].romMask];
|
||||
break;
|
||||
case MemoryDesignation.ROMLo:
|
||||
result = cart.chips[cart.bank].data[addr & cart.chips[cart.bank].romMask];
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
busData = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
public byte PeekRam(int addr)
|
||||
{
|
||||
return ram[addr & 0xFFFF];
|
||||
}
|
||||
|
||||
public void Poke(ushort addr, byte val)
|
||||
{
|
||||
return;
|
||||
/*
|
||||
if (addr == 0x0000)
|
||||
{
|
||||
cpuPort.Direction = val;
|
||||
}
|
||||
else if (addr == 0x0001)
|
||||
{
|
||||
cpuPort.Data = val;
|
||||
UpdateLayout();
|
||||
}
|
||||
else
|
||||
{
|
||||
MemoryDesignation des = GetDesignation(addr);
|
||||
|
||||
switch (des)
|
||||
{
|
||||
case MemoryDesignation.Vic:
|
||||
vic.Poke(addr, val);
|
||||
break;
|
||||
case MemoryDesignation.Sid:
|
||||
sid.Poke(addr, val);
|
||||
break;
|
||||
case MemoryDesignation.ColorRam:
|
||||
colorRam[addr & 0x03FF] = (byte)(val & 0x0F);
|
||||
break;
|
||||
case MemoryDesignation.Cia0:
|
||||
cia0.Poke(addr, val);
|
||||
break;
|
||||
case MemoryDesignation.Cia1:
|
||||
cia1.Poke(addr, val);
|
||||
break;
|
||||
case MemoryDesignation.Expansion0:
|
||||
if (cart != null)
|
||||
cart.WritePort(addr, val);
|
||||
break;
|
||||
case MemoryDesignation.Expansion1:
|
||||
break;
|
||||
case MemoryDesignation.RAM:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// write through to ram
|
||||
if (des != MemoryDesignation.Disabled)
|
||||
{
|
||||
ram[addr] = val;
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
public void PokeRam(int addr, byte val)
|
||||
{
|
||||
ram[addr & 0xFFFF] = val;
|
||||
}
|
||||
|
||||
public byte Read(ushort addr)
|
||||
{
|
||||
byte result;
|
||||
|
||||
if (addr == 0x0000)
|
||||
{
|
||||
result = cpuPort.Direction;
|
||||
}
|
||||
else if (addr == 0x0001)
|
||||
{
|
||||
result = cpuPort.Data;
|
||||
}
|
||||
else
|
||||
{
|
||||
MemoryDesignation des = GetDesignation(addr);
|
||||
|
||||
switch (des)
|
||||
{
|
||||
case MemoryDesignation.Basic:
|
||||
result = basicRom[addr & 0x1FFF];
|
||||
break;
|
||||
case MemoryDesignation.Character:
|
||||
result = charRom[addr & 0x0FFF];
|
||||
break;
|
||||
case MemoryDesignation.Vic:
|
||||
result = vic.Read(addr);
|
||||
break;
|
||||
case MemoryDesignation.Sid:
|
||||
result = sid.Read(addr);
|
||||
break;
|
||||
case MemoryDesignation.ColorRam:
|
||||
result = ReadColorRam(addr);
|
||||
break;
|
||||
case MemoryDesignation.Cia0:
|
||||
if ((addr & 0xF) < 0x02)
|
||||
inputWasRead = true;
|
||||
result = cia0.Read(addr);
|
||||
break;
|
||||
case MemoryDesignation.Cia1:
|
||||
result = cia1.Read(addr);
|
||||
break;
|
||||
case MemoryDesignation.Expansion0:
|
||||
if (cart != null)
|
||||
result = cart.ReadPort(addr);
|
||||
else
|
||||
result = 0;
|
||||
break;
|
||||
case MemoryDesignation.Expansion1:
|
||||
result = 0;
|
||||
break;
|
||||
case MemoryDesignation.Kernal:
|
||||
result = kernalRom[addr & 0x1FFF];
|
||||
break;
|
||||
case MemoryDesignation.RAM:
|
||||
result = ram[addr];
|
||||
break;
|
||||
case MemoryDesignation.ROMHi:
|
||||
result = cart.Read(addr);
|
||||
break;
|
||||
case MemoryDesignation.ROMLo:
|
||||
result = cart.Read(addr);
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
busData = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
public byte ReadColorRam(ushort addr)
|
||||
{
|
||||
return (byte)((busData & 0xF0) | (colorRam[addr & 0x03FF]));
|
||||
}
|
||||
|
||||
public void SyncState(Serializer ser)
|
||||
{
|
||||
ser.Sync("BUSDATA", ref busData);
|
||||
ser.Sync("EXROMPIN", ref exRomPin);
|
||||
ser.Sync("GAMEPIN", ref gamePin);
|
||||
ser.Sync("INPUTWASREAD", ref inputWasRead);
|
||||
|
||||
ser.Sync("COLORRAM", ref colorRam, false);
|
||||
ser.Sync("RAM", ref ram, false);
|
||||
|
||||
byte cpuData = cpuPort.Latch;
|
||||
byte cpuDir = cpuPort.Direction;
|
||||
ser.Sync("CPUPORT", ref cpuData);
|
||||
ser.Sync("CPUDIR", ref cpuDir);
|
||||
|
||||
if (ser.IsReader)
|
||||
{
|
||||
cpuPort.Latch = cpuData;
|
||||
cpuPort.Direction = cpuDir;
|
||||
UpdateLayout();
|
||||
UpdateVicOffset();
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateLayout()
|
||||
{
|
||||
byte cpuData = cpuPort.Data;
|
||||
bool loRom = ((cpuData & 0x01) != 0);
|
||||
bool hiRom = ((cpuData & 0x02) != 0);
|
||||
bool ioEnable = ((cpuData & 0x04) != 0);
|
||||
|
||||
if (loRom && hiRom && gamePin && exRomPin)
|
||||
{
|
||||
layout.Mem1000 = MemoryDesignation.RAM;
|
||||
layout.Mem8000 = MemoryDesignation.RAM;
|
||||
layout.MemA000 = MemoryDesignation.Basic;
|
||||
layout.MemC000 = MemoryDesignation.RAM;
|
||||
layout.MemD000 = ioEnable ? MemoryDesignation.IO : MemoryDesignation.Character;
|
||||
layout.MemE000 = MemoryDesignation.Kernal;
|
||||
}
|
||||
else if (loRom && !hiRom && gamePin)
|
||||
{
|
||||
layout.Mem1000 = MemoryDesignation.RAM;
|
||||
layout.Mem8000 = MemoryDesignation.RAM;
|
||||
layout.MemA000 = MemoryDesignation.RAM;
|
||||
layout.MemC000 = MemoryDesignation.RAM;
|
||||
layout.MemD000 = ioEnable ? MemoryDesignation.IO : MemoryDesignation.Character;
|
||||
layout.MemE000 = MemoryDesignation.RAM;
|
||||
}
|
||||
else if (loRom && !hiRom && !exRomPin && !gamePin)
|
||||
{
|
||||
layout.Mem1000 = MemoryDesignation.RAM;
|
||||
layout.Mem8000 = MemoryDesignation.RAM;
|
||||
layout.MemA000 = MemoryDesignation.RAM;
|
||||
layout.MemC000 = MemoryDesignation.RAM;
|
||||
layout.MemD000 = ioEnable ? MemoryDesignation.IO : MemoryDesignation.RAM;
|
||||
layout.MemE000 = MemoryDesignation.RAM;
|
||||
}
|
||||
else if ((!loRom && hiRom && gamePin) || (!loRom && !hiRom && !exRomPin))
|
||||
{
|
||||
layout.Mem1000 = MemoryDesignation.RAM;
|
||||
layout.Mem8000 = MemoryDesignation.RAM;
|
||||
layout.MemA000 = MemoryDesignation.RAM;
|
||||
layout.MemC000 = MemoryDesignation.RAM;
|
||||
layout.MemD000 = ioEnable ? MemoryDesignation.IO : MemoryDesignation.Character;
|
||||
layout.MemE000 = MemoryDesignation.Kernal;
|
||||
}
|
||||
else if (!loRom && !hiRom && gamePin)
|
||||
{
|
||||
layout.Mem1000 = MemoryDesignation.RAM;
|
||||
layout.Mem8000 = MemoryDesignation.RAM;
|
||||
layout.MemA000 = MemoryDesignation.RAM;
|
||||
layout.MemC000 = MemoryDesignation.RAM;
|
||||
layout.MemD000 = MemoryDesignation.RAM;
|
||||
layout.MemE000 = MemoryDesignation.RAM;
|
||||
}
|
||||
else if (loRom && hiRom && gamePin && !exRomPin)
|
||||
{
|
||||
layout.Mem1000 = MemoryDesignation.RAM;
|
||||
layout.Mem8000 = MemoryDesignation.ROMLo;
|
||||
layout.MemA000 = MemoryDesignation.Basic;
|
||||
layout.MemC000 = MemoryDesignation.RAM;
|
||||
layout.MemD000 = ioEnable ? MemoryDesignation.IO : MemoryDesignation.Character;
|
||||
layout.MemE000 = MemoryDesignation.Kernal;
|
||||
}
|
||||
else if (!loRom && hiRom && !gamePin && !exRomPin)
|
||||
{
|
||||
layout.Mem1000 = MemoryDesignation.RAM;
|
||||
layout.Mem8000 = MemoryDesignation.RAM;
|
||||
layout.MemA000 = MemoryDesignation.ROMHi;
|
||||
layout.MemC000 = MemoryDesignation.RAM;
|
||||
layout.MemD000 = ioEnable ? MemoryDesignation.IO : MemoryDesignation.Character;
|
||||
layout.MemE000 = MemoryDesignation.Kernal;
|
||||
}
|
||||
else if (loRom && hiRom && !gamePin && !exRomPin)
|
||||
{
|
||||
layout.Mem1000 = MemoryDesignation.RAM;
|
||||
layout.Mem8000 = MemoryDesignation.ROMLo;
|
||||
layout.MemA000 = MemoryDesignation.ROMHi;
|
||||
layout.MemC000 = MemoryDesignation.RAM;
|
||||
layout.MemD000 = ioEnable ? MemoryDesignation.IO : MemoryDesignation.Character;
|
||||
layout.MemE000 = MemoryDesignation.Kernal;
|
||||
}
|
||||
else if (!gamePin && exRomPin)
|
||||
{
|
||||
layout.Mem1000 = MemoryDesignation.Disabled;
|
||||
layout.Mem8000 = MemoryDesignation.ROMLo;
|
||||
layout.MemA000 = MemoryDesignation.Disabled;
|
||||
layout.MemC000 = MemoryDesignation.Disabled;
|
||||
layout.MemD000 = MemoryDesignation.IO;
|
||||
layout.MemE000 = MemoryDesignation.ROMHi;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateVicOffset()
|
||||
{
|
||||
switch (cia1.Peek(0x00) & 0x03)
|
||||
{
|
||||
case 0:
|
||||
vicCharEnabled = false;
|
||||
vicOffset = 0xC000;
|
||||
break;
|
||||
case 1:
|
||||
vicCharEnabled = true;
|
||||
vicOffset = 0x8000;
|
||||
break;
|
||||
case 2:
|
||||
vicCharEnabled = false;
|
||||
vicOffset = 0x4000;
|
||||
break;
|
||||
default:
|
||||
vicCharEnabled = true;
|
||||
vicOffset = 0x0000;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public byte VicRead(ushort addr)
|
||||
{
|
||||
addr = (ushort)(addr & 0x3FFF);
|
||||
if (vicCharEnabled && (addr >= 0x1000 && addr < 0x2000))
|
||||
{
|
||||
return charRom[addr & 0x0FFF];
|
||||
}
|
||||
else
|
||||
{
|
||||
return ram[addr | vicOffset];
|
||||
}
|
||||
}
|
||||
|
||||
public void WipeMemory()
|
||||
{
|
||||
// memory is striped in sections 00/FF
|
||||
for (int i = 0; i < 0x10000; i += 0x80)
|
||||
{
|
||||
for (int j = 0; j < 0x40; j++)
|
||||
ram[i + j] = 0x00;
|
||||
for (int j = 0x40; j < 0x80; j++)
|
||||
ram[i + j] = 0xFF;
|
||||
}
|
||||
for (int i = 0; i < 0x1000; i++)
|
||||
{
|
||||
colorRam[i] = 0x0E;
|
||||
}
|
||||
}
|
||||
|
||||
public void Write(ushort addr, byte val)
|
||||
{
|
||||
if (addr == 0x0000)
|
||||
{
|
||||
cpuPort.Direction = val;
|
||||
}
|
||||
else if (addr == 0x0001)
|
||||
{
|
||||
cpuPort.Data = val;
|
||||
}
|
||||
else
|
||||
{
|
||||
MemoryDesignation des = GetDesignation(addr);
|
||||
|
||||
switch (des)
|
||||
{
|
||||
case MemoryDesignation.Vic:
|
||||
vic.Write(addr, val);
|
||||
break;
|
||||
case MemoryDesignation.Sid:
|
||||
sid.Write(addr, val);
|
||||
break;
|
||||
case MemoryDesignation.ColorRam:
|
||||
colorRam[addr & 0x03FF] = (byte)(val & 0x0F);
|
||||
break;
|
||||
case MemoryDesignation.Cia0:
|
||||
cia0.Write(addr, val);
|
||||
break;
|
||||
case MemoryDesignation.Cia1:
|
||||
cia1.Write(addr, val);
|
||||
break;
|
||||
case MemoryDesignation.Expansion0:
|
||||
if (cart != null)
|
||||
cart.WritePort(addr, val);
|
||||
break;
|
||||
case MemoryDesignation.Expansion1:
|
||||
break;
|
||||
case MemoryDesignation.RAM:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// write through to ram
|
||||
if (des != MemoryDesignation.Disabled)
|
||||
{
|
||||
ram[addr] = val;
|
||||
}
|
||||
}
|
||||
busData = val;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,10 +5,7 @@ using System.Text;
|
|||
|
||||
namespace BizHawk.Emulation.Computers.Commodore64
|
||||
{
|
||||
public interface IMedia
|
||||
class Memory
|
||||
{
|
||||
void Apply();
|
||||
bool Loaded();
|
||||
bool Ready();
|
||||
}
|
||||
}
|
|
@ -1,58 +0,0 @@
|
|||
using BizHawk.Emulation.CPUs.M6502;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace BizHawk.Emulation.Computers.Commodore64
|
||||
{
|
||||
public class PRGFile : IMedia
|
||||
{
|
||||
private MOS6502X cpu;
|
||||
private byte[] data;
|
||||
private bool loaded;
|
||||
private Memory mem;
|
||||
|
||||
public PRGFile(byte[] file, Memory checkMem, MOS6502X targetCpu)
|
||||
{
|
||||
cpu = targetCpu;
|
||||
mem = checkMem;
|
||||
data = file;
|
||||
}
|
||||
|
||||
public void Apply()
|
||||
{
|
||||
int address = data[1];
|
||||
address <<= 8;
|
||||
address |= data[0];
|
||||
|
||||
int count = data.Length;
|
||||
|
||||
for (int i = 2; i < count; i++)
|
||||
{
|
||||
mem.Write((ushort)(address & 0xFFFF), data[i]);
|
||||
address++;
|
||||
}
|
||||
loaded = true;
|
||||
}
|
||||
|
||||
public bool Loaded()
|
||||
{
|
||||
return loaded;
|
||||
}
|
||||
|
||||
public bool Ready()
|
||||
{
|
||||
// wait for READY. to be on display
|
||||
return (
|
||||
mem[0x04C8] == 0x12 &&
|
||||
mem[0x04C9] == 0x05 &&
|
||||
mem[0x04CA] == 0x01 &&
|
||||
mem[0x04CB] == 0x04 &&
|
||||
mem[0x04CC] == 0x19 &&
|
||||
mem[0x04CD] == 0x2E &&
|
||||
mem[0x04CE] == 0x20
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace BizHawk.Emulation.Computers.Commodore64
|
||||
{
|
||||
public class DataPortSerialInputConverter : DataPortConverter
|
||||
{
|
||||
public override byte Convert(byte local, byte remote)
|
||||
{
|
||||
int via = (local & 0x70);
|
||||
via |= ((remote & 0x08) == 0) ? 0x80 : 0x00; //CIA ATNOUT - VIA ATNIN
|
||||
via |= ((remote & 0x10) == 0) ? 0x04 : 0x00; //CIA CLKOUT - VIA CLKIN
|
||||
via |= ((remote & 0x20) == 0) ? 0x01 : 0x00; //CIA DATOUT - VIA DATIN
|
||||
via |= ((remote & 0x40) == 0) ? 0x08 : 0x00; //CIA CLKIN - VIA CLKOUT
|
||||
via |= ((remote & 0x80) == 0) ? 0x02 : 0x00; //CIA DATIN - VIA DATOUT
|
||||
return (byte)via;
|
||||
}
|
||||
}
|
||||
|
||||
public class DataPortSerialOutputConverter : DataPortConverter
|
||||
{
|
||||
public override byte Convert(byte local, byte remote)
|
||||
{
|
||||
int cia = (remote & 0x7);
|
||||
cia |= ((local & 0x01) == 0) ? 0x20 : 0x00; //VIA DATIN - CIA DATOUT
|
||||
cia |= ((local & 0x02) == 0) ? 0x80 : 0x00; //VIA DATOUT - CIA DATIN
|
||||
cia |= ((local & 0x04) == 0) ? 0x10 : 0x00; //VIA CLKIN - CIA CLKOUT
|
||||
cia |= ((local & 0x08) == 0) ? 0x40 : 0x00; //VIA CLKOUT - CIA CLKIN
|
||||
cia |= ((local & 0x80) == 0) ? 0x08 : 0x00; //VIA DATIN - CIA DATOUT
|
||||
return (byte)cia;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,305 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace BizHawk.Emulation.Computers.Commodore64
|
||||
{
|
||||
public enum SidMode
|
||||
{
|
||||
Sid6581,
|
||||
Sid8580
|
||||
}
|
||||
|
||||
public class VoiceRegs
|
||||
{
|
||||
public EnvelopeGenerator Envelope;
|
||||
public WaveformGenerator Generator;
|
||||
public WaveformGenerator SyncSource;
|
||||
|
||||
public int EnvelopeLatch;
|
||||
public int GeneratorLatch;
|
||||
|
||||
public VoiceRegs()
|
||||
{
|
||||
Envelope = new EnvelopeGenerator();
|
||||
Generator = new WaveformGenerator();
|
||||
}
|
||||
|
||||
public void Clock()
|
||||
{
|
||||
Generator.Clock();
|
||||
GeneratorLatch = Generator.Output(SyncSource);
|
||||
Envelope.Clock();
|
||||
EnvelopeLatch = Envelope.Output;
|
||||
}
|
||||
|
||||
public short Output()
|
||||
{
|
||||
int result = (GeneratorLatch * EnvelopeLatch) >> 4;
|
||||
result -= 32768;
|
||||
if (result > 32767)
|
||||
result = 32767;
|
||||
else if (result < -32768)
|
||||
result = -32768;
|
||||
return (short)result;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
Generator.Reset();
|
||||
Envelope.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
public class SidRegs
|
||||
{
|
||||
public bool BP;
|
||||
public bool D3;
|
||||
public int FC;
|
||||
public bool[] FILT = new bool[3];
|
||||
public bool FILTEX;
|
||||
public bool HP;
|
||||
public bool LP;
|
||||
public int POTX;
|
||||
public int POTY;
|
||||
public int RES;
|
||||
public int VOL;
|
||||
|
||||
public VoiceRegs[] Voices;
|
||||
|
||||
public SidRegs()
|
||||
{
|
||||
Voices = new VoiceRegs[3];
|
||||
Voices[0] = new VoiceRegs();
|
||||
Voices[1] = new VoiceRegs();
|
||||
Voices[2] = new VoiceRegs();
|
||||
|
||||
Voices[0].SyncSource = Voices[2].Generator;
|
||||
Voices[1].SyncSource = Voices[0].Generator;
|
||||
Voices[2].SyncSource = Voices[1].Generator;
|
||||
}
|
||||
|
||||
public byte this[int addr]
|
||||
{
|
||||
|
||||
get
|
||||
{
|
||||
int result;
|
||||
|
||||
addr &= 0x1F;
|
||||
switch (addr)
|
||||
{
|
||||
case 0x19:
|
||||
result = POTX;
|
||||
break;
|
||||
case 0x1A:
|
||||
result = POTY;
|
||||
break;
|
||||
case 0x1B:
|
||||
result = Voices[2].GeneratorLatch >> 4;
|
||||
break;
|
||||
case 0x1C:
|
||||
result = Voices[2].EnvelopeLatch;
|
||||
break;
|
||||
default:
|
||||
result = 0;
|
||||
break;
|
||||
}
|
||||
return (byte)(result & 0xFF);
|
||||
}
|
||||
set
|
||||
{
|
||||
int val = value;
|
||||
int index;
|
||||
|
||||
addr &= 0x1F;
|
||||
switch (addr)
|
||||
{
|
||||
case 0x00:
|
||||
case 0x07:
|
||||
case 0x0E:
|
||||
index = addr / 7;
|
||||
Voices[index].Generator.Frequency &= 0xFF00;
|
||||
Voices[index].Generator.Frequency |= value;
|
||||
break;
|
||||
case 0x01:
|
||||
case 0x08:
|
||||
case 0x0F:
|
||||
index = addr / 7;
|
||||
Voices[index].Generator.Frequency &= 0xFF;
|
||||
Voices[index].Generator.Frequency |= (int)value << 8;
|
||||
break;
|
||||
case 0x02:
|
||||
case 0x09:
|
||||
case 0x10:
|
||||
index = addr / 7;
|
||||
Voices[index].Generator.PulseWidth &= 0x0F00;
|
||||
Voices[index].Generator.PulseWidth |= value;
|
||||
break;
|
||||
case 0x03:
|
||||
case 0x0A:
|
||||
case 0x11:
|
||||
index = addr / 7;
|
||||
Voices[index].Generator.PulseWidth &= 0xFF;
|
||||
Voices[index].Generator.PulseWidth |= (int)(value & 0xF) << 8;
|
||||
break;
|
||||
case 0x04:
|
||||
case 0x0B:
|
||||
case 0x12:
|
||||
index = addr / 7;
|
||||
Voices[index].Generator.Control = value;
|
||||
Voices[index].Envelope.Gate = ((value & 0x01) != 0x00);
|
||||
break;
|
||||
case 0x05:
|
||||
case 0x0C:
|
||||
case 0x13:
|
||||
index = addr / 7;
|
||||
Voices[index].Envelope.Attack = (value >> 4);
|
||||
Voices[index].Envelope.Decay = (value & 0xF);
|
||||
break;
|
||||
case 0x06:
|
||||
case 0x0D:
|
||||
case 0x14:
|
||||
index = addr / 7;
|
||||
Voices[index].Envelope.Sustain = (value >> 4);
|
||||
Voices[index].Envelope.Release = (value & 0xF);
|
||||
break;
|
||||
case 0x15:
|
||||
FC &= 0x7F8;
|
||||
FC |= val & 0x7;
|
||||
break;
|
||||
case 0x16:
|
||||
FC &= 0x7;
|
||||
FC |= val << 3;
|
||||
break;
|
||||
case 0x17:
|
||||
FILT[0] = ((val & 0x01) != 0x00);
|
||||
FILT[1] = ((val & 0x02) != 0x00);
|
||||
FILT[2] = ((val & 0x04) != 0x00);
|
||||
FILTEX = ((val & 0x08) != 0x00);
|
||||
RES = (val >> 4);
|
||||
break;
|
||||
case 0x18:
|
||||
VOL = (val & 0xF);
|
||||
LP = ((val & 0x10) != 0x00);
|
||||
BP = ((val & 0x20) != 0x00);
|
||||
HP = ((val & 0x40) != 0x00);
|
||||
D3 = ((val & 0x80) != 0x00);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public partial class Sid : ISoundProvider
|
||||
{
|
||||
private int[] syncIndex = { 2, 0, 1 };
|
||||
|
||||
private VoiceRegs[] voices;
|
||||
|
||||
public Func<int> ReadPotX;
|
||||
public Func<int> ReadPotY;
|
||||
|
||||
public int clock;
|
||||
public int cyclesPerSample;
|
||||
public int output;
|
||||
public SidRegs regs;
|
||||
|
||||
public Sid(Region newRegion, int sampleRate)
|
||||
{
|
||||
ReadPotX = DummyReadPot;
|
||||
ReadPotY = DummyReadPot;
|
||||
switch (newRegion)
|
||||
{
|
||||
case Region.NTSC:
|
||||
cyclesPerSample = 14318181 / 14 / sampleRate;
|
||||
break;
|
||||
case Region.PAL:
|
||||
cyclesPerSample = 17734472 / 18 / sampleRate;
|
||||
break;
|
||||
}
|
||||
InitSound(sampleRate);
|
||||
HardReset();
|
||||
}
|
||||
|
||||
private int DummyReadPot()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void HardReset()
|
||||
{
|
||||
regs = new SidRegs();
|
||||
voices = regs.Voices;
|
||||
}
|
||||
|
||||
public byte Peek(int addr)
|
||||
{
|
||||
return regs[addr & 0x1F];
|
||||
}
|
||||
|
||||
public void PerformCycle()
|
||||
{
|
||||
// process each voice
|
||||
voices[0].Clock();
|
||||
voices[1].Clock();
|
||||
voices[2].Clock();
|
||||
|
||||
// sync voices
|
||||
voices[0].Generator.Synchronize(voices[1].Generator, voices[2].Generator);
|
||||
voices[1].Generator.Synchronize(voices[2].Generator, voices[0].Generator);
|
||||
voices[2].Generator.Synchronize(voices[0].Generator, voices[1].Generator);
|
||||
|
||||
// finalize sample and put into buffer
|
||||
SubmitSample();
|
||||
|
||||
// query pots every 512 cycles
|
||||
if ((clock & 0x1FF) == 0x000)
|
||||
{
|
||||
regs.POTX = ReadPotX() & 0xFF;
|
||||
regs.POTY = ReadPotY() & 0xFF;
|
||||
}
|
||||
|
||||
clock = (clock + 1) & 0xFFFFFF;
|
||||
}
|
||||
|
||||
public void Poke(int addr, byte val)
|
||||
{
|
||||
regs[addr & 0x1F] = val;
|
||||
}
|
||||
|
||||
public byte Read(ushort addr)
|
||||
{
|
||||
addr &= 0x1F;
|
||||
switch (addr)
|
||||
{
|
||||
case 0x19:
|
||||
case 0x1A:
|
||||
case 0x1B:
|
||||
case 0x1C:
|
||||
// can only read these regs
|
||||
return regs[addr];
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void Write(ushort addr, byte val)
|
||||
{
|
||||
addr &= 0x1F;
|
||||
switch (addr)
|
||||
{
|
||||
case 0x19:
|
||||
case 0x1A:
|
||||
case 0x1B:
|
||||
case 0x1C:
|
||||
// can't write these regs
|
||||
break;
|
||||
default:
|
||||
regs[addr] = val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,255 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace BizHawk.Emulation.Computers.Commodore64
|
||||
{
|
||||
|
||||
// constants for the EnvelopeGenerator and calculation
|
||||
// methods are based from the libsidplayfp residfp library.
|
||||
|
||||
public partial class EnvelopeGenerator
|
||||
{
|
||||
public enum EnvelopeState
|
||||
{
|
||||
Attack, Decay, Release
|
||||
}
|
||||
|
||||
// value table for the envelope shift register
|
||||
static int[] adsrTable = new int[]
|
||||
{
|
||||
0x7F00, 0x0006, 0x003C, 0x0330,
|
||||
0x20C0, 0x6755, 0x3800, 0x500E,
|
||||
0x1212, 0x0222, 0x1848, 0x59B8,
|
||||
0x3840, 0x77E2, 0x7625, 0x0A93
|
||||
};
|
||||
|
||||
int attack;
|
||||
int decay;
|
||||
bool gate;
|
||||
int release;
|
||||
int sustain;
|
||||
|
||||
public byte envelopeCounter;
|
||||
public bool envelopeProcessEnabled;
|
||||
public int exponentialCounter;
|
||||
public int exponentialCounterPeriod;
|
||||
public bool freeze;
|
||||
public int lfsr;
|
||||
public int rate;
|
||||
public EnvelopeState state;
|
||||
|
||||
public EnvelopeGenerator()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
public int Attack
|
||||
{
|
||||
get
|
||||
{
|
||||
return attack;
|
||||
}
|
||||
set
|
||||
{
|
||||
attack = value;
|
||||
if (state == EnvelopeState.Attack)
|
||||
rate = adsrTable[attack];
|
||||
}
|
||||
}
|
||||
|
||||
public void Clock()
|
||||
{
|
||||
if (envelopeProcessEnabled)
|
||||
{
|
||||
envelopeProcessEnabled = false;
|
||||
envelopeCounter--;
|
||||
UpdateExponentialCounter();
|
||||
}
|
||||
|
||||
if (lfsr != rate)
|
||||
{
|
||||
int feedback = ((lfsr >> 14) ^ (lfsr >> 13)) & 0x01;
|
||||
lfsr = ((lfsr << 1) & 0x7FFF) | feedback;
|
||||
return;
|
||||
}
|
||||
|
||||
lfsr = 0x7FFF;
|
||||
|
||||
if ((state == EnvelopeState.Attack) || (++exponentialCounter == exponentialCounterPeriod))
|
||||
{
|
||||
exponentialCounter = 0;
|
||||
if (!freeze)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case EnvelopeState.Attack:
|
||||
++envelopeCounter;
|
||||
if (envelopeCounter == 0xFF)
|
||||
{
|
||||
state = EnvelopeState.Decay;
|
||||
rate = adsrTable[decay];
|
||||
}
|
||||
break;
|
||||
case EnvelopeState.Decay:
|
||||
if (envelopeCounter == ((sustain << 4) | sustain))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (exponentialCounterPeriod != 1)
|
||||
{
|
||||
envelopeProcessEnabled = true;
|
||||
return;
|
||||
}
|
||||
envelopeCounter--;
|
||||
break;
|
||||
case EnvelopeState.Release:
|
||||
if (exponentialCounterPeriod != 1)
|
||||
{
|
||||
envelopeProcessEnabled = true;
|
||||
return;
|
||||
}
|
||||
envelopeCounter--;
|
||||
break;
|
||||
}
|
||||
|
||||
UpdateExponentialCounter();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int Decay
|
||||
{
|
||||
get
|
||||
{
|
||||
return decay;
|
||||
}
|
||||
set
|
||||
{
|
||||
decay = value;
|
||||
if (state == EnvelopeState.Decay)
|
||||
rate = adsrTable[decay];
|
||||
}
|
||||
}
|
||||
|
||||
public bool Gate
|
||||
{
|
||||
get
|
||||
{
|
||||
return gate;
|
||||
}
|
||||
set
|
||||
{
|
||||
bool gateThis = value;
|
||||
|
||||
if (!gate && gateThis)
|
||||
{
|
||||
state = EnvelopeState.Attack;
|
||||
rate = adsrTable[attack];
|
||||
freeze = false;
|
||||
envelopeProcessEnabled = false;
|
||||
}
|
||||
else if (gate && !gateThis)
|
||||
{
|
||||
state = EnvelopeState.Release;
|
||||
rate = adsrTable[release];
|
||||
}
|
||||
|
||||
gate = gateThis;
|
||||
}
|
||||
}
|
||||
|
||||
public short Output
|
||||
{
|
||||
get
|
||||
{
|
||||
return envelopeCounter;
|
||||
}
|
||||
}
|
||||
|
||||
public int Release
|
||||
{
|
||||
get
|
||||
{
|
||||
return release;
|
||||
}
|
||||
set
|
||||
{
|
||||
release = value;
|
||||
if (state == EnvelopeState.Release)
|
||||
rate = adsrTable[release];
|
||||
}
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
attack = 0;
|
||||
decay = 0;
|
||||
sustain = 0;
|
||||
release = 0;
|
||||
gate = false;
|
||||
|
||||
envelopeCounter = 0;
|
||||
envelopeProcessEnabled = false;
|
||||
exponentialCounter = 0;
|
||||
exponentialCounterPeriod = 1;
|
||||
|
||||
lfsr = 0x7FFF;
|
||||
state = EnvelopeState.Release;
|
||||
rate = adsrTable[release];
|
||||
freeze = true;
|
||||
}
|
||||
|
||||
public void SetState(int stateAtk, int stateDcy, int stateSus, int stateRls, bool stateGate, EnvelopeState stateState)
|
||||
{
|
||||
attack = stateAtk;
|
||||
decay = stateDcy;
|
||||
sustain = stateSus;
|
||||
release = stateRls;
|
||||
gate = stateGate;
|
||||
state = stateState;
|
||||
}
|
||||
|
||||
public int Sustain
|
||||
{
|
||||
get
|
||||
{
|
||||
return sustain;
|
||||
}
|
||||
set
|
||||
{
|
||||
sustain = value;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateExponentialCounter()
|
||||
{
|
||||
switch (envelopeCounter)
|
||||
{
|
||||
case 0x00:
|
||||
exponentialCounterPeriod = 1;
|
||||
freeze = true;
|
||||
break;
|
||||
case 0x06:
|
||||
exponentialCounterPeriod = 30;
|
||||
break;
|
||||
case 0x0E:
|
||||
exponentialCounterPeriod = 16;
|
||||
break;
|
||||
case 0x1A:
|
||||
exponentialCounterPeriod = 8;
|
||||
break;
|
||||
case 0x36:
|
||||
exponentialCounterPeriod = 4;
|
||||
break;
|
||||
case 0x5D:
|
||||
exponentialCounterPeriod = 2;
|
||||
break;
|
||||
case 0xFF:
|
||||
exponentialCounterPeriod = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,143 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace BizHawk.Emulation.Computers.Commodore64
|
||||
{
|
||||
public partial class Sid : ISoundProvider
|
||||
{
|
||||
private short[] sampleBuffer;
|
||||
private int sampleBufferCapacity;
|
||||
private int sampleBufferCount;
|
||||
private int sampleBufferIndex;
|
||||
private int sampleBufferReadIndex;
|
||||
private int sampleCounter;
|
||||
|
||||
public void DiscardSamples()
|
||||
{
|
||||
sampleBuffer = new short[sampleBufferCapacity];
|
||||
ResetBuffer();
|
||||
}
|
||||
|
||||
public short[] GetAllSamples()
|
||||
{
|
||||
if (sampleBufferCount > 0)
|
||||
{
|
||||
short[] samples = new short[sampleBufferCount];
|
||||
GetSamples(samples);
|
||||
return samples;
|
||||
}
|
||||
else
|
||||
{
|
||||
return new short[] { };
|
||||
}
|
||||
}
|
||||
|
||||
public void GetSamples(short[] samples)
|
||||
{
|
||||
int count = samples.Length;
|
||||
int copied = 0;
|
||||
|
||||
while (copied < count)
|
||||
{
|
||||
samples[copied] = sampleBuffer[sampleBufferReadIndex];
|
||||
if (sampleBufferIndex != sampleBufferReadIndex)
|
||||
sampleBufferReadIndex++;
|
||||
copied++;
|
||||
if (sampleBufferReadIndex == sampleBufferCapacity)
|
||||
sampleBufferReadIndex = 0;
|
||||
}
|
||||
|
||||
// catch buffer up
|
||||
sampleBufferReadIndex = sampleBufferIndex;
|
||||
sampleBufferCount = 0;
|
||||
}
|
||||
|
||||
private void InitSound(int initSampleRate)
|
||||
{
|
||||
sampleBufferCapacity = initSampleRate;
|
||||
DiscardSamples();
|
||||
}
|
||||
|
||||
public int MaxVolume
|
||||
{
|
||||
get
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
set
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private void ResetBuffer()
|
||||
{
|
||||
sampleBufferReadIndex = 0;
|
||||
sampleBufferIndex = 0;
|
||||
}
|
||||
|
||||
private void SubmitSample()
|
||||
{
|
||||
if (sampleCounter == 0)
|
||||
{
|
||||
int mixer;
|
||||
|
||||
mixer = voices[0].Output();
|
||||
mixer += voices[1].Output();
|
||||
if (!regs.D3 || !regs.FILT[2])
|
||||
mixer += voices[2].Output();
|
||||
|
||||
// apply volume
|
||||
mixer *= regs.VOL;
|
||||
mixer >>= 4;
|
||||
|
||||
// apply filter
|
||||
// (todo)
|
||||
|
||||
// the mixer is very loud at this point, let's make it quieter
|
||||
mixer /= 4;
|
||||
|
||||
if (mixer > 32767)
|
||||
mixer = 326767;
|
||||
else if (mixer < -32768)
|
||||
mixer = -32768;
|
||||
|
||||
short output = (short)mixer;
|
||||
|
||||
// run twice since the buffer expects stereo sound (I THINK)
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
sampleBufferIndex++;
|
||||
sampleBufferCount++;
|
||||
if (sampleBufferIndex == sampleBufferCapacity)
|
||||
sampleBufferIndex = 0;
|
||||
sampleBuffer[sampleBufferIndex] = output;
|
||||
}
|
||||
sampleCounter = cyclesPerSample;
|
||||
}
|
||||
sampleCounter--;
|
||||
}
|
||||
}
|
||||
|
||||
public class SidSyncSoundProvider : ISyncSoundProvider
|
||||
{
|
||||
private Sid sid;
|
||||
|
||||
public SidSyncSoundProvider(Sid source)
|
||||
{
|
||||
sid = source;
|
||||
}
|
||||
|
||||
public void DiscardSamples()
|
||||
{
|
||||
sid.DiscardSamples();
|
||||
}
|
||||
|
||||
public void GetSamples(out short[] samples, out int nsamp)
|
||||
{
|
||||
samples = sid.GetAllSamples();
|
||||
nsamp = samples.Length / 2;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,82 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace BizHawk.Emulation.Computers.Commodore64
|
||||
{
|
||||
public partial class Sid : ISoundProvider
|
||||
{
|
||||
public void SyncState(Serializer ser)
|
||||
{
|
||||
// voices
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
string iTag = i.ToString();
|
||||
ser.Sync("GENACCUM" + iTag, ref regs.Voices[i].Generator.accumulator);
|
||||
ser.Sync("GENFOTTL" + iTag, ref regs.Voices[i].Generator.floatingOutputTtl);
|
||||
ser.Sync("GENMSBRISING" + iTag, ref regs.Voices[i].Generator.msbRising);
|
||||
ser.Sync("GENNOISEOUT" + iTag, ref regs.Voices[i].Generator.noiseOutput);
|
||||
ser.Sync("GENPULSEOUT" + iTag, ref regs.Voices[i].Generator.pulseOutput);
|
||||
ser.Sync("GENSR" + iTag, ref regs.Voices[i].Generator.shiftRegister);
|
||||
ser.Sync("GENSRDELAY" + iTag, ref regs.Voices[i].Generator.shiftRegisterDelay);
|
||||
ser.Sync("GENSRRESETDELAY" + iTag, ref regs.Voices[i].Generator.shiftRegisterResetDelay);
|
||||
ser.Sync("GENWAVEFORMOUT" + iTag, ref regs.Voices[i].Generator.waveformOutput);
|
||||
|
||||
ser.Sync("ENVCOUNTER" + iTag, ref regs.Voices[i].Envelope.envelopeCounter);
|
||||
ser.Sync("ENVENABLE" + iTag, ref regs.Voices[i].Envelope.envelopeProcessEnabled);
|
||||
ser.Sync("ENVEXPCOUNTER" + iTag, ref regs.Voices[i].Envelope.exponentialCounter);
|
||||
ser.Sync("ENVEXPCOUNTERPERIOD" + iTag, ref regs.Voices[i].Envelope.exponentialCounterPeriod);
|
||||
ser.Sync("ENVFREEZE" + iTag, ref regs.Voices[i].Envelope.freeze);
|
||||
ser.Sync("ENVLFSR" + iTag, ref regs.Voices[i].Envelope.lfsr);
|
||||
ser.Sync("ENVRATE" + iTag, ref regs.Voices[i].Envelope.rate);
|
||||
|
||||
byte control = regs.Voices[i].Generator.Control;
|
||||
int freq = regs.Voices[i].Generator.Frequency;
|
||||
int pw = regs.Voices[i].Generator.PulseWidth;
|
||||
int attack = regs.Voices[i].Envelope.Attack;
|
||||
int decay = regs.Voices[i].Envelope.Decay;
|
||||
int sustain = regs.Voices[i].Envelope.Sustain;
|
||||
int release = regs.Voices[i].Envelope.Release;
|
||||
bool gate = regs.Voices[i].Envelope.Gate;
|
||||
int state = (int)regs.Voices[i].Envelope.state;
|
||||
|
||||
ser.Sync("GENCONTROL" + iTag, ref control);
|
||||
ser.Sync("GENFREQ" + iTag, ref freq);
|
||||
ser.Sync("GENPW" + iTag, ref pw);
|
||||
ser.Sync("ENVATTACK" + iTag, ref attack);
|
||||
ser.Sync("ENVDECAY" + iTag, ref decay);
|
||||
ser.Sync("ENVSUSTAIN" + iTag, ref sustain);
|
||||
ser.Sync("ENVRELEASE" + iTag, ref release);
|
||||
ser.Sync("ENVGATE" + iTag, ref gate);
|
||||
ser.Sync("ENVSTATE" + iTag, ref state);
|
||||
|
||||
if (ser.IsReader)
|
||||
{
|
||||
regs.Voices[i].Generator.SetState(control, freq, pw);
|
||||
regs.Voices[i].Envelope.SetState(attack, decay, sustain, release, gate, (EnvelopeGenerator.EnvelopeState)state);
|
||||
}
|
||||
}
|
||||
|
||||
// regs
|
||||
ser.Sync("BP", ref regs.BP);
|
||||
ser.Sync("D3", ref regs.D3);
|
||||
ser.Sync("FC", ref regs.FC);
|
||||
ser.Sync("FILT0", ref regs.FILT[0]);
|
||||
ser.Sync("FILT1", ref regs.FILT[1]);
|
||||
ser.Sync("FILT2", ref regs.FILT[2]);
|
||||
ser.Sync("FILTEX", ref regs.FILTEX);
|
||||
ser.Sync("HP", ref regs.HP);
|
||||
ser.Sync("LP", ref regs.LP);
|
||||
ser.Sync("POTX", ref regs.POTX);
|
||||
ser.Sync("POTY", ref regs.POTY);
|
||||
ser.Sync("RES", ref regs.RES);
|
||||
ser.Sync("VOL", ref regs.VOL);
|
||||
|
||||
// vars
|
||||
ser.Sync("CLOCK", ref clock);
|
||||
ser.Sync("CYCLESPERSAMPLE", ref cyclesPerSample);
|
||||
ser.Sync("OUTPUT", ref output);
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,255 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace BizHawk.Emulation.Computers.Commodore64
|
||||
{
|
||||
|
||||
// constants for the WaveformGenerator and calculation
|
||||
// methods come from the libsidplayfp residfp library.
|
||||
|
||||
public partial class WaveformGenerator
|
||||
{
|
||||
// internal
|
||||
private byte control;
|
||||
private int freq;
|
||||
private int pw;
|
||||
|
||||
public int accumulator;
|
||||
public int floatingOutputTtl;
|
||||
public bool msbRising;
|
||||
public int noiseOutput;
|
||||
public int pulseOutput;
|
||||
public int ringMsbMask;
|
||||
public int shiftRegister;
|
||||
public int shiftRegisterDelay;
|
||||
public int shiftRegisterResetDelay;
|
||||
public bool sync;
|
||||
public bool test;
|
||||
private short[] wave;
|
||||
public int waveform;
|
||||
public int waveformOutput;
|
||||
|
||||
// these are temp values used to speed up calculation
|
||||
private int noNoise;
|
||||
private int noNoiseOrNoiseOutput;
|
||||
private int noPulse;
|
||||
|
||||
public WaveformGenerator()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
public void Clock()
|
||||
{
|
||||
if (test)
|
||||
{
|
||||
pulseOutput = 0xFFF;
|
||||
if (shiftRegisterResetDelay != 0 && --shiftRegisterResetDelay == 0)
|
||||
{
|
||||
ResetShiftRegister();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int accumulatorNext = (accumulator + freq) & 0xFFFFFF;
|
||||
int accumulatorBitsSet = ~accumulator & accumulatorNext;
|
||||
|
||||
accumulator = accumulatorNext;
|
||||
msbRising = (accumulatorBitsSet & 0x800000) != 0;
|
||||
|
||||
if ((accumulatorBitsSet & 0x080000) != 0)
|
||||
{
|
||||
shiftRegisterDelay = 2;
|
||||
}
|
||||
else if (shiftRegisterDelay != 0 && --shiftRegisterDelay == 0)
|
||||
{
|
||||
ClockShiftRegister();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ClockShiftRegister()
|
||||
{
|
||||
int bit0 = ((shiftRegister >> 22) ^ (shiftRegister >> 17)) & 0x1;
|
||||
shiftRegister = ((shiftRegister << 1) | bit0) & 0x7FFFFF;
|
||||
UpdateNoiseOutput();
|
||||
}
|
||||
|
||||
public byte Control
|
||||
{
|
||||
get
|
||||
{
|
||||
return control;
|
||||
}
|
||||
set
|
||||
{
|
||||
control = value;
|
||||
|
||||
int waveformPrev = waveform;
|
||||
bool testPrev = test;
|
||||
waveform = (control >> 4) & 0x0F;
|
||||
test = (control & 0x08) != 0;
|
||||
sync = (control & 0x02) != 0;
|
||||
|
||||
wave = WaveformSamples[waveform & 0x7];
|
||||
ringMsbMask = ((~control >> 5) & (control >> 2) & 0x1) << 23;
|
||||
noNoise = (waveform & 0x8) != 0 ? 0x000 : 0xFFF;
|
||||
noNoiseOrNoiseOutput = noNoise | noiseOutput;
|
||||
noPulse = (waveform & 0x4) != 0 ? 0x000 : 0xFFF;
|
||||
|
||||
if (!testPrev && test)
|
||||
{
|
||||
accumulator = 0;
|
||||
shiftRegisterDelay = 0;
|
||||
shiftRegisterResetDelay = 0x8000;
|
||||
}
|
||||
else if (testPrev && !test)
|
||||
{
|
||||
int bit0 = (~shiftRegister >> 17) & 0x1;
|
||||
shiftRegister = ((shiftRegister << 1) | bit0) & 0x7FFFFF;
|
||||
UpdateNoiseOutput();
|
||||
}
|
||||
|
||||
if (waveform == 0 && waveformPrev != 0)
|
||||
{
|
||||
floatingOutputTtl = 0x28000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public int Frequency
|
||||
{
|
||||
get
|
||||
{
|
||||
return freq;
|
||||
}
|
||||
set
|
||||
{
|
||||
freq = value;
|
||||
}
|
||||
}
|
||||
|
||||
public short Output(WaveformGenerator ringModulator)
|
||||
{
|
||||
if (waveform != 0)
|
||||
{
|
||||
int ix = (accumulator ^ (ringModulator.accumulator & ringMsbMask)) >> 12;
|
||||
waveformOutput = wave[ix] & (noPulse | pulseOutput) & noNoiseOrNoiseOutput;
|
||||
if (waveform > 0x8)
|
||||
{
|
||||
WriteShiftRegister();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (floatingOutputTtl != 0 && --floatingOutputTtl == 0)
|
||||
{
|
||||
waveformOutput = 0;
|
||||
}
|
||||
}
|
||||
pulseOutput = ((accumulator >> 12) >= pw) ? 0xFFF : 0x000;
|
||||
return (short)waveformOutput;
|
||||
}
|
||||
|
||||
public int PulseWidth
|
||||
{
|
||||
get
|
||||
{
|
||||
return pw;
|
||||
}
|
||||
set
|
||||
{
|
||||
pw = value;
|
||||
}
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
control = 0;
|
||||
waveform = 0;
|
||||
freq = 0;
|
||||
pw = 0;
|
||||
accumulator = 0;
|
||||
test = false;
|
||||
sync = false;
|
||||
|
||||
msbRising = false;
|
||||
wave = WaveformSamples[0];
|
||||
ringMsbMask = 0;
|
||||
|
||||
noNoise = 0xFFF;
|
||||
noPulse = 0xFFF;
|
||||
pulseOutput = 0xFFF;
|
||||
|
||||
ResetShiftRegister();
|
||||
|
||||
shiftRegisterDelay = 0;
|
||||
waveformOutput = 0;
|
||||
floatingOutputTtl = 0;
|
||||
}
|
||||
|
||||
private void ResetShiftRegister()
|
||||
{
|
||||
shiftRegister = 0x7FFFFF;
|
||||
shiftRegisterResetDelay = 0;
|
||||
UpdateNoiseOutput();
|
||||
}
|
||||
|
||||
public void SetState(byte stateControl, int stateFreq, int statePulseWidth)
|
||||
{
|
||||
pw = statePulseWidth;
|
||||
freq = stateFreq;
|
||||
control = stateControl;
|
||||
noNoise = (waveform & 0x8) != 0 ? 0x000 : 0xFFF;
|
||||
noNoiseOrNoiseOutput = noNoise | noiseOutput;
|
||||
noPulse = (waveform & 0x4) != 0 ? 0x000 : 0xFFF;
|
||||
ringMsbMask = ((~control >> 5) & (control >> 2) & 0x1) << 23;
|
||||
waveform = (control >> 4) & 0x0F;
|
||||
test = (control & 0x08) != 0;
|
||||
sync = (control & 0x02) != 0;
|
||||
wave = WaveformSamples[waveform & 0x7];
|
||||
}
|
||||
|
||||
public void Synchronize(WaveformGenerator syncDest, WaveformGenerator syncSource)
|
||||
{
|
||||
if (msbRising && syncDest.sync && !(sync && syncSource.msbRising))
|
||||
{
|
||||
syncDest.accumulator = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateNoiseOutput()
|
||||
{
|
||||
noiseOutput =
|
||||
((shiftRegister & 0x100000) >> 9) |
|
||||
((shiftRegister & 0x040000) >> 8) |
|
||||
((shiftRegister & 0x004000) >> 5) |
|
||||
((shiftRegister & 0x000800) >> 3) |
|
||||
((shiftRegister & 0x000200) >> 2) |
|
||||
((shiftRegister & 0x000020) << 1) |
|
||||
((shiftRegister & 0x000004) << 3) |
|
||||
((shiftRegister & 0x000001) << 4);
|
||||
noNoiseOrNoiseOutput = noNoise | noiseOutput;
|
||||
}
|
||||
|
||||
private void WriteShiftRegister()
|
||||
{
|
||||
shiftRegister &=
|
||||
~((1 << 20) | (1 << 18) | (1 << 14) | (1 << 11) |
|
||||
(1 << 9) | (1 << 5) | (1 << 2) | (1 << 0)) |
|
||||
((waveformOutput & 0x800) << 9) |
|
||||
((waveformOutput & 0x400) << 8) |
|
||||
((waveformOutput & 0x200) << 5) |
|
||||
((waveformOutput & 0x100) << 3) |
|
||||
((waveformOutput & 0x080) << 2) |
|
||||
((waveformOutput & 0x040) >> 1) |
|
||||
((waveformOutput & 0x020) >> 3) |
|
||||
((waveformOutput & 0x010) >> 4);
|
||||
noiseOutput &= waveformOutput;
|
||||
noNoiseOrNoiseOutput = noNoise | noiseOutput;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace BizHawk.Emulation.Computers.Commodore64
|
||||
{
|
||||
public class Timing
|
||||
{
|
||||
public int crystalFreq;
|
||||
public uint timer;
|
||||
|
||||
public Timing(Region timingRegion)
|
||||
{
|
||||
switch (timingRegion)
|
||||
{
|
||||
case Region.NTSC:
|
||||
crystalFreq = 14318181;
|
||||
break;
|
||||
case Region.PAL:
|
||||
crystalFreq = 17734472;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void Advance()
|
||||
{
|
||||
// need an unchecked block here since the timer will wrap
|
||||
unchecked
|
||||
{
|
||||
timer++;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsCycle(int divisor)
|
||||
{
|
||||
return (timer % divisor) == 0;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,420 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace BizHawk.Emulation.Computers.Commodore64
|
||||
{
|
||||
// MOS Technologies VIA 6522
|
||||
// register count: 16
|
||||
// IO port count: 2
|
||||
|
||||
public class ViaRegs
|
||||
{
|
||||
public int[] CACONTROL = new int[2];
|
||||
public int[] CBCONTROL = new int[2];
|
||||
public bool[] EICA = new bool[2];
|
||||
public bool[] EICB = new bool[2];
|
||||
public bool EISR;
|
||||
public bool[] EIT = new bool[2];
|
||||
public bool[] ICA = new bool[2];
|
||||
public bool[] ICB = new bool[2];
|
||||
public bool IRQ;
|
||||
public bool ISR;
|
||||
public bool[] IT = new bool[2];
|
||||
public bool PALE;
|
||||
public bool PBLE;
|
||||
public int SR;
|
||||
public int SRCONTROL;
|
||||
public int[] TC = new int[2];
|
||||
public int[] TCONTROL = new int[2];
|
||||
public int[] TL = new int[2];
|
||||
|
||||
private DataPortConnector[] connectors;
|
||||
|
||||
public ViaRegs()
|
||||
{
|
||||
// power on state
|
||||
connectors = new DataPortConnector[2];
|
||||
connectors[0] = new DataPortConnector();
|
||||
connectors[1] = new DataPortConnector();
|
||||
}
|
||||
|
||||
public byte this[int addr]
|
||||
{
|
||||
get
|
||||
{
|
||||
int result = 0xFF;
|
||||
|
||||
addr &= 0xF;
|
||||
switch (addr)
|
||||
{
|
||||
case 0x0: // port B data
|
||||
result = connectors[1].Data;
|
||||
break;
|
||||
case 0x1: // port A data
|
||||
case 0xF: // port A data without handshake
|
||||
result = connectors[0].Data;
|
||||
break;
|
||||
case 0x2: // port B direction
|
||||
result = connectors[1].Direction;
|
||||
break;
|
||||
case 0x3: // port A direction
|
||||
result = connectors[0].Direction;
|
||||
break;
|
||||
case 0x4: // timer 0 lo
|
||||
result = TC[0] & 0xFF;
|
||||
break;
|
||||
case 0x5: // timer 0 hi
|
||||
result = (TC[0] & 0xFF00) >> 8;
|
||||
break;
|
||||
case 0x6: // timer 0 latch lo
|
||||
result = TL[0] & 0xFF;
|
||||
break;
|
||||
case 0x7: // timer 0 latch hi
|
||||
result = (TL[0] & 0xFF00) >> 8;
|
||||
break;
|
||||
case 0x8: // timer 1 lo
|
||||
result = TC[1] & 0xFF;
|
||||
break;
|
||||
case 0x9: // timer 1 hi
|
||||
result = (TC[1] & 0xFF00) >> 8;
|
||||
break;
|
||||
case 0xA: // shift register
|
||||
result = SR;
|
||||
break;
|
||||
case 0xB: // peripheral control register
|
||||
result = (CACONTROL[0] & 0x01);
|
||||
result |= (CACONTROL[1] & 0x07) << 1;
|
||||
result |= (CBCONTROL[0] & 0x01) << 4;
|
||||
result |= (CBCONTROL[1] & 0x07) << 5;
|
||||
break;
|
||||
case 0xC: // auxilary control register
|
||||
result = (PALE) ? 0x01 : 0x00;
|
||||
result |= (PBLE) ? 0x02 : 0x00;
|
||||
result |= (SRCONTROL & 0x7) << 2;
|
||||
result |= (TCONTROL[0] & 0x1) << 5;
|
||||
result |= (TCONTROL[1] & 0x3) << 6;
|
||||
break;
|
||||
case 0xD: // interrupt status register
|
||||
result = ICA[1] ? 0x01 : 0x00;
|
||||
result |= ICA[0] ? 0x02 : 0x00;
|
||||
result |= ISR ? 0x04 : 0x00;
|
||||
result |= ICB[1] ? 0x08 : 0x00;
|
||||
result |= ICB[0] ? 0x10 : 0x00;
|
||||
result |= IT[1] ? 0x20 : 0x00;
|
||||
result |= IT[0] ? 0x40 : 0x00;
|
||||
result |= IRQ ? 0x80 : 0x00;
|
||||
break;
|
||||
case 0xE: // interrupt control register
|
||||
result = EICA[1] ? 0x01 : 0x00;
|
||||
result |= EICA[0] ? 0x02 : 0x00;
|
||||
result |= EISR ? 0x04 : 0x00;
|
||||
result |= EICB[1] ? 0x08 : 0x00;
|
||||
result |= EICB[0] ? 0x10 : 0x00;
|
||||
result |= EIT[1] ? 0x20 : 0x00;
|
||||
result |= EIT[0] ? 0x40 : 0x00;
|
||||
result |= 0x80; // TODO: check if this is needed
|
||||
break;
|
||||
}
|
||||
|
||||
return (byte)result;
|
||||
}
|
||||
set
|
||||
{
|
||||
byte val = value;
|
||||
addr &= 0xF;
|
||||
|
||||
switch (addr)
|
||||
{
|
||||
case 0x0: // port B data
|
||||
connectors[1].Data = val;
|
||||
break;
|
||||
case 0x1: // port A data
|
||||
case 0xF: // port A data without handshake
|
||||
connectors[0].Data = val;
|
||||
break;
|
||||
case 0x2: // port B direction
|
||||
connectors[1].Direction = val;
|
||||
break;
|
||||
case 0x3: // port A direction
|
||||
connectors[0].Direction = val;
|
||||
break;
|
||||
case 0x4: // timer 0 lo
|
||||
TC[0] &= 0xFF00;
|
||||
TC[0] |= val;
|
||||
break;
|
||||
case 0x5: // timer 0 hi
|
||||
TC[0] &= 0x00FF;
|
||||
TC[0] |= (int)val << 8;
|
||||
break;
|
||||
case 0x6: // timer 0 latch lo
|
||||
TL[0] &= 0xFF00;
|
||||
TL[0] |= val;
|
||||
break;
|
||||
case 0x7: // timer 0 latch hi
|
||||
TL[0] &= 0x00FF;
|
||||
TL[0] |= (int)val << 8;
|
||||
break;
|
||||
case 0x8: // timer 1 lo
|
||||
TC[1] &= 0xFF00;
|
||||
TC[1] |= val;
|
||||
break;
|
||||
case 0x9: // timer 1 hi
|
||||
TC[1] &= 0x00FF;
|
||||
TC[1] |= (int)val << 8;
|
||||
break;
|
||||
case 0xA: // shift register
|
||||
SR = val;
|
||||
break;
|
||||
case 0xB: // peripheral control register
|
||||
CACONTROL[0] = (val & 0x1);
|
||||
CACONTROL[1] = ((val >> 1) & 0x7);
|
||||
CBCONTROL[0] = ((val >> 4) & 0x1);
|
||||
CBCONTROL[1] = ((val >> 5) & 0x7);
|
||||
break;
|
||||
case 0xC: // auxilary control register
|
||||
PALE = ((val & 0x01) != 0);
|
||||
PBLE = ((val & 0x02) != 0);
|
||||
SRCONTROL = (val >> 2) & 0x7;
|
||||
TCONTROL[0] = (val >> 5) & 0x1;
|
||||
TCONTROL[1] = (val >> 6) & 0x3;
|
||||
break;
|
||||
case 0xD: // interrupt status register
|
||||
ICA[1] = ((val & 0x01) != 0);
|
||||
ICA[0] = ((val & 0x02) != 0);
|
||||
ISR = ((val & 0x04) != 0);
|
||||
ICB[1] = ((val & 0x08) != 0);
|
||||
ICB[0] = ((val & 0x10) != 0);
|
||||
IT[1] = ((val & 0x20) != 0);
|
||||
IT[0] = ((val & 0x40) != 0);
|
||||
IRQ = ((val & 0x80) != 0);
|
||||
break;
|
||||
case 0xE: // interrupt control register
|
||||
EICA[1] = ((val & 0x01) != 0);
|
||||
EICA[0] = ((val & 0x02) != 0);
|
||||
EISR = ((val & 0x04) != 0);
|
||||
EICB[1] = ((val & 0x08) != 0);
|
||||
EICB[0] = ((val & 0x10) != 0);
|
||||
EIT[1] = ((val & 0x20) != 0);
|
||||
EIT[0] = ((val & 0x40) != 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Connect(DataPortConnector connector, int index)
|
||||
{
|
||||
connectors[index] = connector;
|
||||
}
|
||||
|
||||
public bool PB6
|
||||
{
|
||||
get
|
||||
{
|
||||
return ((connectors[1].Latch & 0x40) != 0);
|
||||
}
|
||||
set
|
||||
{
|
||||
connectors[1].Data = (byte)((connectors[1].Latch & 0xBF) | (value ? 0x40 : 0x00));
|
||||
}
|
||||
}
|
||||
|
||||
public bool PB7
|
||||
{
|
||||
get
|
||||
{
|
||||
return ((connectors[1].Latch & 0x80) != 0);
|
||||
}
|
||||
set
|
||||
{
|
||||
connectors[1].Data = (byte)((connectors[1].Latch & 0x7F) | (value ? 0x80 : 0x00));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 0x0: port B
|
||||
// 0x1: port A
|
||||
// 0x2: port B data direction
|
||||
// 0x3: port A data direction
|
||||
// 0x4: timer lo
|
||||
// 0x5: timer hi
|
||||
// 0x6: timer latch lo
|
||||
// 0x7: timer latch hi
|
||||
// 0x8: unused
|
||||
// 0x9: unused
|
||||
// 0xA: unused
|
||||
// 0xB: timer control
|
||||
// 0xC: auxilary control
|
||||
// 0xD: interrupt status
|
||||
// 0xE: interrupt control
|
||||
// 0xF: unused
|
||||
|
||||
public class Via
|
||||
{
|
||||
private ViaRegs regs;
|
||||
|
||||
public Via()
|
||||
{
|
||||
HardReset();
|
||||
}
|
||||
|
||||
public void Connect(DataPortConnector connector)
|
||||
{
|
||||
regs.Connect(connector, 1);
|
||||
}
|
||||
|
||||
public void HardReset()
|
||||
{
|
||||
regs = new ViaRegs();
|
||||
}
|
||||
|
||||
public bool IRQ
|
||||
{
|
||||
get
|
||||
{
|
||||
return regs.IRQ;
|
||||
}
|
||||
}
|
||||
|
||||
public byte Peek(int addr)
|
||||
{
|
||||
addr &= 0xF;
|
||||
return regs[addr];
|
||||
}
|
||||
|
||||
public void PerformCycle()
|
||||
{
|
||||
Tick0();
|
||||
Tick1();
|
||||
UpdateInterrupts();
|
||||
}
|
||||
|
||||
public void Poke(int addr, byte val)
|
||||
{
|
||||
addr &= 0xF;
|
||||
regs[addr] = val;
|
||||
}
|
||||
|
||||
public byte Read(ushort addr)
|
||||
{
|
||||
byte result;
|
||||
|
||||
addr &= 0xF;
|
||||
switch (addr)
|
||||
{
|
||||
case 0x4:
|
||||
result = regs[0x4];
|
||||
regs.IT[0] = false;
|
||||
break;
|
||||
case 0x8:
|
||||
result = (byte)(regs.TC[1] & 0xFF);
|
||||
regs.IT[1] = false;
|
||||
break;
|
||||
case 0x9:
|
||||
result = (byte)(regs.TC[1] >> 8);
|
||||
regs.IT[1] = false;
|
||||
break;
|
||||
case 0xD:
|
||||
// reading this clears it
|
||||
result = regs[addr];
|
||||
regs[addr] = 0x00;
|
||||
break;
|
||||
default:
|
||||
result = regs[addr];
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void Tick0()
|
||||
{
|
||||
bool underflow = false;
|
||||
|
||||
switch (regs.TCONTROL[0] & 0x1)
|
||||
{
|
||||
case 0:
|
||||
if (regs.TC[0] > 0)
|
||||
{
|
||||
if (--regs.TC[0] <= 0)
|
||||
{
|
||||
regs.IT[0] = true;
|
||||
underflow = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
if (--regs.TC[0] <= 0)
|
||||
{
|
||||
regs.IT[0] = true;
|
||||
regs.TC[0] = regs.TL[0];
|
||||
underflow = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (underflow)
|
||||
{
|
||||
if ((regs.TCONTROL[0] & 0x2) != 0)
|
||||
{
|
||||
regs.PB7 = !regs.PB7;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Tick1()
|
||||
{
|
||||
switch (regs.TCONTROL[1])
|
||||
{
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateInterrupts()
|
||||
{
|
||||
regs.IRQ =
|
||||
(regs.ICA[0] & regs.EICA[0]) |
|
||||
(regs.ICA[1] & regs.EICA[1]) |
|
||||
(regs.ICB[0] & regs.EICB[0]) |
|
||||
(regs.ICB[1] & regs.EICB[1]) |
|
||||
(regs.IT[0] & regs.EIT[0]) |
|
||||
(regs.IT[1] & regs.EIT[1]) |
|
||||
(regs.ISR & regs.EISR);
|
||||
}
|
||||
|
||||
public void Write(ushort addr, byte val)
|
||||
{
|
||||
addr &= 0xF;
|
||||
switch (addr)
|
||||
{
|
||||
case 0x4: // write low counter
|
||||
regs[0x6] = val;
|
||||
break;
|
||||
case 0x5: // write high counter
|
||||
regs[0x4] = regs[0x06];
|
||||
regs[0x5] = val;
|
||||
regs[0x7] = val;
|
||||
regs.IT[0] = false;
|
||||
break;
|
||||
case 0x7:
|
||||
regs[0x7] = val;
|
||||
regs.IT[0] = false;
|
||||
break;
|
||||
case 0x8:
|
||||
regs.TL[1] = val;
|
||||
break;
|
||||
case 0x9:
|
||||
regs.TC[1] = ((int)val << 8) | regs.TL[1];
|
||||
regs.IT[1] = false;
|
||||
break;
|
||||
default:
|
||||
regs[addr] = val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,68 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace BizHawk.Emulation.Computers.Commodore64
|
||||
{
|
||||
public partial class VicII : IVideoProvider
|
||||
{
|
||||
public Memory mem;
|
||||
public Region region;
|
||||
public ChipSignals signal;
|
||||
|
||||
public VicII(ChipSignals newSignal, Region newRegion)
|
||||
{
|
||||
region = newRegion;
|
||||
signal = newSignal;
|
||||
InitPipeline(newRegion);
|
||||
HardReset();
|
||||
}
|
||||
|
||||
public int CyclesPerFrame
|
||||
{
|
||||
get
|
||||
{
|
||||
return pipelineLength * rasterLines;
|
||||
}
|
||||
}
|
||||
|
||||
public double FramesPerSecond
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (region)
|
||||
{
|
||||
case Region.NTSC:
|
||||
return (14318181d / 14d) / (double)CyclesPerFrame;
|
||||
case Region.PAL:
|
||||
return (17734472d / 18d) / (double)CyclesPerFrame;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void HardReset()
|
||||
{
|
||||
InitRegs();
|
||||
InitVideoBuffer();
|
||||
cycle = 0;
|
||||
}
|
||||
|
||||
public bool Interrupt
|
||||
{
|
||||
get
|
||||
{
|
||||
return IRQ;
|
||||
}
|
||||
}
|
||||
|
||||
public void PerformCycle()
|
||||
{
|
||||
ExecutePipeline();
|
||||
UpdateInterrupts();
|
||||
signal.VicIRQ = IRQ;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -1,670 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace BizHawk.Emulation.Computers.Commodore64
|
||||
{
|
||||
public partial class VicII : IVideoProvider
|
||||
{
|
||||
private int baCount;
|
||||
private int cycle;
|
||||
private uint[][] pipeline;
|
||||
private bool pipelineGAccess;
|
||||
private int pipelineLength;
|
||||
|
||||
private void ExecutePipeline()
|
||||
{
|
||||
pipelineGAccess = false;
|
||||
advanceX = true;
|
||||
baCount = 0;
|
||||
|
||||
uint tableX = pipeline[0][cycle];
|
||||
uint tableFetch = pipeline[1][cycle];
|
||||
uint tableBA = pipeline[2][cycle];
|
||||
uint tableOps = pipeline[3][cycle];
|
||||
|
||||
#region Pipeline Cycle Init
|
||||
{
|
||||
|
||||
//rasterX = (int)tableX;
|
||||
|
||||
if (cycle == 0)
|
||||
{
|
||||
if (!rasterInterruptTriggered && RASTER == rasterInterruptLine && RASTER > 0)
|
||||
{
|
||||
IRST = true;
|
||||
rasterInterruptTriggered = true;
|
||||
}
|
||||
}
|
||||
else if (cycle == 1)
|
||||
{
|
||||
if (!rasterInterruptTriggered && RASTER == 0 && rasterInterruptLine == 0)
|
||||
{
|
||||
IRST = true;
|
||||
rasterInterruptTriggered = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (RASTER == 0x030)
|
||||
displayEnabled = (displayEnabled | DEN);
|
||||
|
||||
if (RASTER >= 0x030 && RASTER < 0x0F8)
|
||||
badline = ((YSCROLL == (RASTER & 0x07)) && displayEnabled);
|
||||
else
|
||||
badline = false;
|
||||
|
||||
if (badline)
|
||||
idle = false;
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
if (!sprites[i].MxYE)
|
||||
sprites[i].MxYEToggle = true;
|
||||
}
|
||||
#endregion
|
||||
#region Pipeline Fetch
|
||||
{
|
||||
switch (tableFetch)
|
||||
{
|
||||
case 0x00: PipelineFetchSpriteP(0); break;
|
||||
case 0x01: PipelineFetchSpriteP(1); break;
|
||||
case 0x02: PipelineFetchSpriteP(2); break;
|
||||
case 0x03: PipelineFetchSpriteP(3); break;
|
||||
case 0x04: PipelineFetchSpriteP(4); break;
|
||||
case 0x05: PipelineFetchSpriteP(5); break;
|
||||
case 0x06: PipelineFetchSpriteP(6); break;
|
||||
case 0x07: PipelineFetchSpriteP(7); break;
|
||||
case 0x08: PipelineFetchSpriteS(0); break;
|
||||
case 0x09: PipelineFetchSpriteS(1); break;
|
||||
case 0x0A: PipelineFetchSpriteS(2); break;
|
||||
case 0x0B: PipelineFetchSpriteS(3); break;
|
||||
case 0x0C: PipelineFetchSpriteS(4); break;
|
||||
case 0x0D: PipelineFetchSpriteS(5); break;
|
||||
case 0x0E: PipelineFetchSpriteS(6); break;
|
||||
case 0x0F: PipelineFetchSpriteS(7); break;
|
||||
case 0x10:
|
||||
mem.VicRead(ECM ? (ushort)0x39FF : (ushort)0x3FFF);
|
||||
break;
|
||||
case 0x11:
|
||||
mem.VicRead((ushort)refreshAddress);
|
||||
refreshAddress = (refreshAddress - 1) & 0xFF;
|
||||
refreshAddress |= 0x3F00;
|
||||
break;
|
||||
case 0x12:
|
||||
PipelineFetchC();
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
#region BA
|
||||
{
|
||||
uint baSprite0 = tableBA & 0xF;
|
||||
uint baSprite1 = (tableBA >> 4) & 0xF;
|
||||
uint baSprite2 = (tableBA >> 8) & 0xF;
|
||||
bool baFetch = ((tableBA >> 12) & 0x1) != 0;
|
||||
|
||||
if ((baSprite0 < 8 && sprites[baSprite0].MDMA) || (baSprite1 < 8 && sprites[baSprite1].MDMA) || (baSprite2 < 8 && sprites[baSprite2].MDMA) || (baFetch && badline))
|
||||
baCount++;
|
||||
}
|
||||
#endregion
|
||||
#region Operations
|
||||
{
|
||||
//if ((tableOps & OpChkBrdL0) != 0)
|
||||
//{
|
||||
//}
|
||||
//if ((tableOps & OpChkBrdL1) != 0)
|
||||
//{
|
||||
//}
|
||||
//if ((tableOps & OpChkBrdR0) != 0)
|
||||
//{
|
||||
//}
|
||||
//if ((tableOps & OpChkBrdR1) != 0)
|
||||
//{
|
||||
//}
|
||||
//if ((tableOps & OpChkSprCrunch) != 0)
|
||||
//{
|
||||
//}
|
||||
if ((tableOps & OpChkSprDisp) != 0)
|
||||
{
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
VicIISprite sprite = sprites[i];
|
||||
sprite.MC = sprite.MCBASE;
|
||||
if (sprite.MDMA && sprite.MxY == (RASTER & 0xFF))
|
||||
{
|
||||
sprite.MxXEToggle = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((tableOps & OpChkSprDma) != 0)
|
||||
{
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
VicIISprite sprite = sprites[i];
|
||||
sprite.MD = false;
|
||||
if (sprite.MxE == true && sprite.MxY == (RASTER & 0xFF) && sprite.MDMA == false)
|
||||
{
|
||||
sprite.MDMA = true;
|
||||
sprite.MCBASE = 0;
|
||||
if (sprite.MxYE)
|
||||
sprite.MxYEToggle = false;
|
||||
}
|
||||
sprite.MxXEToggle = false;
|
||||
}
|
||||
}
|
||||
if ((tableOps & OpChkSprExp) != 0)
|
||||
{
|
||||
for (int i = 0; i < 8; i++)
|
||||
if (sprites[i].MxYE)
|
||||
sprites[i].MxYEToggle = !sprites[i].MxYEToggle;
|
||||
}
|
||||
if ((tableOps & OpUpdateMcBase) != 0)
|
||||
{
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
VicIISprite sprite = sprites[i];
|
||||
if (sprite.MxYEToggle)
|
||||
{
|
||||
sprite.MCBASE += 3;
|
||||
if (sprite.MxYEToggle && sprite.MCBASE == 63)
|
||||
{
|
||||
sprite.MDMA = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((tableOps & OpUpdateRc) != 0)
|
||||
{
|
||||
if (RC == 7)
|
||||
{
|
||||
idle = true;
|
||||
VCBASE = VC;
|
||||
}
|
||||
if (!idle)
|
||||
{
|
||||
RC = (RC + 1) & 0x7;
|
||||
}
|
||||
}
|
||||
if ((tableOps & OpUpdateVc) != 0)
|
||||
{
|
||||
VC = VCBASE;
|
||||
VMLI = 0;
|
||||
bitmapColumn = 0;
|
||||
if (badline)
|
||||
{
|
||||
RC = 0;
|
||||
}
|
||||
bitmapData = 0;
|
||||
colorData = 0;
|
||||
characterData = 0;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
#region Render
|
||||
{
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
int pixel;
|
||||
|
||||
if ((pipelineGAccess) && XSCROLL == i)
|
||||
{
|
||||
#region Fetch G
|
||||
{
|
||||
int gAddress;
|
||||
bitmapColumn = 0;
|
||||
|
||||
if (idle || VMLI >= 40 || !displayEnabled)
|
||||
{
|
||||
mem.VicRead(ECM ? (ushort)0x39FF : (ushort)0x3FFF);
|
||||
characterData = 0;
|
||||
colorData = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
characterData = characterDataBus;
|
||||
colorData = colorDataBus;
|
||||
}
|
||||
switch (graphicsMode)
|
||||
{
|
||||
case 0: // 000
|
||||
case 1: // 001
|
||||
gAddress = (CB << 11) | (characterData << 3) | RC;
|
||||
bitmapData = mem.VicRead((ushort)gAddress);
|
||||
break;
|
||||
case 2: // 010
|
||||
case 3: // 011
|
||||
gAddress = ((CB & 0x4) << 11) | (VC << 3) | RC;
|
||||
bitmapData = mem.VicRead((ushort)gAddress);
|
||||
break;
|
||||
case 4: // 100
|
||||
case 5: // 101
|
||||
gAddress = (CB << 11) | ((characterData & 0x3F) << 3) | RC;
|
||||
bitmapData = mem.VicRead((ushort)gAddress);
|
||||
break;
|
||||
case 6: // 110
|
||||
case 7: // 111
|
||||
gAddress = ((CB & 0x4) << 11) | ((VC & 0x33F) << 3) | RC;
|
||||
bitmapData = mem.VicRead((ushort)gAddress);
|
||||
break;
|
||||
}
|
||||
if (!idle)
|
||||
{
|
||||
VC++;
|
||||
VMLI++;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
if (rasterX == borderRight)
|
||||
borderOnMain = true;
|
||||
if (rasterX == borderLeft)
|
||||
{
|
||||
if (RASTER == borderBottom)
|
||||
borderOnVertical = true;
|
||||
if ((RASTER == borderTop) && DEN)
|
||||
borderOnVertical = false;
|
||||
if (!borderOnVertical)
|
||||
borderOnMain = false;
|
||||
}
|
||||
|
||||
#region Plotter
|
||||
switch (graphicsMode)
|
||||
{
|
||||
case 0x00:
|
||||
if ((bitmapData & 0x80) != 0x00)
|
||||
{
|
||||
plotterData = 0x03;
|
||||
plotterPixel = colorData;
|
||||
}
|
||||
else
|
||||
{
|
||||
plotterData = 0x00;
|
||||
plotterPixel = BxC[0];
|
||||
}
|
||||
bitmapData <<= 1;
|
||||
break;
|
||||
case 0x01:
|
||||
if ((colorData & 0x08) != 0x00)
|
||||
{
|
||||
if ((bitmapColumn & 0x01) == 0x00)
|
||||
{
|
||||
plotterData = (bitmapData >> 6) & 0x03;
|
||||
bitmapData <<= 2;
|
||||
}
|
||||
switch (plotterData)
|
||||
{
|
||||
case 0x00:
|
||||
plotterPixel = BxC[0];
|
||||
break;
|
||||
case 0x01:
|
||||
plotterPixel = BxC[1];
|
||||
break;
|
||||
case 0x02:
|
||||
plotterPixel = BxC[2];
|
||||
break;
|
||||
case 0x03:
|
||||
plotterPixel = colorData & 0x7;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((bitmapData & 0x80) != 0x00)
|
||||
{
|
||||
plotterData = 0x03;
|
||||
plotterPixel = colorData;
|
||||
}
|
||||
else
|
||||
{
|
||||
plotterData = 0x00;
|
||||
plotterPixel = BxC[0];
|
||||
}
|
||||
bitmapData <<= 1;
|
||||
}
|
||||
break;
|
||||
case 0x02:
|
||||
if ((bitmapData & 0x80) != 0x00)
|
||||
{
|
||||
plotterData = 0x03;
|
||||
plotterPixel = characterData >> 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
plotterData = 0x00;
|
||||
plotterPixel = characterData & 0xF;
|
||||
}
|
||||
bitmapData <<= 1;
|
||||
break;
|
||||
case 0x03:
|
||||
if ((bitmapColumn & 0x01) == 0x00)
|
||||
{
|
||||
plotterData = (bitmapData >> 6) & 0x03;
|
||||
bitmapData <<= 2;
|
||||
}
|
||||
switch (plotterData)
|
||||
{
|
||||
case 0x00:
|
||||
plotterPixel = BxC[0];
|
||||
break;
|
||||
case 0x01:
|
||||
plotterPixel = characterData >> 4;
|
||||
break;
|
||||
case 0x02:
|
||||
plotterPixel = characterData & 0xF;
|
||||
break;
|
||||
case 0x03:
|
||||
plotterPixel = colorData & 0xF;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 0x04:
|
||||
if ((bitmapData & 0x80) != 0x00)
|
||||
{
|
||||
plotterData = 0x03;
|
||||
plotterPixel = colorData;
|
||||
}
|
||||
else
|
||||
{
|
||||
plotterData = 0x00;
|
||||
plotterPixel = BxC[characterData >> 6];
|
||||
}
|
||||
bitmapData <<= 1;
|
||||
break;
|
||||
case 0x05:
|
||||
if ((colorData & 0x08) != 0x00)
|
||||
{
|
||||
if ((bitmapColumn & 0x01) == 0x00)
|
||||
{
|
||||
plotterData = bitmapData >> 6;
|
||||
plotterPixel = 0;
|
||||
bitmapData <<= 2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
plotterData = bitmapData >> 7;
|
||||
plotterPixel = 0;
|
||||
bitmapData <<= 1;
|
||||
}
|
||||
break;
|
||||
case 0x06:
|
||||
if ((bitmapData & 0x80) != 0x00)
|
||||
{
|
||||
plotterData = 0x03;
|
||||
plotterPixel = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
plotterData = 0x00;
|
||||
plotterPixel = 0;
|
||||
}
|
||||
bitmapData <<= 1;
|
||||
break;
|
||||
case 0x07:
|
||||
if ((bitmapColumn & 0x01) == 0x00)
|
||||
{
|
||||
plotterData = bitmapData >> 6;
|
||||
bitmapData <<= 2;
|
||||
}
|
||||
plotterPixel = 0;
|
||||
break;
|
||||
}
|
||||
#endregion
|
||||
#region Sprites
|
||||
{
|
||||
int pixelOwner = -1;
|
||||
int sData = 0;
|
||||
int sPixel = 0;
|
||||
|
||||
spriteData = 0;
|
||||
spritePixel = 0;
|
||||
spritePriority = false;
|
||||
|
||||
for (int j = 0; j < 8; j++)
|
||||
{
|
||||
VicIISprite sprite = sprites[j];
|
||||
|
||||
if (sprite.MSR == 0)
|
||||
{
|
||||
sprite.MD = false;
|
||||
}
|
||||
else if ((!sprite.MD) && (sprite.MxX == rasterX))
|
||||
{
|
||||
sprite.MD = true;
|
||||
}
|
||||
|
||||
if (sprite.MD)
|
||||
{
|
||||
if (sprite.MxMC)
|
||||
{
|
||||
sData = ((sprite.MSR >> 22) & 0x3);
|
||||
if ((rasterX & 0x1) != (sprite.MxX & 0x1))
|
||||
{
|
||||
if (!sprite.MxXE || sprite.MxXEToggle)
|
||||
{
|
||||
sprite.MSR <<= 2;
|
||||
}
|
||||
sprite.MxXEToggle = !sprite.MxXEToggle;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sData = ((sprite.MSR >> 22) & 0x2);
|
||||
if (!sprite.MxXE || sprite.MxXEToggle)
|
||||
{
|
||||
sprite.MSR <<= 1;
|
||||
}
|
||||
sprite.MxXEToggle = !sprite.MxXEToggle;
|
||||
}
|
||||
|
||||
if (!borderOnVertical)
|
||||
{
|
||||
if (sData != 0)
|
||||
{
|
||||
if (pixelOwner >= 0)
|
||||
{
|
||||
sprite.MxM = true;
|
||||
sprites[pixelOwner].MxM = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (sData)
|
||||
{
|
||||
case 1:
|
||||
sPixel = MMx[0];
|
||||
break;
|
||||
case 2:
|
||||
sPixel = sprite.MxC;
|
||||
break;
|
||||
case 3:
|
||||
sPixel = MMx[1];
|
||||
break;
|
||||
}
|
||||
|
||||
spritePriority = sprite.MxDP;
|
||||
spritePixel = sPixel;
|
||||
spriteData = sData;
|
||||
pixelOwner = j;
|
||||
}
|
||||
if (plotterDataBuffer[plotterBufferIndex] >= 0x2)
|
||||
{
|
||||
sprite.MxD = true;
|
||||
IMBC = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
#region Pixelbuffer Write
|
||||
{
|
||||
if (borderOnMain || borderOnVertical)
|
||||
pixel = EC;
|
||||
else
|
||||
{
|
||||
if ((spriteData == 0) || (spritePriority == true && plotterDataBuffer[plotterBufferIndex] >= 0x2))
|
||||
{
|
||||
pixel = plotterPixelBuffer[plotterBufferIndex];
|
||||
}
|
||||
else
|
||||
{
|
||||
pixel = spritePixel;
|
||||
}
|
||||
}
|
||||
|
||||
// write pixel to buffer
|
||||
videoBuffer[videoBufferIndex++] = palette[pixel & 0xF];
|
||||
if (videoBufferIndex == videoBufferSize)
|
||||
videoBufferIndex = 0;
|
||||
|
||||
plotterPixelBuffer[plotterBufferIndex] = plotterPixel;
|
||||
plotterDataBuffer[plotterBufferIndex] = plotterData;
|
||||
plotterBufferIndex++;
|
||||
if (plotterBufferIndex == plotterDelay)
|
||||
plotterBufferIndex = 0;
|
||||
|
||||
bitmapColumn++;
|
||||
if (advanceX)
|
||||
{
|
||||
rasterX++;
|
||||
if (rasterX >= rasterWidth)
|
||||
rasterX -= rasterWidth;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
cycle++;
|
||||
if (cycle >= pipelineLength)
|
||||
{
|
||||
cycle = 0;
|
||||
rasterInterruptTriggered = false;
|
||||
RASTER++;
|
||||
if (RASTER == rasterLines)
|
||||
{
|
||||
RASTER = 0;
|
||||
VCBASE = 0;
|
||||
displayEnabled = false;
|
||||
rasterX = rasterLeft;
|
||||
}
|
||||
badline = false;
|
||||
}
|
||||
|
||||
signal.VicBA = (baCount > 0);
|
||||
PipelineBA(signal.VicBA);
|
||||
if (baCount > 0)
|
||||
{
|
||||
if (fetchCounter > 0)
|
||||
fetchCounter--;
|
||||
signal.VicAEC = (fetchCounter != 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
fetchCounter = 0;
|
||||
signal.VicAEC = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void InitPipeline(Region region)
|
||||
{
|
||||
switch (region)
|
||||
{
|
||||
case Region.NTSC:
|
||||
plotterDelay = 12;
|
||||
rasterLines = 263;
|
||||
rasterLeft = 0x19C;
|
||||
pipeline = cycleTabNTSC;
|
||||
break;
|
||||
case Region.PAL:
|
||||
plotterDelay = 4;
|
||||
rasterLines = 312;
|
||||
rasterLeft = 0x194;
|
||||
pipeline = cycleTabPAL;
|
||||
break;
|
||||
}
|
||||
|
||||
pipelineLength = pipeline[0].Length;
|
||||
}
|
||||
|
||||
private void PipelineBA(bool val)
|
||||
{
|
||||
if (val)
|
||||
{
|
||||
if (signal.VicAEC == true && fetchCounter == 0)
|
||||
fetchCounter = 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
fetchCounter = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private void PipelineBorderCheck()
|
||||
{
|
||||
if ((RASTER == borderTop) && (DEN))
|
||||
borderOnVertical = false;
|
||||
if (RASTER == borderBottom)
|
||||
borderOnVertical = true;
|
||||
}
|
||||
|
||||
private void PipelineFetchC()
|
||||
{
|
||||
pipelineGAccess = true;
|
||||
if (idle || VMLI >= 40)
|
||||
{
|
||||
characterDataBus = 0;
|
||||
colorDataBus = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (badline)
|
||||
{
|
||||
int cAddress = (VM << 10) | VC;
|
||||
characterDataBus = mem.VicRead((ushort)cAddress);
|
||||
colorDataBus = mem.colorRam[VC];
|
||||
}
|
||||
else
|
||||
{
|
||||
characterDataBus = characterMemory[VMLI];
|
||||
colorDataBus = colorMemory[VMLI];
|
||||
return;
|
||||
}
|
||||
colorMemory[VMLI] = colorDataBus;
|
||||
characterMemory[VMLI] = characterDataBus;
|
||||
}
|
||||
}
|
||||
|
||||
private void PipelineFetchSpriteP(int index)
|
||||
{
|
||||
VicIISprite spr = sprites[index];
|
||||
ushort pointerOffset = (ushort)((VM << 10) | 0x3F8 | index);
|
||||
|
||||
spr.MPTR = mem.VicRead(pointerOffset);
|
||||
|
||||
if (spr.MDMA)
|
||||
{
|
||||
spr.MSR = mem.VicRead((ushort)((spr.MPTR << 6) | (spr.MC)));
|
||||
spr.MC++;
|
||||
}
|
||||
}
|
||||
|
||||
private void PipelineFetchSpriteS(int index)
|
||||
{
|
||||
VicIISprite spr = sprites[index];
|
||||
if (spr.MDMA)
|
||||
{
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
spr.MSR <<= 8;
|
||||
spr.MSR |= mem.VicRead((ushort)((spr.MPTR << 6) | (spr.MC)));
|
||||
spr.MC++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,165 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace BizHawk.Emulation.Computers.Commodore64
|
||||
{
|
||||
public partial class VicII
|
||||
{
|
||||
private const uint OpNone = 0x000;
|
||||
private const uint OpUpdateVc = 0x001;
|
||||
private const uint OpChkSprCrunch = 0x002;
|
||||
private const uint OpUpdateMcBase = 0x004;
|
||||
private const uint OpChkBrdL1 = 0x008;
|
||||
private const uint OpChkBrdL0 = 0x010;
|
||||
private const uint OpChkBrdR0 = 0x020;
|
||||
private const uint OpChkBrdR1 = 0x040;
|
||||
private const uint OpChkSprDma = 0x080;
|
||||
private const uint OpChkSprExp = 0x100;
|
||||
private const uint OpUpdateRc = 0x200;
|
||||
private const uint OpChkSprDisp = 0x400;
|
||||
|
||||
private static uint[][] cycleTabNTSC = new uint[][]
|
||||
{
|
||||
new uint[] // x
|
||||
{
|
||||
0x19C, 0x1A4, 0x1AC, 0x1B4, 0x1BC,
|
||||
0x1C4, 0x1CC, 0x1D4, 0x1DC, 0x1E4,
|
||||
0x1EC, 0x1F4, 0x1FC, 0x004, 0x00C,
|
||||
0x014, 0x01C, 0x024, 0x02C, 0x034,
|
||||
0x03C, 0x044, 0x04C, 0x054, 0x05C,
|
||||
0x064, 0x06C, 0x074, 0x07C, 0x084,
|
||||
0x08C, 0x094, 0x09C, 0x0A4, 0x0AC,
|
||||
0x0B4, 0x0BC, 0x0C4, 0x0CC, 0x0D4,
|
||||
0x0DC, 0x0E4, 0x0EC, 0x0F4, 0x0FC,
|
||||
0x104, 0x10C, 0x114, 0x11C, 0x124,
|
||||
0x12C, 0x134, 0x13C, 0x144, 0x14C,
|
||||
0x154, 0x15C, 0x164, 0x16C, 0x174,
|
||||
0x17C, 0x184, 0x184, 0x18C, 0x194
|
||||
},
|
||||
new uint[] // fetch (0-7=P 8-F=S 10=I 11=R 12=G/C)
|
||||
{
|
||||
0x0B, 0x04, 0x0C, 0x05, 0x0D,
|
||||
0x06, 0x0E, 0x07, 0x0F, 0x10,
|
||||
0x11, 0x11, 0x11, 0x11, 0x11,
|
||||
0x12, 0x12, 0x12, 0x12, 0x12,
|
||||
0x12, 0x12, 0x12, 0x12, 0x12,
|
||||
0x12, 0x12, 0x12, 0x12, 0x12,
|
||||
0x12, 0x12, 0x12, 0x12, 0x12,
|
||||
0x12, 0x12, 0x12, 0x12, 0x12,
|
||||
0x12, 0x12, 0x12, 0x12, 0x12,
|
||||
0x12, 0x12, 0x12, 0x12, 0x12,
|
||||
0x12, 0x12, 0x12, 0x12, 0x12,
|
||||
0x10, 0x10, 0x10, 0x00, 0x08,
|
||||
0x01, 0x09, 0x02, 0x0A, 0x03
|
||||
},
|
||||
new uint[] // ba (flg/spr/spr/spr 8=none)
|
||||
{
|
||||
0x0845, 0x0885, 0x0856, 0x0886, 0x0867,
|
||||
0x0887, 0x0887, 0x0888, 0x0888, 0x0888,
|
||||
0x0888, 0x0888, 0x1888, 0x1888, 0x1888,
|
||||
0x1888, 0x1888, 0x1888, 0x1888, 0x1888,
|
||||
0x1888, 0x1888, 0x1888, 0x1888, 0x1888,
|
||||
0x1888, 0x1888, 0x1888, 0x1888, 0x1888,
|
||||
0x1888, 0x1888, 0x1888, 0x1888, 0x1888,
|
||||
0x1888, 0x1888, 0x1888, 0x1888, 0x1888,
|
||||
0x1888, 0x1888, 0x1888, 0x1888, 0x1888,
|
||||
0x1888, 0x1888, 0x1888, 0x1888, 0x1888,
|
||||
0x1888, 0x1888, 0x1888, 0x1888, 0x1888,
|
||||
0x0880, 0x0880, 0x0801, 0x0881, 0x0812,
|
||||
0x0882, 0x0823, 0x0883, 0x0834, 0x0884
|
||||
},
|
||||
new uint[] // operations
|
||||
{
|
||||
OpNone, OpNone, OpNone, OpNone, OpNone,
|
||||
OpNone, OpNone, OpNone, OpNone, OpNone,
|
||||
OpNone, OpNone, OpNone, OpUpdateVc, OpChkSprCrunch,
|
||||
OpUpdateMcBase,OpChkBrdL1, OpChkBrdL0, OpNone, OpNone,
|
||||
OpNone, OpNone, OpNone, OpNone, OpNone,
|
||||
OpNone, OpNone, OpNone, OpNone, OpNone,
|
||||
OpNone, OpNone, OpNone, OpNone, OpNone,
|
||||
OpNone, OpNone, OpNone, OpNone, OpNone,
|
||||
OpNone, OpNone, OpNone, OpNone, OpNone,
|
||||
OpNone, OpNone, OpNone, OpNone, OpNone,
|
||||
OpNone, OpNone, OpNone, OpNone, OpNone,
|
||||
OpChkSprDma|OpChkBrdR0|OpChkSprExp,
|
||||
OpChkSprDma|OpChkBrdR1,
|
||||
OpUpdateRc,
|
||||
OpChkSprDisp,
|
||||
OpNone,
|
||||
OpNone, OpNone, OpNone, OpNone, OpNone
|
||||
}
|
||||
};
|
||||
|
||||
private static uint[][] cycleTabPAL = new uint[][]
|
||||
{
|
||||
new uint[] // x
|
||||
{
|
||||
0x194, 0x19C, 0x1A4, 0x1AC, 0x1B4,
|
||||
0x1BC, 0x1C4, 0x1CC, 0x1D4, 0x1DC,
|
||||
0x1E4, 0x1EC, 0x1F4, 0x004, 0x00C,
|
||||
0x014, 0x01C, 0x024, 0x02C, 0x034,
|
||||
0x03C, 0x044, 0x04C, 0x054, 0x05C,
|
||||
0x064, 0x06C, 0x074, 0x07C, 0x084,
|
||||
0x08C, 0x094, 0x09C, 0x0A4, 0x0AC,
|
||||
0x0B4, 0x0BC, 0x0C4, 0x0CC, 0x0D4,
|
||||
0x0DC, 0x0E4, 0x0EC, 0x0F4, 0x0FC,
|
||||
0x104, 0x10C, 0x114, 0x11C, 0x124,
|
||||
0x12C, 0x134, 0x13C, 0x144, 0x14C,
|
||||
0x154, 0x15C, 0x164, 0x16C, 0x174,
|
||||
0x17C, 0x184, 0x18C
|
||||
},
|
||||
new uint[] // fetch (0-7=P 8-F=S 10=I 11=R 12=G/C)
|
||||
{
|
||||
0x03, 0x0B, 0x04, 0x0C, 0x05,
|
||||
0x0D, 0x06, 0x0E, 0x07, 0x0F,
|
||||
0x11, 0x11, 0x11, 0x11, 0x11,
|
||||
0x12, 0x12, 0x12, 0x12, 0x12,
|
||||
0x12, 0x12, 0x12, 0x12, 0x12,
|
||||
0x12, 0x12, 0x12, 0x12, 0x12,
|
||||
0x12, 0x12, 0x12, 0x12, 0x12,
|
||||
0x12, 0x12, 0x12, 0x12, 0x12,
|
||||
0x12, 0x12, 0x12, 0x12, 0x12,
|
||||
0x12, 0x12, 0x12, 0x12, 0x12,
|
||||
0x12, 0x12, 0x12, 0x12, 0x12,
|
||||
0x10, 0x10, 0x00, 0x08, 0x01,
|
||||
0x09, 0x02, 0x0A
|
||||
},
|
||||
new uint[] // ba (flg/spr/spr/spr 8=none)
|
||||
{
|
||||
0x0884, 0x0845, 0x0885, 0x0856, 0x0886,
|
||||
0x0867, 0x0887, 0x0887, 0x0888, 0x0888,
|
||||
0x0888, 0x0888, 0x1888, 0x1888, 0x1888,
|
||||
0x1888, 0x1888, 0x1888, 0x1888, 0x1888,
|
||||
0x1888, 0x1888, 0x1888, 0x1888, 0x1888,
|
||||
0x1888, 0x1888, 0x1888, 0x1888, 0x1888,
|
||||
0x1888, 0x1888, 0x1888, 0x1888, 0x1888,
|
||||
0x1888, 0x1888, 0x1888, 0x1888, 0x1888,
|
||||
0x1888, 0x1888, 0x1888, 0x1888, 0x1888,
|
||||
0x1888, 0x1888, 0x1888, 0x1888, 0x1888,
|
||||
0x1888, 0x1888, 0x1888, 0x1888, 0x1880,
|
||||
0x0880, 0x0801, 0x0881, 0x0812, 0x0882,
|
||||
0x0823, 0x0883, 0x0834
|
||||
},
|
||||
new uint[] // operations
|
||||
{
|
||||
OpNone, OpNone, OpNone, OpNone, OpNone,
|
||||
OpNone, OpNone, OpNone, OpNone, OpNone,
|
||||
OpNone, OpNone, OpNone, OpUpdateVc, OpChkSprCrunch,
|
||||
OpUpdateMcBase,OpChkBrdL1, OpChkBrdL0, OpNone, OpNone,
|
||||
OpNone, OpNone, OpNone, OpNone, OpNone,
|
||||
OpNone, OpNone, OpNone, OpNone, OpNone,
|
||||
OpNone, OpNone, OpNone, OpNone, OpNone,
|
||||
OpNone, OpNone, OpNone, OpNone, OpNone,
|
||||
OpNone, OpNone, OpNone, OpNone, OpNone,
|
||||
OpNone, OpNone, OpNone, OpNone, OpNone,
|
||||
OpNone, OpNone, OpNone, OpNone, OpChkSprDma,
|
||||
OpChkSprDma|OpChkBrdR0|OpChkSprExp,
|
||||
OpChkBrdR1,
|
||||
OpUpdateRc|OpChkSprDisp,
|
||||
OpNone, OpNone, OpNone, OpNone, OpNone
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,614 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace BizHawk.Emulation.Computers.Commodore64
|
||||
{
|
||||
public partial class VicII : IVideoProvider
|
||||
{
|
||||
private class VicIISprite
|
||||
{
|
||||
// internal regs
|
||||
public int MC;
|
||||
public int MCBASE;
|
||||
public bool MD;
|
||||
public bool MDMA;
|
||||
public int MPTR;
|
||||
public int MSR;
|
||||
public bool MxXEToggle;
|
||||
public bool MxYEToggle;
|
||||
|
||||
// external regs
|
||||
public int MxC; // sprite color
|
||||
public bool MxD; // sprite-data collision
|
||||
public bool MxDP; // sprite priority
|
||||
public bool MxE; // sprite enabled
|
||||
public bool MxM; // sprite-sprite collision
|
||||
public bool MxMC; // sprite multicolor
|
||||
public int MxX; // sprite X coordinate
|
||||
public bool MxXE; // sprite X expansion
|
||||
public int MxY; // sprite Y coordinate
|
||||
public bool MxYE; // sprite Y expansion
|
||||
}
|
||||
|
||||
// internal regs
|
||||
private int RC;
|
||||
private int VC;
|
||||
private int VCBASE;
|
||||
private int VMLI;
|
||||
|
||||
// external regs
|
||||
private bool BMM; // bitmap mode
|
||||
private int[] BxC = new int[4]; // background colors
|
||||
private int CB; // character bitmap offset
|
||||
private bool CSEL; // column select
|
||||
private bool DEN; // display enabled
|
||||
private int EC; // border color
|
||||
private bool ECM; // extra color mode
|
||||
private bool ELP; // enable lightpen interrupt
|
||||
private bool EMBC; // enable sprite-data interrupt
|
||||
private bool EMMC; // enable sprite-sprite interrupt
|
||||
private bool ERST; // enable raster line interrupt
|
||||
private bool ILP; // light pen interrupt active
|
||||
private bool IMBC; // sprite-data interrupt active
|
||||
private bool IMMC; // sprite-sprite interrupt active
|
||||
private bool IRQ; // interrupt was triggered
|
||||
private bool IRST; // raster line interrupt active
|
||||
private int LPX; // lightpen X coordinate
|
||||
private int LPY; // lightpen Y coordinate
|
||||
private bool MCM; // multicolor mode
|
||||
private int[] MMx = new int[2]; // sprite extra color
|
||||
private int RASTER; // current raster line
|
||||
private bool RES; // reset bit (does nothing in this version of the VIC)
|
||||
private bool RSEL; // row select
|
||||
private int VM; // video memory offset
|
||||
private int XSCROLL; // X scroll position
|
||||
private int YSCROLL; // Y scroll position
|
||||
|
||||
private int spriteData;
|
||||
private int spritePixel;
|
||||
private bool spritePriority;
|
||||
private VicIISprite[] sprites;
|
||||
|
||||
private bool advanceX;
|
||||
private bool badline;
|
||||
private int bitmapColumn;
|
||||
private byte bitmapData;
|
||||
private int borderBottom;
|
||||
private int borderLeft;
|
||||
private bool borderOnMain;
|
||||
private bool borderOnVertical;
|
||||
private int borderRight;
|
||||
private int borderTop;
|
||||
private bool centerEnabled;
|
||||
private byte characterData;
|
||||
private byte characterDataBus;
|
||||
private byte[] characterMemory;
|
||||
private byte colorData;
|
||||
private byte colorDataBus;
|
||||
private byte[] colorMemory;
|
||||
private bool displayEnabled;
|
||||
private int fetchCounter;
|
||||
private int graphicsMode;
|
||||
private bool idle;
|
||||
private int plotterBufferIndex;
|
||||
private int plotterData;
|
||||
private int[] plotterDataBuffer;
|
||||
private int plotterDelay;
|
||||
private int plotterPixel;
|
||||
private int[] plotterPixelBuffer;
|
||||
private int rasterInterruptLine;
|
||||
private bool rasterInterruptTriggered;
|
||||
private int rasterLeft;
|
||||
private int rasterLines;
|
||||
private int rasterWidth;
|
||||
private int rasterX;
|
||||
private int refreshAddress;
|
||||
|
||||
private void InitRegs()
|
||||
{
|
||||
// init sprites
|
||||
sprites = new VicIISprite[8];
|
||||
for (int i = 0; i < 8; i++)
|
||||
sprites[i] = new VicIISprite();
|
||||
|
||||
// init buffers
|
||||
plotterDataBuffer = new int[plotterDelay];
|
||||
plotterPixelBuffer = new int[plotterDelay];
|
||||
characterMemory = new byte[40];
|
||||
colorMemory = new byte[40];
|
||||
|
||||
// init raster data
|
||||
rasterX = rasterLeft;
|
||||
rasterWidth = pipelineLength * 8;
|
||||
|
||||
// reset regs
|
||||
for (int i = 0; i < 0x40; i++)
|
||||
this[i] = 0x00;
|
||||
|
||||
// power on state
|
||||
this[0x16] = 0xC0;
|
||||
this[0x18] = 0x01;
|
||||
this[0x19] = 0x71;
|
||||
this[0x1A] = 0xF0;
|
||||
RC = 7;
|
||||
refreshAddress = 0x3FFF;
|
||||
idle = true;
|
||||
}
|
||||
|
||||
private byte this[int addr]
|
||||
{
|
||||
get
|
||||
{
|
||||
int result = 0xFF; // value for any open bits
|
||||
addr &= 0x3F;
|
||||
|
||||
switch (addr)
|
||||
{
|
||||
case 0x00:
|
||||
case 0x02:
|
||||
case 0x04:
|
||||
case 0x06:
|
||||
case 0x08:
|
||||
case 0x0A:
|
||||
case 0x0C:
|
||||
case 0x0E:
|
||||
result = sprites[addr >> 1].MxX;
|
||||
break;
|
||||
case 0x01:
|
||||
case 0x03:
|
||||
case 0x05:
|
||||
case 0x07:
|
||||
case 0x09:
|
||||
case 0x0B:
|
||||
case 0x0D:
|
||||
case 0x0F:
|
||||
result = sprites[addr >> 1].MxY;
|
||||
break;
|
||||
case 0x10:
|
||||
result = ((sprites[0].MxX & 0x100) != 0) ? 0x01 : 0x00;
|
||||
result |= ((sprites[1].MxX & 0x100) != 0) ? 0x02 : 0x00;
|
||||
result |= ((sprites[2].MxX & 0x100) != 0) ? 0x04 : 0x00;
|
||||
result |= ((sprites[3].MxX & 0x100) != 0) ? 0x08 : 0x00;
|
||||
result |= ((sprites[4].MxX & 0x100) != 0) ? 0x10 : 0x00;
|
||||
result |= ((sprites[5].MxX & 0x100) != 0) ? 0x20 : 0x00;
|
||||
result |= ((sprites[6].MxX & 0x100) != 0) ? 0x40 : 0x00;
|
||||
result |= ((sprites[7].MxX & 0x100) != 0) ? 0x80 : 0x00;
|
||||
break;
|
||||
case 0x11:
|
||||
result = YSCROLL & 0x07;
|
||||
result |= (RSEL ? 0x08 : 0x00);
|
||||
result |= (DEN ? 0x10 : 0x00);
|
||||
result |= (BMM ? 0x20 : 0x00);
|
||||
result |= (ECM ? 0x40 : 0x00);
|
||||
result |= ((RASTER & 0x100) >> 1);
|
||||
break;
|
||||
case 0x12:
|
||||
result = RASTER & 0xFF;
|
||||
break;
|
||||
case 0x13:
|
||||
result = LPX;
|
||||
break;
|
||||
case 0x14:
|
||||
result = LPY;
|
||||
break;
|
||||
case 0x15:
|
||||
result = (sprites[0].MxE ? 0x01 : 0x00);
|
||||
result |= (sprites[1].MxE ? 0x02 : 0x00);
|
||||
result |= (sprites[2].MxE ? 0x04 : 0x00);
|
||||
result |= (sprites[3].MxE ? 0x08 : 0x00);
|
||||
result |= (sprites[4].MxE ? 0x10 : 0x00);
|
||||
result |= (sprites[5].MxE ? 0x20 : 0x00);
|
||||
result |= (sprites[6].MxE ? 0x40 : 0x00);
|
||||
result |= (sprites[7].MxE ? 0x80 : 0x00);
|
||||
break;
|
||||
case 0x16:
|
||||
result &= 0xC0;
|
||||
result |= XSCROLL & 0x07;
|
||||
result |= (CSEL ? 0x08 : 0x00);
|
||||
result |= (MCM ? 0x10 : 0x00);
|
||||
result |= (RES ? 0x20 : 0x00);
|
||||
break;
|
||||
case 0x17:
|
||||
result = (sprites[0].MxYE ? 0x01 : 0x00);
|
||||
result |= (sprites[1].MxYE ? 0x02 : 0x00);
|
||||
result |= (sprites[2].MxYE ? 0x04 : 0x00);
|
||||
result |= (sprites[3].MxYE ? 0x08 : 0x00);
|
||||
result |= (sprites[4].MxYE ? 0x10 : 0x00);
|
||||
result |= (sprites[5].MxYE ? 0x20 : 0x00);
|
||||
result |= (sprites[6].MxYE ? 0x40 : 0x00);
|
||||
result |= (sprites[7].MxYE ? 0x80 : 0x00);
|
||||
break;
|
||||
case 0x18:
|
||||
result &= 0x01;
|
||||
result |= (CB & 0x07) << 1;
|
||||
result |= (VM & 0x0F) << 4;
|
||||
break;
|
||||
case 0x19:
|
||||
result &= 0x70;
|
||||
result |= (IRST ? 0x01 : 0x00);
|
||||
result |= (IMBC ? 0x02 : 0x00);
|
||||
result |= (IMMC ? 0x04 : 0x00);
|
||||
result |= (ILP ? 0x08 : 0x00);
|
||||
result |= (IRQ ? 0x80 : 0x00);
|
||||
break;
|
||||
case 0x1A:
|
||||
result &= 0xF0;
|
||||
result |= (ERST ? 0x01 : 0x00);
|
||||
result |= (EMBC ? 0x02 : 0x00);
|
||||
result |= (EMMC ? 0x04 : 0x00);
|
||||
result |= (ELP ? 0x08 : 0x00);
|
||||
break;
|
||||
case 0x1B:
|
||||
result = (sprites[0].MxDP ? 0x01 : 0x00);
|
||||
result |= (sprites[1].MxDP ? 0x02 : 0x00);
|
||||
result |= (sprites[2].MxDP ? 0x04 : 0x00);
|
||||
result |= (sprites[3].MxDP ? 0x08 : 0x00);
|
||||
result |= (sprites[4].MxDP ? 0x10 : 0x00);
|
||||
result |= (sprites[5].MxDP ? 0x20 : 0x00);
|
||||
result |= (sprites[6].MxDP ? 0x40 : 0x00);
|
||||
result |= (sprites[7].MxDP ? 0x80 : 0x00);
|
||||
break;
|
||||
case 0x1C:
|
||||
result = (sprites[0].MxMC ? 0x01 : 0x00);
|
||||
result |= (sprites[1].MxMC ? 0x02 : 0x00);
|
||||
result |= (sprites[2].MxMC ? 0x04 : 0x00);
|
||||
result |= (sprites[3].MxMC ? 0x08 : 0x00);
|
||||
result |= (sprites[4].MxMC ? 0x10 : 0x00);
|
||||
result |= (sprites[5].MxMC ? 0x20 : 0x00);
|
||||
result |= (sprites[6].MxMC ? 0x40 : 0x00);
|
||||
result |= (sprites[7].MxMC ? 0x80 : 0x00);
|
||||
break;
|
||||
case 0x1D:
|
||||
result = (sprites[0].MxXE ? 0x01 : 0x00);
|
||||
result |= (sprites[1].MxXE ? 0x02 : 0x00);
|
||||
result |= (sprites[2].MxXE ? 0x04 : 0x00);
|
||||
result |= (sprites[3].MxXE ? 0x08 : 0x00);
|
||||
result |= (sprites[4].MxXE ? 0x10 : 0x00);
|
||||
result |= (sprites[5].MxXE ? 0x20 : 0x00);
|
||||
result |= (sprites[6].MxXE ? 0x40 : 0x00);
|
||||
result |= (sprites[7].MxXE ? 0x80 : 0x00);
|
||||
break;
|
||||
case 0x1E:
|
||||
result = (sprites[0].MxM ? 0x01 : 0x00);
|
||||
result |= (sprites[1].MxM ? 0x02 : 0x00);
|
||||
result |= (sprites[2].MxM ? 0x04 : 0x00);
|
||||
result |= (sprites[3].MxM ? 0x08 : 0x00);
|
||||
result |= (sprites[4].MxM ? 0x10 : 0x00);
|
||||
result |= (sprites[5].MxM ? 0x20 : 0x00);
|
||||
result |= (sprites[6].MxM ? 0x40 : 0x00);
|
||||
result |= (sprites[7].MxM ? 0x80 : 0x00);
|
||||
break;
|
||||
case 0x1F:
|
||||
result = (sprites[0].MxD ? 0x01 : 0x00);
|
||||
result |= (sprites[1].MxD ? 0x02 : 0x00);
|
||||
result |= (sprites[2].MxD ? 0x04 : 0x00);
|
||||
result |= (sprites[3].MxD ? 0x08 : 0x00);
|
||||
result |= (sprites[4].MxD ? 0x10 : 0x00);
|
||||
result |= (sprites[5].MxD ? 0x20 : 0x00);
|
||||
result |= (sprites[6].MxD ? 0x40 : 0x00);
|
||||
result |= (sprites[7].MxD ? 0x80 : 0x00);
|
||||
break;
|
||||
case 0x20:
|
||||
result &= 0xF0;
|
||||
result |= EC & 0x0F;
|
||||
break;
|
||||
case 0x21:
|
||||
case 0x22:
|
||||
case 0x23:
|
||||
case 0x24:
|
||||
result &= 0xF0;
|
||||
result |= BxC[addr - 0x21] & 0x0F;
|
||||
break;
|
||||
case 0x25:
|
||||
case 0x26:
|
||||
result &= 0xF0;
|
||||
result |= MMx[addr - 0x25] & 0x0F;
|
||||
break;
|
||||
case 0x27:
|
||||
case 0x28:
|
||||
case 0x29:
|
||||
case 0x2A:
|
||||
case 0x2B:
|
||||
case 0x2C:
|
||||
case 0x2D:
|
||||
case 0x2E:
|
||||
result &= 0xF0;
|
||||
result |= sprites[addr - 0x27].MxC & 0x0F;
|
||||
break;
|
||||
default:
|
||||
result = 0xFF;
|
||||
break;
|
||||
}
|
||||
|
||||
return (byte)(result);
|
||||
}
|
||||
set
|
||||
{
|
||||
int index;
|
||||
int val = value;
|
||||
addr &= 0x3F;
|
||||
|
||||
switch (addr)
|
||||
{
|
||||
case 0x00:
|
||||
case 0x02:
|
||||
case 0x04:
|
||||
case 0x06:
|
||||
case 0x08:
|
||||
case 0x0A:
|
||||
case 0x0C:
|
||||
case 0x0E:
|
||||
index = addr >> 1;
|
||||
sprites[index].MxX &= 0x100;
|
||||
sprites[index].MxX |= (val & 0xFF);
|
||||
break;
|
||||
case 0x01:
|
||||
case 0x03:
|
||||
case 0x05:
|
||||
case 0x07:
|
||||
case 0x09:
|
||||
case 0x0B:
|
||||
case 0x0D:
|
||||
case 0x0F:
|
||||
index = addr >> 1;
|
||||
sprites[index].MxY &= 0x100;
|
||||
sprites[index].MxY |= (val & 0xFF);
|
||||
break;
|
||||
case 0x10:
|
||||
sprites[0].MxX = (sprites[0].MxX & 0xFF) | ((val & 0x01) << 8);
|
||||
sprites[1].MxX = (sprites[1].MxX & 0xFF) | ((val & 0x02) << 7);
|
||||
sprites[2].MxX = (sprites[2].MxX & 0xFF) | ((val & 0x04) << 6);
|
||||
sprites[3].MxX = (sprites[3].MxX & 0xFF) | ((val & 0x08) << 5);
|
||||
sprites[4].MxX = (sprites[4].MxX & 0xFF) | ((val & 0x10) << 4);
|
||||
sprites[5].MxX = (sprites[5].MxX & 0xFF) | ((val & 0x20) << 3);
|
||||
sprites[6].MxX = (sprites[6].MxX & 0xFF) | ((val & 0x40) << 2);
|
||||
sprites[7].MxX = (sprites[7].MxX & 0xFF) | ((val & 0x80) << 1);
|
||||
break;
|
||||
case 0x11:
|
||||
YSCROLL = (val & 0x07);
|
||||
RSEL = ((val & 0x08) != 0x00);
|
||||
DEN = ((val & 0x10) != 0x00);
|
||||
BMM = ((val & 0x20) != 0x00);
|
||||
ECM = ((val & 0x40) != 0x00);
|
||||
RASTER &= 0xFF;
|
||||
RASTER |= ((val & 0x80) << 1);
|
||||
break;
|
||||
case 0x12:
|
||||
RASTER &= 0x100;
|
||||
RASTER |= (val & 0xFF);
|
||||
break;
|
||||
case 0x13:
|
||||
LPX = (val & 0xFF);
|
||||
break;
|
||||
case 0x14:
|
||||
LPY = (val & 0xFF);
|
||||
break;
|
||||
case 0x15:
|
||||
sprites[0].MxE = ((val & 0x01) != 0x00);
|
||||
sprites[1].MxE = ((val & 0x02) != 0x00);
|
||||
sprites[2].MxE = ((val & 0x04) != 0x00);
|
||||
sprites[3].MxE = ((val & 0x08) != 0x00);
|
||||
sprites[4].MxE = ((val & 0x10) != 0x00);
|
||||
sprites[5].MxE = ((val & 0x20) != 0x00);
|
||||
sprites[6].MxE = ((val & 0x40) != 0x00);
|
||||
sprites[7].MxE = ((val & 0x80) != 0x00);
|
||||
break;
|
||||
case 0x16:
|
||||
XSCROLL = (val & 0x07);
|
||||
CSEL = ((val & 0x08) != 0x00);
|
||||
MCM = ((val & 0x10) != 0x00);
|
||||
RES = ((val & 0x20) != 0x00);
|
||||
break;
|
||||
case 0x17:
|
||||
sprites[0].MxYE = ((val & 0x01) != 0x00);
|
||||
sprites[1].MxYE = ((val & 0x02) != 0x00);
|
||||
sprites[2].MxYE = ((val & 0x04) != 0x00);
|
||||
sprites[3].MxYE = ((val & 0x08) != 0x00);
|
||||
sprites[4].MxYE = ((val & 0x10) != 0x00);
|
||||
sprites[5].MxYE = ((val & 0x20) != 0x00);
|
||||
sprites[6].MxYE = ((val & 0x40) != 0x00);
|
||||
sprites[7].MxYE = ((val & 0x80) != 0x00);
|
||||
break;
|
||||
case 0x18:
|
||||
CB = (val & 0x0E) >> 1;
|
||||
VM = (val & 0xF0) >> 4;
|
||||
break;
|
||||
case 0x19:
|
||||
IRST = ((val & 0x01) != 0x00);
|
||||
IMBC = ((val & 0x02) != 0x00);
|
||||
IMMC = ((val & 0x04) != 0x00);
|
||||
ILP = ((val & 0x08) != 0x00);
|
||||
break;
|
||||
case 0x1A:
|
||||
ERST = ((val & 0x01) != 0x00);
|
||||
EMBC = ((val & 0x02) != 0x00);
|
||||
EMMC = ((val & 0x04) != 0x00);
|
||||
ELP = ((val & 0x08) != 0x00);
|
||||
break;
|
||||
case 0x1B:
|
||||
sprites[0].MxDP = ((val & 0x01) != 0x00);
|
||||
sprites[1].MxDP = ((val & 0x02) != 0x00);
|
||||
sprites[2].MxDP = ((val & 0x04) != 0x00);
|
||||
sprites[3].MxDP = ((val & 0x08) != 0x00);
|
||||
sprites[4].MxDP = ((val & 0x10) != 0x00);
|
||||
sprites[5].MxDP = ((val & 0x20) != 0x00);
|
||||
sprites[6].MxDP = ((val & 0x40) != 0x00);
|
||||
sprites[7].MxDP = ((val & 0x80) != 0x00);
|
||||
break;
|
||||
case 0x1C:
|
||||
sprites[0].MxMC = ((val & 0x01) != 0x00);
|
||||
sprites[1].MxMC = ((val & 0x02) != 0x00);
|
||||
sprites[2].MxMC = ((val & 0x04) != 0x00);
|
||||
sprites[3].MxMC = ((val & 0x08) != 0x00);
|
||||
sprites[4].MxMC = ((val & 0x10) != 0x00);
|
||||
sprites[5].MxMC = ((val & 0x20) != 0x00);
|
||||
sprites[6].MxMC = ((val & 0x40) != 0x00);
|
||||
sprites[7].MxMC = ((val & 0x80) != 0x00);
|
||||
break;
|
||||
case 0x1D:
|
||||
sprites[0].MxXE = ((val & 0x01) != 0x00);
|
||||
sprites[1].MxXE = ((val & 0x02) != 0x00);
|
||||
sprites[2].MxXE = ((val & 0x04) != 0x00);
|
||||
sprites[3].MxXE = ((val & 0x08) != 0x00);
|
||||
sprites[4].MxXE = ((val & 0x10) != 0x00);
|
||||
sprites[5].MxXE = ((val & 0x20) != 0x00);
|
||||
sprites[6].MxXE = ((val & 0x40) != 0x00);
|
||||
sprites[7].MxXE = ((val & 0x80) != 0x00);
|
||||
break;
|
||||
case 0x1E:
|
||||
sprites[0].MxM = ((val & 0x01) != 0x00);
|
||||
sprites[1].MxM = ((val & 0x02) != 0x00);
|
||||
sprites[2].MxM = ((val & 0x04) != 0x00);
|
||||
sprites[3].MxM = ((val & 0x08) != 0x00);
|
||||
sprites[4].MxM = ((val & 0x10) != 0x00);
|
||||
sprites[5].MxM = ((val & 0x20) != 0x00);
|
||||
sprites[6].MxM = ((val & 0x40) != 0x00);
|
||||
sprites[7].MxM = ((val & 0x80) != 0x00);
|
||||
break;
|
||||
case 0x1F:
|
||||
sprites[0].MxD = ((val & 0x01) != 0x00);
|
||||
sprites[1].MxD = ((val & 0x02) != 0x00);
|
||||
sprites[2].MxD = ((val & 0x04) != 0x00);
|
||||
sprites[3].MxD = ((val & 0x08) != 0x00);
|
||||
sprites[4].MxD = ((val & 0x10) != 0x00);
|
||||
sprites[5].MxD = ((val & 0x20) != 0x00);
|
||||
sprites[6].MxD = ((val & 0x40) != 0x00);
|
||||
sprites[7].MxD = ((val & 0x80) != 0x00);
|
||||
break;
|
||||
case 0x20:
|
||||
EC = (val & 0x0F);
|
||||
break;
|
||||
case 0x21:
|
||||
case 0x22:
|
||||
case 0x23:
|
||||
case 0x24:
|
||||
BxC[addr - 0x21] = val & 0x0F;
|
||||
break;
|
||||
case 0x25:
|
||||
case 0x26:
|
||||
MMx[addr - 0x25] = val & 0x0F;
|
||||
break;
|
||||
case 0x27:
|
||||
case 0x28:
|
||||
case 0x29:
|
||||
case 0x2A:
|
||||
case 0x2B:
|
||||
case 0x2C:
|
||||
case 0x2D:
|
||||
case 0x2E:
|
||||
sprites[addr - 0x27].MxC = val & 0x0F;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public byte Peek(int addr)
|
||||
{
|
||||
return this[addr & 0x3F];
|
||||
}
|
||||
|
||||
public void Poke(int addr, byte val)
|
||||
{
|
||||
this[addr & 0x3F] = val;
|
||||
}
|
||||
|
||||
public byte Read(ushort addr)
|
||||
{
|
||||
byte result = 0;
|
||||
addr &= 0x3F;
|
||||
|
||||
switch (addr)
|
||||
{
|
||||
case 0x1E:
|
||||
// clear after read
|
||||
result = this[addr];
|
||||
this[addr] = 0x00;
|
||||
IMMC = false;
|
||||
UpdateInterrupts();
|
||||
break;
|
||||
case 0x1F:
|
||||
// clear after read
|
||||
result = this[addr];
|
||||
this[addr] = 0x00;
|
||||
IMBC = false;
|
||||
UpdateInterrupts();
|
||||
break;
|
||||
default:
|
||||
result = this[addr];
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void UpdateBorder()
|
||||
{
|
||||
borderTop = RSEL ? 0x033 : 0x037;
|
||||
borderBottom = RSEL ? 0x0FB : 0x0F7;
|
||||
borderLeft = CSEL ? 0x018 : 0x01F;
|
||||
borderRight = CSEL ? 0x158 : 0x14F;
|
||||
}
|
||||
|
||||
private void UpdateInterrupts()
|
||||
{
|
||||
IRQ = ((IRST & ERST) || (IMMC & EMMC) || (IMBC & EMBC) || (ILP & ELP));
|
||||
}
|
||||
|
||||
private void UpdatePlotter()
|
||||
{
|
||||
graphicsMode = (ECM ? 0x04 : 0x00) | (BMM ? 0x02 : 0x00) | (MCM ? 0x01 : 0x00);
|
||||
}
|
||||
|
||||
public void Write(ushort addr, byte val)
|
||||
{
|
||||
addr &= 0x3F;
|
||||
|
||||
switch (addr)
|
||||
{
|
||||
case 0x11:
|
||||
rasterInterruptTriggered = false;
|
||||
rasterInterruptLine &= 0xFF;
|
||||
rasterInterruptLine |= (val & 0x80) << 1;
|
||||
// raster upper bit can't be changed, save and restore the value
|
||||
val &= 0x7F;
|
||||
val |= (byte)(this[addr] & 0x80);
|
||||
this[addr] = val;
|
||||
UpdateBorder();
|
||||
UpdatePlotter();
|
||||
break;
|
||||
case 0x12:
|
||||
// raster interrupt lower 8 bits
|
||||
rasterInterruptTriggered = false;
|
||||
rasterInterruptLine &= 0x100;
|
||||
rasterInterruptLine |= (val & 0xFF);
|
||||
break;
|
||||
case 0x16:
|
||||
this[addr] = val;
|
||||
UpdateBorder();
|
||||
UpdatePlotter();
|
||||
break;
|
||||
case 0x19:
|
||||
// only allow clearing of these flags
|
||||
if ((val & 0x01) != 0x00)
|
||||
IRST = false;
|
||||
if ((val & 0x02) != 0x00)
|
||||
IMBC = false;
|
||||
if ((val & 0x04) != 0x00)
|
||||
IMMC = false;
|
||||
if ((val & 0x08) != 0x00)
|
||||
ILP = false;
|
||||
UpdateInterrupts();
|
||||
break;
|
||||
case 0x1E:
|
||||
case 0x1F:
|
||||
// can't write to these regs
|
||||
break;
|
||||
default:
|
||||
this[addr] = val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,71 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace BizHawk.Emulation.Computers.Commodore64
|
||||
{
|
||||
public partial class VicII : IVideoProvider
|
||||
{
|
||||
private int[] videoBuffer;
|
||||
private int videoBufferHeight;
|
||||
private int videoBufferIndex;
|
||||
private int videoBufferSize;
|
||||
private int videoBufferWidth;
|
||||
|
||||
// palette
|
||||
private static int[] palette =
|
||||
{
|
||||
Colors.ARGB(0x00, 0x00, 0x00),
|
||||
Colors.ARGB(0xFF, 0xFF, 0xFF),
|
||||
Colors.ARGB(0x68, 0x37, 0x2B),
|
||||
Colors.ARGB(0x70, 0xA4, 0xB2),
|
||||
Colors.ARGB(0x6F, 0x3D, 0x86),
|
||||
Colors.ARGB(0x58, 0x8D, 0x43),
|
||||
Colors.ARGB(0x35, 0x28, 0x79),
|
||||
Colors.ARGB(0xB8, 0xC7, 0x6F),
|
||||
Colors.ARGB(0x6F, 0x4F, 0x25),
|
||||
Colors.ARGB(0x43, 0x39, 0x00),
|
||||
Colors.ARGB(0x9A, 0x67, 0x59),
|
||||
Colors.ARGB(0x44, 0x44, 0x44),
|
||||
Colors.ARGB(0x6C, 0x6C, 0x6C),
|
||||
Colors.ARGB(0x9A, 0xD2, 0x84),
|
||||
Colors.ARGB(0x6C, 0x5E, 0xB5),
|
||||
Colors.ARGB(0x95, 0x95, 0x95)
|
||||
};
|
||||
|
||||
public int BackgroundColor
|
||||
{
|
||||
get { return -16777216; } //FF000000
|
||||
}
|
||||
|
||||
public int BufferHeight
|
||||
{
|
||||
get { return videoBufferHeight; }
|
||||
}
|
||||
|
||||
public int BufferWidth
|
||||
{
|
||||
get { return videoBufferWidth; }
|
||||
}
|
||||
|
||||
public int[] GetVideoBuffer()
|
||||
{
|
||||
return videoBuffer;
|
||||
}
|
||||
|
||||
private void InitVideoBuffer()
|
||||
{
|
||||
videoBufferIndex = 0;
|
||||
videoBufferWidth = pipelineLength * 8;
|
||||
videoBufferHeight = rasterLines;
|
||||
videoBufferSize = videoBufferWidth * videoBufferHeight;
|
||||
videoBuffer = new int[videoBufferSize];
|
||||
}
|
||||
|
||||
public int VirtualWidth
|
||||
{
|
||||
get { return videoBufferWidth; }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,110 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace BizHawk.Emulation.Computers.Commodore64
|
||||
{
|
||||
public partial class VicII : IVideoProvider
|
||||
{
|
||||
public void SyncState(Serializer ser)
|
||||
{
|
||||
// internal
|
||||
ser.Sync("RC", ref RC);
|
||||
ser.Sync("VC", ref VC);
|
||||
ser.Sync("VCBASE", ref VCBASE);
|
||||
ser.Sync("VMLI", ref VMLI);
|
||||
|
||||
// external
|
||||
ser.Sync("BMM", ref BMM);
|
||||
ser.Sync("BxC", ref BxC, false);
|
||||
ser.Sync("CB", ref CB);
|
||||
ser.Sync("CSEL", ref CSEL);
|
||||
ser.Sync("DEN", ref DEN);
|
||||
ser.Sync("EC", ref EC);
|
||||
ser.Sync("ECM", ref ECM);
|
||||
ser.Sync("ELP", ref ELP);
|
||||
ser.Sync("EMBC", ref EMBC);
|
||||
ser.Sync("EMMC", ref EMMC);
|
||||
ser.Sync("ERST", ref ERST);
|
||||
ser.Sync("ILP", ref ILP);
|
||||
ser.Sync("IMBC", ref IMBC);
|
||||
ser.Sync("IMMC", ref IMMC);
|
||||
ser.Sync("IRQ", ref IRQ);
|
||||
ser.Sync("IRST", ref IRST);
|
||||
ser.Sync("LPX", ref LPX);
|
||||
ser.Sync("LPY", ref LPY);
|
||||
ser.Sync("MCM", ref MCM);
|
||||
ser.Sync("MMx", ref MMx, false);
|
||||
ser.Sync("RASTER", ref RASTER);
|
||||
ser.Sync("RES", ref RES);
|
||||
ser.Sync("RSEL", ref RSEL);
|
||||
ser.Sync("VM", ref VM);
|
||||
ser.Sync("XSCROLL", ref XSCROLL);
|
||||
ser.Sync("YSCROLL", ref YSCROLL);
|
||||
|
||||
// state
|
||||
ser.Sync("ADVANCEX", ref advanceX);
|
||||
ser.Sync("BADLINE", ref badline);
|
||||
ser.Sync("BITMAPCOLUMN", ref bitmapColumn);
|
||||
ser.Sync("BITMAPDATA", ref bitmapData);
|
||||
ser.Sync("BORDERONMAIN", ref borderOnMain);
|
||||
ser.Sync("BORDERONVERTICAL", ref borderOnVertical);
|
||||
ser.Sync("CENTERENABLED", ref centerEnabled);
|
||||
ser.Sync("CHARACTERDATA", ref characterData);
|
||||
ser.Sync("CHARACTERDATABUS", ref characterDataBus);
|
||||
ser.Sync("CHARMEM", ref characterMemory, false);
|
||||
ser.Sync("COLORDATA", ref colorData);
|
||||
ser.Sync("COLORDATABUS", ref colorDataBus);
|
||||
ser.Sync("COLORMEM", ref colorMemory, false);
|
||||
ser.Sync("DISPLAYENABLED", ref displayEnabled);
|
||||
ser.Sync("FETCHCOUNTER", ref fetchCounter);
|
||||
ser.Sync("IDLE", ref idle);
|
||||
ser.Sync("PLOTTERBUFFERINDEX", ref plotterBufferIndex);
|
||||
ser.Sync("PLOTTERDATA", ref plotterData);
|
||||
ser.Sync("PLOTTERDATABUFFER", ref plotterDataBuffer, false);
|
||||
ser.Sync("PLOTTERDELAY", ref plotterDelay);
|
||||
ser.Sync("PLOTTERPIXEL", ref plotterPixel);
|
||||
ser.Sync("PLOTTERPIXELBUFFER", ref plotterPixelBuffer, false);
|
||||
ser.Sync("RASTERINTERRUPTLINE", ref rasterInterruptLine);
|
||||
ser.Sync("RASTERINTERRUPTTRIGGERED", ref rasterInterruptTriggered);
|
||||
ser.Sync("RASTERX", ref rasterX);
|
||||
ser.Sync("REFRESHADDRESS", ref refreshAddress);
|
||||
|
||||
// pipeline
|
||||
ser.Sync("CYCLE", ref cycle);
|
||||
ser.Sync("PIPELINEGACCESS", ref pipelineGAccess);
|
||||
|
||||
// sprites
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
string iTag = i.ToString();
|
||||
ser.Sync("MC" + iTag, ref sprites[i].MC);
|
||||
ser.Sync("MCBASE" + iTag, ref sprites[i].MCBASE);
|
||||
ser.Sync("MD" + iTag, ref sprites[i].MD);
|
||||
ser.Sync("MDMA" + iTag, ref sprites[i].MDMA);
|
||||
ser.Sync("MPTR" + iTag, ref sprites[i].MPTR);
|
||||
ser.Sync("MSR" + iTag, ref sprites[i].MSR);
|
||||
ser.Sync("MxXEToggle" + iTag, ref sprites[i].MxXEToggle);
|
||||
ser.Sync("MxYEToggle" + iTag, ref sprites[i].MxYEToggle);
|
||||
|
||||
ser.Sync("MxC" + iTag, ref sprites[i].MxC);
|
||||
ser.Sync("MxD" + iTag, ref sprites[i].MxD);
|
||||
ser.Sync("MxDP" + iTag, ref sprites[i].MxDP);
|
||||
ser.Sync("MxE" + iTag, ref sprites[i].MxE);
|
||||
ser.Sync("MxM" + iTag, ref sprites[i].MxM);
|
||||
ser.Sync("MxMC" + iTag, ref sprites[i].MxMC);
|
||||
ser.Sync("MxX" + iTag, ref sprites[i].MxX);
|
||||
ser.Sync("MxXE" + iTag, ref sprites[i].MxXE);
|
||||
ser.Sync("MxY" + iTag, ref sprites[i].MxY);
|
||||
ser.Sync("MxYE" + iTag, ref sprites[i].MxYE);
|
||||
}
|
||||
|
||||
if (ser.IsReader)
|
||||
{
|
||||
UpdateBorder();
|
||||
UpdatePlotter();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue