1
0
mirror of https://github.com/TomHarte/CLK.git synced 2025-02-02 01:31:15 +00:00

Believing the previous implementation to be overly complicated through premature optimisation, simplified video. It's slightly slower now. Will need work. Immediately to do: figure out how to deal with blank scan lines.

This commit is contained in:
Thomas Harte 2016-03-10 22:08:50 -05:00
parent 20ac630e4d
commit b836d74e18
2 changed files with 309 additions and 264 deletions

View File

@ -14,24 +14,35 @@
using namespace Electron;
static const unsigned int cycles_per_line = 128;
static const unsigned int cycles_per_frame = 312*cycles_per_line + 64;
static const unsigned int crt_cycles_multiplier = 8;
static const unsigned int crt_cycles_per_line = crt_cycles_multiplier * cycles_per_line;
namespace {
static const unsigned int cycles_per_line = 128;
static const unsigned int lines_per_frame = 625;
static const unsigned int cycles_per_frame = lines_per_frame * cycles_per_line;
static const unsigned int crt_cycles_multiplier = 8;
static const unsigned int crt_cycles_per_line = crt_cycles_multiplier * cycles_per_line;
static const int first_graphics_line = 38;
static const int first_graphics_cycle = 33;
static const int last_graphics_cycle = 80 + first_graphics_cycle;
static const unsigned int field_divider_line = 312; // i.e. the line, simultaneous with which, the first field's sync ends. So if
// the first line with pixels in field 1 is the 20th in the frame, the first line
// with pixels in field 2 will be 20+field_divider_line
static const unsigned int first_graphics_line = 38;
static const unsigned int first_graphics_cycle = 33;
static const unsigned int last_graphics_cycle = 80 + first_graphics_cycle;
static const unsigned int real_time_clock_interrupt_line = 156;
static const unsigned int display_end_interrupt_line = 256;
}
#define graphics_line(v) ((((v) >> 7) - first_graphics_line + field_divider_line) % field_divider_line)
#define graphics_column(v) ((((v) %127) - first_graphics_cycle) % 127)
Machine::Machine() :
_interrupt_control(0),
_interrupt_status(Interrupt::PowerOnReset),
_fieldCycles(0),
_frameCycles(0),
_displayOutputPosition(0),
_audioOutputPosition(0),
_audioOutputPositionError(0),
_currentOutputLine(0),
_is_odd_field(false),
_current_pixel_line(-1),
_crt(std::unique_ptr<Outputs::CRT::CRT>(new Outputs::CRT::CRT(crt_cycles_per_line, 8, Outputs::CRT::DisplayType::PAL50, 1, 1)))
{
_crt->set_rgb_sampling_function(
@ -69,10 +80,10 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin
// base address) then there's no way this write can affect the current frame. Sp
// no need to flush the display. Otherwise, output up until now so that any
// write doesn't have retroactive effect on the video output.
if(!(
(_fieldCycles < first_graphics_line * cycles_per_line) ||
(address < _startLineAddress && address < 0x3000)
))
// if(!(
// (_fieldCycles < first_graphics_line * cycles_per_line) ||
// (address < _startLineAddress && address < 0x3000)
// ))
update_display();
_ram[address] = *value;
@ -80,13 +91,13 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin
// for the entire frame, RAM is accessible only on odd cycles; in modes below 4
// it's also accessible only outside of the pixel regions
cycles += 1 + ((_fieldCycles&1));
cycles += 1 + (_frameCycles&1);
if(_screen_mode < 4)
{
const int current_line = _fieldCycles >> 7;
const int line_position = (_fieldCycles+cycles) & 127;
if(current_line >= first_graphics_line && current_line < first_graphics_line+256 && line_position >= first_graphics_cycle && line_position < first_graphics_cycle + 80)
cycles += (unsigned int)(80 + first_graphics_cycle - line_position);
const int current_line = graphics_line(_frameCycles + cycles);
const int current_column = graphics_column(_frameCycles + cycles);
if(current_line < 256 && current_column < 80)
cycles += (unsigned int)(80 - current_column);
}
}
else
@ -183,7 +194,7 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin
if(new_screen_mode == 7) new_screen_mode = 4;
if(new_screen_mode != _screen_mode)
{
printf("To mode %d, %d cycles into field (%d)\n", new_screen_mode, _fieldCycles, _fieldCycles >> 7);
// printf("To mode %d, at %d cycles into field (%d)\n", new_screen_mode, _fieldCycles, _fieldCycles >> 7);
update_display();
_screen_mode = new_screen_mode;
switch(_screen_mode)
@ -282,59 +293,50 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin
// printf("%04x: %02x (%d)\n", address, *value, _fieldCycles);
// }
unsigned int start_of_graphics = get_first_graphics_cycle();
const unsigned int real_time_clock_interrupt_time = start_of_graphics + 128*128;
const unsigned int display_end_interrupt_time = start_of_graphics + 264*128;
const unsigned int line_before_cycle = graphics_line(_frameCycles);
const unsigned int line_after_cycle = graphics_line(_frameCycles + cycles);
if(_fieldCycles < real_time_clock_interrupt_time && _fieldCycles + cycles >= real_time_clock_interrupt_time)
// implicit assumption here: the number of 2Mhz cycles this bus operation will take
// is never longer than a line. On the Electron, it's a safe one.
switch(line_before_cycle)
{
update_audio();
signal_interrupt(Interrupt::RealTimeClock);
case real_time_clock_interrupt_line:
if(line_after_cycle > real_time_clock_interrupt_line)
signal_interrupt(Interrupt::RealTimeClock);
break;
case real_time_clock_interrupt_line+1:
if(line_after_cycle > real_time_clock_interrupt_line+1)
clear_interrupt(Interrupt::RealTimeClock);
break;
case display_end_interrupt_line:
if(line_after_cycle > display_end_interrupt_line)
signal_interrupt(Interrupt::DisplayEnd);
break;
case display_end_interrupt_line+1:
if(line_after_cycle > display_end_interrupt_line+1)
clear_interrupt(Interrupt::DisplayEnd);
break;
}
if(_fieldCycles < real_time_clock_interrupt_time+128 && _fieldCycles + cycles >= real_time_clock_interrupt_time+128)
{
update_audio();
_interrupt_status &= ~Interrupt::RealTimeClock;
evaluate_interrupts();
}
_frameCycles += cycles;
else if(_fieldCycles < display_end_interrupt_time && _fieldCycles + cycles >= display_end_interrupt_time)
{
update_audio();
signal_interrupt(Interrupt::DisplayEnd);
}
if(_fieldCycles < display_end_interrupt_time+128 && _fieldCycles + cycles >= display_end_interrupt_time+128)
{
update_audio();
_interrupt_status &= ~Interrupt::DisplayEnd;
evaluate_interrupts();
}
_fieldCycles += cycles;
if(_fieldCycles >= cycles_per_frame)
// deal with frame wraparound by updating the two dependent subsystems
// as though the exact end of frame had been hit, then reset those
// and allow the frame cycle counter to assume its real value
if(_frameCycles >= cycles_per_frame)
{
unsigned int nextFrameCycles = _frameCycles - cycles_per_frame;
_frameCycles = cycles_per_frame;
update_display();
update_audio();
_fieldCycles -= cycles_per_frame;
_displayOutputPosition = 0;
_audioOutputPosition -= _audioOutputPosition;
_currentOutputLine = 0;
}
switch(_fieldCycles)
{
case 64*128:
case 196*128:
update_audio();
break;
//
// case cycles_per_frame:
// break;
_audioOutputPosition = 0;
_frameCycles = nextFrameCycles;
}
if(!(_frameCycles&31))
update_audio();
_tape.run_for_cycles(cycles);
return cycles;
@ -369,6 +371,12 @@ inline void Machine::signal_interrupt(Electron::Interrupt interrupt)
evaluate_interrupts();
}
inline void Machine::clear_interrupt(Electron::Interrupt interrupt)
{
_interrupt_status &= ~interrupt;
evaluate_interrupts();
}
void Machine::tape_did_change_interrupt_status(Tape *tape)
{
_interrupt_status = (_interrupt_status & ~(Interrupt::TransmitDataEmpty | Interrupt::ReceiveDataFull | Interrupt::HighToneDetect)) | _tape.get_interrupt_status();
@ -390,180 +398,155 @@ inline void Machine::evaluate_interrupts()
inline void Machine::update_audio()
{
int difference = (int)_fieldCycles - _audioOutputPosition;
_audioOutputPosition = (int)_fieldCycles;
int difference = (int)_frameCycles - _audioOutputPosition;
_audioOutputPosition = (int)_frameCycles;
_speaker.run_for_cycles((_audioOutputPositionError + difference) >> 4);
_audioOutputPositionError = (_audioOutputPositionError + difference)&15;
}
inline void Machine::reset_pixel_output()
inline void Machine::start_pixel_line()
{
display_x = 0;
display_y = 0;
_currentScreenAddress = _startLineAddress = _startScreenAddress;
_currentOutputLine = 0;
_current_pixel_line = (_current_pixel_line+1)&255;
if(!(_current_pixel_line&7))
{
_startLineAddress += ((_screen_mode < 4) ? 80 : 40) * 8 - 8;
}
if(!_current_pixel_line)
{
_startLineAddress = _startScreenAddress;
}
else
{
_startLineAddress++;
}
_currentScreenAddress = _startLineAddress;
_current_pixel_column = 0;
_crt->allocate_write_area(640);
_currentLine = _crt->get_write_target_for_buffer(0);
}
inline void Machine::output_pixels(int start_x, int number_of_pixels)
inline void Machine::end_pixel_line()
{
if(number_of_pixels)
_crt->output_data(640, 1);
}
inline void Machine::output_pixels(unsigned int number_of_cycles)
{
while(number_of_cycles--)
{
if(
((_screen_mode == 3) || (_screen_mode == 6)) &&
(((display_y%10) >= 8) || (display_y >= 250))
)
if(!(_current_pixel_column&1) || _screen_mode < 4)
{
end_pixel_output();
_crt->output_blank((unsigned int)number_of_pixels * crt_cycles_multiplier);
return;
if(_currentScreenAddress&32768)
{
_currentScreenAddress = (_screenModeBaseAddress + _currentScreenAddress)&32767;
}
_last_pixel_byte = _ram[_currentScreenAddress];
_currentScreenAddress = _currentScreenAddress+8;
}
unsigned int newDivider = 0;
switch(_screen_mode)
{
case 0: case 3: newDivider = 1; break;
case 1: case 4: case 6: newDivider = 2; break;
case 2: case 5: newDivider = 4; break;
}
bool is40Column = (_screen_mode > 3);
if(!_writePointer || newDivider != _currentOutputDivider || _isOutputting40Columns != is40Column)
{
end_pixel_output();
_currentOutputDivider = newDivider;
_isOutputting40Columns = is40Column;
_crt->allocate_write_area(640 / newDivider);
_currentLine = _writePointer = _crt->get_write_target_for_buffer(0);
}
if(is40Column)
{
number_of_pixels = ((start_x + number_of_pixels) >> 1) - (start_x >> 1);
}
#define GetNextPixels() \
if(_currentScreenAddress&32768)\
{\
_currentScreenAddress = (_screenModeBaseAddress + _currentScreenAddress)&32767;\
}\
uint8_t pixels = _ram[_currentScreenAddress];\
_currentScreenAddress = _currentScreenAddress+8
switch(_screen_mode)
{
default:
case 0: case 3: case 4: case 6:
while(number_of_pixels--)
{
GetNextPixels();
_writePointer[0] = _palette[(pixels&0x80) >> 4];
_writePointer[1] = _palette[(pixels&0x40) >> 3];
_writePointer[2] = _palette[(pixels&0x20) >> 2];
_writePointer[3] = _palette[(pixels&0x10) >> 1];
_writePointer[4] = _palette[(pixels&0x08) >> 0];
_writePointer[5] = _palette[(pixels&0x04) << 1];
_writePointer[6] = _palette[(pixels&0x02) << 2];
_writePointer[7] = _palette[(pixels&0x01) << 3];
_writePointer += 8;
}
case 3:
case 0:
{
_currentLine[0] = _palette[(_last_pixel_byte&0x80) >> 4];
_currentLine[1] = _palette[(_last_pixel_byte&0x40) >> 3];
_currentLine[2] = _palette[(_last_pixel_byte&0x20) >> 2];
_currentLine[3] = _palette[(_last_pixel_byte&0x10) >> 1];
_currentLine[4] = _palette[(_last_pixel_byte&0x08) >> 0];
_currentLine[5] = _palette[(_last_pixel_byte&0x04) << 1];
_currentLine[6] = _palette[(_last_pixel_byte&0x02) << 2];
_currentLine[7] = _palette[(_last_pixel_byte&0x01) << 3];
}
break;
case 1: case 5:
while(number_of_pixels--)
{
GetNextPixels();
_writePointer[0] = _palette[((pixels&0x80) >> 4) | ((pixels&0x08) >> 2)];
_writePointer[1] = _palette[((pixels&0x40) >> 3) | ((pixels&0x04) >> 1)];
_writePointer[2] = _palette[((pixels&0x20) >> 2) | ((pixels&0x02) >> 0)];
_writePointer[3] = _palette[((pixels&0x10) >> 1) | ((pixels&0x01) << 1)];
_writePointer += 4;
}
case 1:
{
_currentLine[0] =
_currentLine[1] = _palette[((_last_pixel_byte&0x80) >> 4) | ((_last_pixel_byte&0x08) >> 2)];
_currentLine[2] =
_currentLine[3] = _palette[((_last_pixel_byte&0x40) >> 3) | ((_last_pixel_byte&0x04) >> 1)];
_currentLine[4] =
_currentLine[5] = _palette[((_last_pixel_byte&0x20) >> 2) | ((_last_pixel_byte&0x02) >> 0)];
_currentLine[6] =
_currentLine[7] = _palette[((_last_pixel_byte&0x10) >> 1) | ((_last_pixel_byte&0x01) << 1)];
}
break;
case 2:
while(number_of_pixels--)
{
_currentLine[0] =
_currentLine[1] =
_currentLine[2] =
_currentLine[3] = _palette[((_last_pixel_byte&0x80) >> 4) | ((_last_pixel_byte&0x20) >> 3) | ((_last_pixel_byte&0x08) >> 2) | ((_last_pixel_byte&0x02) >> 1)];
_currentLine[4] =
_currentLine[5] =
_currentLine[6] =
_currentLine[7] = _palette[((_last_pixel_byte&0x40) >> 3) | ((_last_pixel_byte&0x10) >> 2) | ((_last_pixel_byte&0x04) >> 1) | ((_last_pixel_byte&0x01) >> 0)];
}
break;
case 6:
case 4:
{
if(_current_pixel_column&1)
{
GetNextPixels();
_writePointer[0] = _palette[((pixels&0x80) >> 4) | ((pixels&0x20) >> 3) | ((pixels&0x08) >> 2) | ((pixels&0x02) >> 1)];
_writePointer[1] = _palette[((pixels&0x40) >> 3) | ((pixels&0x10) >> 2) | ((pixels&0x04) >> 1) | ((pixels&0x01) >> 0)];
_writePointer += 2;
_currentLine[0] =
_currentLine[1] = _palette[(_last_pixel_byte&0x08) >> 0];
_currentLine[2] =
_currentLine[3] = _palette[(_last_pixel_byte&0x04) << 1];
_currentLine[4] =
_currentLine[5] = _palette[(_last_pixel_byte&0x02) << 2];
_currentLine[6] =
_currentLine[7] = _palette[(_last_pixel_byte&0x01) << 3];
}
else
{
_currentLine[0] =
_currentLine[1] = _palette[(_last_pixel_byte&0x80) >> 4];
_currentLine[2] =
_currentLine[3] = _palette[(_last_pixel_byte&0x40) >> 3];
_currentLine[4] =
_currentLine[5] = _palette[(_last_pixel_byte&0x20) >> 2];
_currentLine[6] =
_currentLine[7] = _palette[(_last_pixel_byte&0x10) >> 1];
}
}
break;
case 5:
{
if(_current_pixel_column&1)
{
_currentLine[0] =
_currentLine[1] =
_currentLine[2] =
_currentLine[3] = _palette[((_last_pixel_byte&0x20) >> 2) | ((_last_pixel_byte&0x02) >> 0)];
_currentLine[4] =
_currentLine[5] =
_currentLine[6] =
_currentLine[7] = _palette[((_last_pixel_byte&0x10) >> 1) | ((_last_pixel_byte&0x01) << 1)];
}
else
{
_currentLine[0] =
_currentLine[1] =
_currentLine[2] =
_currentLine[3] = _palette[((_last_pixel_byte&0x80) >> 4) | ((_last_pixel_byte&0x08) >> 2)];
_currentLine[4] =
_currentLine[5] =
_currentLine[6] =
_currentLine[7] = _palette[((_last_pixel_byte&0x40) >> 3) | ((_last_pixel_byte&0x04) >> 1)];
}
}
break;
}
_current_pixel_column++;
_currentLine += 8;
}
#undef GetNextPixels
}
inline void Machine::end_pixel_output()
{
if(_currentLine != nullptr)
{
_crt->output_data((unsigned int)((_writePointer - _currentLine) * _currentOutputDivider), _currentOutputDivider);
_writePointer = _currentLine = nullptr;
}
}
inline void Machine::update_pixels_to_position(int x, int y)
{
while((display_x < x) || (display_y < y))
{
if(display_x < first_graphics_cycle)
{
display_x++;
if(display_x == first_graphics_cycle)
{
_crt->output_sync(9 * crt_cycles_multiplier);
_crt->output_blank((first_graphics_cycle - 9) * crt_cycles_multiplier);
_currentScreenAddress = _startLineAddress;
}
continue;
}
if(display_x < last_graphics_cycle)
{
int cycles_to_output = (display_y < y) ? last_graphics_cycle - display_x : std::min(last_graphics_cycle - display_x, x - display_x);
output_pixels(display_x, cycles_to_output);
display_x += cycles_to_output;
if(display_x == last_graphics_cycle)
{
end_pixel_output();
}
continue;
}
display_x++;
if(display_x == 128)
{
_crt->output_blank((128 - 80 - first_graphics_cycle) * crt_cycles_multiplier);
display_x = 0;
display_y++;
if(((_screen_mode != 3) && (_screen_mode != 6)) || ((display_y%10) < 8))
{
_startLineAddress++;
_currentOutputLine++;
if(_currentOutputLine == 8)
{
_currentOutputLine = 0;
_startLineAddress = (_startLineAddress - 8) + ((_screen_mode < 4) ? 80 : 40)*8;
}
}
}
}
}
inline unsigned int Machine::get_first_graphics_cycle()
{
return (first_graphics_line * cycles_per_line) - (_is_odd_field ? 0 : 64);
}
inline void Machine::update_display()
@ -582,58 +565,126 @@ inline void Machine::update_display()
*/
const unsigned int end_of_top = get_first_graphics_cycle();
const unsigned int end_of_graphics = end_of_top + 256 * cycles_per_line;
// does the top region need to be output?
if(_displayOutputPosition < end_of_top && _fieldCycles >= end_of_top)
int final_line = _frameCycles >> 7;
while(_displayOutputPosition < _frameCycles)
{
_crt->output_sync(320 * crt_cycles_multiplier);
if(_is_odd_field) _crt->output_blank(64 * crt_cycles_multiplier);
_displayOutputPosition += 320 + (_is_odd_field ? 64 : 0);
int line = _displayOutputPosition >> 7;
while(_displayOutputPosition < end_of_top)
// Priority one: sync.
// ===================
// full sync lines are 0, 1, field_divider_line+1 and field_divider_line+2
if(line == 0 || line == 1 || line == field_divider_line+1 || line == field_divider_line+2)
{
// wait for the line to complete before signalling
if(final_line == line) return;
_crt->output_sync(128 * crt_cycles_multiplier);
_displayOutputPosition += 128;
continue;
}
// line 2 is a left-sync line
if(line == 2)
{
// wait for the line to complete before signalling
if(final_line == line) return;
_crt->output_sync(64 * crt_cycles_multiplier);
_crt->output_blank(64 * crt_cycles_multiplier);
_displayOutputPosition += 128;
continue;
}
// line field_divider_line is a right-sync line
if(line == field_divider_line)
{
// wait for the line to complete before signalling
if(final_line == line) return;
_crt->output_blank(64 * crt_cycles_multiplier);
_crt->output_sync(64 * crt_cycles_multiplier);
_displayOutputPosition += 128;
continue;
}
// Priority two: blank lines.
// ==========================
//
// Given that it is not a sync line, this is a blank line if it is less than first_graphics_line, or greater
// than first_graphics_line+255 and less than first_graphics_line+field_divider_line, or greater than
// first_graphics_line+field_divider_line+255 (TODO: or this is Mode 3 or 6 and this should be blank)
if(
line < first_graphics_line ||
(line > first_graphics_line+255 && line < first_graphics_line+field_divider_line) ||
line > first_graphics_line+field_divider_line+255)
{
if(final_line == line) return;
_crt->output_sync(9 * crt_cycles_multiplier);
_crt->output_blank(119 * crt_cycles_multiplier);
_displayOutputPosition += 128;
continue;
}
assert(_displayOutputPosition == end_of_top);
// Final possibility: this is a pixel line.
// ========================================
reset_pixel_output();
}
// is this the pixel region?
if(_displayOutputPosition >= end_of_top && _displayOutputPosition < end_of_graphics)
{
unsigned int final_position = std::min(_fieldCycles, end_of_graphics) - end_of_top;
unsigned int final_line = final_position >> 7;
unsigned int final_pixel = final_position & 127;
update_pixels_to_position((int)final_pixel, (int)final_line);
_displayOutputPosition = final_position + end_of_top;
}
// is this the bottom region?
if(_displayOutputPosition >= end_of_graphics && _displayOutputPosition < cycles_per_frame)
{
unsigned int remaining_time = cycles_per_frame - _displayOutputPosition;
while(remaining_time >= 128)
// determine how far we're going from left to right
unsigned int this_cycle = _displayOutputPosition&127;
unsigned int final_cycle = _frameCycles&127;
if(final_line > line)
{
_crt->output_sync(9 * crt_cycles_multiplier);
_crt->output_blank(119 * crt_cycles_multiplier);
remaining_time -= 128;
final_cycle = 128;
}
if(remaining_time == 64)
// output format is:
// 9 cycles: sync
// ... to 24 cycles: colour burst
// ... to first_graphics_cycle: blank
// ... for 80 cycles: pixels
// ... until end of line: blank
while(this_cycle < final_cycle)
{
_crt->output_sync(9 * crt_cycles_multiplier);
_crt->output_blank(55 * crt_cycles_multiplier);
if(this_cycle < 9)
{
if(final_cycle < 9) return;
_crt->output_sync(9 * crt_cycles_multiplier);
_displayOutputPosition += 9;
this_cycle = 9;
}
if(this_cycle < 24)
{
if(final_cycle < 24) return;
_crt->output_colour_burst((24-9) * crt_cycles_multiplier, 0, 12);
_displayOutputPosition += 24-9;
this_cycle = 24;
// TODO: phase shouldn't be zero on every line
}
if(this_cycle < first_graphics_cycle)
{
if(final_cycle < first_graphics_cycle) return;
_crt->output_blank((first_graphics_cycle - 24) * crt_cycles_multiplier);
_displayOutputPosition += first_graphics_cycle - 24;
this_cycle = first_graphics_cycle;
start_pixel_line();
}
if(this_cycle < first_graphics_cycle + 80)
{
unsigned int length_to_output = std::min(final_cycle, (first_graphics_cycle + 80)) - this_cycle;
output_pixels(length_to_output);
_displayOutputPosition += length_to_output;
this_cycle += length_to_output;
}
if(this_cycle >= first_graphics_cycle + 80)
{
if(final_cycle < 128) return;
end_pixel_line();
_crt->output_blank((128 - (first_graphics_cycle + 80)) * crt_cycles_multiplier);
_displayOutputPosition += 128 - (first_graphics_cycle + 80);
this_cycle = 128;
}
}
_displayOutputPosition = cycles_per_frame;
_is_odd_field ^= true;
}
}

View File

@ -157,19 +157,15 @@ class Machine: public CPU6502::Processor<Machine>, Tape::Delegate {
private:
inline void update_display();
inline void update_pixels_to_position(int x, int y);
inline void output_pixels(int start_x, int number_of_pixels);
inline void end_pixel_output();
inline void reset_pixel_output();
inline unsigned int get_first_graphics_cycle();
inline void start_pixel_line();
inline void end_pixel_line();
inline void output_pixels(unsigned int number_of_cycles);
inline void update_audio();
inline void signal_interrupt(Interrupt interrupt);
inline void clear_interrupt(Interrupt interrupt);
inline void evaluate_interrupts();
// Pixel tracker;
int display_x, display_y;
// Things that directly constitute the memory map.
uint8_t _roms[16][16384];
uint8_t _os[16384], _ram[32768];
@ -184,18 +180,16 @@ class Machine: public CPU6502::Processor<Machine>, Tape::Delegate {
uint16_t _startScreenAddress;
// Counters related to simultaneous subsystems;
unsigned int _fieldCycles, _displayOutputPosition;
unsigned int _frameCycles, _displayOutputPosition;
int _audioOutputPosition, _audioOutputPositionError;
// Display generation.
uint16_t _startLineAddress, _currentScreenAddress;
int _currentOutputLine;
bool _is_odd_field;
int _current_pixel_line, _current_pixel_column;
uint8_t _last_pixel_byte;
// CRT output
unsigned int _currentOutputDivider;
bool _isOutputting40Columns;
uint8_t *_currentLine, *_writePointer;
uint8_t *_currentLine;
// Tape.
Tape _tape;