diff --git a/Machines/Electron/Electron.cpp b/Machines/Electron/Electron.cpp index 9a628bf26..bd043717d 100644 --- a/Machines/Electron/Electron.cpp +++ b/Machines/Electron/Electron.cpp @@ -12,9 +12,17 @@ using namespace Electron; -Machine::Machine() +static const int cycles_per_line = 128; +static const int cycles_per_frame = 312*cycles_per_line; +static const int crt_cycles_multiplier = 8; +static const int crt_cycles_per_line = crt_cycles_multiplier * cycles_per_line; + +Machine::Machine() : + _interruptControl(0), + _frameCycles(0), + _outputPosition(0) { - _crt = new Outputs::CRT(128, 312, 1, 1); + _crt = new Outputs::CRT(crt_cycles_per_line, 312, 1, 1); _interruptStatus = 0x02; setup6502(); } @@ -25,6 +33,8 @@ Machine::~Machine() unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value) { + unsigned int cycles = 1; + if(address < 32768) { if(isReadOperation(operation)) @@ -34,9 +44,15 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin else { _ram[address] = *value; + +// TODO: range check on address; a lot of the time the machine will be running code outside of +// the screen area, meaning that no update is required. +// if (address + update_display(); } - // TODO: RAM timing + // TODO: RAM timing for Modes 0–3 + cycles += (_frameCycles&1)^1; } else { @@ -44,7 +60,7 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin { if((address & 0xff00) == 0xfe00) { - printf("%c: %02x: ", isReadOperation(operation) ? 'r' : 'w', *value); +// printf("%c: %02x: ", isReadOperation(operation) ? 'r' : 'w', *value); switch(address&0xf) { @@ -57,18 +73,16 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin else { _interruptControl = *value; + evaluate_interrupts(); } - printf("Interrupt status or control\n"); break; case 0x1: break; case 0x2: - _screenStartAddress = (_screenStartAddress & 0xff00) | ((*value) & 0xe0); - printf("Screen start address low, now %04x\n", _screenStartAddress); + _startScreenAddress = (_startScreenAddress & 0xff00) | ((*value) & 0xe0); break; case 0x3: - _screenStartAddress = (_screenStartAddress & 0x00ff) | (uint16_t)(((*value) & 0x3f) << 8); - printf("Screen start address high, now %04x\n", _screenStartAddress); + _startScreenAddress = (_startScreenAddress & 0x00ff) | (uint16_t)(((*value) & 0x3f) << 8); break; case 0x4: printf("Cassette\n"); @@ -81,7 +95,13 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin { _activeRom = (Electron::ROMSlot)nextROM; } - printf("Interrupt clear and paging\n"); + + if( (*value)&0x10 ) _interruptStatus &= ~InterruptDisplayEnd; + if( (*value)&0x20 ) _interruptStatus &= InterruptRealTimeClock; + if( (*value)&0x40 ) _interruptStatus &= InterruptHighToneDetect; + evaluate_interrupts(); + + // TODO: NMI (?) } break; case 0x6: @@ -91,7 +111,8 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin printf("Misc. control\n"); break; default: - printf("Palette\n"); + update_display(); +// printf("Palette\n"); break; } } @@ -121,7 +142,16 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin } } - return 1; + _frameCycles += cycles; + if(_frameCycles == cycles_per_frame) + { + update_display(); + _frameCycles = 0; + } + if(_frameCycles == 128*128) signal_interrupt(InterruptRealTimeClock); + if(_frameCycles == 284*128) signal_interrupt(InterruptDisplayEnd); + + return cycles; } void Machine::set_rom(ROMSlot slot, size_t length, const uint8_t *data) @@ -136,3 +166,81 @@ void Machine::set_rom(ROMSlot slot, size_t length, const uint8_t *data) memcpy(target, data, std::min((size_t)16384, length)); } + +inline void Machine::signal_interrupt(Electron::Interrupt interrupt) +{ + _interruptStatus |= (interrupt << 2); + evaluate_interrupts(); +} + +inline void Machine::evaluate_interrupts() +{ + if(_interruptStatus & _interruptControl) + { + _interruptStatus |= 1; + } + set_irq_line(_interruptStatus & 1); +} + +inline void Machine::update_display() +{ + const int end_of_hsync = 3 * cycles_per_line; + + if(_frameCycles >= end_of_hsync) + { + // assert sync for the first three lines of the display + if(_outputPosition < end_of_hsync) + { + _crt->output_sync(end_of_hsync * crt_cycles_multiplier); + _outputPosition = end_of_hsync; + } + + while(_outputPosition < _frameCycles) + { + const int current_line = _outputPosition >> 7; + const int line_position = _outputPosition & 127; + + // all lines then start with 9 cycles of sync + if(!line_position) + { + _crt->output_sync(9 * crt_cycles_multiplier); + _outputPosition += 9; + } + else + { + // on lines prior to 28 or after or equal to 284, or on a line that is equal to 8 or 9 modulo 10 in a line-spaced mode, + // the line is then definitely blank. + if(current_line < 28 || current_line >= 284) + { + if(line_position == 9) + { + _crt->output_blank(119 * crt_cycles_multiplier); + _outputPosition += 119; + } + } + else + { + // there are then 15 cycles of blank, 80 cycles of pixels, and 24 further cycles of blank + if(line_position == 9) + { + _crt->output_blank(15 * crt_cycles_multiplier); + _outputPosition += 15; + _crt->output_data(80 * crt_cycles_multiplier); + } + + if(line_position >= 24 && line_position < 104) + { + // TODO: actually output some pixels, why not? + _outputPosition++; + } + + if(line_position == 104) + { + _crt->output_blank(24 * crt_cycles_multiplier); + _outputPosition += 24; + } + } + } + } + } +} diff --git a/Machines/Electron/Electron.hpp b/Machines/Electron/Electron.hpp index 60459f343..68d3eae93 100644 --- a/Machines/Electron/Electron.hpp +++ b/Machines/Electron/Electron.hpp @@ -29,6 +29,14 @@ enum ROMSlot: uint8_t { ROMSlotOS }; +enum Interrupt: uint8_t { + InterruptRealTimeClock = 0x01, + InterruptDisplayEnd = 0x02, + InterruptTransmitDataEmpty = 0x04, + InterruptReceiveDataFull = 0x08, + InterruptHighToneDetect = 0x10 +}; + class Machine: public CPU6502::Processor { public: @@ -45,10 +53,16 @@ class Machine: public CPU6502::Processor { private: uint8_t _os[16384], _basic[16384], _ram[32768]; uint8_t _interruptStatus, _interruptControl; - uint16_t _screenStartAddress; ROMSlot _activeRom; Outputs::CRT *_crt; + + int _frameCycles, _outputPosition; + uint16_t _startScreenAddress, _currentScreenAddress; + + inline void update_display(); + inline void signal_interrupt(Interrupt interrupt); + inline void evaluate_interrupts(); }; } diff --git a/Processors/6502/CPU6502.hpp b/Processors/6502/CPU6502.hpp index 2411bc30b..f0f41d876 100644 --- a/Processors/6502/CPU6502.hpp +++ b/Processors/6502/CPU6502.hpp @@ -981,6 +981,16 @@ template class Processor { _reset_line_is_enabled = active; } + void set_irq_line(bool active) + { + // TODO + } + + void set_nmi_line(bool active) + { + // TODO + } + bool is_jammed() { return _is_jammed;