From 65413f078cd9991f0f235b4fceb2d3441236fd01 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 19 Jun 2016 18:57:40 -0400 Subject: [PATCH] Factored out the 6532, eventually to make it testable. --- Components/6522/6522.hpp | 16 ++-- Components/6532/6532.hpp | 124 ++++++++++++++++++++++++++++++- Machines/Atari2600/Atari2600.cpp | 80 ++++---------------- Machines/Atari2600/Atari2600.hpp | 35 ++++++--- 4 files changed, 170 insertions(+), 85 deletions(-) diff --git a/Components/6522/6522.hpp b/Components/6522/6522.hpp index 58180edcf..509e83109 100644 --- a/Components/6522/6522.hpp +++ b/Components/6522/6522.hpp @@ -15,15 +15,15 @@ namespace MOS { /*! - Implements the template for an emulation of the MOS 6522 Versatile Interface Adaptor ('VIA'). + Implements a template for emulation of the MOS 6522 Versatile Interface Adaptor ('VIA'). The VIA provides: * two timers, each of which may trigger interrupts and one of which may repeat; * two digial input/output ports; and * a serial-to-parallel shifter. - Consumers should derive their own curiously-recurring-template-pattern subclass of MOS6522, - implementing bus communications as required for the specific machine. + Consumers should derive their own curiously-recurring-template-pattern subclass, + implementing bus communications as required. */ template class MOS6522 { private: @@ -39,7 +39,7 @@ template class MOS6522 { public: /*! Sets a register value. */ - void set_register(int address, uint8_t value) + inline void set_register(int address, uint8_t value) { address &= 0xf; // printf("6522 %p: %d <- %02x\n", this, address, value); @@ -111,7 +111,7 @@ template class MOS6522 { } /*! Gets a register value. */ - uint8_t get_register(int address) + inline uint8_t get_register(int address) { address &= 0xf; // printf("6522 %p: %d\n", this, address); @@ -152,7 +152,7 @@ template class MOS6522 { return 0xff; } - void set_control_line_input(int port, int line, bool value) + inline void set_control_line_input(int port, int line, bool value) { } @@ -166,7 +166,7 @@ template class MOS6522 { next rising edge. So it should align with a full system's phase-1. The next emulated half-cycle will be that which occurs during phase-2. */ - void run_for_half_cycles(unsigned int number_of_cycles) + inline void run_for_half_cycles(unsigned int number_of_cycles) { while(number_of_cycles--) { @@ -212,7 +212,7 @@ template class MOS6522 { } /*! @returns @c true if the IRQ line is currently active; @c false otherwise. */ - bool get_interrupt_line() + inline bool get_interrupt_line() { uint8_t interrupt_status = _registers.interrupt_flags & _registers.interrupt_enable & 0x7f; return !!interrupt_status; diff --git a/Components/6532/6532.hpp b/Components/6532/6532.hpp index 2fd8a1989..3df6178d3 100644 --- a/Components/6532/6532.hpp +++ b/Components/6532/6532.hpp @@ -9,6 +9,128 @@ #ifndef _532_hpp #define _532_hpp -#include +#include + +namespace MOS { + +/*! + Implements a template for emulation of the MOS 6532 RAM-I/O-Timer ('RIOT'). + + The RIOT provides: + * 128 bytes of static RAM; + * an interval timer; and + * two digital input/output ports. + + Consumers should derive their own curiously-recurring-template-pattern subclass, + implementing bus communications as required. +*/ +template class MOS6532 { + public: + inline void set_ram(uint16_t address, uint8_t value) { _ram[address&0x7f] = value; } + inline uint8_t get_ram(uint16_t address) { return _ram[address & 0x7f]; } + + inline void set_register(int address, uint8_t value) + { + const uint8_t decodedAddress = address & 0x0f; + switch(decodedAddress) { + case 0x00: + case 0x02: + static_cast(this)->set_port_output(decodedAddress / 2, value, _port[decodedAddress / 2].direction); + _port[decodedAddress/2].output = value; + break; + + case 0x01: + case 0x03: + _port[decodedAddress / 2].direction = value; + break; + + case 0x04: + case 0x05: + case 0x06: + case 0x07: + _timer.writtenShift = _timer.activeShift = (decodedAddress - 0x04) * 3 + (decodedAddress / 0x07); // i.e. 0, 3, 6, 10 + _timer.value = ((unsigned int)(value) << _timer.activeShift) | ((1 << _timer.activeShift)-1); + _timer.status &= ~0x40; + break; + } + } + + inline uint8_t get_register(int address) + { + const uint8_t decodedAddress = address & 0xf; + switch(decodedAddress) { + case 0x00: + case 0x02: + { + const int port = decodedAddress / 2; + uint8_t input = static_cast(this)->get_port_input(port); + return (input & ~_port[port].direction) | (_port[port].output & _port[port].direction); + } + break; + case 0x01: + case 0x03: + return _port[decodedAddress / 2].direction; + break; + case 0x04: + case 0x06: + { + uint8_t value = (uint8_t)(_timer.value >> _timer.activeShift); + + if(_timer.activeShift != _timer.writtenShift) { + unsigned int shift = _timer.writtenShift - _timer.activeShift; + _timer.value = (_timer.value << shift) | ((1 << shift) - 1); + _timer.activeShift = _timer.writtenShift; + } + + return value; + } + break; + case 0x05: + case 0x07: + { + uint8_t value = _timer.status; + _timer.status &= ~0x80; + return value; + } + break; + } + + return 0xff; + } + + inline void run_for_cycles(unsigned int number_of_cycles) + { + if(_timer.value >= number_of_cycles) { + _timer.value -= number_of_cycles; + } else { + _timer.value = 0x100 + ((_timer.value - (number_of_cycles / 3)) >> _timer.activeShift); + _timer.activeShift = 0; + _timer.status |= 0xc0; + } + } + + MOS6532() : + _timer({.status = 0}) + {} + + private: + uint8_t _ram[128]; + + struct { + unsigned int value; + unsigned int activeShift, writtenShift; + uint8_t status; + } _timer; + + struct { + uint8_t direction, output; + } _port[2]; + + // expected to be overridden + uint8_t get_port_input(int port) { return 0xff; } + void set_port_output(int port, uint8_t value, uint8_t direction_mask) {} +}; + +} #endif /* _532_hpp */ diff --git a/Machines/Atari2600/Atari2600.cpp b/Machines/Atari2600/Atari2600.cpp index 8912b56c0..26014eb98 100644 --- a/Machines/Atari2600/Atari2600.cpp +++ b/Machines/Atari2600/Atari2600.cpp @@ -19,9 +19,7 @@ Machine::Machine() : _horizontalTimer(0), _lastOutputStateDuration(0), _lastOutputState(OutputState::Sync), - _piaTimerStatus(0xff), _rom(nullptr), - _piaDataValue{0xff, 0xff}, _tiaInputValue{0xff, 0xff}, _upcomingEventsPointer(0), _objectCounterPointer(0), @@ -455,9 +453,9 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin // check for a RAM access if((address&0x1280) == 0x80) { if(isReadOperation(operation)) { - returnValue &= _ram[address&0x7f]; + returnValue &= _mos6532.get_ram(address); } else { - _ram[address&0x7f] = *value; + _mos6532.set_ram(address, *value); } } @@ -687,44 +685,9 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin // check for a PIA access if((address&0x1280) == 0x280) { if(isReadOperation(operation)) { - const uint8_t decodedAddress = address & 0xf; - switch(address & 0xf) { - case 0x00: - case 0x02: - returnValue &= _piaDataValue[decodedAddress / 2]; - break; - case 0x01: - case 0x03: - // TODO: port DDR - printf("!!!DDR!!!"); - break; - case 0x04: - case 0x06: - returnValue &= _piaTimerValue >> _piaTimerShift; - - if(_writtenPiaTimerShift != _piaTimerShift) { - _piaTimerShift = _writtenPiaTimerShift; - _piaTimerValue <<= _writtenPiaTimerShift; - } - break; - case 0x05: - case 0x07: - returnValue &= _piaTimerStatus; - _piaTimerStatus &= ~0x80; - break; - } + returnValue &= _mos6532.get_register(address); } else { - const uint8_t decodedAddress = address & 0x0f; - switch(decodedAddress) { - case 0x04: - case 0x05: - case 0x06: - case 0x07: - _writtenPiaTimerShift = _piaTimerShift = (decodedAddress - 0x04) * 3 + (decodedAddress / 0x07); // i.e. 0, 3, 6, 10 - _piaTimerValue = ((unsigned int)(*value) << _piaTimerShift) | ((1 << _piaTimerShift)-1); - _piaTimerStatus &= ~0x40; - break; - } + _mos6532.set_register(address, *value); } } @@ -733,24 +696,7 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin } } - if(_piaTimerValue >= cycles_run_for / 3) { - _piaTimerValue -= cycles_run_for / 3; - } else { - _piaTimerValue = 0x100 + ((_piaTimerValue - (cycles_run_for / 3)) >> _piaTimerShift); - _piaTimerShift = 0; - _piaTimerStatus |= 0xc0; - } - -// static unsigned int total_cycles = 0; -// total_cycles += cycles_run_for / 3; -// static time_t logged_time = 0; -// time_t time_now = time(nullptr); -// if(time_now - logged_time > 0) -// { -// printf("[c] %ld : %d\n", time_now - logged_time, total_cycles); -// total_cycles = 0; -// logged_time = time_now; -// } + _mos6532.run_for_cycles(cycles_run_for / 3); return cycles_run_for / 3; } @@ -758,15 +704,15 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin void Machine::set_digital_input(Atari2600DigitalInput input, bool state) { switch (input) { - case Atari2600DigitalInputJoy1Up: if(state) _piaDataValue[0] &= ~0x10; else _piaDataValue[0] |= 0x10; break; - case Atari2600DigitalInputJoy1Down: if(state) _piaDataValue[0] &= ~0x20; else _piaDataValue[0] |= 0x20; break; - case Atari2600DigitalInputJoy1Left: if(state) _piaDataValue[0] &= ~0x40; else _piaDataValue[0] |= 0x40; break; - case Atari2600DigitalInputJoy1Right: if(state) _piaDataValue[0] &= ~0x80; else _piaDataValue[0] |= 0x80; break; + case Atari2600DigitalInputJoy1Up: _mos6532.update_port_input(0, 0x10, state); break; + case Atari2600DigitalInputJoy1Down: _mos6532.update_port_input(0, 0x20, state); break; + case Atari2600DigitalInputJoy1Left: _mos6532.update_port_input(0, 0x40, state); break; + case Atari2600DigitalInputJoy1Right: _mos6532.update_port_input(0, 0x80, state); break; - case Atari2600DigitalInputJoy2Up: if(state) _piaDataValue[0] &= ~0x01; else _piaDataValue[0] |= 0x01; break; - case Atari2600DigitalInputJoy2Down: if(state) _piaDataValue[0] &= ~0x02; else _piaDataValue[0] |= 0x02; break; - case Atari2600DigitalInputJoy2Left: if(state) _piaDataValue[0] &= ~0x04; else _piaDataValue[0] |= 0x04; break; - case Atari2600DigitalInputJoy2Right: if(state) _piaDataValue[0] &= ~0x08; else _piaDataValue[0] |= 0x08; break; + case Atari2600DigitalInputJoy2Up: _mos6532.update_port_input(0, 0x01, state); break; + case Atari2600DigitalInputJoy2Down: _mos6532.update_port_input(0, 0x02, state); break; + case Atari2600DigitalInputJoy2Left: _mos6532.update_port_input(0, 0x04, state); break; + case Atari2600DigitalInputJoy2Right: _mos6532.update_port_input(0, 0x08, state); break; // TODO: latching case Atari2600DigitalInputJoy1Fire: if(state) _tiaInputValue[0] &= ~0x80; else _tiaInputValue[0] |= 0x80; break; diff --git a/Machines/Atari2600/Atari2600.hpp b/Machines/Atari2600/Atari2600.hpp index 109565257..a03157e63 100644 --- a/Machines/Atari2600/Atari2600.hpp +++ b/Machines/Atari2600/Atari2600.hpp @@ -9,9 +9,12 @@ #ifndef Atari2600_cpp #define Atari2600_cpp -#include "../../Processors/6502/CPU6502.hpp" -#include "../CRTMachine.hpp" #include + +#include "../../Processors/6502/CPU6502.hpp" +#include "../../Components/6532/6532.hpp" +#include "../CRTMachine.hpp" + #include "Atari2600Inputs.h" namespace Atari2600 { @@ -47,6 +50,24 @@ class Speaker: public ::Outputs::Filter { int _patterns[16][512]; }; +class PIA: public MOS::MOS6532 { + public: + inline uint8_t get_port_input(int port) + { + return _portValues[port]; + } + + inline void update_port_input(int port, uint8_t mask, bool set) + { + if(set) _portValues[port] |= mask; else _portValues[port] &= ~mask; + } + + private: + uint8_t _portValues[2]; + +}; + + class Machine: public CPU6502::Processor, public CRTMachine::Machine { public: @@ -72,13 +93,11 @@ class Machine: public CPU6502::Processor, public CRTMachine::Machine { // TODO: different rate for PAL private: - uint8_t *_rom, *_romPages[4], _ram[128]; + uint8_t *_rom, *_romPages[4]; size_t _rom_size; - // the timer - unsigned int _piaTimerValue; - unsigned int _piaTimerShift, _writtenPiaTimerShift; - uint8_t _piaTimerStatus; + // the RIOT + PIA _mos6532; // playfield registers uint8_t _playfieldControl; @@ -162,8 +181,6 @@ class Machine: public CPU6502::Processor, public CRTMachine::Machine { uint8_t _hMoveFlags; // joystick state - uint8_t _piaDataDirection[2]; - uint8_t _piaDataValue[2]; uint8_t _tiaInputValue[2]; // collisions