mirror of
https://github.com/TomHarte/CLK.git
synced 2025-01-25 09:30:14 +00:00
Merge pull request #318 from TomHarte/TMSVRAMTiming
Attempts real VRAM access timings for the TMS9918a
This commit is contained in:
commit
38c912b968
@ -144,33 +144,72 @@ void TMS9918::run_for(const HalfCycles cycles) {
|
|||||||
// potential errors mapping back and forth.
|
// potential errors mapping back and forth.
|
||||||
half_cycles_into_frame_ = (half_cycles_into_frame_ + cycles) % HalfCycles(frame_lines_ * 228 * 2);
|
half_cycles_into_frame_ = (half_cycles_into_frame_ + cycles) % HalfCycles(frame_lines_ * 228 * 2);
|
||||||
|
|
||||||
// Convert to 342 cycles per line; the internal clock is 1.5 times the
|
// Convert 456 clocked half cycles per line to 342 internal cycles per line;
|
||||||
// nominal 3.579545 Mhz that I've advertised for this part.
|
// the internal clock is 1.5 times the nominal 3.579545 Mhz that I've advertised
|
||||||
|
// for this part. So multiply by three quarters.
|
||||||
int int_cycles = (cycles.as_int() * 3) + cycles_error_;
|
int int_cycles = (cycles.as_int() * 3) + cycles_error_;
|
||||||
cycles_error_ = int_cycles & 7;
|
cycles_error_ = int_cycles & 3;
|
||||||
int_cycles >>= 3;
|
int_cycles >>= 2;
|
||||||
if(!int_cycles) return;
|
if(!int_cycles) return;
|
||||||
|
|
||||||
while(int_cycles) {
|
while(int_cycles) {
|
||||||
// Determine how much time has passed in the remainder of this line, and proceed.
|
// Determine how much time has passed in the remainder of this line, and proceed.
|
||||||
int cycles_left = std::min(342 - column_, int_cycles);
|
int cycles_left = std::min(342 - column_, int_cycles);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// ------------------------------------
|
||||||
|
// Potentially perform a memory access.
|
||||||
|
// ------------------------------------
|
||||||
|
if(queued_access_ != MemoryAccess::None) {
|
||||||
|
int time_until_access_slot = 0;
|
||||||
|
switch(line_mode_) {
|
||||||
|
case LineMode::Refresh:
|
||||||
|
if(column_ < 53 || column_ >= 307) time_until_access_slot = column_&1;
|
||||||
|
else time_until_access_slot = 3 - ((column_ - 53)&3);
|
||||||
|
// i.e. 53 -> 3, 52 -> 2, 51 -> 1, 50 -> 0, etc
|
||||||
|
break;
|
||||||
|
case LineMode::Text:
|
||||||
|
if(column_ < 59 || column_ >= 299) time_until_access_slot = column_&1;
|
||||||
|
else time_until_access_slot = 5 - ((column_ + 3)%6);
|
||||||
|
// i.e. 59 -> 3, 60 -> 2, 61 -> 1, etc
|
||||||
|
break;
|
||||||
|
case LineMode::Character:
|
||||||
|
if(column_ < 9) time_until_access_slot = column_&1;
|
||||||
|
else if(column_ < 30) time_until_access_slot = 30 - column_;
|
||||||
|
else if(column_ < 37) time_until_access_slot = column_&1;
|
||||||
|
else if(column_ < 311) time_until_access_slot = 31 - ((column_ + 7)&31);
|
||||||
|
// i.e. 53 -> 3, 54 -> 2, 55 -> 1, 56 -> 0, 57 -> 31, etc
|
||||||
|
else if(column_ < 313) time_until_access_slot = column_&1;
|
||||||
|
else time_until_access_slot = 342 - column_;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(cycles_left >= time_until_access_slot) {
|
||||||
|
if(queued_access_ == MemoryAccess::Write) {
|
||||||
|
ram_[queued_address_] = read_ahead_buffer_;
|
||||||
|
} else {
|
||||||
|
read_ahead_buffer_ = ram_[queued_address_];
|
||||||
|
}
|
||||||
|
queued_access_ = MemoryAccess::None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
column_ += cycles_left; // column_ is now the column that has been reached in this line.
|
column_ += cycles_left; // column_ is now the column that has been reached in this line.
|
||||||
int_cycles -= cycles_left; // Count down duration to run for.
|
int_cycles -= cycles_left; // Count down duration to run for.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ------------------------------
|
|
||||||
// TODO: memory access slot here.
|
|
||||||
// ------------------------------
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ------------------------------
|
// ------------------------------
|
||||||
// Perform video memory accesses.
|
// Perform video memory accesses.
|
||||||
// ------------------------------
|
// ------------------------------
|
||||||
if(((row_ < 192) || (row_ == frame_lines_-1)) && !blank_screen_) {
|
if(((row_ < 192) || (row_ == frame_lines_-1)) && !blank_screen_) {
|
||||||
const int access_slot = column_ >> 1; // There are only 171 available memory accesses per line.
|
const int access_slot = column_ >> 1; // There are only 171 available memory accesses per line.
|
||||||
switch(line_mode_) {
|
switch(line_mode_) {
|
||||||
|
default: break;
|
||||||
|
|
||||||
case LineMode::Text:
|
case LineMode::Text:
|
||||||
access_pointer_ = std::min(30, access_slot);
|
access_pointer_ = std::min(30, access_slot);
|
||||||
if(access_pointer_ >= 30 && access_pointer_ < 150) {
|
if(access_pointer_ >= 30 && access_pointer_ < 150) {
|
||||||
@ -327,6 +366,8 @@ void TMS9918::run_for(const HalfCycles cycles) {
|
|||||||
|
|
||||||
if(output_column_ < pixels_end) {
|
if(output_column_ < pixels_end) {
|
||||||
switch(line_mode_) {
|
switch(line_mode_) {
|
||||||
|
default: break;
|
||||||
|
|
||||||
case LineMode::Text: {
|
case LineMode::Text: {
|
||||||
const uint32_t colours[2] = { palette[background_colour_], palette[text_colour_] };
|
const uint32_t colours[2] = { palette[background_colour_], palette[text_colour_] };
|
||||||
|
|
||||||
@ -499,6 +540,7 @@ void TMS9918::run_for(const HalfCycles cycles) {
|
|||||||
first_right_border_column_ = 319;
|
first_right_border_column_ = 319;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if(blank_screen_ || (row_ >= 192 && row_ != frame_lines_-1)) line_mode_ = LineMode::Refresh;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -509,15 +551,17 @@ void TMS9918::output_border(int cycles) {
|
|||||||
crt_->output_level(static_cast<unsigned int>(cycles) * 4);
|
crt_->output_level(static_cast<unsigned int>(cycles) * 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: as a temporary development measure, memory access below is magically instantaneous. Correct that.
|
|
||||||
|
|
||||||
void TMS9918::set_register(int address, uint8_t value) {
|
void TMS9918::set_register(int address, uint8_t value) {
|
||||||
// Writes to address 0 are writes to the video RAM. Store
|
// Writes to address 0 are writes to the video RAM. Store
|
||||||
// the value and return.
|
// the value and return.
|
||||||
if(!(address & 1)) {
|
if(!(address & 1)) {
|
||||||
write_phase_ = false;
|
write_phase_ = false;
|
||||||
read_ahead_buffer_ = value;
|
read_ahead_buffer_ = value;
|
||||||
ram_[ram_pointer_ & 16383] = value;
|
|
||||||
|
// Enqueue the write to occur at the next available slot.
|
||||||
|
queued_access_ = MemoryAccess::Write;
|
||||||
|
queued_address_ = ram_pointer_ & 16383;
|
||||||
|
|
||||||
ram_pointer_++;
|
ram_pointer_++;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -594,7 +638,11 @@ uint8_t TMS9918::get_register(int address) {
|
|||||||
// Reads from address 0 read video RAM, via the read-ahead buffer.
|
// Reads from address 0 read video RAM, via the read-ahead buffer.
|
||||||
if(!(address & 1)) {
|
if(!(address & 1)) {
|
||||||
uint8_t result = read_ahead_buffer_;
|
uint8_t result = read_ahead_buffer_;
|
||||||
read_ahead_buffer_ = ram_[ram_pointer_ & 16383];
|
|
||||||
|
// Enqueue the write to occur at the next available slot.
|
||||||
|
queued_access_ = MemoryAccess::Read;
|
||||||
|
queued_address_ = ram_pointer_ & 16383;
|
||||||
|
|
||||||
ram_pointer_++;
|
ram_pointer_++;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -65,6 +65,10 @@ class TMS9918 {
|
|||||||
|
|
||||||
uint16_t ram_pointer_ = 0;
|
uint16_t ram_pointer_ = 0;
|
||||||
uint8_t read_ahead_buffer_ = 0;
|
uint8_t read_ahead_buffer_ = 0;
|
||||||
|
enum class MemoryAccess {
|
||||||
|
Read, Write, None
|
||||||
|
} queued_access_ = MemoryAccess::None;
|
||||||
|
uint16_t queued_address_;
|
||||||
|
|
||||||
uint8_t status_ = 0;
|
uint8_t status_ = 0;
|
||||||
|
|
||||||
@ -100,8 +104,9 @@ class TMS9918 {
|
|||||||
|
|
||||||
// Horizontal selections.
|
// Horizontal selections.
|
||||||
enum class LineMode {
|
enum class LineMode {
|
||||||
Text,
|
Text = 0,
|
||||||
Character
|
Character = 1,
|
||||||
|
Refresh = 2
|
||||||
} line_mode_ = LineMode::Text;
|
} line_mode_ = LineMode::Text;
|
||||||
int first_pixel_column_, first_right_border_column_;
|
int first_pixel_column_, first_right_border_column_;
|
||||||
|
|
||||||
|
@ -175,6 +175,10 @@ class ConcreteMachine:
|
|||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
case CPU::Z80::PartialMachineCycle::Interrupt:
|
||||||
|
*cycle.value = 0xff;
|
||||||
|
break;
|
||||||
|
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user