diff options
Diffstat (limited to 'Q1.core/Components')
| -rw-r--r-- | Q1.core/Components/Bus.cs | 100 | ||||
| -rw-r--r-- | Q1.core/Components/IAddressable.cs | 29 | ||||
| -rw-r--r-- | Q1.core/Components/Memory.cs | 21 |
3 files changed, 150 insertions, 0 deletions
diff --git a/Q1.core/Components/Bus.cs b/Q1.core/Components/Bus.cs new file mode 100644 index 0000000..02b20a7 --- /dev/null +++ b/Q1.core/Components/Bus.cs @@ -0,0 +1,100 @@ +namespace Q1.core.Components; + +using Q1.core.Arch; + +public class Bus : IAddressable +{ + private Chip? _chip; + private readonly List<IAddressable> _devices; + + public u16 AddressableStart { get; } + public u16 AddressableEnd { get; } + + public Bus(u16 addressableStart, u16 addressableEnd) + { + this.AddressableStart = addressableStart; + this.AddressableEnd = addressableEnd; + this._devices = new(); + } + + public void SendInterrupt(u16 code) + { + if (this._chip == null) + throw new InvalidOperationException("Chip is not attached to bus"); + + this._chip.Interrupt(code); + } + + public void AttachChip(Chip chip) + { + if (this._chip != null) + throw new InvalidOperationException("Bus already has a chip attached."); + + this._chip = chip; + chip.Bus = this; + } + + public void MountDevice(IAddressable device) + { + if (device.AddressableStart >= device.AddressableEnd) + throw new ArgumentException("Addressable range is invalid.", nameof(device)); + + int index = 0; + while (index < this._devices.Count && this._devices[index].AddressableStart < device.AddressableStart) + index++; + + if (index > 0) + { + IAddressable prev = this._devices[index - 1]; + if (device.AddressableStart < prev.AddressableEnd) + throw new InvalidOperationException("Address range overlaps with previous device."); + } + + if (index < this._devices.Count) + { + IAddressable next = this._devices[index]; + if (device.AddressableEnd > next.AddressableStart) + throw new InvalidOperationException("Address range overlaps with next device."); + } + + this._devices.Insert(index, device); + } + + private IAddressable? GetDevice(u16 address) + { + foreach (IAddressable device in this._devices) + { + if (address < device.AddressableStart) + break; + + if (address < device.AddressableEnd) + return device; + } + + return null; + } + + public u8 Read(u16 address) + { + IAddressable? device = this.GetDevice(address); + if (device == null) + return 0; + + return device.Read((u16) (address - device.AddressableStart)); + } + + public void Write(u16 address, u8 value) + { + IAddressable? device = this.GetDevice(address); + if (device == null) + return; + + device.Write((u16) (address - device.AddressableStart), value); + } + + public void Clock() + { + foreach (IAddressable addressable in this._devices) + addressable.Clock(); + } +}
\ No newline at end of file diff --git a/Q1.core/Components/IAddressable.cs b/Q1.core/Components/IAddressable.cs new file mode 100644 index 0000000..54218d9 --- /dev/null +++ b/Q1.core/Components/IAddressable.cs @@ -0,0 +1,29 @@ +namespace Q1.core.Components; + +public interface IAddressable +{ + public u8 Read(u16 address); + public void Write(u16 address, u8 value); + public void Clock(); + + public u16 AddressableStart { get; } + public u16 AddressableEnd { get; } +} + +public static class AddressableExtensions +{ + public static u16 ReadWord(this IAddressable bus, u16 address) + { + u8 high = bus.Read(address); + u8 low = bus.Read((u16) (address + 1)); + return (u16) ((high << 8) | low); + } + + public static void WriteWord(this IAddressable bus, u16 address, u16 value) + { + u8 high = (u8) (value & 0xFF); + u8 low = (u8) ((value >> 8) & 0xFF); + bus.Write(address, low); + bus.Write((u16) (address + 1), high); + } +}
\ No newline at end of file diff --git a/Q1.core/Components/Memory.cs b/Q1.core/Components/Memory.cs new file mode 100644 index 0000000..9d23c7a --- /dev/null +++ b/Q1.core/Components/Memory.cs @@ -0,0 +1,21 @@ +namespace Q1.core.Components; + +public class Memory : IAddressable +{ + private readonly u8[] _memory; + + public u16 AddressableStart { get; } + public u16 AddressableEnd { get; } + + public Memory(u16 start, u16 size) + { + this.AddressableStart = start; + this.AddressableEnd = (u16) (start + size); + + this._memory = new u8[size]; + } + + public u8 Read(u16 address) => this._memory[address]; + public void Write(u16 address, u8 value) => this._memory[address] = value; + public void Clock() { /* No timing behavior for simple memory */ } +}
\ No newline at end of file |
