Started Spectrum128 implementation
This commit is contained in:
parent
f92269657d
commit
30019d68fc
|
@ -52,10 +52,11 @@ namespace BizHawk.Emulation.Common
|
|||
|
||||
// ZX Spectrum
|
||||
FirmwareAndOption("5EA7C2B824672E914525D1D5C419D71B84A426A2", 16384, "ZXSpectrum", "48ROM", "48.ROM", "Spectrum 48K ROM");
|
||||
FirmwareAndOption("16375D42EA109B47EDDED7A16028DE7FDB3013A1", 32768, "ZXSpectrum", "128ROM", "128.ROM", "Spectrum 128K ROM");
|
||||
|
||||
// for saturn, we think any bios region can pretty much run any iso
|
||||
// so, we're going to lay this out carefully so that we choose things in a sensible order, but prefer the correct region
|
||||
var ss_100_j = File("2B8CB4F87580683EB4D760E4ED210813D667F0A2", 524288, "saturn-1.00-(J).bin", "Bios v1.00 (J)");
|
||||
// for saturn, we think any bios region can pretty much run any iso
|
||||
// so, we're going to lay this out carefully so that we choose things in a sensible order, but prefer the correct region
|
||||
var ss_100_j = File("2B8CB4F87580683EB4D760E4ED210813D667F0A2", 524288, "saturn-1.00-(J).bin", "Bios v1.00 (J)");
|
||||
var ss_100_ue = File("FAA8EA183A6D7BBE5D4E03BB1332519800D3FBC3", 524288, "saturn-1.00-(U+E).bin", "Bios v1.00 (U+E)");
|
||||
var ss_100a_ue = File("3BB41FEB82838AB9A35601AC666DE5AACFD17A58", 524288, "saturn-1.00a-(U+E).bin", "Bios v1.00a (U+E)"); // ?? is this size correct?
|
||||
var ss_101_j = File("DF94C5B4D47EB3CC404D88B33A8FDA237EAF4720", 524288, "saturn-1.01-(J).bin", "Bios v1.01 (J)"); // ?? is this size correct?
|
||||
|
|
|
@ -276,7 +276,11 @@
|
|||
<Compile Include="Computers\SinclairSpectrum\Machine\SpectrumBase.Port.cs" />
|
||||
<Compile Include="Computers\SinclairSpectrum\Machine\SpectrumBase.Memory.cs" />
|
||||
<Compile Include="Computers\SinclairSpectrum\Machine\SpectrumBase.Sound.cs" />
|
||||
<Compile Include="Computers\SinclairSpectrum\Machine\ZXSpectrum128K\ZX128.cs" />
|
||||
<Compile Include="Computers\SinclairSpectrum\Machine\ZXSpectrum128K\ZX128.Memory.cs" />
|
||||
<Compile Include="Computers\SinclairSpectrum\Machine\ZXSpectrum128K\ZX128.Port.cs" />
|
||||
<Compile Include="Computers\SinclairSpectrum\Machine\ZXSpectrum48K\ZX48.cs" />
|
||||
<Compile Include="Computers\SinclairSpectrum\Machine\ZXSpectrum48K\ZX48.Memory.cs" />
|
||||
<Compile Include="Computers\SinclairSpectrum\Media\Tape\TAP\TapDataBlock.cs" />
|
||||
<Compile Include="Computers\SinclairSpectrum\Media\Tape\TAP\TapPlayer.cs" />
|
||||
<Compile Include="Computers\SinclairSpectrum\Media\Tape\TAP\TapReader.cs" />
|
||||
|
@ -1377,6 +1381,7 @@
|
|||
<ItemGroup>
|
||||
<Compile Include="Computers\SinclairSpectrum\Machine\SpectrumBase.Screen.cs" />
|
||||
<Compile Include="Computers\SinclairSpectrum\Machine\ZXSpectrum48K\ZX48.Keyboard.cs" />
|
||||
<Compile Include="Computers\SinclairSpectrum\Machine\ZXSpectrum48K\ZX48.Port.cs" />
|
||||
<None Include="Computers\SinclairSpectrum\readme.md" />
|
||||
<None Include="Consoles\Atari\docs\stella.pdf" />
|
||||
<None Include="Consoles\Coleco\docs\colecovision tech1.pdf" />
|
||||
|
|
|
@ -11,6 +11,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
|||
/// <summary>
|
||||
/// Sinclair Spectrum 48K model
|
||||
/// </summary>
|
||||
ZXSpectrum48
|
||||
ZXSpectrum48,
|
||||
|
||||
/// <summary>
|
||||
/// Sinclair Spectrum 128K model
|
||||
/// </summary>
|
||||
ZXSpectrum128
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,28 +44,25 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
|||
/// </summary>
|
||||
/// <param name="addr"></param>
|
||||
/// <returns></returns>
|
||||
public virtual byte ReadBus(ushort addr)
|
||||
public abstract byte ReadBus(ushort addr);
|
||||
|
||||
/// <summary>
|
||||
/// Pushes a value onto the data bus that should be valid as long as the interrupt is true
|
||||
/// </summary>
|
||||
/// <param name="addr"></param>
|
||||
/// <returns></returns>
|
||||
public virtual byte PushBus()
|
||||
{
|
||||
throw new NotImplementedException("Must be overriden");
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pushes a value onto the data bus that should be valid as long as the interrupt is true
|
||||
/// </summary>
|
||||
/// <param name="addr"></param>
|
||||
/// <returns></returns>
|
||||
public virtual byte PushBus()
|
||||
{
|
||||
throw new NotImplementedException("Must be overriden");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Simulates writing to the bus
|
||||
/// Paging should be handled here
|
||||
/// </summary>
|
||||
/// <param name="addr"></param>
|
||||
/// <param name="value"></param>
|
||||
public virtual void WriteBus(ushort addr, byte value)
|
||||
/// <summary>
|
||||
/// Simulates writing to the bus
|
||||
/// Paging should be handled here
|
||||
/// </summary>
|
||||
/// <param name="addr"></param>
|
||||
/// <param name="value"></param>
|
||||
public virtual void WriteBus(ushort addr, byte value)
|
||||
{
|
||||
throw new NotImplementedException("Must be overriden");
|
||||
}
|
||||
|
@ -76,23 +73,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
|||
/// </summary>
|
||||
/// <param name="addr"></param>
|
||||
/// <returns></returns>
|
||||
public virtual byte ReadMemory(ushort addr)
|
||||
{
|
||||
throw new NotImplementedException("Must be overriden");
|
||||
}
|
||||
/*
|
||||
/// <summary>
|
||||
/// Reads a byte of data from a specified memory address
|
||||
/// (with no memory contention)
|
||||
/// </summary>
|
||||
/// <param name="addr"></param>
|
||||
/// <returns></returns>
|
||||
public virtual byte PeekMemory(ushort addr)
|
||||
{
|
||||
var data = ReadBus(addr);
|
||||
return data;
|
||||
}
|
||||
*/
|
||||
public abstract byte ReadMemory(ushort addr);
|
||||
|
||||
/// <summary>
|
||||
/// Writes a byte of data to a specified memory address
|
||||
|
@ -100,51 +81,14 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
|||
/// </summary>
|
||||
/// <param name="addr"></param>
|
||||
/// <param name="value"></param>
|
||||
public virtual void WriteMemory(ushort addr, byte value)
|
||||
{
|
||||
throw new NotImplementedException("Must be overriden");
|
||||
}
|
||||
|
||||
/*
|
||||
/// <summary>
|
||||
/// Writes a byte of data to a specified memory address
|
||||
/// (without contention)
|
||||
/// </summary>
|
||||
/// <param name="addr"></param>
|
||||
/// <param name="value"></param>
|
||||
public virtual void PokeMemory(ushort addr, byte value)
|
||||
{
|
||||
if (addr < 0x4000)
|
||||
{
|
||||
// Do nothing - we cannot write to ROM
|
||||
return;
|
||||
}
|
||||
|
||||
WriteBus(addr, value);
|
||||
}
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// Fills memory from buffer
|
||||
/// </summary>
|
||||
/// <param name="buffer"></param>
|
||||
/// <param name="startAddress"></param>
|
||||
public virtual void FillMemory(byte[] buffer, ushort startAddress)
|
||||
{
|
||||
//buffer?.CopyTo(RAM, startAddress);
|
||||
}
|
||||
public abstract void WriteMemory(ushort addr, byte value);
|
||||
|
||||
/// <summary>
|
||||
/// Sets up the ROM
|
||||
/// </summary>
|
||||
/// <param name="buffer"></param>
|
||||
/// <param name="startAddress"></param>
|
||||
public virtual void InitROM(RomData romData)
|
||||
{
|
||||
RomData = romData;
|
||||
// for 16/48k machines only ROM0 is used (no paging)
|
||||
RomData.RomBytes?.CopyTo(ROM0, 0);
|
||||
}
|
||||
public abstract void InitROM(RomData romData);
|
||||
|
||||
/// <summary>
|
||||
/// ULA reads the memory at the specified address
|
||||
|
@ -154,7 +98,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
|||
/// <returns></returns>
|
||||
public virtual byte FetchScreenMemory(ushort addr)
|
||||
{
|
||||
//var value = RAM0[(addr & 0x3FFF)];// + 0x4000];
|
||||
var value = ReadBus((ushort)((addr & 0x3FFF) + 0x4000));
|
||||
return value;
|
||||
}
|
||||
|
@ -162,10 +105,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
|||
/// <summary>
|
||||
/// Helper function to refresh memory array (probably not the best way to do things)
|
||||
/// </summary>
|
||||
public virtual void ReInitMemory()
|
||||
{
|
||||
throw new NotImplementedException("Must be overriden");
|
||||
}
|
||||
public abstract void ReInitMemory();
|
||||
|
||||
/// <summary>
|
||||
/// Returns the memory contention value for the specified T-State (cycle)
|
||||
|
|
|
@ -22,150 +22,14 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
|||
/// </summary>
|
||||
/// <param name="port"></param>
|
||||
/// <returns></returns>
|
||||
public virtual byte ReadPort(ushort port)
|
||||
{
|
||||
int result = 0xFF;
|
||||
|
||||
// Check whether the low bit is reset
|
||||
// Technically the ULA should respond to every even I/O address
|
||||
bool lowBitReset = (port & 0x0001) == 0;
|
||||
|
||||
ContendPort((ushort)port);
|
||||
|
||||
// Kempston Joystick
|
||||
if ((port & 0xe0) == 0 || (port & 0x20) == 0)
|
||||
{
|
||||
return (byte)KempstonDevice.JoyLine;
|
||||
}
|
||||
else if (lowBitReset)
|
||||
{
|
||||
// Even I/O address so get input
|
||||
// The high byte indicates which half-row of keys is being polled
|
||||
/*
|
||||
IN: Reads keys (bit 0 to bit 4 inclusive)
|
||||
0xfefe SHIFT, Z, X, C, V 0xeffe 0, 9, 8, 7, 6
|
||||
0xfdfe A, S, D, F, G 0xdffe P, O, I, U, Y
|
||||
0xfbfe Q, W, E, R, T 0xbffe ENTER, L, K, J, H
|
||||
0xf7fe 1, 2, 3, 4, 5 0x7ffe SPACE, SYM SHFT, M, N, B
|
||||
*/
|
||||
|
||||
if ((port & 0x8000) == 0)
|
||||
result &= KeyboardDevice.KeyLine[7];
|
||||
|
||||
if ((port & 0x4000) == 0)
|
||||
result &= KeyboardDevice.KeyLine[6];
|
||||
|
||||
if ((port & 0x2000) == 0)
|
||||
result &= KeyboardDevice.KeyLine[5];
|
||||
|
||||
if ((port & 0x1000) == 0)
|
||||
result &= KeyboardDevice.KeyLine[4];
|
||||
|
||||
if ((port & 0x800) == 0)
|
||||
result &= KeyboardDevice.KeyLine[3];
|
||||
|
||||
if ((port & 0x400) == 0)
|
||||
result &= KeyboardDevice.KeyLine[2];
|
||||
|
||||
if ((port & 0x200) == 0)
|
||||
result &= KeyboardDevice.KeyLine[1];
|
||||
|
||||
if ((port & 0x100) == 0)
|
||||
result &= KeyboardDevice.KeyLine[0];
|
||||
|
||||
result = result & 0x1f; //mask out lower 4 bits
|
||||
result = result | 0xa0; //set bit 5 & 7 to 1
|
||||
|
||||
|
||||
if (TapeDevice.CurrentMode == TapeOperationMode.Load)
|
||||
{
|
||||
if (!TapeDevice.GetEarBit(CPU.TotalExecutedCycles))
|
||||
{
|
||||
result &= ~(TAPE_BIT); // reset is EAR ON
|
||||
}
|
||||
else
|
||||
{
|
||||
result |= (TAPE_BIT); // set is EAR Off
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (KeyboardDevice.IsIssue2Keyboard)
|
||||
{
|
||||
if ((LastULAOutByte & (EAR_BIT + MIC_BIT)) == 0)
|
||||
{
|
||||
result &= ~(TAPE_BIT);
|
||||
}
|
||||
else
|
||||
{
|
||||
result |= TAPE_BIT;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((LastULAOutByte & EAR_BIT) == 0)
|
||||
{
|
||||
result &= ~(TAPE_BIT);
|
||||
}
|
||||
else
|
||||
{
|
||||
result |= TAPE_BIT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// devices other than the ULA will respond here
|
||||
// (e.g. the AY sound chip in a 128k spectrum
|
||||
|
||||
// AY register activate
|
||||
// Kemptson Mouse
|
||||
|
||||
|
||||
// if unused port the floating memory bus should be returned (still todo)
|
||||
}
|
||||
|
||||
return (byte)result;
|
||||
}
|
||||
public abstract byte ReadPort(ushort port);
|
||||
|
||||
/// <summary>
|
||||
/// Writes a byte of data to a specified port address
|
||||
/// </summary>
|
||||
/// <param name="port"></param>
|
||||
/// <param name="value"></param>
|
||||
public virtual void WritePort(ushort port, byte value)
|
||||
{
|
||||
// Check whether the low bit is reset
|
||||
// Technically the ULA should respond to every even I/O address
|
||||
bool lowBitReset = (port & 0x01) == 0;
|
||||
|
||||
ContendPort(port);
|
||||
|
||||
// Only even addresses address the ULA
|
||||
if (lowBitReset)
|
||||
{
|
||||
// store the last OUT byte
|
||||
LastULAOutByte = value;
|
||||
|
||||
/*
|
||||
Bit 7 6 5 4 3 2 1 0
|
||||
+-------------------------------+
|
||||
| | | | E | M | Border |
|
||||
+-------------------------------+
|
||||
*/
|
||||
|
||||
// Border - LSB 3 bits hold the border colour
|
||||
BorderColour = value & BORDER_BIT;
|
||||
|
||||
// Buzzer
|
||||
BuzzerDevice.ProcessPulseValue(false, (value & EAR_BIT) != 0);
|
||||
|
||||
// Tape
|
||||
TapeDevice.ProcessMicBit((value & MIC_BIT) != 0);
|
||||
}
|
||||
}
|
||||
public abstract void WritePort(ushort port, byte value);
|
||||
|
||||
/// <summary>
|
||||
/// Apply I/O contention if necessary
|
||||
|
|
|
@ -179,7 +179,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
|||
/// The time of displaying right part of the border.
|
||||
/// Given in Z80 clock cycles.
|
||||
/// </summary>
|
||||
protected int BorderRightTime = 24;
|
||||
protected int BorderRightTime = 24;
|
||||
|
||||
/// <summary>
|
||||
/// The time used to render the nonvisible right part of the border.
|
||||
|
@ -900,6 +900,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
|||
public int VsyncNumerator
|
||||
{
|
||||
get { return 3500000; }
|
||||
set { }
|
||||
}
|
||||
|
||||
public int VsyncDenominator
|
||||
|
|
|
@ -10,7 +10,13 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
|||
/// * Main properties / fields / contruction*
|
||||
/// </summary>
|
||||
public abstract partial class SpectrumBase
|
||||
{
|
||||
{
|
||||
|
||||
public bool ROMPaged { get; set; }
|
||||
public bool SHADOWPaged { get; set; }
|
||||
public int RAMPaged { get; set; }
|
||||
public bool PagingDisabled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The calling ZXSpectrum class (piped in via constructor)
|
||||
/// </summary>
|
||||
|
|
|
@ -0,0 +1,284 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
||||
{
|
||||
public partial class ZX128 : SpectrumBase
|
||||
{
|
||||
/* 128k paging controlled by writes to port 0x7ffd
|
||||
*
|
||||
*
|
||||
|
||||
#7FFD (32765) - decoded as A15=0, A1=0 and /IORQ=0. Bits 0..5 are latched. Bits 0..2 select RAM bank in secton D. Bit 3 selects RAM bank to dispay screen (0 - RAM5, 1 - RAM7). Bit 4 selects ROM bank (0 - ROM0, 1 - ROM1). Bit 5, when set locks future writing to #7FFD port until reset. Reading #7FFD port is the same as writing #FF into it.
|
||||
#BFFD (49149) - write data byte into AY-3-8912 chip.
|
||||
#FFFD (65533) - select AY-3-8912 addres (D4..D7 ignored) and reading data byte.
|
||||
|
||||
* 0xffff +--------+--------+--------+--------+--------+--------+--------+--------+
|
||||
| Bank 0 | Bank 1 | Bank 2 | Bank 3 | Bank 4 | Bank 5 | Bank 6 | Bank 7 |
|
||||
| | |(also at| | |(also at| | |
|
||||
| | | 0x8000)| | | 0x4000)| | |
|
||||
| | | | | | screen | | screen |
|
||||
0xc000 +--------+--------+--------+--------+--------+--------+--------+--------+
|
||||
| Bank 2 | Any one of these pages may be switched in.
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
0x8000 +--------+
|
||||
| Bank 5 |
|
||||
| |
|
||||
| |
|
||||
| screen |
|
||||
0x4000 +--------+--------+
|
||||
| ROM 0 | ROM 1 | Either ROM may be switched in.
|
||||
| | |
|
||||
| | |
|
||||
| | |
|
||||
0x0000 +--------+--------+
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// Simulates reading from the bus (no contention)
|
||||
/// Paging should be handled here
|
||||
/// </summary>
|
||||
/// <param name="addr"></param>
|
||||
/// <returns></returns>
|
||||
public override byte ReadBus(ushort addr)
|
||||
{
|
||||
int divisor = addr / 0x4000;
|
||||
byte result = 0xff;
|
||||
switch (divisor)
|
||||
{
|
||||
// ROM 0x000
|
||||
case 0:
|
||||
if (!ROMPaged)
|
||||
result = Memory[0][addr % 0x4000];
|
||||
else
|
||||
result = Memory[1][addr % 0x4000];
|
||||
break;
|
||||
|
||||
// RAM 0x4000 (RAM5 - Bank5 or shadow bank RAM7)
|
||||
case 1:
|
||||
result = Memory[7][addr % 0x4000];
|
||||
break;
|
||||
|
||||
// RAM 0x8000 (RAM2 - Bank2)
|
||||
case 2:
|
||||
result = Memory[4][addr % 0x4000];
|
||||
break;
|
||||
|
||||
// RAM 0xc000 (any ram bank 0 - 7 may be paged in - default bank0)
|
||||
case 3:
|
||||
switch (RAMPaged)
|
||||
{
|
||||
case 0:
|
||||
result = Memory[2][addr % 0x4000];
|
||||
break;
|
||||
case 1:
|
||||
result = Memory[3][addr % 0x4000];
|
||||
break;
|
||||
case 2:
|
||||
result = Memory[4][addr % 0x4000];
|
||||
break;
|
||||
case 3:
|
||||
result = Memory[5][addr % 0x4000];
|
||||
break;
|
||||
case 4:
|
||||
result = Memory[6][addr % 0x4000];
|
||||
break;
|
||||
case 5:
|
||||
result = Memory[7][addr % 0x4000];
|
||||
break;
|
||||
case 6:
|
||||
result = Memory[8][addr % 0x4000];
|
||||
break;
|
||||
case 7:
|
||||
result = Memory[9][addr % 0x4000];
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Simulates writing to the bus (no contention)
|
||||
/// Paging should be handled here
|
||||
/// </summary>
|
||||
/// <param name="addr"></param>
|
||||
/// <param name="value"></param>
|
||||
public override void WriteBus(ushort addr, byte value)
|
||||
{
|
||||
int divisor = addr / 0x4000;
|
||||
switch (divisor)
|
||||
{
|
||||
// ROM 0x000
|
||||
case 0:
|
||||
if (!ROMPaged)
|
||||
Memory[0][addr % 0x4000] = value;
|
||||
else
|
||||
Memory[1][addr % 0x4000] = value;
|
||||
break;
|
||||
|
||||
// RAM 0x4000 (RAM5 - Bank5 or shadow bank RAM7)
|
||||
case 1:
|
||||
Memory[7][addr % 0x4000] = value;
|
||||
break;
|
||||
|
||||
// RAM 0x8000 (RAM2 - Bank2)
|
||||
case 2:
|
||||
Memory[4][addr % 0x4000] = value;
|
||||
break;
|
||||
|
||||
// RAM 0xc000 (any ram bank 0 - 7 may be paged in - default bank0)
|
||||
case 3:
|
||||
switch (RAMPaged)
|
||||
{
|
||||
case 0:
|
||||
Memory[2][addr % 0x4000] = value;
|
||||
break;
|
||||
case 1:
|
||||
Memory[3][addr % 0x4000] = value;
|
||||
break;
|
||||
case 2:
|
||||
Memory[4][addr % 0x4000] = value;
|
||||
break;
|
||||
case 3:
|
||||
Memory[5][addr % 0x4000] = value;
|
||||
break;
|
||||
case 4:
|
||||
Memory[6][addr % 0x4000] = value;
|
||||
break;
|
||||
case 5:
|
||||
Memory[7][addr % 0x4000] = value;
|
||||
break;
|
||||
case 6:
|
||||
Memory[8][addr % 0x4000] = value;
|
||||
break;
|
||||
case 7:
|
||||
Memory[9][addr % 0x4000] = value;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a byte of data from a specified memory address
|
||||
/// (with memory contention if appropriate)
|
||||
/// </summary>
|
||||
/// <param name="addr"></param>
|
||||
/// <returns></returns>
|
||||
public override byte ReadMemory(ushort addr)
|
||||
{
|
||||
var data = ReadBus(addr);
|
||||
if ((addr & 0xC000) == 0x4000)
|
||||
{
|
||||
// addr is in RAM not ROM - apply memory contention if neccessary
|
||||
var delay = GetContentionValue(CurrentFrameCycle);
|
||||
CPU.TotalExecutedCycles += delay;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a byte of data to a specified memory address
|
||||
/// (with memory contention if appropriate)
|
||||
/// </summary>
|
||||
/// <param name="addr"></param>
|
||||
/// <param name="value"></param>
|
||||
public override void WriteMemory(ushort addr, byte value)
|
||||
{
|
||||
if (addr < 0x4000)
|
||||
{
|
||||
// Do nothing - we cannot write to ROM
|
||||
return;
|
||||
}
|
||||
else if (addr < 0xC000)
|
||||
{
|
||||
// possible contended RAM
|
||||
var delay = GetContentionValue(CurrentFrameCycle);
|
||||
CPU.TotalExecutedCycles += delay;
|
||||
}
|
||||
|
||||
WriteBus(addr, value);
|
||||
}
|
||||
|
||||
public override void ReInitMemory()
|
||||
{
|
||||
if (Memory.ContainsKey(0))
|
||||
Memory[0] = ROM0;
|
||||
else
|
||||
Memory.Add(0, ROM0);
|
||||
|
||||
if (Memory.ContainsKey(1))
|
||||
Memory[1] = ROM1;
|
||||
else
|
||||
Memory.Add(1, ROM1);
|
||||
|
||||
if (Memory.ContainsKey(2))
|
||||
Memory[2] = RAM0;
|
||||
else
|
||||
Memory.Add(2, RAM0);
|
||||
|
||||
if (Memory.ContainsKey(3))
|
||||
Memory[3] = RAM1;
|
||||
else
|
||||
Memory.Add(3, RAM1);
|
||||
|
||||
if (Memory.ContainsKey(4))
|
||||
Memory[4] = RAM2;
|
||||
else
|
||||
Memory.Add(4, RAM2);
|
||||
|
||||
if (Memory.ContainsKey(5))
|
||||
Memory[5] = RAM3;
|
||||
else
|
||||
Memory.Add(5, RAM3);
|
||||
|
||||
if (Memory.ContainsKey(6))
|
||||
Memory[6] = RAM4;
|
||||
else
|
||||
Memory.Add(6, RAM4);
|
||||
|
||||
if (Memory.ContainsKey(7))
|
||||
Memory[7] = RAM5;
|
||||
else
|
||||
Memory.Add(7, RAM5);
|
||||
|
||||
if (Memory.ContainsKey(8))
|
||||
Memory[8] = RAM6;
|
||||
else
|
||||
Memory.Add(8, RAM6);
|
||||
|
||||
if (Memory.ContainsKey(9))
|
||||
Memory[9] = RAM7;
|
||||
else
|
||||
Memory.Add(9, RAM7);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets up the ROM
|
||||
/// </summary>
|
||||
/// <param name="buffer"></param>
|
||||
/// <param name="startAddress"></param>
|
||||
public override void InitROM(RomData romData)
|
||||
{
|
||||
RomData = romData;
|
||||
// 128k uses ROM0 and ROM1
|
||||
// 128k loader is in ROM0, and fallback 48k rom is in ROM1
|
||||
for (int i = 0; i < 0x4000; i++)
|
||||
{
|
||||
ROM0[i] = RomData.RomBytes[i];
|
||||
ROM1[i] = RomData.RomBytes[i + 0x4000];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,188 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
||||
{
|
||||
public partial class ZX128 : SpectrumBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Reads a byte of data from a specified port address
|
||||
/// </summary>
|
||||
/// <param name="port"></param>
|
||||
/// <returns></returns>
|
||||
public override byte ReadPort(ushort port)
|
||||
{
|
||||
int result = 0xFF;
|
||||
|
||||
// Check whether the low bit is reset
|
||||
// Technically the ULA should respond to every even I/O address
|
||||
bool lowBitReset = (port & 0x0001) == 0;
|
||||
|
||||
ContendPort((ushort)port);
|
||||
|
||||
// Kempston Joystick
|
||||
if ((port & 0xe0) == 0 || (port & 0x20) == 0)
|
||||
{
|
||||
return (byte)KempstonDevice.JoyLine;
|
||||
}
|
||||
else if (lowBitReset)
|
||||
{
|
||||
// Even I/O address so get input
|
||||
// The high byte indicates which half-row of keys is being polled
|
||||
/*
|
||||
IN: Reads keys (bit 0 to bit 4 inclusive)
|
||||
0xfefe SHIFT, Z, X, C, V 0xeffe 0, 9, 8, 7, 6
|
||||
0xfdfe A, S, D, F, G 0xdffe P, O, I, U, Y
|
||||
0xfbfe Q, W, E, R, T 0xbffe ENTER, L, K, J, H
|
||||
0xf7fe 1, 2, 3, 4, 5 0x7ffe SPACE, SYM SHFT, M, N, B
|
||||
*/
|
||||
|
||||
if ((port & 0x8000) == 0)
|
||||
result &= KeyboardDevice.KeyLine[7];
|
||||
|
||||
if ((port & 0x4000) == 0)
|
||||
result &= KeyboardDevice.KeyLine[6];
|
||||
|
||||
if ((port & 0x2000) == 0)
|
||||
result &= KeyboardDevice.KeyLine[5];
|
||||
|
||||
if ((port & 0x1000) == 0)
|
||||
result &= KeyboardDevice.KeyLine[4];
|
||||
|
||||
if ((port & 0x800) == 0)
|
||||
result &= KeyboardDevice.KeyLine[3];
|
||||
|
||||
if ((port & 0x400) == 0)
|
||||
result &= KeyboardDevice.KeyLine[2];
|
||||
|
||||
if ((port & 0x200) == 0)
|
||||
result &= KeyboardDevice.KeyLine[1];
|
||||
|
||||
if ((port & 0x100) == 0)
|
||||
result &= KeyboardDevice.KeyLine[0];
|
||||
|
||||
result = result & 0x1f; //mask out lower 4 bits
|
||||
result = result | 0xa0; //set bit 5 & 7 to 1
|
||||
|
||||
|
||||
if (TapeDevice.CurrentMode == TapeOperationMode.Load)
|
||||
{
|
||||
if (!TapeDevice.GetEarBit(CPU.TotalExecutedCycles))
|
||||
{
|
||||
result &= ~(TAPE_BIT); // reset is EAR ON
|
||||
}
|
||||
else
|
||||
{
|
||||
result |= (TAPE_BIT); // set is EAR Off
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (KeyboardDevice.IsIssue2Keyboard)
|
||||
{
|
||||
if ((LastULAOutByte & (EAR_BIT + MIC_BIT)) == 0)
|
||||
{
|
||||
result &= ~(TAPE_BIT);
|
||||
}
|
||||
else
|
||||
{
|
||||
result |= TAPE_BIT;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((LastULAOutByte & EAR_BIT) == 0)
|
||||
{
|
||||
result &= ~(TAPE_BIT);
|
||||
}
|
||||
else
|
||||
{
|
||||
result |= TAPE_BIT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// devices other than the ULA will respond here
|
||||
// (e.g. the AY sound chip in a 128k spectrum
|
||||
|
||||
// AY register activate
|
||||
// Kemptson Mouse
|
||||
|
||||
|
||||
// if unused port the floating memory bus should be returned (still todo)
|
||||
}
|
||||
|
||||
return (byte)result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a byte of data to a specified port address
|
||||
/// </summary>
|
||||
/// <param name="port"></param>
|
||||
/// <param name="value"></param>
|
||||
public override void WritePort(ushort port, byte value)
|
||||
{
|
||||
// paging
|
||||
if (port == 0x7ffd)
|
||||
{
|
||||
// Bits 0, 1, 2 select the RAM page
|
||||
var rp = value & 0x07;
|
||||
if (rp < 8)
|
||||
RAMPaged = rp;
|
||||
|
||||
// ROM page
|
||||
if ((value & 0x10) != 0)
|
||||
{
|
||||
// 48k ROM
|
||||
ROMPaged = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
ROMPaged = false;
|
||||
}
|
||||
|
||||
// Bit 5 signifies that paging is disabled until next reboot
|
||||
if ((value & 0x20) != 0)
|
||||
PagingDisabled = true;
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Check whether the low bit is reset
|
||||
// Technically the ULA should respond to every even I/O address
|
||||
bool lowBitReset = (port & 0x01) == 0;
|
||||
|
||||
ContendPort(port);
|
||||
|
||||
// Only even addresses address the ULA
|
||||
if (lowBitReset)
|
||||
{
|
||||
// store the last OUT byte
|
||||
LastULAOutByte = value;
|
||||
|
||||
/*
|
||||
Bit 7 6 5 4 3 2 1 0
|
||||
+-------------------------------+
|
||||
| | | | E | M | Border |
|
||||
+-------------------------------+
|
||||
*/
|
||||
|
||||
// Border - LSB 3 bits hold the border colour
|
||||
BorderColour = value & BORDER_BIT;
|
||||
|
||||
// Buzzer
|
||||
BuzzerDevice.ProcessPulseValue(false, (value & EAR_BIT) != 0);
|
||||
|
||||
// Tape
|
||||
TapeDevice.ProcessMicBit((value & MIC_BIT) != 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using BizHawk.Emulation.Cores.Components.Z80A;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
||||
{
|
||||
public partial class ZX128 : SpectrumBase
|
||||
{
|
||||
#region Construction
|
||||
|
||||
/// <summary>
|
||||
/// Main constructor
|
||||
/// </summary>
|
||||
/// <param name="spectrum"></param>
|
||||
/// <param name="cpu"></param>
|
||||
public ZX128(ZXSpectrum spectrum, Z80A cpu, byte[] file)
|
||||
{
|
||||
Spectrum = spectrum;
|
||||
CPU = cpu;
|
||||
|
||||
ROMPaged = false;
|
||||
SHADOWPaged = false;
|
||||
RAMPaged = 0;
|
||||
PagingDisabled = false;
|
||||
|
||||
// init addressable memory from ROM and RAM banks
|
||||
ReInitMemory();
|
||||
|
||||
//RAM = new byte[0x4000 + 0xC000];
|
||||
|
||||
//DisplayLineTime = 132;
|
||||
VsyncNumerator = 3546900;
|
||||
|
||||
InitScreenConfig();
|
||||
InitScreen();
|
||||
|
||||
ResetULACycle();
|
||||
|
||||
BuzzerDevice = new Buzzer(this);
|
||||
BuzzerDevice.Init(44100, UlaFrameCycleCount);
|
||||
|
||||
KeyboardDevice = new Keyboard48(this);
|
||||
KempstonDevice = new KempstonJoystick(this);
|
||||
|
||||
TapeProvider = new DefaultTapeProvider(file);
|
||||
|
||||
TapeDevice = new Tape(TapeProvider);
|
||||
TapeDevice.Init(this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,149 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
||||
{
|
||||
public partial class ZX48 : SpectrumBase
|
||||
{
|
||||
/* 48K Spectrum has NO memory paging
|
||||
*
|
||||
* 0xffff +--------+
|
||||
| Bank 2 |
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
0xc000 +--------+
|
||||
| Bank 1 |
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
0x8000 +--------+
|
||||
| Bank 0 |
|
||||
| |
|
||||
| |
|
||||
| screen |
|
||||
0x4000 +--------+
|
||||
| ROM 0 |
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
0x0000 +--------+
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// Simulates reading from the bus (no contention)
|
||||
/// Paging should be handled here
|
||||
/// </summary>
|
||||
/// <param name="addr"></param>
|
||||
/// <returns></returns>
|
||||
public override byte ReadBus(ushort addr)
|
||||
{
|
||||
int divisor = addr / 0x4000;
|
||||
// paging logic goes here
|
||||
|
||||
var bank = Memory[divisor];
|
||||
var index = addr % 0x4000;
|
||||
return bank[index];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Simulates writing to the bus (no contention)
|
||||
/// Paging should be handled here
|
||||
/// </summary>
|
||||
/// <param name="addr"></param>
|
||||
/// <param name="value"></param>
|
||||
public override void WriteBus(ushort addr, byte value)
|
||||
{
|
||||
int divisor = addr / 0x4000;
|
||||
// paging logic goes here
|
||||
|
||||
var bank = Memory[divisor];
|
||||
var index = addr % 0x4000;
|
||||
bank[index] = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a byte of data from a specified memory address
|
||||
/// (with memory contention if appropriate)
|
||||
/// </summary>
|
||||
/// <param name="addr"></param>
|
||||
/// <returns></returns>
|
||||
public override byte ReadMemory(ushort addr)
|
||||
{
|
||||
var data = ReadBus(addr);
|
||||
if ((addr & 0xC000) == 0x4000)
|
||||
{
|
||||
// addr is in RAM not ROM - apply memory contention if neccessary
|
||||
var delay = GetContentionValue(CurrentFrameCycle);
|
||||
CPU.TotalExecutedCycles += delay;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a byte of data to a specified memory address
|
||||
/// (with memory contention if appropriate)
|
||||
/// </summary>
|
||||
/// <param name="addr"></param>
|
||||
/// <param name="value"></param>
|
||||
public override void WriteMemory(ushort addr, byte value)
|
||||
{
|
||||
if (addr < 0x4000)
|
||||
{
|
||||
// Do nothing - we cannot write to ROM
|
||||
return;
|
||||
}
|
||||
else if (addr < 0xC000)
|
||||
{
|
||||
// possible contended RAM
|
||||
var delay = GetContentionValue(CurrentFrameCycle);
|
||||
CPU.TotalExecutedCycles += delay;
|
||||
}
|
||||
|
||||
WriteBus(addr, value);
|
||||
}
|
||||
|
||||
public override void ReInitMemory()
|
||||
{
|
||||
if (Memory.ContainsKey(0))
|
||||
Memory[0] = ROM0;
|
||||
else
|
||||
Memory.Add(0, ROM0);
|
||||
|
||||
if (Memory.ContainsKey(1))
|
||||
Memory[1] = RAM0;
|
||||
else
|
||||
Memory.Add(1, RAM0);
|
||||
|
||||
if (Memory.ContainsKey(2))
|
||||
Memory[2] = RAM1;
|
||||
else
|
||||
Memory.Add(2, RAM1);
|
||||
|
||||
if (Memory.ContainsKey(3))
|
||||
Memory[3] = RAM2;
|
||||
else
|
||||
Memory.Add(3, RAM2);
|
||||
|
||||
if (Memory.ContainsKey(4))
|
||||
Memory[4] = RAM3;
|
||||
else
|
||||
Memory.Add(4, RAM3);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets up the ROM
|
||||
/// </summary>
|
||||
/// <param name="buffer"></param>
|
||||
/// <param name="startAddress"></param>
|
||||
public override void InitROM(RomData romData)
|
||||
{
|
||||
RomData = romData;
|
||||
// for 16/48k machines only ROM0 is used (no paging)
|
||||
RomData.RomBytes?.CopyTo(ROM0, 0);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,161 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
||||
{
|
||||
public partial class ZX48 : SpectrumBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Reads a byte of data from a specified port address
|
||||
/// </summary>
|
||||
/// <param name="port"></param>
|
||||
/// <returns></returns>
|
||||
public override byte ReadPort(ushort port)
|
||||
{
|
||||
int result = 0xFF;
|
||||
|
||||
// Check whether the low bit is reset
|
||||
// Technically the ULA should respond to every even I/O address
|
||||
bool lowBitReset = (port & 0x0001) == 0;
|
||||
|
||||
ContendPort((ushort)port);
|
||||
|
||||
// Kempston Joystick
|
||||
if ((port & 0xe0) == 0 || (port & 0x20) == 0)
|
||||
{
|
||||
return (byte)KempstonDevice.JoyLine;
|
||||
}
|
||||
else if (lowBitReset)
|
||||
{
|
||||
// Even I/O address so get input
|
||||
// The high byte indicates which half-row of keys is being polled
|
||||
/*
|
||||
IN: Reads keys (bit 0 to bit 4 inclusive)
|
||||
0xfefe SHIFT, Z, X, C, V 0xeffe 0, 9, 8, 7, 6
|
||||
0xfdfe A, S, D, F, G 0xdffe P, O, I, U, Y
|
||||
0xfbfe Q, W, E, R, T 0xbffe ENTER, L, K, J, H
|
||||
0xf7fe 1, 2, 3, 4, 5 0x7ffe SPACE, SYM SHFT, M, N, B
|
||||
*/
|
||||
|
||||
if ((port & 0x8000) == 0)
|
||||
result &= KeyboardDevice.KeyLine[7];
|
||||
|
||||
if ((port & 0x4000) == 0)
|
||||
result &= KeyboardDevice.KeyLine[6];
|
||||
|
||||
if ((port & 0x2000) == 0)
|
||||
result &= KeyboardDevice.KeyLine[5];
|
||||
|
||||
if ((port & 0x1000) == 0)
|
||||
result &= KeyboardDevice.KeyLine[4];
|
||||
|
||||
if ((port & 0x800) == 0)
|
||||
result &= KeyboardDevice.KeyLine[3];
|
||||
|
||||
if ((port & 0x400) == 0)
|
||||
result &= KeyboardDevice.KeyLine[2];
|
||||
|
||||
if ((port & 0x200) == 0)
|
||||
result &= KeyboardDevice.KeyLine[1];
|
||||
|
||||
if ((port & 0x100) == 0)
|
||||
result &= KeyboardDevice.KeyLine[0];
|
||||
|
||||
result = result & 0x1f; //mask out lower 4 bits
|
||||
result = result | 0xa0; //set bit 5 & 7 to 1
|
||||
|
||||
|
||||
if (TapeDevice.CurrentMode == TapeOperationMode.Load)
|
||||
{
|
||||
if (!TapeDevice.GetEarBit(CPU.TotalExecutedCycles))
|
||||
{
|
||||
result &= ~(TAPE_BIT); // reset is EAR ON
|
||||
}
|
||||
else
|
||||
{
|
||||
result |= (TAPE_BIT); // set is EAR Off
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (KeyboardDevice.IsIssue2Keyboard)
|
||||
{
|
||||
if ((LastULAOutByte & (EAR_BIT + MIC_BIT)) == 0)
|
||||
{
|
||||
result &= ~(TAPE_BIT);
|
||||
}
|
||||
else
|
||||
{
|
||||
result |= TAPE_BIT;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((LastULAOutByte & EAR_BIT) == 0)
|
||||
{
|
||||
result &= ~(TAPE_BIT);
|
||||
}
|
||||
else
|
||||
{
|
||||
result |= TAPE_BIT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// devices other than the ULA will respond here
|
||||
// (e.g. the AY sound chip in a 128k spectrum
|
||||
|
||||
// AY register activate
|
||||
// Kemptson Mouse
|
||||
|
||||
|
||||
// if unused port the floating memory bus should be returned (still todo)
|
||||
}
|
||||
|
||||
return (byte)result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a byte of data to a specified port address
|
||||
/// </summary>
|
||||
/// <param name="port"></param>
|
||||
/// <param name="value"></param>
|
||||
public override void WritePort(ushort port, byte value)
|
||||
{
|
||||
// Check whether the low bit is reset
|
||||
// Technically the ULA should respond to every even I/O address
|
||||
bool lowBitReset = (port & 0x01) == 0;
|
||||
|
||||
ContendPort(port);
|
||||
|
||||
// Only even addresses address the ULA
|
||||
if (lowBitReset)
|
||||
{
|
||||
// store the last OUT byte
|
||||
LastULAOutByte = value;
|
||||
|
||||
/*
|
||||
Bit 7 6 5 4 3 2 1 0
|
||||
+-------------------------------+
|
||||
| | | | E | M | Border |
|
||||
+-------------------------------+
|
||||
*/
|
||||
|
||||
// Border - LSB 3 bits hold the border colour
|
||||
BorderColour = value & BORDER_BIT;
|
||||
|
||||
// Buzzer
|
||||
BuzzerDevice.ProcessPulseValue(false, (value & EAR_BIT) != 0);
|
||||
|
||||
// Tape
|
||||
TapeDevice.ProcessMicBit((value & MIC_BIT) != 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,7 +7,7 @@ using System.Threading.Tasks;
|
|||
|
||||
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
||||
{
|
||||
public class ZX48 : SpectrumBase
|
||||
public partial class ZX48 : SpectrumBase
|
||||
{
|
||||
#region Construction
|
||||
|
||||
|
@ -21,17 +21,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
|||
Spectrum = spectrum;
|
||||
CPU = cpu;
|
||||
|
||||
// init addressable memory from ROM and RAM banks
|
||||
/*
|
||||
Memory.Add(0, ROM0);
|
||||
Memory.Add(1, RAM0);
|
||||
Memory.Add(2, RAM1);
|
||||
Memory.Add(3, RAM2);
|
||||
*/
|
||||
ReInitMemory();
|
||||
|
||||
//RAM = new byte[0x4000 + 0xC000];
|
||||
|
||||
|
||||
InitScreenConfig();
|
||||
InitScreen();
|
||||
|
||||
|
@ -50,148 +41,5 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
|||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region MemoryMapping
|
||||
|
||||
/* 48K Spectrum has NO memory paging
|
||||
*
|
||||
* 0xffff +--------+
|
||||
| Bank 2 |
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
0xc000 +--------+
|
||||
| Bank 1 |
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
0x8000 +--------+
|
||||
| Bank 0 |
|
||||
| |
|
||||
| |
|
||||
| screen |
|
||||
0x4000 +--------+
|
||||
| ROM 0 |
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
0x0000 +--------+
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// Simulates reading from the bus (no contention)
|
||||
/// Paging should be handled here
|
||||
/// </summary>
|
||||
/// <param name="addr"></param>
|
||||
/// <returns></returns>
|
||||
public override byte ReadBus(ushort addr)
|
||||
{
|
||||
int divisor = addr / 0x4000;
|
||||
// paging logic goes here
|
||||
|
||||
var bank = Memory[divisor];
|
||||
var index = addr % 0x4000;
|
||||
return bank[index];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pushes a value onto the data bus that should be valid as long as the interrupt is true
|
||||
/// </summary>
|
||||
/// <param name="addr"></param>
|
||||
/// <returns></returns>
|
||||
public override byte PushBus()
|
||||
{
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Simulates writing to the bus (no contention)
|
||||
/// Paging should be handled here
|
||||
/// </summary>
|
||||
/// <param name="addr"></param>
|
||||
/// <param name="value"></param>
|
||||
public override void WriteBus(ushort addr, byte value)
|
||||
{
|
||||
int divisor = addr / 0x4000;
|
||||
// paging logic goes here
|
||||
|
||||
var bank = Memory[divisor];
|
||||
var index = addr % 0x4000;
|
||||
bank[index] = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a byte of data from a specified memory address
|
||||
/// (with memory contention if appropriate)
|
||||
/// </summary>
|
||||
/// <param name="addr"></param>
|
||||
/// <returns></returns>
|
||||
public override byte ReadMemory(ushort addr)
|
||||
{
|
||||
var data = ReadBus(addr);
|
||||
if ((addr & 0xC000) == 0x4000)
|
||||
{
|
||||
// addr is in RAM not ROM - apply memory contention if neccessary
|
||||
var delay = GetContentionValue(CurrentFrameCycle);
|
||||
CPU.TotalExecutedCycles += delay;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a byte of data to a specified memory address
|
||||
/// (with memory contention if appropriate)
|
||||
/// </summary>
|
||||
/// <param name="addr"></param>
|
||||
/// <param name="value"></param>
|
||||
public override void WriteMemory(ushort addr, byte value)
|
||||
{
|
||||
if (addr < 0x4000)
|
||||
{
|
||||
// Do nothing - we cannot write to ROM
|
||||
return;
|
||||
}
|
||||
else if (addr < 0xC000)
|
||||
{
|
||||
// possible contended RAM
|
||||
var delay = GetContentionValue(CurrentFrameCycle);
|
||||
CPU.TotalExecutedCycles += delay;
|
||||
}
|
||||
|
||||
WriteBus(addr, value);
|
||||
}
|
||||
|
||||
public override void ReInitMemory()
|
||||
{
|
||||
if (Memory.ContainsKey(0))
|
||||
Memory[0] = ROM0;
|
||||
else
|
||||
Memory.Add(0, ROM0);
|
||||
|
||||
if (Memory.ContainsKey(1))
|
||||
Memory[1] = RAM0;
|
||||
else
|
||||
Memory.Add(1, RAM0);
|
||||
|
||||
if (Memory.ContainsKey(2))
|
||||
Memory[2] = RAM1;
|
||||
else
|
||||
Memory.Add(2, RAM1);
|
||||
|
||||
if (Memory.ContainsKey(3))
|
||||
Memory[3] = RAM2;
|
||||
else
|
||||
Memory.Add(3, RAM2);
|
||||
|
||||
if (Memory.ContainsKey(4))
|
||||
Memory[4] = RAM3;
|
||||
else
|
||||
Memory.Add(4, RAM3);
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -71,6 +71,14 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
|||
RD.LoadBytesResumeAddress = 0x05E2;
|
||||
RD.LoadBytesInvalidHeaderAddress = 0x05B6;
|
||||
break;
|
||||
|
||||
case MachineType.ZXSpectrum128:
|
||||
RD.SaveBytesRoutineAddress = 0x04C2;
|
||||
RD.SaveBytesResumeAddress = 0x0000;
|
||||
RD.LoadBytesRoutineAddress = 0x0808; //0x0556; //0x056C;
|
||||
RD.LoadBytesResumeAddress = 0x05E2;
|
||||
RD.LoadBytesInvalidHeaderAddress = 0x05B6;
|
||||
break;
|
||||
}
|
||||
|
||||
return RD;
|
||||
|
|
|
@ -40,6 +40,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
|||
ControllerDefinition = ZXSpectrumControllerDefinition;
|
||||
Init(MachineType.ZXSpectrum48, SyncSettings.BorderType, SyncSettings.TapeLoadSpeed, _file);
|
||||
break;
|
||||
case MachineType.ZXSpectrum128:
|
||||
ControllerDefinition = ZXSpectrumControllerDefinition;
|
||||
Init(MachineType.ZXSpectrum128, SyncSettings.BorderType, SyncSettings.TapeLoadSpeed, _file);
|
||||
break;
|
||||
default:
|
||||
throw new InvalidOperationException("Machine not yet emulated");
|
||||
}
|
||||
|
@ -110,6 +114,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
|||
var romData = RomData.InitROM(machineType, _systemRom);
|
||||
_machine.InitROM(romData);
|
||||
break;
|
||||
case MachineType.ZXSpectrum128:
|
||||
_machine = new ZX128(this, _cpu, file);
|
||||
var _systemRom128 = GetFirmware(0x8000, "128ROM");
|
||||
var romData128 = RomData.InitROM(machineType, _systemRom128);
|
||||
_machine.InitROM(romData128);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue