1
0
mirror of https://github.com/TomHarte/CLK.git synced 2025-01-27 06:35:04 +00:00

Made an attempt at a full and thorough 6532 implementation (and got a bit more explicit about flag size in the 6502).

This commit is contained in:
Thomas Harte 2016-06-20 18:57:35 -04:00
parent d4b9ff0ca4
commit 88e2b382e5
3 changed files with 91 additions and 34 deletions

View File

@ -10,6 +10,7 @@
#define _532_hpp
#include <cstdint>
#include <cstdio>
namespace MOS {
@ -33,24 +34,31 @@ template <class T> class MOS6532 {
{
const uint8_t decodedAddress = address & 0x07;
switch(decodedAddress) {
case 0x00:
case 0x02:
static_cast<T *>(this)->set_port_output(decodedAddress / 2, value, _port[decodedAddress / 2].direction);
_port[decodedAddress/2].output = value;
// Port output
case 0x00: case 0x02:
_port[decodedAddress / 2].output = value;
static_cast<T *>(this)->set_port_output(decodedAddress / 2, _port[decodedAddress/2].output, _port[decodedAddress / 2].output_mask);
break;
case 0x01: case 0x03:
_port[decodedAddress / 2].output_mask = value;
static_cast<T *>(this)->set_port_output(decodedAddress / 2, _port[decodedAddress/2].output, _port[decodedAddress / 2].output_mask);
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 &= ~0x80;
// The timer and edge detect control
case 0x04: case 0x05: case 0x06: case 0x07:
if(address & 0x10)
{
_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.interrupt_enabled = !!(address&0x08);
_interrupt_status &= ~InterruptFlag::Timer;
evaluate_interrupts();
}
else
{
_a7_interrupt.enabled = !!(address&0x2);
_a7_interrupt.active_on_positive = !!(address & 0x01);
}
break;
}
}
@ -59,22 +67,25 @@ template <class T> class MOS6532 {
{
const uint8_t decodedAddress = address & 0x7;
switch(decodedAddress) {
case 0x00:
case 0x02:
// Port input
case 0x00: case 0x02:
{
const int port = decodedAddress / 2;
uint8_t input = static_cast<T *>(this)->get_port_input(port);
return (input & ~_port[port].direction) | (_port[port].output & _port[port].direction);
return (input & ~_port[port].output_mask) | (_port[port].output & _port[port].output_mask);
}
break;
case 0x01:
case 0x03:
return _port[decodedAddress / 2].direction;
case 0x01: case 0x03:
return _port[decodedAddress / 2].output_mask;
break;
case 0x04:
case 0x06:
// Timer and interrupt control
case 0x04: case 0x06:
{
uint8_t value = (uint8_t)(_timer.value >> _timer.activeShift);
_timer.interrupt_enabled = !!(address&0x08);
_interrupt_status &= ~InterruptFlag::Timer;
evaluate_interrupts();
if(_timer.activeShift != _timer.writtenShift) {
unsigned int shift = _timer.writtenShift - _timer.activeShift;
@ -85,11 +96,12 @@ template <class T> class MOS6532 {
return value;
}
break;
case 0x05:
case 0x07:
case 0x05: case 0x07:
{
uint8_t value = _timer.status;
_timer.status &= ~0x40;
uint8_t value = _interrupt_status;
_interrupt_status &= ~InterruptFlag::PA7;
evaluate_interrupts();
return value;
}
break;
@ -100,36 +112,80 @@ template <class T> class MOS6532 {
inline void run_for_cycles(unsigned int number_of_cycles)
{
// permit counting _to_ zero; counting _through_ zero initiates the other behaviour
if(_timer.value >= number_of_cycles) {
_timer.value -= number_of_cycles;
} else {
number_of_cycles -= _timer.value;
_timer.value = 0x100 - number_of_cycles;
_timer.activeShift = 0;
_timer.status |= 0xc0;
_interrupt_status |= InterruptFlag::Timer;
evaluate_interrupts();
}
}
MOS6532() :
_timer({.status = 0})
_interrupt_status(0), _port{{.output_mask = 0, .output = 0}, {.output_mask = 0, .output = 0}}, _a7_interrupt({.last_port_value = 0, .enabled = false})
{}
inline void set_port_did_change(int port)
{
if(!port)
{
uint8_t new_port_a_value = (get_port_input(0) & ~_port[0].output_mask) | (_port[0].output & _port[0].output_mask);
uint8_t difference = new_port_a_value ^ _a7_interrupt.last_port_value;
_a7_interrupt.last_port_value = new_port_a_value;
if(difference&0x80)
{
if(
((new_port_a_value&0x80) && _a7_interrupt.active_on_positive) ||
(!(new_port_a_value&0x80) && !_a7_interrupt.active_on_positive)
)
{
_interrupt_status |= InterruptFlag::PA7;
evaluate_interrupts();
}
}
}
}
private:
uint8_t _ram[128];
struct {
unsigned int value;
unsigned int activeShift, writtenShift;
uint8_t status;
bool interrupt_enabled;
} _timer;
struct {
uint8_t direction, output;
bool enabled;
bool active_on_positive;
uint8_t last_port_value;
} _a7_interrupt;
struct {
uint8_t output_mask, output;
} _port[2];
uint8_t _interrupt_status;
enum InterruptFlag: uint8_t {
Timer = 0x80,
PA7 = 0x40
};
// 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) {}
void set_port_output(int port, uint8_t value, uint8_t output_mask) {}
void set_irq_line(bool new_value) {}
inline void evaluate_interrupts()
{
set_irq_line(
((_interrupt_status&InterruptFlag::Timer) && _timer.interrupt_enabled) ||
((_interrupt_status&InterruptFlag::PA7) && _a7_interrupt.enabled)
);
}
};
}

View File

@ -60,6 +60,7 @@ class PIA: public MOS::MOS6532<PIA> {
inline void update_port_input(int port, uint8_t mask, bool set)
{
if(set) _portValues[port] &= ~mask; else _portValues[port] |= mask;
set_port_did_change(port);
}
PIA() :

View File

@ -31,7 +31,7 @@ enum Register {
/*
Flags as defined on the 6502; can be used to decode the result of @c get_flags or to form a value for @c set_flags.
*/
enum Flag {
enum Flag: uint8_t {
Sign = 0x80,
Overflow = 0x40,
Always = 0x20,