1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-06-25 18:30:07 +00:00

Switch to absolute placement of deferred events; properly serialise commands.

This commit is contained in:
Thomas Harte 2023-01-28 11:55:12 -05:00
parent 7e69e33ec2
commit c6cda7c401
2 changed files with 69 additions and 43 deletions

View File

@ -167,10 +167,6 @@ void TMS9918<personality>::run_for(const HalfCycles cycles) {
const int end_column = this->fetch_pointer_.column + fetch_cycles;
LineBuffer &line_buffer = this->line_buffers_[this->fetch_pointer_.row];
// Determine what this does to any enqueued VRAM access.
this->minimum_access_column_ = this->fetch_pointer_.column + this->cycles_until_access_;
this->cycles_until_access_ -= fetch_cycles;
// ... and to any pending Yamaha commands.
if constexpr (is_yamaha_vdp(personality)) {
if(Storage<personality>::command_) {
@ -279,11 +275,26 @@ void TMS9918<personality>::run_for(const HalfCycles cycles) {
this->fetch_pointer_.column = end_column;
fetch_cycles_pool -= fetch_cycles;
// Check for end of line.
if(this->fetch_pointer_.column == Timing<personality>::CyclesPerLine) {
this->fetch_pointer_.column = 0;
this->fetch_pointer_.row = (this->fetch_pointer_.row + 1) % this->mode_timing_.total_lines;
LineBuffer &next_line_buffer = this->line_buffers_[this->fetch_pointer_.row];
// Progress towards any delayed events.
this->minimum_access_column_ =
std::max(
0,
this->minimum_access_column_ - Timing<personality>::CyclesPerLine
);
if constexpr (is_yamaha_vdp(personality)) {
Storage<personality>::minimum_command_column_ =
std::max(
0,
Storage<personality>::minimum_command_column_ - Timing<personality>::CyclesPerLine
);
}
// Establish the current screen output mode, which will be captured as a
// line mode momentarily.
this->screen_mode_ = this->current_screen_mode();
@ -586,7 +597,7 @@ void Base<personality>::write_vram(uint8_t value) {
// Enqueue the write to occur at the next available slot.
read_ahead_buffer_ = value;
queued_access_ = MemoryAccess::Write;
cycles_until_access_ = Timing<personality>::VRAMAccessDelay;
minimum_access_column_ = fetch_pointer_.column + Timing<personality>::VRAMAccessDelay;
}
template <Personality personality>
@ -890,34 +901,9 @@ void Base<personality>::commit_register(int reg, uint8_t value) {
#undef Begin
// Seed timing information if a command was found.
if(Storage<personality>::command_) {
// TODO: eliminate SUPER HACK.
// This is currently here primarily to help me to hash out the
// proper interface, and also because I'm just not persuaded
// that minimum_access_column_ works properly. Why doesn't it
// ever end up out of bounds?
Storage<personality>::update_command_step(fetch_pointer_.column);
// TODO: undo temporary assumption of Graphics Mode 5 and of logical operation.
uint8_t packed_colour = Storage<personality>::command_context_.colour & 3;
packed_colour |= packed_colour << 2;
packed_colour |= packed_colour << 4;
while(!Storage<personality>::command_->done()) {
const unsigned int address =
(Storage<personality>::command_->location.v[0] >> 2) +
(Storage<personality>::command_->location.v[1] << 7);
const uint8_t mask = 0xc0 >> ((Storage<personality>::command_->location.v[0] & 3) << 1);
uint8_t v = ram_[address];
v &= ~mask;
v |= packed_colour & mask;
ram_[address] = v;
Storage<personality>::command_->advance();
}
Storage<personality>::command_ = nullptr;
} else {
if(!Storage<personality>::command_) {
LOG("TODO: Yamaha command " << PADHEX(2) << +value);
}
break;
@ -951,7 +937,7 @@ void Base<personality>::write_register(uint8_t value) {
// A read request is enqueued upon setting the address; conversely a write
// won't be enqueued unless and until some actual data is supplied.
queued_access_ = MemoryAccess::Read;
cycles_until_access_ = Timing<personality>::VRAMAccessDelay;
minimum_access_column_ = fetch_pointer_.column + Timing<personality>::VRAMAccessDelay;
}
if constexpr (is_sega_vdp(personality)) {
@ -1027,7 +1013,8 @@ uint8_t Base<personality>::read_register() {
return
(queued_access_ == MemoryAccess::None ? 0x80 : 0x00) |
(is_vertical_blank() ? 0x40 : 0x00) |
(is_horizontal_blank() ? 0x20 : 0x00);
(is_horizontal_blank() ? 0x20 : 0x00) |
(Storage<personality>::command_ ? 0x01 : 0x00);
break;
}

View File

@ -194,13 +194,15 @@ template <Personality personality> struct Storage<personality, std::enable_if_t<
};
CommandStep next_command_step_ = CommandStep::None;
int minimum_command_column_ = 0;
uint8_t command_latch_ = 0;
void update_command_step() {
void update_command_step(int current_column) {
if(!command_) {
next_command_step_ = CommandStep::None;
return;
}
minimum_command_column_ = current_column + command_->cycles;
switch(command_->access) {
case Command::AccessType::PlotPoint:
next_command_step_ = CommandStep::ReadPixel;
@ -423,7 +425,6 @@ template <Personality personality> struct Base: public Storage<personality> {
size_t ram_pointer_ = 0;
uint8_t read_ahead_buffer_ = 0;
MemoryAccess queued_access_ = MemoryAccess::None;
int cycles_until_access_ = 0;
int minimum_access_column_ = 0;
// The main status register.
@ -574,14 +575,52 @@ template <Personality personality> struct Base: public Storage<personality> {
// has yet to pass.
if(queued_access_ == MemoryAccess::None || access_column < minimum_access_column_) {
if constexpr (is_yamaha_vdp(personality)) {
// if(
// Storage<personality>::next_command_step_ == Storage<personality>::CommandStep::None ||
// access_column < Storage<personality>::minimum_access_column_
// ) {
// return;
// }
using CommandStep = typename Storage<personality>::CommandStep;
// TODO: command-engine action.
if(
Storage<personality>::next_command_step_ == CommandStep::None ||
access_column < Storage<personality>::minimum_command_column_
) {
return;
}
// TODO: undo assumption of Graphics Mode 5, pervasively but starting here.
const unsigned int address =
(Storage<personality>::command_->location.v[0] >> 2) +
(Storage<personality>::command_->location.v[1] << 7);
switch(Storage<personality>::next_command_step_) {
// Duplicative, but keeps the compiler happy.
case CommandStep::None:
break;
case CommandStep::ReadPixel:
Storage<personality>::command_latch_ = ram_[address];
Storage<personality>::minimum_command_column_ = access_column + 24;
Storage<personality>::next_command_step_ = CommandStep::WritePixel;
break;
case CommandStep::WritePixel: {
uint8_t packed_colour = Storage<personality>::command_context_.colour & 3;
packed_colour |= packed_colour << 2;
packed_colour |= packed_colour << 4;
const uint8_t mask = 0xc0 >> ((Storage<personality>::command_->location.v[0] & 3) << 1);
Storage<personality>::command_latch_ &= ~mask;
Storage<personality>::command_latch_ |= packed_colour & mask;
ram_[address] = Storage<personality>::command_latch_;
Storage<personality>::command_->advance();
if(Storage<personality>::command_->done()) {
Storage<personality>::command_ = nullptr;
Storage<personality>::next_command_step_ = CommandStep::None;
} else {
Storage<personality>::update_command_step(access_column);
}
} break;
}
}
return;