using Core.Audio; using Core.Interfaces; using Core.Memory; using System.Diagnostics; using static System.Runtime.InteropServices.JavaScript.JSType; namespace Core.Io { public class IO_Bus { public byte BorderColourIndex { get; set; } = 7; public Ay38912 AyChip { get; private set; } = new Ay38912(); public byte KempstonState { get; set; } = 0x00; public bool BeeperState { get; private set; } = false; public byte[] KeyboardRows = new byte[8] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; TapManager _tapManager = new TapManager(); MemoryBus _memory; public IO_Bus(TapManager tapManager, MemoryBus memoryBus) { _tapManager = tapManager; _memory = memoryBus; } public byte ReadPort(ushort portAddress) { //ULA responds to any even port address (where the lowest bit is 0) if ((portAddress & 0x01) == 0) //Port 0xFE) { byte highByte = (byte)(portAddress >> 8); // The B register byte result = 0xFF; // Start assuming no keys are pressed if ((highByte & 0x01) == 0) result &= KeyboardRows[0]; // 0xFE: CAPS, Z, X, C, V if ((highByte & 0x02) == 0) result &= KeyboardRows[1]; // 0xFD: A, S, D, F, G if ((highByte & 0x04) == 0) result &= KeyboardRows[2]; // 0xFB: Q, W, E, R, T if ((highByte & 0x08) == 0) result &= KeyboardRows[3]; // 0xF7: 1, 2, 3, 4, 5 if ((highByte & 0x10) == 0) result &= KeyboardRows[4]; // 0xEF: 0, 9, 8, 7, 6 if ((highByte & 0x20) == 0) result &= KeyboardRows[5]; // 0xDF: P, O, I, U, Y if ((highByte & 0x40) == 0) result &= KeyboardRows[6]; // 0xBF: ENTER, L, K, J, H if ((highByte & 0x80) == 0) result &= KeyboardRows[7]; // 0x7F: SPACE, SYM, M, N, B if (_tapManager.EarBit) result |= 0x40; // Set Bit 6 high else result &= 0xBF; // Set Bit 6 low //The top 3 bits (5, 6, 7) are unused by the keyboard and usually return 1 on a real Spectrum return (byte)(result | 0xA0); } //Kempston Joystick Port if ((portAddress & 0xFF) == 0x1F) { return KempstonState; } // AY-3-8912 Data Read (Port 0xFFFD) if ((portAddress & 0xC002) == 0xC000) { return AyChip.ReadRegister(); } return 0xFF; // Default floating bus } public void WritePort(ushort portAddress, byte portValue) { // The ULA intercepts any write to an even port address if ((portAddress & 0x01) == 0) { // The bottom 3 bits (0-2) define the border color BorderColourIndex = (byte)(portValue & 0x07); // Bit 4 controls the speaker BeeperState = (portValue & 0x10) != 0; // Bit 3 handles the cassette MIC output } // 128K Standard Paging Port (0x7FFD) // Mask 0xC002 checks A15=0, A14=1, A1=0 to prevent 0x1FFD from triggering this! if ((portAddress & 0xC002) == 0x4000) { _memory.HandlePaging(0x7FFD, portValue); } // +2A/+3 Extended Paging Port (0x1FFD) // Mask 0xF002 checks A15=0, A14=0, A13=0, A12=1, A1=0 else if ((portAddress & 0xF002) == 0x1000) { _memory.HandlePaging(0x1FFD, portValue); } //// 128K Standard Paging Port (0x7FFD) //if ((portAddress & 0x8002) == 0) //{ // _memory.HandlePaging(0x7FFD, portValue); //} //// +2A/+3 Extended Paging Port (0x1FFD) //if ((portAddress & 0xF002) == 0x1000) //{ // _memory.HandlePaging(0x1FFD, portValue); //} // AY-3-8912 Register Select (Port 0xFFFD) if ((portAddress & 0xC002) == 0xC000) { AyChip.SelectRegister(portValue); } // AY-3-8912 Data Write (Port 0xBFFD) else if ((portAddress & 0xC002) == 0x8000) { AyChip.WriteRegister(portValue); } } } }