mirror of
https://github.com/TomHarte/CLK.git
synced 2024-07-10 12:29:01 +00:00
Merge pull request #29 from TomHarte/6560Improvements
Improved 6560 emulation and the Vic-20's bus
This commit is contained in:
commit
3a4e844a07
@ -29,62 +29,87 @@ MOS6560::MOS6560() :
|
|||||||
"return mix(y, step(yC, 14) * chroma, amplitude);"
|
"return mix(y, step(yC, 14) * chroma, amplitude);"
|
||||||
"}");
|
"}");
|
||||||
|
|
||||||
// set up colours table
|
// default to NTSC
|
||||||
// 0
|
set_output_mode(OutputMode::NTSC);
|
||||||
// 2, 6, 9, B,
|
|
||||||
// 4, 5, 8, A, C, E
|
|
||||||
// 3, 7, D, F
|
|
||||||
// 1
|
|
||||||
uint8_t luminances[16] = { // range is 0–4
|
|
||||||
0, 4, 1, 3, 2, 2, 1, 3,
|
|
||||||
2, 1, 2, 1, 2, 3, 2, 3
|
|
||||||
};
|
|
||||||
// uint8_t pal_chrominances[16] = { // range is 0–15; 15 is a special case meaning "no chrominance"
|
|
||||||
// 15, 15, 5, 13, 2, 10, 0, 8,
|
|
||||||
// 6, 7, 5, 13, 2, 10, 0, 8,
|
|
||||||
// };
|
|
||||||
uint8_t ntsc_chrominances[16] = {
|
|
||||||
15, 15, 2, 10, 4, 12, 6, 14,
|
|
||||||
0, 8, 2, 10, 4, 12, 6, 14,
|
|
||||||
};
|
|
||||||
for(int c = 0; c < 16; c++)
|
|
||||||
{
|
|
||||||
_colours[c] = (uint8_t)((luminances[c] << 4) | ntsc_chrominances[c]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// show only the centre
|
// show only the centre
|
||||||
_crt->set_visible_area(_crt->get_rect_for_area(16, 237, 11*4, 55*4, 4.0f / 3.0f));
|
_crt->set_visible_area(_crt->get_rect_for_area(16, 237, 11*4, 55*4, 4.0f / 3.0f));
|
||||||
_speaker.set_input_rate(255681.75); // assuming NTSC; clock rate / 4
|
_speaker.set_input_rate(255681.75); // assuming NTSC; clock rate / 4
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MOS6560::set_output_mode(OutputMode output_mode)
|
||||||
|
{
|
||||||
|
uint8_t luminances[16] = { // range is 0–4
|
||||||
|
0, 4, 1, 3, 2, 2, 1, 3,
|
||||||
|
2, 1, 2, 1, 2, 3, 2, 3
|
||||||
|
};
|
||||||
|
uint8_t pal_chrominances[16] = { // range is 0–15; 15 is a special case meaning "no chrominance"
|
||||||
|
15, 15, 5, 13, 2, 10, 0, 8,
|
||||||
|
6, 7, 5, 13, 2, 10, 0, 8,
|
||||||
|
};
|
||||||
|
uint8_t ntsc_chrominances[16] = {
|
||||||
|
15, 15, 2, 10, 4, 12, 6, 14,
|
||||||
|
0, 8, 2, 10, 4, 12, 6, 14,
|
||||||
|
};
|
||||||
|
uint8_t *chrominances;
|
||||||
|
Outputs::CRT::DisplayType display_type;
|
||||||
|
|
||||||
|
switch(output_mode)
|
||||||
|
{
|
||||||
|
case OutputMode::PAL:
|
||||||
|
chrominances = pal_chrominances;
|
||||||
|
display_type = Outputs::CRT::PAL50;
|
||||||
|
_timing.cycles_per_line = 71;
|
||||||
|
_timing.lines_per_progressive_field = 312;
|
||||||
|
_timing.supports_interlacing = false;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OutputMode::NTSC:
|
||||||
|
chrominances = ntsc_chrominances;
|
||||||
|
display_type = Outputs::CRT::NTSC60;
|
||||||
|
_timing.cycles_per_line = 65;
|
||||||
|
_timing.lines_per_progressive_field = 261;
|
||||||
|
_timing.supports_interlacing = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
_crt->set_new_display_type((unsigned int)(_timing.cycles_per_line*4), display_type);
|
||||||
|
for(int c = 0; c < 16; c++)
|
||||||
|
{
|
||||||
|
_colours[c] = (uint8_t)((luminances[c] << 4) | chrominances[c]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: set clock rate
|
||||||
|
|
||||||
void MOS6560::set_register(int address, uint8_t value)
|
void MOS6560::set_register(int address, uint8_t value)
|
||||||
{
|
{
|
||||||
address &= 0xf;
|
address &= 0xf;
|
||||||
_registers[address] = value;
|
_registers.direct_values[address] = value;
|
||||||
switch(address)
|
switch(address)
|
||||||
{
|
{
|
||||||
case 0x0:
|
case 0x0:
|
||||||
_interlaced = !!(value&0x80);
|
_registers.interlaced = !!(value&0x80) && _timing.supports_interlacing;
|
||||||
_first_column_location = value & 0x7f;
|
_registers.first_column_location = value & 0x7f;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x1:
|
case 0x1:
|
||||||
_first_row_location = value;
|
_registers.first_row_location = value;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x2:
|
case 0x2:
|
||||||
_number_of_columns = value & 0x7f;
|
_registers.number_of_columns = value & 0x7f;
|
||||||
_video_matrix_start_address = (uint16_t)((_video_matrix_start_address & 0x3c00) | ((value & 0x80) << 2));
|
_registers.video_matrix_start_address = (uint16_t)((_registers.video_matrix_start_address & 0x3c00) | ((value & 0x80) << 2));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x3:
|
case 0x3:
|
||||||
_number_of_rows = (value >> 1)&0x3f;
|
_registers.number_of_rows = (value >> 1)&0x3f;
|
||||||
_tall_characters = !!(value&0x01);
|
_registers.tall_characters = !!(value&0x01);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x5:
|
case 0x5:
|
||||||
_character_cell_start_address = (uint16_t)((value & 0x0f) << 10);
|
_registers.character_cell_start_address = (uint16_t)((value & 0x0f) << 10);
|
||||||
_video_matrix_start_address = (uint16_t)((_video_matrix_start_address & 0x0200) | ((value & 0xf0) << 6));
|
_registers.video_matrix_start_address = (uint16_t)((_registers.video_matrix_start_address & 0x0200) | ((value & 0xf0) << 6));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0xa:
|
case 0xa:
|
||||||
@ -97,7 +122,7 @@ void MOS6560::set_register(int address, uint8_t value)
|
|||||||
|
|
||||||
case 0xe:
|
case 0xe:
|
||||||
update_audio();
|
update_audio();
|
||||||
_auxiliary_colour = _colours[value >> 4];
|
_registers.auxiliary_colour = _colours[value >> 4];
|
||||||
_speaker.set_volume(value & 0xf);
|
_speaker.set_volume(value & 0xf);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -107,12 +132,12 @@ void MOS6560::set_register(int address, uint8_t value)
|
|||||||
output_border(_cycles_in_state * 4);
|
output_border(_cycles_in_state * 4);
|
||||||
_cycles_in_state = 0;
|
_cycles_in_state = 0;
|
||||||
}
|
}
|
||||||
_invertedCells = !((value >> 3)&1);
|
_registers.invertedCells = !((value >> 3)&1);
|
||||||
_borderColour = _colours[value & 0x07];
|
_registers.borderColour = _colours[value & 0x07];
|
||||||
_backgroundColour = _colours[value >> 4];
|
_registers.backgroundColour = _colours[value >> 4];
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// TODO: audio, primarily
|
// TODO: the lightpen, etc
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@ -124,8 +149,8 @@ uint8_t MOS6560::get_register(int address)
|
|||||||
address &= 0xf;
|
address &= 0xf;
|
||||||
switch(address)
|
switch(address)
|
||||||
{
|
{
|
||||||
default: return _registers[address];
|
default: return _registers.direct_values[address];
|
||||||
case 0x03: return (uint8_t)(_vertical_counter << 7) | (_registers[3] & 0x7f);
|
case 0x03: return (uint8_t)(_vertical_counter << 7) | (_registers.direct_values[3] & 0x7f);
|
||||||
case 0x04: return (_vertical_counter >> 1) & 0xff;
|
case 0x04: return (_vertical_counter >> 1) & 0xff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -133,7 +158,7 @@ uint8_t MOS6560::get_register(int address)
|
|||||||
void MOS6560::output_border(unsigned int number_of_cycles)
|
void MOS6560::output_border(unsigned int number_of_cycles)
|
||||||
{
|
{
|
||||||
uint8_t *colour_pointer = _crt->allocate_write_area(1);
|
uint8_t *colour_pointer = _crt->allocate_write_area(1);
|
||||||
if(colour_pointer) *colour_pointer = _borderColour;
|
if(colour_pointer) *colour_pointer = _registers.borderColour;
|
||||||
_crt->output_level(number_of_cycles);
|
_crt->output_level(number_of_cycles);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,66 +167,104 @@ uint16_t MOS6560::get_address()
|
|||||||
// keep track of the amount of time since the speaker was updated; lazy updates are applied
|
// keep track of the amount of time since the speaker was updated; lazy updates are applied
|
||||||
_cycles_since_speaker_update++;
|
_cycles_since_speaker_update++;
|
||||||
|
|
||||||
|
// keep an old copy of the vertical count because that test is a cycle later than the actual changes
|
||||||
|
int previous_vertical_counter = _vertical_counter;
|
||||||
|
|
||||||
// keep track of internal time relative to this scanline
|
// keep track of internal time relative to this scanline
|
||||||
_horizontal_counter++;
|
_horizontal_counter++;
|
||||||
|
if(_horizontal_counter == _timing.cycles_per_line)
|
||||||
// check for end of scanline
|
|
||||||
if(_horizontal_counter == 65)
|
|
||||||
{
|
{
|
||||||
|
if(_horizontal_drawing_latch)
|
||||||
|
{
|
||||||
|
_current_character_row++;
|
||||||
|
if(
|
||||||
|
(_current_character_row == 16) ||
|
||||||
|
(_current_character_row == 8 && !_registers.tall_characters)
|
||||||
|
) {
|
||||||
|
_current_character_row = 0;
|
||||||
|
_current_row++;
|
||||||
|
}
|
||||||
|
|
||||||
|
_pixel_line_cycle = -1;
|
||||||
|
_columns_this_line = -1;
|
||||||
|
_column_counter = -1;
|
||||||
|
}
|
||||||
|
|
||||||
_horizontal_counter = 0;
|
_horizontal_counter = 0;
|
||||||
|
_horizontal_drawing_latch = false;
|
||||||
|
|
||||||
_vertical_counter++;
|
_vertical_counter ++;
|
||||||
_column_counter = -1;
|
if(_vertical_counter == (_registers.interlaced ? (_is_odd_frame ? 262 : 263) : _timing.lines_per_progressive_field))
|
||||||
|
|
||||||
if(_vertical_counter == (_interlaced ? (_is_odd_frame ? 262 : 263) : 261))
|
|
||||||
{
|
{
|
||||||
_is_odd_frame ^= true;
|
|
||||||
_vertical_counter = 0;
|
_vertical_counter = 0;
|
||||||
_row_counter = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(_row_counter >= 0)
|
_is_odd_frame ^= true;
|
||||||
{
|
_current_row = 0;
|
||||||
_row_counter++;
|
_rows_this_field = -1;
|
||||||
if(_row_counter == _number_of_rows*(_tall_characters ? 16 : 8)) _row_counter = -1;
|
_vertical_drawing_latch = false;
|
||||||
}
|
_base_video_matrix_address_counter = 0;
|
||||||
else if(_vertical_counter == _first_row_location * 2)
|
_current_character_row = 0;
|
||||||
{
|
|
||||||
_video_matrix_line_address_counter = _video_matrix_start_address;
|
|
||||||
_row_counter = 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(_column_counter >= 0)
|
// check for vertical starting events
|
||||||
|
_vertical_drawing_latch |= _registers.first_row_location == (previous_vertical_counter >> 1);
|
||||||
|
_horizontal_drawing_latch |= _vertical_drawing_latch && (_horizontal_counter == _registers.first_column_location);
|
||||||
|
|
||||||
|
if(_pixel_line_cycle >= 0) _pixel_line_cycle++;
|
||||||
|
switch(_pixel_line_cycle)
|
||||||
{
|
{
|
||||||
_column_counter++;
|
case -1:
|
||||||
if(_column_counter == _number_of_columns*2)
|
if(_horizontal_drawing_latch)
|
||||||
{
|
|
||||||
_column_counter = -1;
|
|
||||||
if((_row_counter&(_tall_characters ? 15 : 7)) == (_tall_characters ? 15 : 7))
|
|
||||||
{
|
{
|
||||||
_video_matrix_line_address_counter = _video_matrix_address_counter;
|
_pixel_line_cycle = 0;
|
||||||
|
_video_matrix_address_counter = _base_video_matrix_address_counter;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 1: _columns_this_line = _registers.number_of_columns; break;
|
||||||
|
case 2: if(_rows_this_field < 0) _rows_this_field = _registers.number_of_rows; break;
|
||||||
|
case 3: if(_current_row < _rows_this_field) _column_counter = 0; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t fetch_address = 0x1c;
|
||||||
|
if(_column_counter >= 0 && _column_counter < _columns_this_line*2)
|
||||||
|
{
|
||||||
|
if(_column_counter&1)
|
||||||
|
{
|
||||||
|
fetch_address = _registers.character_cell_start_address + (_character_code*(_registers.tall_characters ? 16 : 8)) + _current_character_row;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fetch_address = (uint16_t)(_registers.video_matrix_start_address + _video_matrix_address_counter);
|
||||||
|
_video_matrix_address_counter++;
|
||||||
|
if(
|
||||||
|
(_current_character_row == 15) ||
|
||||||
|
(_current_character_row == 7 && !_registers.tall_characters)
|
||||||
|
) {
|
||||||
|
_base_video_matrix_address_counter = _video_matrix_address_counter;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(_horizontal_counter == _first_column_location)
|
return fetch_address;
|
||||||
{
|
}
|
||||||
_column_counter = 0;
|
|
||||||
_video_matrix_address_counter = _video_matrix_line_address_counter;
|
void MOS6560::set_graphics_value(uint8_t value, uint8_t colour_value)
|
||||||
}
|
{
|
||||||
|
// TODO: there should be a further two-cycle delay on pixels being output; the reverse bit should
|
||||||
|
// divide the byte it is set for 3:1 and then continue as usual.
|
||||||
|
|
||||||
// determine output state; colour burst and sync timing are currently a guess
|
// determine output state; colour burst and sync timing are currently a guess
|
||||||
if(_horizontal_counter > 61) _this_state = State::ColourBurst;
|
if(_horizontal_counter > _timing.cycles_per_line-4) _this_state = State::ColourBurst;
|
||||||
else if(_horizontal_counter > 57) _this_state = State::Sync;
|
else if(_horizontal_counter > _timing.cycles_per_line-7) _this_state = State::Sync;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_this_state = (_column_counter >= 0 && _row_counter >= 0) ? State::Pixels : State::Border;
|
_this_state = (_column_counter >= 0 && _column_counter < _columns_this_line*2) ? State::Pixels : State::Border;
|
||||||
}
|
}
|
||||||
|
|
||||||
// apply vertical sync
|
// apply vertical sync
|
||||||
if(
|
if(
|
||||||
(_vertical_counter < 3 && (_is_odd_frame || !_interlaced)) ||
|
(_vertical_counter < 3 && (_is_odd_frame || !_registers.interlaced)) ||
|
||||||
(_interlaced &&
|
(_registers.interlaced &&
|
||||||
(
|
(
|
||||||
(_vertical_counter == 0 && _horizontal_counter > 32) ||
|
(_vertical_counter == 0 && _horizontal_counter > 32) ||
|
||||||
(_vertical_counter == 1) || (_vertical_counter == 2) ||
|
(_vertical_counter == 1) || (_vertical_counter == 2) ||
|
||||||
@ -231,44 +294,6 @@ uint16_t MOS6560::get_address()
|
|||||||
}
|
}
|
||||||
_cycles_in_state++;
|
_cycles_in_state++;
|
||||||
|
|
||||||
// compute the address
|
|
||||||
if(_this_state == State::Pixels)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
Per http://tinyvga.com/6561 :
|
|
||||||
|
|
||||||
The basic video timing is very simple. For
|
|
||||||
every character the VIC-I is about to display, it first fetches the
|
|
||||||
character code and colour, then the character appearance (from the
|
|
||||||
character generator memory). The character codes are read on every
|
|
||||||
raster line, thus making every line a "bad line". When the raster
|
|
||||||
beam is outside of the text window, the videochip reads from $001c for
|
|
||||||
most time. (Some videochips read from $181c instead.) The address
|
|
||||||
occasionally varies, but it might also be due to a flaky bus. (By
|
|
||||||
reading from unconnected address space, such as $9100-$910f, you can
|
|
||||||
read the data fetched by the videochip on the previous clock cycle.)
|
|
||||||
*/
|
|
||||||
if(_column_counter&1)
|
|
||||||
{
|
|
||||||
return _character_cell_start_address + (_character_code*(_tall_characters ? 16 : 8)) + (_row_counter&(_tall_characters ? 15 : 7));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
uint16_t result = _video_matrix_address_counter;
|
|
||||||
_video_matrix_address_counter++;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0x1c;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MOS6560::set_graphics_value(uint8_t value, uint8_t colour_value)
|
|
||||||
{
|
|
||||||
// TODO: this isn't correct, as _character_value will be
|
|
||||||
// accessed second, then output will roll over. Probably it's
|
|
||||||
// correct (given the delays upstream) to output all 8 on an &1
|
|
||||||
// and to adjust the signalling to the CRT above?
|
|
||||||
if(_this_state == State::Pixels)
|
if(_this_state == State::Pixels)
|
||||||
{
|
{
|
||||||
if(_column_counter&1)
|
if(_column_counter&1)
|
||||||
@ -281,14 +306,14 @@ void MOS6560::set_graphics_value(uint8_t value, uint8_t colour_value)
|
|||||||
if(!(_character_colour&0x8))
|
if(!(_character_colour&0x8))
|
||||||
{
|
{
|
||||||
uint8_t colours[2];
|
uint8_t colours[2];
|
||||||
if(_invertedCells)
|
if(_registers.invertedCells)
|
||||||
{
|
{
|
||||||
colours[0] = cell_colour;
|
colours[0] = cell_colour;
|
||||||
colours[1] = _backgroundColour;
|
colours[1] = _registers.backgroundColour;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
colours[0] = _backgroundColour;
|
colours[0] = _registers.backgroundColour;
|
||||||
colours[1] = cell_colour;
|
colours[1] = cell_colour;
|
||||||
}
|
}
|
||||||
pixel_pointer[0] = colours[(_character_value >> 7)&1];
|
pixel_pointer[0] = colours[(_character_value >> 7)&1];
|
||||||
@ -302,7 +327,7 @@ void MOS6560::set_graphics_value(uint8_t value, uint8_t colour_value)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
uint8_t colours[4] = {_backgroundColour, _borderColour, cell_colour, _auxiliary_colour};
|
uint8_t colours[4] = {_registers.backgroundColour, _registers.borderColour, cell_colour, _registers.auxiliary_colour};
|
||||||
pixel_pointer[0] =
|
pixel_pointer[0] =
|
||||||
pixel_pointer[1] = colours[(_character_value >> 6)&3];
|
pixel_pointer[1] = colours[(_character_value >> 6)&3];
|
||||||
pixel_pointer[2] =
|
pixel_pointer[2] =
|
||||||
@ -320,6 +345,8 @@ void MOS6560::set_graphics_value(uint8_t value, uint8_t colour_value)
|
|||||||
_character_code = value;
|
_character_code = value;
|
||||||
_character_colour = colour_value;
|
_character_colour = colour_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_column_counter++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,6 +28,14 @@ class MOS6560 {
|
|||||||
Outputs::CRT::CRT *get_crt() { return _crt.get(); }
|
Outputs::CRT::CRT *get_crt() { return _crt.get(); }
|
||||||
Outputs::Speaker *get_speaker() { return &_speaker; }
|
Outputs::Speaker *get_speaker() { return &_speaker; }
|
||||||
|
|
||||||
|
enum OutputMode {
|
||||||
|
PAL, NTSC
|
||||||
|
};
|
||||||
|
/*!
|
||||||
|
Sets the output mode to either PAL or NTSC.
|
||||||
|
*/
|
||||||
|
void set_output_mode(OutputMode output_mode);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
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.
|
||||||
*/
|
*/
|
||||||
@ -56,6 +64,8 @@ class MOS6560 {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<Outputs::CRT::CRT> _crt;
|
std::unique_ptr<Outputs::CRT::CRT> _crt;
|
||||||
|
|
||||||
|
// audio state
|
||||||
class Speaker: public ::Outputs::Filter<Speaker> {
|
class Speaker: public ::Outputs::Filter<Speaker> {
|
||||||
public:
|
public:
|
||||||
Speaker();
|
Speaker();
|
||||||
@ -72,38 +82,56 @@ class MOS6560 {
|
|||||||
uint8_t _control_registers[4];
|
uint8_t _control_registers[4];
|
||||||
uint8_t _volume;
|
uint8_t _volume;
|
||||||
} _speaker;
|
} _speaker;
|
||||||
|
unsigned int _cycles_since_speaker_update;
|
||||||
|
void update_audio();
|
||||||
|
|
||||||
bool _interlaced, _tall_characters;
|
// register state
|
||||||
uint8_t _first_column_location, _first_row_location;
|
struct {
|
||||||
uint8_t _number_of_columns, _number_of_rows;
|
bool interlaced, tall_characters;
|
||||||
uint16_t _character_cell_start_address, _video_matrix_start_address;
|
uint8_t first_column_location, first_row_location;
|
||||||
uint8_t _backgroundColour, _borderColour, _auxiliary_colour;
|
uint8_t number_of_columns, number_of_rows;
|
||||||
bool _invertedCells;
|
uint16_t character_cell_start_address, video_matrix_start_address;
|
||||||
|
uint8_t backgroundColour, borderColour, auxiliary_colour;
|
||||||
|
bool invertedCells;
|
||||||
|
|
||||||
int _horizontal_counter, _vertical_counter;
|
uint8_t direct_values[16];
|
||||||
bool _did_output_graphics;
|
} _registers;
|
||||||
|
|
||||||
int _column_counter, _row_counter;
|
|
||||||
uint16_t _video_matrix_address_counter, _video_matrix_line_address_counter;
|
|
||||||
|
|
||||||
|
// output state
|
||||||
enum State {
|
enum State {
|
||||||
Sync, ColourBurst, Border, Pixels
|
Sync, ColourBurst, Border, Pixels
|
||||||
} _this_state, _output_state;
|
} _this_state, _output_state;
|
||||||
unsigned int _cycles_in_state;
|
unsigned int _cycles_in_state;
|
||||||
|
|
||||||
|
// counters that cover an entire field
|
||||||
|
int _horizontal_counter, _vertical_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;
|
uint8_t _character_code, _character_colour, _character_value;
|
||||||
|
|
||||||
uint8_t *pixel_pointer;
|
|
||||||
|
|
||||||
uint8_t _registers[16];
|
|
||||||
uint8_t _colours[16];
|
|
||||||
|
|
||||||
bool _is_odd_frame;
|
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);
|
void output_border(unsigned int number_of_cycles);
|
||||||
|
|
||||||
unsigned int _cycles_since_speaker_update;
|
struct {
|
||||||
void update_audio();
|
int cycles_per_line;
|
||||||
|
int lines_per_progressive_field;
|
||||||
|
bool supports_interlacing;
|
||||||
|
} _timing;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -47,35 +47,36 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin
|
|||||||
// 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))
|
||||||
{
|
{
|
||||||
*value = read_memory(address);
|
uint8_t result = read_memory(address);
|
||||||
if((address&0xfff0) == 0x9000)
|
if((address&0xff00) == 0x9000)
|
||||||
{
|
{
|
||||||
*value = _mos6560->get_register(address - 0x9000);
|
result &= _mos6560->get_register(address);
|
||||||
}
|
}
|
||||||
else if((address&0xfff0) == 0x9110)
|
if((address&0xfc10) == 0x9010)
|
||||||
{
|
{
|
||||||
*value = _userPortVIA.get_register(address - 0x9110);
|
result &= _userPortVIA.get_register(address);
|
||||||
}
|
}
|
||||||
else if((address&0xfff0) == 0x9120)
|
if((address&0xfc20) == 0x9020)
|
||||||
{
|
{
|
||||||
*value = _keyboardVIA.get_register(address - 0x9120);
|
result &= _keyboardVIA.get_register(address);
|
||||||
}
|
}
|
||||||
|
*value = result;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
uint8_t *ram = ram_pointer(address);
|
uint8_t *ram = ram_pointer(address);
|
||||||
if(ram) *ram = *value;
|
if(ram) *ram = *value;
|
||||||
else if((address&0xfff0) == 0x9000)
|
if((address&0xff00) == 0x9000)
|
||||||
{
|
{
|
||||||
_mos6560->set_register(address - 0x9000, *value);
|
_mos6560->set_register(address, *value);
|
||||||
}
|
}
|
||||||
else if((address&0xfff0) == 0x9110)
|
if((address&0xfc10) == 0x9010)
|
||||||
{
|
{
|
||||||
_userPortVIA.set_register(address - 0x9110, *value);
|
_userPortVIA.set_register(address, *value);
|
||||||
}
|
}
|
||||||
else if((address&0xfff0) == 0x9120)
|
if((address&0xfc20) == 0x9020)
|
||||||
{
|
{
|
||||||
_keyboardVIA.set_register(address - 0x9120, *value);
|
_keyboardVIA.set_register(address, *value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user