2019-02-20 22:23:58 +00:00
|
|
|
|
// <copyright file="Board.cs" company="Adrian Conlon">
|
|
|
|
|
// Copyright (c) Adrian Conlon. All rights reserved.
|
|
|
|
|
// </copyright>
|
2019-02-19 00:58:17 +00:00
|
|
|
|
|
|
|
|
|
namespace Z80.Test
|
|
|
|
|
{
|
2019-02-20 22:23:58 +00:00
|
|
|
|
using EightBit;
|
|
|
|
|
|
|
|
|
|
internal class Board : Bus
|
2019-02-19 00:58:17 +00:00
|
|
|
|
{
|
2019-02-20 22:23:58 +00:00
|
|
|
|
private readonly Configuration configuration;
|
|
|
|
|
private readonly Ram ram;
|
|
|
|
|
private readonly InputOutput ports;
|
|
|
|
|
private readonly Disassembler disassembler;
|
|
|
|
|
private readonly MemoryMapping mapping;
|
|
|
|
|
|
|
|
|
|
private int warmstartCount = 0;
|
|
|
|
|
|
|
|
|
|
public Board(Configuration configuration)
|
|
|
|
|
{
|
|
|
|
|
this.configuration = configuration;
|
|
|
|
|
this.ram = new Ram(0x10000);
|
|
|
|
|
this.ports = new InputOutput();
|
|
|
|
|
this.CPU = new Z80(this, this.ports);
|
|
|
|
|
this.disassembler = new Disassembler(this);
|
|
|
|
|
this.mapping = new MemoryMapping(this.ram, 0x0000, (ushort)Mask.Mask16, AccessLevel.ReadWrite);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Z80 CPU { get; }
|
|
|
|
|
|
|
|
|
|
public override void RaisePOWER()
|
|
|
|
|
{
|
|
|
|
|
base.RaisePOWER();
|
|
|
|
|
this.CPU.RaisePOWER();
|
|
|
|
|
this.CPU.RaiseRESET();
|
|
|
|
|
this.CPU.RaiseINT();
|
|
|
|
|
this.CPU.RaiseNMI();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override void LowerPOWER()
|
|
|
|
|
{
|
|
|
|
|
this.CPU.LowerPOWER();
|
|
|
|
|
base.LowerPOWER();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override void Initialize()
|
|
|
|
|
{
|
|
|
|
|
var programPath = this.configuration.RomDirectory + "/" + this.configuration.Program;
|
|
|
|
|
var loadAddress = this.configuration.LoadAddress;
|
|
|
|
|
this.ram.Load(programPath, loadAddress.Word);
|
|
|
|
|
|
|
|
|
|
this.CPU.LoweredHALT += this.CPU_LoweredHALT;
|
|
|
|
|
this.CPU.ExecutingInstruction += this.CPU_ExecutingInstruction_CPM;
|
|
|
|
|
|
|
|
|
|
if (this.configuration.DebugMode)
|
|
|
|
|
{
|
|
|
|
|
this.CPU.ExecutingInstruction += this.CPU_ExecutingInstruction_Debug;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.Poke(0, 0xc3); // JMP
|
|
|
|
|
this.CPU.PokeWord(1, this.configuration.StartAddress);
|
|
|
|
|
this.Poke(5, 0xc9); // ret
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override MemoryMapping Mapping(ushort absolute) => this.mapping;
|
|
|
|
|
|
|
|
|
|
private void BDOS()
|
|
|
|
|
{
|
2019-02-22 22:33:51 +00:00
|
|
|
|
switch (this.CPU.C)
|
2019-02-20 22:23:58 +00:00
|
|
|
|
{
|
|
|
|
|
case 0x2:
|
2019-02-22 22:33:51 +00:00
|
|
|
|
System.Console.Out.Write(this.CPU.E.ToString());
|
2019-02-20 22:23:58 +00:00
|
|
|
|
break;
|
|
|
|
|
case 0x9:
|
2019-02-22 22:33:51 +00:00
|
|
|
|
for (var i = this.CPU.DE.Word; this.Peek(i) != '$'; ++i)
|
2019-02-20 22:23:58 +00:00
|
|
|
|
{
|
2019-02-22 22:33:51 +00:00
|
|
|
|
System.Console.Out.Write((char)this.Peek(i));
|
2019-02-20 22:23:58 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void CPU_ExecutingInstruction_CPM(object sender, System.EventArgs e)
|
|
|
|
|
{
|
2019-02-21 19:58:49 +00:00
|
|
|
|
switch (this.CPU.PC.Word)
|
2019-02-20 22:23:58 +00:00
|
|
|
|
{
|
|
|
|
|
case 0x0: // CP/M warm start
|
|
|
|
|
if (++this.warmstartCount == 2)
|
|
|
|
|
{
|
|
|
|
|
this.LowerPOWER();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
case 0x5: // BDOS
|
2019-02-22 22:33:51 +00:00
|
|
|
|
this.BDOS();
|
2019-02-20 22:23:58 +00:00
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-22 22:33:51 +00:00
|
|
|
|
private void CPU_LoweredHALT(object sender, System.EventArgs e) => this.LowerPOWER();
|
2019-02-20 22:23:58 +00:00
|
|
|
|
|
2019-02-22 22:33:51 +00:00
|
|
|
|
private void CPU_ExecutingInstruction_Debug(object sender, System.EventArgs e) => System.Console.Error.WriteLine($"{EightBit.Disassembler.State(this.CPU)}\t{this.disassembler.Disassemble(this.CPU)}");
|
2019-02-19 00:58:17 +00:00
|
|
|
|
}
|
|
|
|
|
}
|