using Core.Audio; using Core.Interfaces; using Core.Video; namespace Core.Io { public class SmsIoBus : IIoBus { public SmsVdp VideoProcessor { get; set; } public SmsApu AudioProcessor { get; set; } // Joypad State (0xFF means no buttons pressed - the SMS uses Active-Low logic!) public byte Joypad1Keyboard = 0xFF; public byte Joypad1Gamepad = 0xFF; public byte Joypad2State = 0xFF; public byte ReadPort(ushort port) { // The Z80 can output 16-bit port addresses, but the Master System // hardware only physically wires up the bottom 8 bits. byte lowerPort = (byte)(port & 0xFF); if (lowerPort == 0x7E) { // VDP V-Counter (Vertical Scanline Position) return VideoProcessor.ReadVCounter(); } if (lowerPort >= 0x80 && lowerPort <= 0xBF) { // Even ports (like 0xBE) are Data. Odd ports (like 0xBF) are Control. if ((lowerPort & 0x01) == 0) return VideoProcessor.ReadDataPort(); else return VideoProcessor.ReadControlPort(); } if (lowerPort == 0xDC) return (byte)(Joypad1Keyboard & Joypad1Gamepad); if (lowerPort == 0xDD) return Joypad2State; return 0xFF; // Floating bus } public void WritePort(ushort port, byte value) { byte lowerPort = (byte)(port & 0xFF); if (lowerPort == 0x7E || lowerPort == 0x7F) { AudioProcessor.WritePort7F(value); } // THE FIX: Video Ports are mirrored across the entire 0x80 to 0xBF range! else if (lowerPort >= 0x80 && lowerPort <= 0xBF) { // Even ports are Data, Odd ports are Control if ((lowerPort & 0x01) == 0) VideoProcessor.WriteDataPort(value); else VideoProcessor.WriteControlPort(value); } else if (lowerPort <= 0x3F) { // Port 0x3E is used by the BIOS to enable/disable the cartridge slot // We can usually ignore this if we are just directly booting game ROMs! } } } }