mirror of
https://github.com/TomHarte/CLK.git
synced 2024-09-29 16:55:59 +00:00
Switch to absolute placement of deferred events; properly serialise commands.
This commit is contained in:
parent
7e69e33ec2
commit
c6cda7c401
@ -167,10 +167,6 @@ void TMS9918<personality>::run_for(const HalfCycles cycles) {
|
|||||||
const int end_column = this->fetch_pointer_.column + fetch_cycles;
|
const int end_column = this->fetch_pointer_.column + fetch_cycles;
|
||||||
LineBuffer &line_buffer = this->line_buffers_[this->fetch_pointer_.row];
|
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.
|
// ... and to any pending Yamaha commands.
|
||||||
if constexpr (is_yamaha_vdp(personality)) {
|
if constexpr (is_yamaha_vdp(personality)) {
|
||||||
if(Storage<personality>::command_) {
|
if(Storage<personality>::command_) {
|
||||||
@ -279,11 +275,26 @@ void TMS9918<personality>::run_for(const HalfCycles cycles) {
|
|||||||
this->fetch_pointer_.column = end_column;
|
this->fetch_pointer_.column = end_column;
|
||||||
fetch_cycles_pool -= fetch_cycles;
|
fetch_cycles_pool -= fetch_cycles;
|
||||||
|
|
||||||
|
// Check for end of line.
|
||||||
if(this->fetch_pointer_.column == Timing<personality>::CyclesPerLine) {
|
if(this->fetch_pointer_.column == Timing<personality>::CyclesPerLine) {
|
||||||
this->fetch_pointer_.column = 0;
|
this->fetch_pointer_.column = 0;
|
||||||
this->fetch_pointer_.row = (this->fetch_pointer_.row + 1) % this->mode_timing_.total_lines;
|
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];
|
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
|
// Establish the current screen output mode, which will be captured as a
|
||||||
// line mode momentarily.
|
// line mode momentarily.
|
||||||
this->screen_mode_ = this->current_screen_mode();
|
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.
|
// Enqueue the write to occur at the next available slot.
|
||||||
read_ahead_buffer_ = value;
|
read_ahead_buffer_ = value;
|
||||||
queued_access_ = MemoryAccess::Write;
|
queued_access_ = MemoryAccess::Write;
|
||||||
cycles_until_access_ = Timing<personality>::VRAMAccessDelay;
|
minimum_access_column_ = fetch_pointer_.column + Timing<personality>::VRAMAccessDelay;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <Personality personality>
|
template <Personality personality>
|
||||||
@ -890,34 +901,9 @@ void Base<personality>::commit_register(int reg, uint8_t value) {
|
|||||||
#undef Begin
|
#undef Begin
|
||||||
|
|
||||||
// Seed timing information if a command was found.
|
// Seed timing information if a command was found.
|
||||||
if(Storage<personality>::command_) {
|
Storage<personality>::update_command_step(fetch_pointer_.column);
|
||||||
// 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?
|
|
||||||
|
|
||||||
// TODO: undo temporary assumption of Graphics Mode 5 and of logical operation.
|
if(!Storage<personality>::command_) {
|
||||||
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 {
|
|
||||||
LOG("TODO: Yamaha command " << PADHEX(2) << +value);
|
LOG("TODO: Yamaha command " << PADHEX(2) << +value);
|
||||||
}
|
}
|
||||||
break;
|
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
|
// A read request is enqueued upon setting the address; conversely a write
|
||||||
// won't be enqueued unless and until some actual data is supplied.
|
// won't be enqueued unless and until some actual data is supplied.
|
||||||
queued_access_ = MemoryAccess::Read;
|
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)) {
|
if constexpr (is_sega_vdp(personality)) {
|
||||||
@ -1027,7 +1013,8 @@ uint8_t Base<personality>::read_register() {
|
|||||||
return
|
return
|
||||||
(queued_access_ == MemoryAccess::None ? 0x80 : 0x00) |
|
(queued_access_ == MemoryAccess::None ? 0x80 : 0x00) |
|
||||||
(is_vertical_blank() ? 0x40 : 0x00) |
|
(is_vertical_blank() ? 0x40 : 0x00) |
|
||||||
(is_horizontal_blank() ? 0x20 : 0x00);
|
(is_horizontal_blank() ? 0x20 : 0x00) |
|
||||||
|
(Storage<personality>::command_ ? 0x01 : 0x00);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -194,13 +194,15 @@ template <Personality personality> struct Storage<personality, std::enable_if_t<
|
|||||||
};
|
};
|
||||||
CommandStep next_command_step_ = CommandStep::None;
|
CommandStep next_command_step_ = CommandStep::None;
|
||||||
int minimum_command_column_ = 0;
|
int minimum_command_column_ = 0;
|
||||||
|
uint8_t command_latch_ = 0;
|
||||||
|
|
||||||
void update_command_step() {
|
void update_command_step(int current_column) {
|
||||||
if(!command_) {
|
if(!command_) {
|
||||||
next_command_step_ = CommandStep::None;
|
next_command_step_ = CommandStep::None;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
minimum_command_column_ = current_column + command_->cycles;
|
||||||
switch(command_->access) {
|
switch(command_->access) {
|
||||||
case Command::AccessType::PlotPoint:
|
case Command::AccessType::PlotPoint:
|
||||||
next_command_step_ = CommandStep::ReadPixel;
|
next_command_step_ = CommandStep::ReadPixel;
|
||||||
@ -423,7 +425,6 @@ template <Personality personality> struct Base: public Storage<personality> {
|
|||||||
size_t ram_pointer_ = 0;
|
size_t ram_pointer_ = 0;
|
||||||
uint8_t read_ahead_buffer_ = 0;
|
uint8_t read_ahead_buffer_ = 0;
|
||||||
MemoryAccess queued_access_ = MemoryAccess::None;
|
MemoryAccess queued_access_ = MemoryAccess::None;
|
||||||
int cycles_until_access_ = 0;
|
|
||||||
int minimum_access_column_ = 0;
|
int minimum_access_column_ = 0;
|
||||||
|
|
||||||
// The main status register.
|
// The main status register.
|
||||||
@ -574,14 +575,52 @@ template <Personality personality> struct Base: public Storage<personality> {
|
|||||||
// has yet to pass.
|
// has yet to pass.
|
||||||
if(queued_access_ == MemoryAccess::None || access_column < minimum_access_column_) {
|
if(queued_access_ == MemoryAccess::None || access_column < minimum_access_column_) {
|
||||||
if constexpr (is_yamaha_vdp(personality)) {
|
if constexpr (is_yamaha_vdp(personality)) {
|
||||||
// if(
|
using CommandStep = typename Storage<personality>::CommandStep;
|
||||||
// Storage<personality>::next_command_step_ == Storage<personality>::CommandStep::None ||
|
|
||||||
// access_column < Storage<personality>::minimum_access_column_
|
|
||||||
// ) {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// 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;
|
return;
|
||||||
|
Loading…
Reference in New Issue
Block a user