mirror of
https://github.com/TomHarte/CLK.git
synced 2025-02-18 16:30:29 +00:00
Merge branch 'MSX2' of github.com:TomHarte/CLK into MSX2
This commit is contained in:
commit
8f5c7fcabc
@ -46,8 +46,8 @@ Base<personality>::Base() :
|
|||||||
// at a random position.
|
// at a random position.
|
||||||
fetch_pointer_.row = rand() % 262;
|
fetch_pointer_.row = rand() % 262;
|
||||||
fetch_pointer_.column = rand() % (Timing<personality>::CyclesPerLine - output_lag);
|
fetch_pointer_.column = rand() % (Timing<personality>::CyclesPerLine - output_lag);
|
||||||
output_pointer_.row = output_pointer_.row;
|
output_pointer_.row = fetch_pointer_.row;
|
||||||
output_pointer_.column = output_pointer_.column + output_lag;
|
output_pointer_.column = fetch_pointer_.column + output_lag;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <Personality personality>
|
template <Personality personality>
|
||||||
@ -490,7 +490,7 @@ void TMS9918<personality>::run_for(const HalfCycles cycles) {
|
|||||||
this->asked_for_write_area_ = true;
|
this->asked_for_write_area_ = true;
|
||||||
|
|
||||||
this->pixel_origin_ = this->pixel_target_ = reinterpret_cast<uint32_t *>(
|
this->pixel_origin_ = this->pixel_target_ = reinterpret_cast<uint32_t *>(
|
||||||
this->crt_.begin_data(line_buffer.pixel_count)
|
this->crt_.begin_data(size_t(line_buffer.pixel_count))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -509,7 +509,7 @@ void TMS9918<personality>::run_for(const HalfCycles cycles) {
|
|||||||
|
|
||||||
if(end == line_buffer.next_border_column) {
|
if(end == line_buffer.next_border_column) {
|
||||||
const int length = line_buffer.next_border_column - line_buffer.first_pixel_output_column;
|
const int length = line_buffer.next_border_column - line_buffer.first_pixel_output_column;
|
||||||
this->crt_.output_data(from_internal<personality, Clock::CRT>(length), line_buffer.pixel_count);
|
this->crt_.output_data(from_internal<personality, Clock::CRT>(length), size_t(line_buffer.pixel_count));
|
||||||
this->pixel_origin_ = this->pixel_target_ = nullptr;
|
this->pixel_origin_ = this->pixel_target_ = nullptr;
|
||||||
this->asked_for_write_area_ = false;
|
this->asked_for_write_area_ = false;
|
||||||
}
|
}
|
||||||
@ -647,32 +647,11 @@ void Base<personality>::commit_register(int reg, uint8_t value) {
|
|||||||
if(sprites_magnified_) sprite_height_ <<= 1;
|
if(sprites_magnified_) sprite_height_ <<= 1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 2:
|
case 2: install_field<10>(pattern_name_address_, value); break;
|
||||||
pattern_name_address_ = size_t((value & 0xf) << 10) | 0x3ff;
|
case 3: install_field<6>(colour_table_address_, value); break;
|
||||||
break;
|
case 4: install_field<11>(pattern_generator_table_address_, value); break;
|
||||||
|
case 5: install_field<7>(sprite_attribute_table_address_, value); break;
|
||||||
case 3:
|
case 6: install_field<11>(sprite_generator_table_address_, value); break;
|
||||||
colour_table_address_ =
|
|
||||||
(colour_table_address_ & ~0x3fc0) |
|
|
||||||
(value << 6) |
|
|
||||||
0x3f;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 4:
|
|
||||||
pattern_generator_table_address_ = size_t((value & 0x07) << 11) | 0x7ff;
|
|
||||||
// TODO: don't mask off so many bits for, at least, the Yamahas.
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 5:
|
|
||||||
sprite_attribute_table_address_ =
|
|
||||||
(sprite_attribute_table_address_ & ~0x3d80) |
|
|
||||||
((value << 7) & 0x3f80) |
|
|
||||||
0x7f;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 6:
|
|
||||||
sprite_generator_table_address_ = size_t((value & 0x07) << 11) | 0x7ff;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 7:
|
case 7:
|
||||||
text_colour_ = value >> 4;
|
text_colour_ = value >> 4;
|
||||||
@ -757,11 +736,6 @@ void Base<personality>::commit_register(int reg, uint8_t value) {
|
|||||||
LOG("Screen mode: " << int(current_screen_mode()));
|
LOG("Screen mode: " << int(current_screen_mode()));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 2:
|
|
||||||
// Retain extra addressing bits.
|
|
||||||
pattern_name_address_ = size_t((value & 0x7f) << 10) | 0x3ff;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 8:
|
case 8:
|
||||||
LOG("TODO: Yamaha VRAM organisation, sprite disable, etc; " << PADHEX(2) << +value);
|
LOG("TODO: Yamaha VRAM organisation, sprite disable, etc; " << PADHEX(2) << +value);
|
||||||
// b7: "1 = input on colour bus, enable mouse; 1 = output on colour bus, disable mouse" [documentation clearly in error]
|
// b7: "1 = input on colour bus, enable mouse; 1 = output on colour bus, disable mouse" [documentation clearly in error]
|
||||||
@ -783,19 +757,11 @@ void Base<personality>::commit_register(int reg, uint8_t value) {
|
|||||||
// b0: 1 = [dot clock] DLCLK is input; 0 = DLCLK is output
|
// b0: 1 = [dot clock] DLCLK is input; 0 = DLCLK is output
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 10:
|
|
||||||
colour_table_address_ =
|
|
||||||
(colour_table_address_ & ~0x1c000) |
|
|
||||||
((value << 14) & 0x1c000);
|
|
||||||
// b0–b2: A14–A16 of the colour table.
|
// b0–b2: A14–A16 of the colour table.
|
||||||
break;
|
case 10: install_field<14>(colour_table_address_, value); break;
|
||||||
|
|
||||||
case 11:
|
|
||||||
sprite_attribute_table_address_ =
|
|
||||||
(sprite_attribute_table_address_ & ~0x18000) |
|
|
||||||
((value << 15) & 0x18000);
|
|
||||||
// b0–b1: A15–A16 of the sprite table.
|
// b0–b1: A15–A16 of the sprite table.
|
||||||
break;
|
case 11: install_field<15>(sprite_attribute_table_address_, value); break;
|
||||||
|
|
||||||
case 12:
|
case 12:
|
||||||
LOG("TODO: Yamaha text and background blink colour; " << PADHEX(2) << +value);
|
LOG("TODO: Yamaha text and background blink colour; " << PADHEX(2) << +value);
|
||||||
@ -808,9 +774,7 @@ void Base<personality>::commit_register(int reg, uint8_t value) {
|
|||||||
// b4–b7: display time for even page.
|
// b4–b7: display time for even page.
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 14:
|
case 14: install_field<14>(ram_pointer_, value); break;
|
||||||
ram_pointer_ = (ram_pointer_ & ~0x1c000) | ((value << 14) & 0x1c000);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 15:
|
case 15:
|
||||||
Storage<personality>::selected_status_ = value & 0xf;
|
Storage<personality>::selected_status_ = value & 0xf;
|
||||||
@ -865,15 +829,23 @@ void Base<personality>::commit_register(int reg, uint8_t value) {
|
|||||||
|
|
||||||
case 44:
|
case 44:
|
||||||
Storage<personality>::command_context_.colour = value;
|
Storage<personality>::command_context_.colour = value;
|
||||||
Storage<personality>::command_context_.colour4bpp = (value & 0xf) | (value << 4);
|
Storage<personality>::command_context_.colour4bpp = uint8_t(
|
||||||
Storage<personality>::command_context_.colour2bpp = (value & 0x3) | ((value & 0x3) << 2) | ((value & 0x3) << 4) | ((value & 0x3) << 6);
|
(value & 0xf) |
|
||||||
|
(value << 4)
|
||||||
|
);
|
||||||
|
Storage<personality>::command_context_.colour2bpp = uint8_t(
|
||||||
|
(value & 0x3) |
|
||||||
|
((value & 0x3) << 2) |
|
||||||
|
((value & 0x3) << 4) |
|
||||||
|
((value & 0x3) << 6)
|
||||||
|
);
|
||||||
|
|
||||||
// Check whether a command was blocked on this.
|
// Check whether a command was blocked on this.
|
||||||
if(
|
if(
|
||||||
Storage<personality>::command_ &&
|
Storage<personality>::command_ &&
|
||||||
Storage<personality>::command_->access == Command::AccessType::WaitForColourReceipt
|
Storage<personality>::command_->access == Command::AccessType::WaitForColourReceipt
|
||||||
) {
|
) {
|
||||||
Storage<personality>::command_->advance();
|
Storage<personality>::command_->advance(pixels_per_byte(this->screen_mode_));
|
||||||
Storage<personality>::update_command_step(fetch_pointer_.column);
|
Storage<personality>::update_command_step(fetch_pointer_.column);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -917,7 +889,7 @@ void Base<personality>::commit_register(int reg, uint8_t value) {
|
|||||||
case 0b1010: break; // TODO: lmcm. [logical move, VRAM to CPU]
|
case 0b1010: break; // TODO: lmcm. [logical move, VRAM to CPU]
|
||||||
case 0b1011: Begin(LogicalMoveFromCPU); break; // LMMC [logical move, CPU to VRAM]
|
case 0b1011: Begin(LogicalMoveFromCPU); break; // LMMC [logical move, CPU to VRAM]
|
||||||
|
|
||||||
case 0b1100: break; // TODO: hmmv. [high-speed move, VRAM to VDP]
|
case 0b1100: Begin(HighSpeedFill); break; // HMMV [high-speed move, VDP to VRAM]
|
||||||
case 0b1101: break; // TODO: hmmm. [high-speed move, VRAM to VRAM]
|
case 0b1101: break; // TODO: hmmm. [high-speed move, VRAM to VRAM]
|
||||||
case 0b1110: break; // TODO: ymmm. [high-speed move, y only, VRAM to VRAM]
|
case 0b1110: break; // TODO: ymmm. [high-speed move, y only, VRAM to VRAM]
|
||||||
case 0b1111: break; // TODO: hmmc. [high-speed move, CPU to VRAM]
|
case 0b1111: break; // TODO: hmmc. [high-speed move, CPU to VRAM]
|
||||||
@ -950,13 +922,13 @@ void Base<personality>::write_register(uint8_t value) {
|
|||||||
write_phase_ = true;
|
write_phase_ = true;
|
||||||
|
|
||||||
// The initial write should half update the access pointer.
|
// The initial write should half update the access pointer.
|
||||||
ram_pointer_ = (ram_pointer_ & ~0xff) | low_write_;
|
install_field<0>(ram_pointer_, value);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The RAM pointer is always set on a second write, regardless of
|
// The RAM pointer is always set on a second write, regardless of
|
||||||
// whether the caller is intending to enqueue a VDP operation.
|
// whether the caller is intending to enqueue a VDP operation.
|
||||||
ram_pointer_ = (ram_pointer_ & ~0x3f00) | ((value << 8) & 0x3f00);
|
install_field<8, 0x3f>(ram_pointer_, value);
|
||||||
|
|
||||||
write_phase_ = false;
|
write_phase_ = false;
|
||||||
if(value & 0x80) {
|
if(value & 0x80) {
|
||||||
|
@ -96,7 +96,7 @@ struct LineBuffer {
|
|||||||
*/
|
*/
|
||||||
int first_pixel_output_column = 94;
|
int first_pixel_output_column = 94;
|
||||||
int next_border_column = 334;
|
int next_border_column = 334;
|
||||||
size_t pixel_count = 256;
|
int pixel_count = 256;
|
||||||
|
|
||||||
// An active sprite is one that has been selected for composition onto
|
// An active sprite is one that has been selected for composition onto
|
||||||
// this line.
|
// this line.
|
||||||
@ -131,11 +131,15 @@ template <Personality personality, typename Enable = void> struct Storage {
|
|||||||
};
|
};
|
||||||
|
|
||||||
template <> struct Storage<Personality::TMS9918A> {
|
template <> struct Storage<Personality::TMS9918A> {
|
||||||
|
using AddressT = uint16_t;
|
||||||
|
|
||||||
void begin_line(ScreenMode, bool, bool) {}
|
void begin_line(ScreenMode, bool, bool) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Yamaha-specific storage.
|
// Yamaha-specific storage.
|
||||||
template <Personality personality> struct Storage<personality, std::enable_if_t<is_yamaha_vdp(personality)>> {
|
template <Personality personality> struct Storage<personality, std::enable_if_t<is_yamaha_vdp(personality)>> {
|
||||||
|
using AddressT = uint32_t;
|
||||||
|
|
||||||
int selected_status_ = 0;
|
int selected_status_ = 0;
|
||||||
|
|
||||||
int indirect_register_ = 0;
|
int indirect_register_ = 0;
|
||||||
@ -202,6 +206,7 @@ template <Personality personality> struct Storage<personality, std::enable_if_t<
|
|||||||
None,
|
None,
|
||||||
ReadPixel,
|
ReadPixel,
|
||||||
WritePixel,
|
WritePixel,
|
||||||
|
WriteByte,
|
||||||
};
|
};
|
||||||
CommandStep next_command_step_ = CommandStep::None;
|
CommandStep next_command_step_ = CommandStep::None;
|
||||||
int minimum_command_column_ = 0;
|
int minimum_command_column_ = 0;
|
||||||
@ -227,6 +232,9 @@ template <Personality personality> struct Storage<personality, std::enable_if_t<
|
|||||||
// i.e. nothing to do until a colour is received.
|
// i.e. nothing to do until a colour is received.
|
||||||
next_command_step_ = CommandStep::None;
|
next_command_step_ = CommandStep::None;
|
||||||
break;
|
break;
|
||||||
|
case Command::AccessType::WriteByte:
|
||||||
|
next_command_step_ = CommandStep::WriteByte;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -357,6 +365,8 @@ template <Personality personality> struct Storage<personality, std::enable_if_t<
|
|||||||
|
|
||||||
// Master System-specific storage.
|
// Master System-specific storage.
|
||||||
template <Personality personality> struct Storage<personality, std::enable_if_t<is_sega_vdp(personality)>> {
|
template <Personality personality> struct Storage<personality, std::enable_if_t<is_sega_vdp(personality)>> {
|
||||||
|
using AddressT = uint16_t;
|
||||||
|
|
||||||
// The SMS VDP has a programmer-set colour palette, with a dedicated patch of RAM. But the RAM is only exactly
|
// The SMS VDP has a programmer-set colour palette, with a dedicated patch of RAM. But the RAM is only exactly
|
||||||
// fast enough for the pixel clock. So when the programmer writes to it, that causes a one-pixel glitch; there
|
// fast enough for the pixel clock. So when the programmer writes to it, that causes a one-pixel glitch; there
|
||||||
// isn't the bandwidth for the read both write to occur simultaneously. The following buffer therefore keeps
|
// isn't the bandwidth for the read both write to occur simultaneously. The following buffer therefore keeps
|
||||||
@ -371,8 +381,6 @@ template <Personality personality> struct Storage<personality, std::enable_if_t<
|
|||||||
uint32_t colour_ram_[32];
|
uint32_t colour_ram_[32];
|
||||||
bool cram_is_selected_ = false;
|
bool cram_is_selected_ = false;
|
||||||
|
|
||||||
// Fields below affect only the Master System output mode.
|
|
||||||
|
|
||||||
// Programmer-set flags.
|
// Programmer-set flags.
|
||||||
bool vertical_scroll_lock_ = false;
|
bool vertical_scroll_lock_ = false;
|
||||||
bool horizontal_scroll_lock_ = false;
|
bool horizontal_scroll_lock_ = false;
|
||||||
@ -387,10 +395,10 @@ template <Personality personality> struct Storage<personality, std::enable_if_t<
|
|||||||
uint8_t latched_vertical_scroll_ = 0;
|
uint8_t latched_vertical_scroll_ = 0;
|
||||||
|
|
||||||
// Various resource addresses with VDP-version-specific modifications
|
// Various resource addresses with VDP-version-specific modifications
|
||||||
// built int.
|
// built in.
|
||||||
size_t pattern_name_address_;
|
AddressT pattern_name_address_;
|
||||||
size_t sprite_attribute_table_address_;
|
AddressT sprite_attribute_table_address_;
|
||||||
size_t sprite_generator_table_address_;
|
AddressT sprite_generator_table_address_;
|
||||||
|
|
||||||
void begin_line(ScreenMode, bool, bool) {}
|
void begin_line(ScreenMode, bool, bool) {}
|
||||||
};
|
};
|
||||||
@ -440,6 +448,18 @@ template <Personality personality> struct Base: public Storage<personality> {
|
|||||||
|
|
||||||
Outputs::CRT::CRT crt_;
|
Outputs::CRT::CRT crt_;
|
||||||
TVStandard tv_standard_ = TVStandard::NTSC;
|
TVStandard tv_standard_ = TVStandard::NTSC;
|
||||||
|
using AddressT = typename Storage<personality>::AddressT;
|
||||||
|
|
||||||
|
/// Mutates @c target such that @c source & @c source_mask replaces the bits that currently start
|
||||||
|
/// at @c shift bits from least significant. Subsequently ensures @c target is constrained by the
|
||||||
|
/// applicable @c memory_mask.
|
||||||
|
template <int shift, uint8_t source_mask = 0xff> void install_field(AddressT &target, uint8_t source) {
|
||||||
|
constexpr auto mask = AddressT(~(source_mask << shift));
|
||||||
|
target = (
|
||||||
|
(target & mask) |
|
||||||
|
AddressT((source & source_mask) << shift)
|
||||||
|
) & memory_mask(personality);
|
||||||
|
}
|
||||||
|
|
||||||
// Personality-specific metrics and converters.
|
// Personality-specific metrics and converters.
|
||||||
ClockConverter<personality> clock_converter_;
|
ClockConverter<personality> clock_converter_;
|
||||||
@ -448,7 +468,7 @@ template <Personality personality> struct Base: public Storage<personality> {
|
|||||||
std::array<uint8_t, memory_size(personality)> ram_;
|
std::array<uint8_t, memory_size(personality)> ram_;
|
||||||
|
|
||||||
// State of the DRAM/CRAM-access mechanism.
|
// State of the DRAM/CRAM-access mechanism.
|
||||||
size_t ram_pointer_ = 0;
|
AddressT 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 minimum_access_column_ = 0;
|
int minimum_access_column_ = 0;
|
||||||
@ -471,11 +491,11 @@ template <Personality personality> struct Base: public Storage<personality> {
|
|||||||
int sprite_height_ = 8;
|
int sprite_height_ = 8;
|
||||||
|
|
||||||
// Programmer-specified addresses.
|
// Programmer-specified addresses.
|
||||||
size_t pattern_name_address_ = 0; // i.e. address of the tile map.
|
AddressT pattern_name_address_ = memory_mask(personality); // Address of the tile map.
|
||||||
size_t colour_table_address_ = 0; // address of the colour map (if applicable).
|
AddressT colour_table_address_ = memory_mask(personality); // Address of the colour map (if applicable).
|
||||||
size_t pattern_generator_table_address_ = 0; // address of the tile contents.
|
AddressT pattern_generator_table_address_ = memory_mask(personality); // Address of the tile contents.
|
||||||
size_t sprite_attribute_table_address_ = 0; // address of the sprite list.
|
AddressT sprite_attribute_table_address_ = memory_mask(personality); // Address of the sprite list.
|
||||||
size_t sprite_generator_table_address_ = 0; // address of the sprite contents.
|
AddressT sprite_generator_table_address_ = memory_mask(personality); // Address of the sprite contents.
|
||||||
|
|
||||||
// Default colours.
|
// Default colours.
|
||||||
uint8_t text_colour_ = 0;
|
uint8_t text_colour_ = 0;
|
||||||
@ -596,29 +616,33 @@ template <Personality personality> struct Base: public Storage<personality> {
|
|||||||
return ScreenMode::Blank;
|
return ScreenMode::Blank;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t command_address() const {
|
AddressT command_address() const {
|
||||||
if constexpr (is_yamaha_vdp(personality)) {
|
if constexpr (is_yamaha_vdp(personality)) {
|
||||||
switch(this->screen_mode_) {
|
switch(this->screen_mode_) {
|
||||||
default:
|
default:
|
||||||
case ScreenMode::YamahaGraphics4: // 256 pixels @ 4bpp
|
case ScreenMode::YamahaGraphics4: // 256 pixels @ 4bpp
|
||||||
return
|
return AddressT(
|
||||||
(Storage<personality>::command_->location.v[0] >> 1) +
|
(Storage<personality>::command_->location.v[0] >> 1) +
|
||||||
(Storage<personality>::command_->location.v[1] << 7);
|
(Storage<personality>::command_->location.v[1] << 7)
|
||||||
|
);
|
||||||
|
|
||||||
case ScreenMode::YamahaGraphics5: // 512 pixels @ 2bpp
|
case ScreenMode::YamahaGraphics5: // 512 pixels @ 2bpp
|
||||||
return
|
return AddressT(
|
||||||
(Storage<personality>::command_->location.v[0] >> 2) +
|
(Storage<personality>::command_->location.v[0] >> 2) +
|
||||||
(Storage<personality>::command_->location.v[1] << 7);
|
(Storage<personality>::command_->location.v[1] << 7)
|
||||||
|
);
|
||||||
|
|
||||||
case ScreenMode::YamahaGraphics6: // 512 pixels @ 4bpp
|
case ScreenMode::YamahaGraphics6: // 512 pixels @ 4bpp
|
||||||
return
|
return AddressT(
|
||||||
(Storage<personality>::command_->location.v[0] >> 1) +
|
(Storage<personality>::command_->location.v[0] >> 1) +
|
||||||
(Storage<personality>::command_->location.v[1] << 8);
|
(Storage<personality>::command_->location.v[1] << 8)
|
||||||
|
);
|
||||||
|
|
||||||
case ScreenMode::YamahaGraphics7: // 256 pixels @ 8bpp
|
case ScreenMode::YamahaGraphics7: // 256 pixels @ 8bpp
|
||||||
return
|
return AddressT(
|
||||||
(Storage<personality>::command_->location.v[0] >> 0) +
|
(Storage<personality>::command_->location.v[0] >> 0) +
|
||||||
(Storage<personality>::command_->location.v[1] << 8);
|
(Storage<personality>::command_->location.v[1] << 8)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return 0;
|
return 0;
|
||||||
@ -713,16 +737,22 @@ template <Personality personality> struct Base: public Storage<personality> {
|
|||||||
|
|
||||||
ram_[address] = Storage<personality>::command_latch_;
|
ram_[address] = Storage<personality>::command_latch_;
|
||||||
|
|
||||||
Storage<personality>::command_->advance();
|
Storage<personality>::command_->advance(pixels_per_byte(this->screen_mode_));
|
||||||
Storage<personality>::update_command_step(access_column);
|
Storage<personality>::update_command_step(access_column);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
case CommandStep::WriteByte:
|
||||||
|
ram_[command_address()] = Storage<personality>::command_context_.colour;
|
||||||
|
Storage<personality>::command_->advance(pixels_per_byte(this->screen_mode_));
|
||||||
|
Storage<personality>::update_command_step(access_column);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t address = ram_pointer_;
|
AddressT address = ram_pointer_;
|
||||||
++ram_pointer_;
|
++ram_pointer_;
|
||||||
|
|
||||||
if constexpr (is_yamaha_vdp(personality)) {
|
if constexpr (is_yamaha_vdp(personality)) {
|
||||||
|
@ -39,6 +39,24 @@ enum class ScreenMode {
|
|||||||
YamahaGraphics2 = Graphics,
|
YamahaGraphics2 = Graphics,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
constexpr int pixels_per_byte(ScreenMode mode) {
|
||||||
|
switch(mode) {
|
||||||
|
default:
|
||||||
|
case ScreenMode::Blank: return 0;
|
||||||
|
case ScreenMode::Text: return 6;
|
||||||
|
case ScreenMode::MultiColour: return 2;
|
||||||
|
case ScreenMode::ColouredText: return 8;
|
||||||
|
case ScreenMode::Graphics: return 8;
|
||||||
|
case ScreenMode::SMSMode4: return 2;
|
||||||
|
case ScreenMode::YamahaText80: return 6;
|
||||||
|
case ScreenMode::YamahaGraphics3: return 8;
|
||||||
|
case ScreenMode::YamahaGraphics4: return 2;
|
||||||
|
case ScreenMode::YamahaGraphics5: return 4;
|
||||||
|
case ScreenMode::YamahaGraphics6: return 2;
|
||||||
|
case ScreenMode::YamahaGraphics7: return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
enum class FetchMode {
|
enum class FetchMode {
|
||||||
Text,
|
Text,
|
||||||
Character,
|
Character,
|
||||||
|
@ -469,7 +469,7 @@ template<bool use_end> void Base<personality>::fetch_sms(LineBuffer &line_buffer
|
|||||||
|
|
||||||
template <Personality personality>
|
template <Personality personality>
|
||||||
template<ScreenMode mode> void Base<personality>::fetch_yamaha([[maybe_unused]] LineBuffer &line_buffer, [[maybe_unused]] int y, int end) {
|
template<ScreenMode mode> void Base<personality>::fetch_yamaha([[maybe_unused]] LineBuffer &line_buffer, [[maybe_unused]] int y, int end) {
|
||||||
const int rotated_name_ = pattern_name_address_ >> 1;
|
const AddressT rotated_name_ = pattern_name_address_ >> 1;
|
||||||
const uint8_t *const ram2 = &ram_[65536];
|
const uint8_t *const ram2 = &ram_[65536];
|
||||||
|
|
||||||
while(Storage<personality>::next_event_->offset < end) {
|
while(Storage<personality>::next_event_->offset < end) {
|
||||||
@ -488,10 +488,10 @@ template<ScreenMode mode> void Base<personality>::fetch_yamaha([[maybe_unused]]
|
|||||||
|
|
||||||
const int start = (y << 7) | column | 0x1'8000;
|
const int start = (y << 7) | column | 0x1'8000;
|
||||||
|
|
||||||
line_buffer.bitmap[column + 0] = ram_[pattern_name_address_ & (start + 0)];
|
line_buffer.bitmap[column + 0] = ram_[pattern_name_address_ & AddressT(start + 0)];
|
||||||
line_buffer.bitmap[column + 1] = ram_[pattern_name_address_ & (start + 1)];
|
line_buffer.bitmap[column + 1] = ram_[pattern_name_address_ & AddressT(start + 1)];
|
||||||
line_buffer.bitmap[column + 2] = ram_[pattern_name_address_ & (start + 2)];
|
line_buffer.bitmap[column + 2] = ram_[pattern_name_address_ & AddressT(start + 2)];
|
||||||
line_buffer.bitmap[column + 3] = ram_[pattern_name_address_ & (start + 3)];
|
line_buffer.bitmap[column + 3] = ram_[pattern_name_address_ & AddressT(start + 3)];
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case ScreenMode::YamahaGraphics6:
|
case ScreenMode::YamahaGraphics6:
|
||||||
@ -502,14 +502,14 @@ template<ScreenMode mode> void Base<personality>::fetch_yamaha([[maybe_unused]]
|
|||||||
const int start = (y << 7) | column | 0x1'8000;
|
const int start = (y << 7) | column | 0x1'8000;
|
||||||
|
|
||||||
// Fetch from alternate banks.
|
// Fetch from alternate banks.
|
||||||
line_buffer.bitmap[column + 0] = ram_[rotated_name_ & (start + 0)];
|
line_buffer.bitmap[column + 0] = ram_[rotated_name_ & AddressT(start + 0)];
|
||||||
line_buffer.bitmap[column + 1] = ram2[rotated_name_ & (start + 0)];
|
line_buffer.bitmap[column + 1] = ram2[rotated_name_ & AddressT(start + 0)];
|
||||||
line_buffer.bitmap[column + 2] = ram_[rotated_name_ & (start + 1)];
|
line_buffer.bitmap[column + 2] = ram_[rotated_name_ & AddressT(start + 1)];
|
||||||
line_buffer.bitmap[column + 3] = ram2[rotated_name_ & (start + 1)];
|
line_buffer.bitmap[column + 3] = ram2[rotated_name_ & AddressT(start + 1)];
|
||||||
line_buffer.bitmap[column + 4] = ram_[rotated_name_ & (start + 2)];
|
line_buffer.bitmap[column + 4] = ram_[rotated_name_ & AddressT(start + 2)];
|
||||||
line_buffer.bitmap[column + 5] = ram2[rotated_name_ & (start + 2)];
|
line_buffer.bitmap[column + 5] = ram2[rotated_name_ & AddressT(start + 2)];
|
||||||
line_buffer.bitmap[column + 6] = ram_[rotated_name_ & (start + 3)];
|
line_buffer.bitmap[column + 6] = ram_[rotated_name_ & AddressT(start + 3)];
|
||||||
line_buffer.bitmap[column + 7] = ram2[rotated_name_ & (start + 3)];
|
line_buffer.bitmap[column + 7] = ram2[rotated_name_ & AddressT(start + 3)];
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -79,6 +79,9 @@ struct Command {
|
|||||||
|
|
||||||
/// Blocks until the next CPU write to the colour register.
|
/// Blocks until the next CPU write to the colour register.
|
||||||
WaitForColourReceipt,
|
WaitForColourReceipt,
|
||||||
|
|
||||||
|
/// Writes an entire byte to the location containing the current @c location.
|
||||||
|
WriteByte,
|
||||||
};
|
};
|
||||||
AccessType access = AccessType::PlotPoint;
|
AccessType access = AccessType::PlotPoint;
|
||||||
int cycles = 0;
|
int cycles = 0;
|
||||||
@ -88,12 +91,19 @@ struct Command {
|
|||||||
/// Current command parameters.
|
/// Current command parameters.
|
||||||
CommandContext &context;
|
CommandContext &context;
|
||||||
Command(CommandContext &context) : context(context) {}
|
Command(CommandContext &context) : context(context) {}
|
||||||
|
virtual ~Command() {}
|
||||||
|
|
||||||
/// @returns @c true if all output from this command is done; @c false otherwise.
|
/// @returns @c true if all output from this command is done; @c false otherwise.
|
||||||
virtual bool done() = 0;
|
virtual bool done() = 0;
|
||||||
|
|
||||||
/// Repopulates the fields above with the next action to take.
|
/// Repopulates the fields above with the next action to take, being provided with the
|
||||||
virtual void advance() = 0;
|
/// number of pixels per byte in the current screen mode.
|
||||||
|
virtual void advance(int pixels_per_byte) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
template <int axis> void advance_axis(int offset = 1) {
|
||||||
|
context.destination.add<axis>(context.arguments & (0x4 << axis) ? -offset : offset);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// MARK: - Line drawing.
|
// MARK: - Line drawing.
|
||||||
@ -127,7 +137,7 @@ struct Line: public Command {
|
|||||||
return !context.size.v[0];
|
return !context.size.v[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
void advance() final {
|
void advance(int) final {
|
||||||
--context.size.v[0];
|
--context.size.v[0];
|
||||||
cycles = 88;
|
cycles = 88;
|
||||||
|
|
||||||
@ -140,9 +150,9 @@ struct Line: public Command {
|
|||||||
// b3: 1 => y direction is up;
|
// b3: 1 => y direction is up;
|
||||||
// 0 => y direction is down.
|
// 0 => y direction is down.
|
||||||
if(context.arguments & 0x1) {
|
if(context.arguments & 0x1) {
|
||||||
location.add<1>(context.arguments & 0x8 ? -1 : 1);
|
advance_axis<1>();
|
||||||
} else {
|
} else {
|
||||||
location.add<0>(context.arguments & 0x4 ? -1 : 1);
|
advance_axis<0>();
|
||||||
}
|
}
|
||||||
|
|
||||||
position_ -= numerator_;
|
position_ -= numerator_;
|
||||||
@ -151,11 +161,13 @@ struct Line: public Command {
|
|||||||
cycles += 32;
|
cycles += 32;
|
||||||
|
|
||||||
if(context.arguments & 0x1) {
|
if(context.arguments & 0x1) {
|
||||||
location.add<0>(context.arguments & 0x4 ? -1 : 1);
|
advance_axis<0>();
|
||||||
} else {
|
} else {
|
||||||
location.add<1>(context.arguments & 0x8 ? -1 : 1);
|
advance_axis<1>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
location = context.destination;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -177,7 +189,7 @@ struct PointSet: public Command {
|
|||||||
return done_;
|
return done_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void advance() final {
|
void advance(int) final {
|
||||||
done_ = true;
|
done_ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -199,7 +211,7 @@ struct LogicalMoveFromCPU: public Command {
|
|||||||
location = context.destination;
|
location = context.destination;
|
||||||
}
|
}
|
||||||
|
|
||||||
void advance() final {
|
void advance(int) final {
|
||||||
switch(access) {
|
switch(access) {
|
||||||
default: break;
|
default: break;
|
||||||
|
|
||||||
@ -212,7 +224,7 @@ struct LogicalMoveFromCPU: public Command {
|
|||||||
case AccessType::PlotPoint:
|
case AccessType::PlotPoint:
|
||||||
cycles = 0;
|
cycles = 0;
|
||||||
access = AccessType::WaitForColourReceipt;
|
access = AccessType::WaitForColourReceipt;
|
||||||
context.destination.add<0>(context.arguments & 0x4 ? -1 : 1);
|
advance_axis<0>();
|
||||||
--context.size.v[0];
|
--context.size.v[0];
|
||||||
|
|
||||||
if(!context.size.v[0]) {
|
if(!context.size.v[0]) {
|
||||||
@ -220,7 +232,7 @@ struct LogicalMoveFromCPU: public Command {
|
|||||||
context.size.v[0] = width_;
|
context.size.v[0] = width_;
|
||||||
context.destination.v[0] = start_x_;
|
context.destination.v[0] = start_x_;
|
||||||
|
|
||||||
context.destination.add<1>(context.arguments & 0x8 ? -1 : 1);
|
advance_axis<1>();
|
||||||
--context.size.v[1];
|
--context.size.v[1];
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -235,6 +247,43 @@ struct LogicalMoveFromCPU: public Command {
|
|||||||
int start_x_ = 0, width_ = 0;
|
int start_x_ = 0, width_ = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct HighSpeedFill: public Command {
|
||||||
|
HighSpeedFill(CommandContext &context) : Command(context) {
|
||||||
|
start_x_ = context.destination.v[0];
|
||||||
|
width_ = context.size.v[0];
|
||||||
|
|
||||||
|
cycles = 56;
|
||||||
|
access = AccessType::WriteByte;
|
||||||
|
location = context.destination;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool done() final {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void advance(int pixels_per_byte) final {
|
||||||
|
cycles = 48;
|
||||||
|
|
||||||
|
// TODO: step at byte speed, not pixel speed.
|
||||||
|
advance_axis<0>(pixels_per_byte);
|
||||||
|
--context.size.v[0];
|
||||||
|
|
||||||
|
if(!context.size.v[0]) {
|
||||||
|
cycles += 56;
|
||||||
|
context.size.v[0] = width_;
|
||||||
|
context.destination.v[0] = start_x_;
|
||||||
|
|
||||||
|
advance_axis<1>();
|
||||||
|
--context.size.v[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
location = context.destination;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
int start_x_ = 0, width_ = 0;
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
namespace CPU {
|
namespace CPU {
|
||||||
|
|
||||||
/// Provides access to all intermediate parts of a larger int.
|
/// Provides access to all intermediate parts of a larger int.
|
||||||
template <typename Full, typename Half> union alignas(Full) RegisterPair {
|
template <typename Full, typename Half> union alignas(Full) alignas(Half) RegisterPair {
|
||||||
RegisterPair(Full v) : full(v) {}
|
RegisterPair(Full v) : full(v) {}
|
||||||
RegisterPair() {}
|
RegisterPair() {}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user