mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-26 08:49:37 +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.
|
||||
*/
|
||||
@ -321,159 +489,6 @@ template <class T> class MOS6560 {
|
||||
_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);
|
||||
|
||||
// establish the memory maps
|
||||
memset(_videoMemoryMap, 0, sizeof(_videoMemoryMap));
|
||||
memset(_processorReadMemoryMap, 0, sizeof(_processorReadMemoryMap));
|
||||
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, _screenMemory, 0x1000, sizeof(_screenMemory));
|
||||
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
|
||||
uint16_t video_address = _mos6560->get_address();
|
||||
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]);
|
||||
_mos6560->run_for_cycles(1);
|
||||
|
||||
// run the phase-2 part of the cycle, which is whatever the 6502 said it should be
|
||||
if(isReadOperation(operation))
|
||||
@ -151,6 +144,12 @@ void Machine::mos6522_did_change_interrupt_status(void *mos6522)
|
||||
void Machine::setup_output(float aspect_ratio)
|
||||
{
|
||||
_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()
|
||||
|
@ -225,7 +225,17 @@ class Tape: public Storage::TapePlayer {
|
||||
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:
|
||||
public CPU6502::Processor<Machine>,
|
||||
@ -291,7 +301,6 @@ class Machine:
|
||||
uint8_t _junkMemory[0x0400];
|
||||
std::unique_ptr<uint8_t> _driveROM;
|
||||
|
||||
uint8_t *_videoMemoryMap[16];
|
||||
uint8_t *_processorReadMemoryMap[64];
|
||||
uint8_t *_processorWriteMemoryMap[64];
|
||||
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"/>
|
||||
<rect key="contentRect" x="83" y="102" width="200" height="103"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="1366" height="768"/>
|
||||
<value key="minSize" type="size" width="200" height="83"/>
|
||||
<value key="maxSize" type="size" width="200" height="83"/>
|
||||
<value key="minSize" type="size" width="200" height="103"/>
|
||||
<value key="maxSize" type="size" width="200" height="103"/>
|
||||
<view key="contentView" id="7Pv-WL-2Rq">
|
||||
<rect key="frame" x="0.0" y="0.0" width="200" height="103"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
|
Loading…
Reference in New Issue
Block a user