mirror of
https://github.com/TomHarte/CLK.git
synced 2025-02-18 01:30:56 +00:00
Turned the 6560 into an ordinary template, similar to the rest of the project, albeit right now with a fairly shonky internal implementation. Fixed a Mac-specific interface sizing issue.
This commit is contained in:
parent
285a288c80
commit
12bad8f23f
@ -125,6 +125,174 @@ template <class T> class MOS6560 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Runs for cycles. Derr.
|
||||||
|
*/
|
||||||
|
inline void run_for_cycles(unsigned int number_of_cycles)
|
||||||
|
{
|
||||||
|
while(number_of_cycles--)
|
||||||
|
{
|
||||||
|
uint16_t address = get_address();
|
||||||
|
uint8_t pixel_data;
|
||||||
|
uint8_t colour_data;
|
||||||
|
static_cast<T *>(this)->perform_read(address, &pixel_data, &colour_data);
|
||||||
|
set_graphics_value(pixel_data, colour_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Causes the 6560 to flush as much pending CRT and speaker communications as possible.
|
||||||
|
*/
|
||||||
|
inline void synchronise() { update_audio(); }
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Writes to a 6560 register.
|
||||||
|
*/
|
||||||
|
void set_register(int address, uint8_t value)
|
||||||
|
{
|
||||||
|
address &= 0xf;
|
||||||
|
_registers.direct_values[address] = value;
|
||||||
|
switch(address)
|
||||||
|
{
|
||||||
|
case 0x0:
|
||||||
|
_registers.interlaced = !!(value&0x80) && _timing.supports_interlacing;
|
||||||
|
_registers.first_column_location = value & 0x7f;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x1:
|
||||||
|
_registers.first_row_location = value;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x2:
|
||||||
|
_registers.number_of_columns = value & 0x7f;
|
||||||
|
_registers.video_matrix_start_address = (uint16_t)((_registers.video_matrix_start_address & 0x3c00) | ((value & 0x80) << 2));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x3:
|
||||||
|
_registers.number_of_rows = (value >> 1)&0x3f;
|
||||||
|
_registers.tall_characters = !!(value&0x01);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x5:
|
||||||
|
_registers.character_cell_start_address = (uint16_t)((value & 0x0f) << 10);
|
||||||
|
_registers.video_matrix_start_address = (uint16_t)((_registers.video_matrix_start_address & 0x0200) | ((value & 0xf0) << 6));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0xa:
|
||||||
|
case 0xb:
|
||||||
|
case 0xc:
|
||||||
|
case 0xd:
|
||||||
|
update_audio();
|
||||||
|
_speaker->set_control(address - 0xa, value);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0xe:
|
||||||
|
update_audio();
|
||||||
|
_registers.auxiliary_colour = _colours[value >> 4];
|
||||||
|
_speaker->set_volume(value & 0xf);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0xf:
|
||||||
|
{
|
||||||
|
uint8_t new_border_colour = _colours[value & 0x07];
|
||||||
|
if(_this_state == State::Border && new_border_colour != _registers.borderColour)
|
||||||
|
{
|
||||||
|
output_border(_cycles_in_state * 4);
|
||||||
|
_cycles_in_state = 0;
|
||||||
|
}
|
||||||
|
_registers.invertedCells = !((value >> 3)&1);
|
||||||
|
_registers.borderColour = new_border_colour;
|
||||||
|
_registers.backgroundColour = _colours[value >> 4];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
// TODO: the lightpen, etc
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Reads from a 6560 register.
|
||||||
|
*/
|
||||||
|
uint8_t get_register(int address)
|
||||||
|
{
|
||||||
|
address &= 0xf;
|
||||||
|
int current_line = (_full_frame_counter + _timing.line_counter_increment_offset) / _timing.cycles_per_line;
|
||||||
|
switch(address)
|
||||||
|
{
|
||||||
|
default: return _registers.direct_values[address];
|
||||||
|
case 0x03: return (uint8_t)(current_line << 7) | (_registers.direct_values[3] & 0x7f);
|
||||||
|
case 0x04: return (current_line >> 1) & 0xff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<Outputs::CRT::CRT> _crt;
|
||||||
|
|
||||||
|
std::shared_ptr<Speaker> _speaker;
|
||||||
|
unsigned int _cycles_since_speaker_update;
|
||||||
|
void update_audio()
|
||||||
|
{
|
||||||
|
_speaker->run_for_cycles(_cycles_since_speaker_update >> 2);
|
||||||
|
_cycles_since_speaker_update &= 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// register state
|
||||||
|
struct {
|
||||||
|
bool interlaced, tall_characters;
|
||||||
|
uint8_t first_column_location, first_row_location;
|
||||||
|
uint8_t number_of_columns, number_of_rows;
|
||||||
|
uint16_t character_cell_start_address, video_matrix_start_address;
|
||||||
|
uint8_t backgroundColour, borderColour, auxiliary_colour;
|
||||||
|
bool invertedCells;
|
||||||
|
|
||||||
|
uint8_t direct_values[16];
|
||||||
|
} _registers;
|
||||||
|
|
||||||
|
// output state
|
||||||
|
enum State {
|
||||||
|
Sync, ColourBurst, Border, Pixels
|
||||||
|
} _this_state, _output_state;
|
||||||
|
unsigned int _cycles_in_state;
|
||||||
|
|
||||||
|
// counters that cover an entire field
|
||||||
|
int _horizontal_counter, _vertical_counter, _full_frame_counter;
|
||||||
|
|
||||||
|
// latches dictating start and length of drawing
|
||||||
|
bool _vertical_drawing_latch, _horizontal_drawing_latch;
|
||||||
|
int _rows_this_field, _columns_this_line;
|
||||||
|
|
||||||
|
// current drawing position counter
|
||||||
|
int _pixel_line_cycle, _column_counter;
|
||||||
|
int _current_row;
|
||||||
|
uint16_t _current_character_row;
|
||||||
|
uint16_t _video_matrix_address_counter, _base_video_matrix_address_counter;
|
||||||
|
|
||||||
|
// data latched from the bus
|
||||||
|
uint8_t _character_code, _character_colour, _character_value;
|
||||||
|
|
||||||
|
bool _is_odd_frame;
|
||||||
|
|
||||||
|
// lookup table from 6560 colour index to appropriate PAL/NTSC value
|
||||||
|
uint8_t _colours[16];
|
||||||
|
|
||||||
|
uint8_t *pixel_pointer;
|
||||||
|
void output_border(unsigned int number_of_cycles)
|
||||||
|
{
|
||||||
|
uint8_t *colour_pointer = _crt->allocate_write_area(1);
|
||||||
|
if(colour_pointer) *colour_pointer = _registers.borderColour;
|
||||||
|
_crt->output_level(number_of_cycles);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct {
|
||||||
|
int cycles_per_line;
|
||||||
|
int line_counter_increment_offset;
|
||||||
|
int lines_per_progressive_field;
|
||||||
|
bool supports_interlacing;
|
||||||
|
} _timing;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Impliedly runs the 6560 for a single cycle, returning the next address that it puts on the bus.
|
Impliedly runs the 6560 for a single cycle, returning the next address that it puts on the bus.
|
||||||
*/
|
*/
|
||||||
@ -321,159 +489,6 @@ template <class T> class MOS6560 {
|
|||||||
_column_counter++;
|
_column_counter++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
Causes the 6560 to flush as much pending CRT and speaker communications as possible.
|
|
||||||
*/
|
|
||||||
inline void synchronise() { update_audio(); }
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Writes to a 6560 register.
|
|
||||||
*/
|
|
||||||
void set_register(int address, uint8_t value)
|
|
||||||
{
|
|
||||||
address &= 0xf;
|
|
||||||
_registers.direct_values[address] = value;
|
|
||||||
switch(address)
|
|
||||||
{
|
|
||||||
case 0x0:
|
|
||||||
_registers.interlaced = !!(value&0x80) && _timing.supports_interlacing;
|
|
||||||
_registers.first_column_location = value & 0x7f;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0x1:
|
|
||||||
_registers.first_row_location = value;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0x2:
|
|
||||||
_registers.number_of_columns = value & 0x7f;
|
|
||||||
_registers.video_matrix_start_address = (uint16_t)((_registers.video_matrix_start_address & 0x3c00) | ((value & 0x80) << 2));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0x3:
|
|
||||||
_registers.number_of_rows = (value >> 1)&0x3f;
|
|
||||||
_registers.tall_characters = !!(value&0x01);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0x5:
|
|
||||||
_registers.character_cell_start_address = (uint16_t)((value & 0x0f) << 10);
|
|
||||||
_registers.video_matrix_start_address = (uint16_t)((_registers.video_matrix_start_address & 0x0200) | ((value & 0xf0) << 6));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0xa:
|
|
||||||
case 0xb:
|
|
||||||
case 0xc:
|
|
||||||
case 0xd:
|
|
||||||
update_audio();
|
|
||||||
_speaker->set_control(address - 0xa, value);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0xe:
|
|
||||||
update_audio();
|
|
||||||
_registers.auxiliary_colour = _colours[value >> 4];
|
|
||||||
_speaker->set_volume(value & 0xf);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0xf:
|
|
||||||
{
|
|
||||||
uint8_t new_border_colour = _colours[value & 0x07];
|
|
||||||
if(_this_state == State::Border && new_border_colour != _registers.borderColour)
|
|
||||||
{
|
|
||||||
output_border(_cycles_in_state * 4);
|
|
||||||
_cycles_in_state = 0;
|
|
||||||
}
|
|
||||||
_registers.invertedCells = !((value >> 3)&1);
|
|
||||||
_registers.borderColour = new_border_colour;
|
|
||||||
_registers.backgroundColour = _colours[value >> 4];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
// TODO: the lightpen, etc
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Reads from a 6560 register.
|
|
||||||
*/
|
|
||||||
uint8_t get_register(int address)
|
|
||||||
{
|
|
||||||
address &= 0xf;
|
|
||||||
int current_line = (_full_frame_counter + _timing.line_counter_increment_offset) / _timing.cycles_per_line;
|
|
||||||
switch(address)
|
|
||||||
{
|
|
||||||
default: return _registers.direct_values[address];
|
|
||||||
case 0x03: return (uint8_t)(current_line << 7) | (_registers.direct_values[3] & 0x7f);
|
|
||||||
case 0x04: return (current_line >> 1) & 0xff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::shared_ptr<Outputs::CRT::CRT> _crt;
|
|
||||||
|
|
||||||
std::shared_ptr<Speaker> _speaker;
|
|
||||||
unsigned int _cycles_since_speaker_update;
|
|
||||||
void update_audio()
|
|
||||||
{
|
|
||||||
_speaker->run_for_cycles(_cycles_since_speaker_update >> 2);
|
|
||||||
_cycles_since_speaker_update &= 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
// register state
|
|
||||||
struct {
|
|
||||||
bool interlaced, tall_characters;
|
|
||||||
uint8_t first_column_location, first_row_location;
|
|
||||||
uint8_t number_of_columns, number_of_rows;
|
|
||||||
uint16_t character_cell_start_address, video_matrix_start_address;
|
|
||||||
uint8_t backgroundColour, borderColour, auxiliary_colour;
|
|
||||||
bool invertedCells;
|
|
||||||
|
|
||||||
uint8_t direct_values[16];
|
|
||||||
} _registers;
|
|
||||||
|
|
||||||
// output state
|
|
||||||
enum State {
|
|
||||||
Sync, ColourBurst, Border, Pixels
|
|
||||||
} _this_state, _output_state;
|
|
||||||
unsigned int _cycles_in_state;
|
|
||||||
|
|
||||||
// counters that cover an entire field
|
|
||||||
int _horizontal_counter, _vertical_counter, _full_frame_counter;
|
|
||||||
|
|
||||||
// latches dictating start and length of drawing
|
|
||||||
bool _vertical_drawing_latch, _horizontal_drawing_latch;
|
|
||||||
int _rows_this_field, _columns_this_line;
|
|
||||||
|
|
||||||
// current drawing position counter
|
|
||||||
int _pixel_line_cycle, _column_counter;
|
|
||||||
int _current_row;
|
|
||||||
uint16_t _current_character_row;
|
|
||||||
uint16_t _video_matrix_address_counter, _base_video_matrix_address_counter;
|
|
||||||
|
|
||||||
// data latched from the bus
|
|
||||||
uint8_t _character_code, _character_colour, _character_value;
|
|
||||||
|
|
||||||
bool _is_odd_frame;
|
|
||||||
|
|
||||||
// lookup table from 6560 colour index to appropriate PAL/NTSC value
|
|
||||||
uint8_t _colours[16];
|
|
||||||
|
|
||||||
uint8_t *pixel_pointer;
|
|
||||||
void output_border(unsigned int number_of_cycles)
|
|
||||||
{
|
|
||||||
uint8_t *colour_pointer = _crt->allocate_write_area(1);
|
|
||||||
if(colour_pointer) *colour_pointer = _registers.borderColour;
|
|
||||||
_crt->output_level(number_of_cycles);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct {
|
|
||||||
int cycles_per_line;
|
|
||||||
int line_counter_increment_offset;
|
|
||||||
int lines_per_progressive_field;
|
|
||||||
bool supports_interlacing;
|
|
||||||
} _timing;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -35,14 +35,9 @@ Machine::Machine() :
|
|||||||
_tape.set_delegate(this);
|
_tape.set_delegate(this);
|
||||||
|
|
||||||
// establish the memory maps
|
// establish the memory maps
|
||||||
memset(_videoMemoryMap, 0, sizeof(_videoMemoryMap));
|
|
||||||
memset(_processorReadMemoryMap, 0, sizeof(_processorReadMemoryMap));
|
memset(_processorReadMemoryMap, 0, sizeof(_processorReadMemoryMap));
|
||||||
memset(_processorWriteMemoryMap, 0, sizeof(_processorWriteMemoryMap));
|
memset(_processorWriteMemoryMap, 0, sizeof(_processorWriteMemoryMap));
|
||||||
|
|
||||||
write_to_map(_videoMemoryMap, _characterROM, 0x0000, sizeof(_characterROM));
|
|
||||||
write_to_map(_videoMemoryMap, _userBASICMemory, 0x2000, sizeof(_userBASICMemory));
|
|
||||||
write_to_map(_videoMemoryMap, _screenMemory, 0x3000, sizeof(_screenMemory));
|
|
||||||
|
|
||||||
write_to_map(_processorReadMemoryMap, _userBASICMemory, 0x0000, sizeof(_userBASICMemory));
|
write_to_map(_processorReadMemoryMap, _userBASICMemory, 0x0000, sizeof(_userBASICMemory));
|
||||||
write_to_map(_processorReadMemoryMap, _screenMemory, 0x1000, sizeof(_screenMemory));
|
write_to_map(_processorReadMemoryMap, _screenMemory, 0x1000, sizeof(_screenMemory));
|
||||||
write_to_map(_processorReadMemoryMap, _colorMemory, 0x9400, sizeof(_colorMemory));
|
write_to_map(_processorReadMemoryMap, _colorMemory, 0x9400, sizeof(_colorMemory));
|
||||||
@ -86,9 +81,7 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
// run the phase-1 part of this cycle, in which the VIC accesses memory
|
// run the phase-1 part of this cycle, in which the VIC accesses memory
|
||||||
uint16_t video_address = _mos6560->get_address();
|
_mos6560->run_for_cycles(1);
|
||||||
uint8_t video_value = _videoMemoryMap[video_address >> 10] ? _videoMemoryMap[video_address >> 10][video_address & 0x3ff] : 0xff; // TODO
|
|
||||||
_mos6560->set_graphics_value(video_value, _colorMemory[video_address & 0x03ff]);
|
|
||||||
|
|
||||||
// run the phase-2 part of the cycle, which is whatever the 6502 said it should be
|
// run the phase-2 part of the cycle, which is whatever the 6502 said it should be
|
||||||
if(isReadOperation(operation))
|
if(isReadOperation(operation))
|
||||||
@ -151,6 +144,12 @@ void Machine::mos6522_did_change_interrupt_status(void *mos6522)
|
|||||||
void Machine::setup_output(float aspect_ratio)
|
void Machine::setup_output(float aspect_ratio)
|
||||||
{
|
{
|
||||||
_mos6560.reset(new Vic6560());
|
_mos6560.reset(new Vic6560());
|
||||||
|
|
||||||
|
memset(_mos6560->_videoMemoryMap, 0, sizeof(_mos6560->_videoMemoryMap));
|
||||||
|
write_to_map(_mos6560->_videoMemoryMap, _characterROM, 0x0000, sizeof(_characterROM));
|
||||||
|
write_to_map(_mos6560->_videoMemoryMap, _userBASICMemory, 0x2000, sizeof(_userBASICMemory));
|
||||||
|
write_to_map(_mos6560->_videoMemoryMap, _screenMemory, 0x3000, sizeof(_screenMemory));
|
||||||
|
_mos6560->_colorMemory = _colorMemory;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Machine::close_output()
|
void Machine::close_output()
|
||||||
|
@ -225,7 +225,17 @@ class Tape: public Storage::TapePlayer {
|
|||||||
bool _input_level;
|
bool _input_level;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Vic6560: public MOS::MOS6560<Vic6560> {};
|
class Vic6560: public MOS::MOS6560<Vic6560> {
|
||||||
|
public:
|
||||||
|
void perform_read(uint16_t address, uint8_t *pixel_data, uint8_t *colour_data)
|
||||||
|
{
|
||||||
|
*pixel_data = _videoMemoryMap[address >> 10] ? _videoMemoryMap[address >> 10][address & 0x3ff] : 0xff; // TODO
|
||||||
|
*colour_data = _colorMemory[address & 0x03ff];
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t *_videoMemoryMap[16];
|
||||||
|
uint8_t *_colorMemory;
|
||||||
|
};
|
||||||
|
|
||||||
class Machine:
|
class Machine:
|
||||||
public CPU6502::Processor<Machine>,
|
public CPU6502::Processor<Machine>,
|
||||||
@ -291,7 +301,6 @@ class Machine:
|
|||||||
uint8_t _junkMemory[0x0400];
|
uint8_t _junkMemory[0x0400];
|
||||||
std::unique_ptr<uint8_t> _driveROM;
|
std::unique_ptr<uint8_t> _driveROM;
|
||||||
|
|
||||||
uint8_t *_videoMemoryMap[16];
|
|
||||||
uint8_t *_processorReadMemoryMap[64];
|
uint8_t *_processorReadMemoryMap[64];
|
||||||
uint8_t *_processorWriteMemoryMap[64];
|
uint8_t *_processorWriteMemoryMap[64];
|
||||||
void write_to_map(uint8_t **map, uint8_t *area, uint16_t address, uint16_t length);
|
void write_to_map(uint8_t **map, uint8_t *area, uint16_t address, uint16_t length);
|
||||||
|
@ -46,8 +46,8 @@
|
|||||||
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
||||||
<rect key="contentRect" x="83" y="102" width="200" height="103"/>
|
<rect key="contentRect" x="83" y="102" width="200" height="103"/>
|
||||||
<rect key="screenRect" x="0.0" y="0.0" width="1366" height="768"/>
|
<rect key="screenRect" x="0.0" y="0.0" width="1366" height="768"/>
|
||||||
<value key="minSize" type="size" width="200" height="83"/>
|
<value key="minSize" type="size" width="200" height="103"/>
|
||||||
<value key="maxSize" type="size" width="200" height="83"/>
|
<value key="maxSize" type="size" width="200" height="103"/>
|
||||||
<view key="contentView" id="7Pv-WL-2Rq">
|
<view key="contentView" id="7Pv-WL-2Rq">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="200" height="103"/>
|
<rect key="frame" x="0.0" y="0.0" width="200" height="103"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user