156 lines
5.1 KiB
C#
156 lines
5.1 KiB
C#
using Core.Interfaces;
|
|
using System;
|
|
using System.Diagnostics;
|
|
|
|
namespace Core.Memory
|
|
{
|
|
public class MemoryBus : IMemory
|
|
{
|
|
// 8 Banks of 16KB RAM (Banks 0 through 7)
|
|
private readonly byte[][] _ramBanks = new byte[8][];
|
|
|
|
// 5 Banks of 16KB ROM (0-3 for the +2A, and 4 for the pure 48K)
|
|
private readonly byte[][] _romBanks = new byte[5][];
|
|
|
|
// --- Hardware State ---
|
|
private int _currentRomBank = 4; // Defaults to 48K ROM
|
|
private int _currentRamBankSlot3 = 0;
|
|
private byte _port7FFD = 0;
|
|
private byte _port1FFD = 0;
|
|
private bool _pagingDisabled = false;
|
|
|
|
public MemoryBus()
|
|
{
|
|
// Initialize the physical silicon chips!
|
|
for (int i = 0; i < 8; i++) _ramBanks[i] = new byte[0x4000];
|
|
for (int i = 0; i < 5; i++) _romBanks[i] = new byte[0x4000];
|
|
}
|
|
|
|
// Called when the machine is turned on or reset
|
|
public void ResetPaging(MachineModel model)
|
|
{
|
|
_pagingDisabled = false;
|
|
_port7FFD = 0;
|
|
_port1FFD = 0;
|
|
_currentRamBankSlot3 = 0;
|
|
|
|
if (model == MachineModel.Spectrum48K)
|
|
{
|
|
_currentRomBank = 4; // Lock to the pure 48.rom
|
|
}
|
|
else
|
|
{
|
|
_currentRomBank = 0; // Boot into the +3 DOS menu!
|
|
}
|
|
}
|
|
|
|
// Called by the IO Bus when the CPU writes to a paging port
|
|
public void HandlePaging(ushort port, byte value)
|
|
{
|
|
//Debug.WriteLine($"[PAGING] 0x7FFD Triggered! Hex: 0x{value:X2} | Bank: {value & 0x07}");
|
|
if (_pagingDisabled || _currentRomBank == 4) return; // Ignore if locked or in 48K mode
|
|
|
|
if (port == 0x7FFD)
|
|
{
|
|
_port7FFD = value;
|
|
_currentRamBankSlot3 = value & 0x07; // Bottom 3 bits select the RAM bank
|
|
if ((value & 0x20) != 0) _pagingDisabled = true; // Bit 5 permanently disables paging
|
|
}
|
|
else if (port == 0x1FFD)
|
|
{
|
|
_port1FFD = value;
|
|
}
|
|
|
|
// Calculate which of the 4 Amstrad ROMs is active based on the ports
|
|
int romLow = (_port7FFD >> 4) & 0x01;
|
|
int romHigh = (_port1FFD >> 2) & 0x01;
|
|
_currentRomBank = (romHigh << 1) | romLow;
|
|
}
|
|
|
|
private int GetBankForSlot(int slot)
|
|
{
|
|
// Amstrad +2A / +3 Special Paging Mode
|
|
bool specialPaging = (_port1FFD & 0x01) != 0;
|
|
|
|
if (specialPaging)
|
|
{
|
|
// Bits 1 and 2 dictate the specific Special RAM layout
|
|
int config = (_port1FFD >> 1) & 0x03;
|
|
switch (config)
|
|
{
|
|
case 0: return slot == 0 ? 0 : slot == 1 ? 1 : slot == 2 ? 2 : 3;
|
|
case 1: return slot == 0 ? 4 : slot == 1 ? 5 : slot == 2 ? 6 : 7;
|
|
case 2: return slot == 0 ? 4 : slot == 1 ? 5 : slot == 2 ? 6 : 3;
|
|
case 3: return slot == 0 ? 4 : slot == 1 ? 7 : slot == 2 ? 6 : 3;
|
|
}
|
|
}
|
|
|
|
// Standard 128K Paging
|
|
switch (slot)
|
|
{
|
|
case 1: return 5;
|
|
case 2: return 2;
|
|
case 3: return _currentRamBankSlot3;
|
|
default: return -1; // -1 means it is pointing to a ROM chip!
|
|
}
|
|
}
|
|
public byte Read(ushort address)
|
|
{
|
|
int slot = address >> 14; // Find the 16KB Slot (0, 1, 2, or 3)
|
|
int offset = address & 0x3FFF;
|
|
|
|
int bank = GetBankForSlot(slot);
|
|
|
|
if (bank == -1)
|
|
{
|
|
return _romBanks[_currentRomBank][offset];
|
|
}
|
|
else
|
|
{
|
|
return _ramBanks[bank][offset];
|
|
}
|
|
}
|
|
|
|
public void Write(ushort address, byte value)
|
|
{
|
|
int slot = address >> 14;
|
|
int offset = address & 0x3FFF;
|
|
|
|
int bank = GetBankForSlot(slot);
|
|
|
|
// If the bank is NOT a ROM chip, we are allowed to write to it!
|
|
// (This allows the BIOS to write to 0x0000 during the self-test)
|
|
if (bank != -1)
|
|
{
|
|
_ramBanks[bank][offset] = value;
|
|
}
|
|
}
|
|
|
|
public byte[] GetVideoRam()
|
|
{
|
|
// Bit 3 of 7FFD dictates the active screen. 0 = Bank 5 (Standard), 1 = Bank 7 (Shadow).
|
|
return (_port7FFD & 0x08) != 0 ? _ramBanks[7] : _ramBanks[5];
|
|
}
|
|
public void LoadRom(byte[] romData, int bankIndex)
|
|
{
|
|
Array.Copy(romData, 0, _romBanks[bankIndex], 0, Math.Min(romData.Length, 0x4000));
|
|
}
|
|
|
|
public void CleanRAMData()
|
|
{
|
|
for (int bank = 0; bank < 8; bank++)
|
|
{
|
|
for (int i = 0; i < 0x4000; i++) _ramBanks[bank][i] = 0x00;
|
|
}
|
|
}
|
|
|
|
public void CrapRAMData()
|
|
{
|
|
Random random = new Random();
|
|
for (int bank = 0; bank < 8; bank++)
|
|
{
|
|
for (int i = 0; i < 0x4000; i++) _ramBanks[bank][i] = (byte)random.Next(0x00, 0xFF);
|
|
}
|
|
}
|
|
}
|
|
} |