mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-22 12:33: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.
|
||||
fetch_pointer_.row = rand() % 262;
|
||||
fetch_pointer_.column = rand() % (Timing<personality>::CyclesPerLine - output_lag);
|
||||
output_pointer_.row = output_pointer_.row;
|
||||
output_pointer_.column = output_pointer_.column + output_lag;
|
||||
output_pointer_.row = fetch_pointer_.row;
|
||||
output_pointer_.column = fetch_pointer_.column + output_lag;
|
||||
}
|
||||
|
||||
template <Personality personality>
|
||||
@ -490,7 +490,7 @@ void TMS9918<personality>::run_for(const HalfCycles cycles) {
|
||||
this->asked_for_write_area_ = true;
|
||||
|
||||
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) {
|
||||
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->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;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
pattern_name_address_ = size_t((value & 0xf) << 10) | 0x3ff;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
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 2: install_field<10>(pattern_name_address_, value); break;
|
||||
case 3: install_field<6>(colour_table_address_, value); break;
|
||||
case 4: install_field<11>(pattern_generator_table_address_, value); break;
|
||||
case 5: install_field<7>(sprite_attribute_table_address_, value); break;
|
||||
case 6: install_field<11>(sprite_generator_table_address_, value); break;
|
||||
|
||||
case 7:
|
||||
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()));
|
||||
break;
|
||||
|
||||
case 2:
|
||||
// Retain extra addressing bits.
|
||||
pattern_name_address_ = size_t((value & 0x7f) << 10) | 0x3ff;
|
||||
break;
|
||||
|
||||
case 8:
|
||||
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]
|
||||
@ -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
|
||||
break;
|
||||
|
||||
case 10:
|
||||
colour_table_address_ =
|
||||
(colour_table_address_ & ~0x1c000) |
|
||||
((value << 14) & 0x1c000);
|
||||
// b0–b2: A14–A16 of the colour table.
|
||||
break;
|
||||
// b0–b2: A14–A16 of the colour table.
|
||||
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.
|
||||
break;
|
||||
// b0–b1: A15–A16 of the sprite table.
|
||||
case 11: install_field<15>(sprite_attribute_table_address_, value); break;
|
||||
|
||||
case 12:
|
||||
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.
|
||||
break;
|
||||
|
||||
case 14:
|
||||
ram_pointer_ = (ram_pointer_ & ~0x1c000) | ((value << 14) & 0x1c000);
|
||||
break;
|
||||
case 14: install_field<14>(ram_pointer_, value); break;
|
||||
|
||||
case 15:
|
||||
Storage<personality>::selected_status_ = value & 0xf;
|
||||
@ -865,15 +829,23 @@ void Base<personality>::commit_register(int reg, uint8_t value) {
|
||||
|
||||
case 44:
|
||||
Storage<personality>::command_context_.colour = value;
|
||||
Storage<personality>::command_context_.colour4bpp = (value & 0xf) | (value << 4);
|
||||
Storage<personality>::command_context_.colour2bpp = (value & 0x3) | ((value & 0x3) << 2) | ((value & 0x3) << 4) | ((value & 0x3) << 6);
|
||||
Storage<personality>::command_context_.colour4bpp = uint8_t(
|
||||
(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.
|
||||
if(
|
||||
Storage<personality>::command_ &&
|
||||
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);
|
||||
}
|
||||
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 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 0b1110: break; // TODO: ymmm. [high-speed move, y only, VRAM 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;
|
||||
|
||||
// The initial write should half update the access pointer.
|
||||
ram_pointer_ = (ram_pointer_ & ~0xff) | low_write_;
|
||||
install_field<0>(ram_pointer_, value);
|
||||
return;
|
||||
}
|
||||
|
||||
// The RAM pointer is always set on a second write, regardless of
|
||||
// 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;
|
||||
if(value & 0x80) {
|
||||
|
@ -96,7 +96,7 @@ struct LineBuffer {
|
||||
*/
|
||||
int first_pixel_output_column = 94;
|
||||
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
|
||||
// this line.
|
||||
@ -131,11 +131,15 @@ template <Personality personality, typename Enable = void> struct Storage {
|
||||
};
|
||||
|
||||
template <> struct Storage<Personality::TMS9918A> {
|
||||
using AddressT = uint16_t;
|
||||
|
||||
void begin_line(ScreenMode, bool, bool) {}
|
||||
};
|
||||
|
||||
// Yamaha-specific storage.
|
||||
template <Personality personality> struct Storage<personality, std::enable_if_t<is_yamaha_vdp(personality)>> {
|
||||
using AddressT = uint32_t;
|
||||
|
||||
int selected_status_ = 0;
|
||||
|
||||
int indirect_register_ = 0;
|
||||
@ -202,6 +206,7 @@ template <Personality personality> struct Storage<personality, std::enable_if_t<
|
||||
None,
|
||||
ReadPixel,
|
||||
WritePixel,
|
||||
WriteByte,
|
||||
};
|
||||
CommandStep next_command_step_ = CommandStep::None;
|
||||
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.
|
||||
next_command_step_ = CommandStep::None;
|
||||
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.
|
||||
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
|
||||
// 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
|
||||
@ -371,8 +381,6 @@ template <Personality personality> struct Storage<personality, std::enable_if_t<
|
||||
uint32_t colour_ram_[32];
|
||||
bool cram_is_selected_ = false;
|
||||
|
||||
// Fields below affect only the Master System output mode.
|
||||
|
||||
// Programmer-set flags.
|
||||
bool vertical_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;
|
||||
|
||||
// Various resource addresses with VDP-version-specific modifications
|
||||
// built int.
|
||||
size_t pattern_name_address_;
|
||||
size_t sprite_attribute_table_address_;
|
||||
size_t sprite_generator_table_address_;
|
||||
// built in.
|
||||
AddressT pattern_name_address_;
|
||||
AddressT sprite_attribute_table_address_;
|
||||
AddressT sprite_generator_table_address_;
|
||||
|
||||
void begin_line(ScreenMode, bool, bool) {}
|
||||
};
|
||||
@ -440,6 +448,18 @@ template <Personality personality> struct Base: public Storage<personality> {
|
||||
|
||||
Outputs::CRT::CRT crt_;
|
||||
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.
|
||||
ClockConverter<personality> clock_converter_;
|
||||
@ -448,7 +468,7 @@ template <Personality personality> struct Base: public Storage<personality> {
|
||||
std::array<uint8_t, memory_size(personality)> ram_;
|
||||
|
||||
// State of the DRAM/CRAM-access mechanism.
|
||||
size_t ram_pointer_ = 0;
|
||||
AddressT ram_pointer_ = 0;
|
||||
uint8_t read_ahead_buffer_ = 0;
|
||||
MemoryAccess queued_access_ = MemoryAccess::None;
|
||||
int minimum_access_column_ = 0;
|
||||
@ -471,11 +491,11 @@ template <Personality personality> struct Base: public Storage<personality> {
|
||||
int sprite_height_ = 8;
|
||||
|
||||
// Programmer-specified addresses.
|
||||
size_t pattern_name_address_ = 0; // i.e. address of the tile map.
|
||||
size_t colour_table_address_ = 0; // address of the colour map (if applicable).
|
||||
size_t pattern_generator_table_address_ = 0; // address of the tile contents.
|
||||
size_t sprite_attribute_table_address_ = 0; // address of the sprite list.
|
||||
size_t sprite_generator_table_address_ = 0; // address of the sprite contents.
|
||||
AddressT pattern_name_address_ = memory_mask(personality); // Address of the tile map.
|
||||
AddressT colour_table_address_ = memory_mask(personality); // Address of the colour map (if applicable).
|
||||
AddressT pattern_generator_table_address_ = memory_mask(personality); // Address of the tile contents.
|
||||
AddressT sprite_attribute_table_address_ = memory_mask(personality); // Address of the sprite list.
|
||||
AddressT sprite_generator_table_address_ = memory_mask(personality); // Address of the sprite contents.
|
||||
|
||||
// Default colours.
|
||||
uint8_t text_colour_ = 0;
|
||||
@ -596,29 +616,33 @@ template <Personality personality> struct Base: public Storage<personality> {
|
||||
return ScreenMode::Blank;
|
||||
}
|
||||
|
||||
uint32_t command_address() const {
|
||||
AddressT command_address() const {
|
||||
if constexpr (is_yamaha_vdp(personality)) {
|
||||
switch(this->screen_mode_) {
|
||||
default:
|
||||
case ScreenMode::YamahaGraphics4: // 256 pixels @ 4bpp
|
||||
return
|
||||
return AddressT(
|
||||
(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
|
||||
return
|
||||
return AddressT(
|
||||
(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
|
||||
return
|
||||
return AddressT(
|
||||
(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
|
||||
return
|
||||
return AddressT(
|
||||
(Storage<personality>::command_->location.v[0] >> 0) +
|
||||
(Storage<personality>::command_->location.v[1] << 8);
|
||||
(Storage<personality>::command_->location.v[1] << 8)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
return 0;
|
||||
@ -713,16 +737,22 @@ template <Personality personality> struct Base: public Storage<personality> {
|
||||
|
||||
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);
|
||||
} 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;
|
||||
}
|
||||
|
||||
size_t address = ram_pointer_;
|
||||
AddressT address = ram_pointer_;
|
||||
++ram_pointer_;
|
||||
|
||||
if constexpr (is_yamaha_vdp(personality)) {
|
||||
|
@ -39,6 +39,24 @@ enum class ScreenMode {
|
||||
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 {
|
||||
Text,
|
||||
Character,
|
||||
|
@ -469,7 +469,7 @@ template<bool use_end> void Base<personality>::fetch_sms(LineBuffer &line_buffer
|
||||
|
||||
template <Personality personality>
|
||||
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];
|
||||
|
||||
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;
|
||||
|
||||
line_buffer.bitmap[column + 0] = ram_[pattern_name_address_ & (start + 0)];
|
||||
line_buffer.bitmap[column + 1] = ram_[pattern_name_address_ & (start + 1)];
|
||||
line_buffer.bitmap[column + 2] = ram_[pattern_name_address_ & (start + 2)];
|
||||
line_buffer.bitmap[column + 3] = ram_[pattern_name_address_ & (start + 3)];
|
||||
line_buffer.bitmap[column + 0] = ram_[pattern_name_address_ & AddressT(start + 0)];
|
||||
line_buffer.bitmap[column + 1] = ram_[pattern_name_address_ & AddressT(start + 1)];
|
||||
line_buffer.bitmap[column + 2] = ram_[pattern_name_address_ & AddressT(start + 2)];
|
||||
line_buffer.bitmap[column + 3] = ram_[pattern_name_address_ & AddressT(start + 3)];
|
||||
} break;
|
||||
|
||||
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;
|
||||
|
||||
// Fetch from alternate banks.
|
||||
line_buffer.bitmap[column + 0] = ram_[rotated_name_ & (start + 0)];
|
||||
line_buffer.bitmap[column + 1] = ram2[rotated_name_ & (start + 0)];
|
||||
line_buffer.bitmap[column + 2] = ram_[rotated_name_ & (start + 1)];
|
||||
line_buffer.bitmap[column + 3] = ram2[rotated_name_ & (start + 1)];
|
||||
line_buffer.bitmap[column + 4] = ram_[rotated_name_ & (start + 2)];
|
||||
line_buffer.bitmap[column + 5] = ram2[rotated_name_ & (start + 2)];
|
||||
line_buffer.bitmap[column + 6] = ram_[rotated_name_ & (start + 3)];
|
||||
line_buffer.bitmap[column + 7] = ram2[rotated_name_ & (start + 3)];
|
||||
line_buffer.bitmap[column + 0] = ram_[rotated_name_ & AddressT(start + 0)];
|
||||
line_buffer.bitmap[column + 1] = ram2[rotated_name_ & AddressT(start + 0)];
|
||||
line_buffer.bitmap[column + 2] = ram_[rotated_name_ & AddressT(start + 1)];
|
||||
line_buffer.bitmap[column + 3] = ram2[rotated_name_ & AddressT(start + 1)];
|
||||
line_buffer.bitmap[column + 4] = ram_[rotated_name_ & AddressT(start + 2)];
|
||||
line_buffer.bitmap[column + 5] = ram2[rotated_name_ & AddressT(start + 2)];
|
||||
line_buffer.bitmap[column + 6] = ram_[rotated_name_ & AddressT(start + 3)];
|
||||
line_buffer.bitmap[column + 7] = ram2[rotated_name_ & AddressT(start + 3)];
|
||||
} break;
|
||||
|
||||
default:
|
||||
|
@ -79,6 +79,9 @@ struct Command {
|
||||
|
||||
/// Blocks until the next CPU write to the colour register.
|
||||
WaitForColourReceipt,
|
||||
|
||||
/// Writes an entire byte to the location containing the current @c location.
|
||||
WriteByte,
|
||||
};
|
||||
AccessType access = AccessType::PlotPoint;
|
||||
int cycles = 0;
|
||||
@ -88,12 +91,19 @@ struct Command {
|
||||
/// Current command parameters.
|
||||
CommandContext &context;
|
||||
Command(CommandContext &context) : context(context) {}
|
||||
virtual ~Command() {}
|
||||
|
||||
/// @returns @c true if all output from this command is done; @c false otherwise.
|
||||
virtual bool done() = 0;
|
||||
|
||||
/// Repopulates the fields above with the next action to take.
|
||||
virtual void advance() = 0;
|
||||
/// Repopulates the fields above with the next action to take, being provided with the
|
||||
/// 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.
|
||||
@ -127,7 +137,7 @@ struct Line: public Command {
|
||||
return !context.size.v[0];
|
||||
}
|
||||
|
||||
void advance() final {
|
||||
void advance(int) final {
|
||||
--context.size.v[0];
|
||||
cycles = 88;
|
||||
|
||||
@ -140,9 +150,9 @@ struct Line: public Command {
|
||||
// b3: 1 => y direction is up;
|
||||
// 0 => y direction is down.
|
||||
if(context.arguments & 0x1) {
|
||||
location.add<1>(context.arguments & 0x8 ? -1 : 1);
|
||||
advance_axis<1>();
|
||||
} else {
|
||||
location.add<0>(context.arguments & 0x4 ? -1 : 1);
|
||||
advance_axis<0>();
|
||||
}
|
||||
|
||||
position_ -= numerator_;
|
||||
@ -151,11 +161,13 @@ struct Line: public Command {
|
||||
cycles += 32;
|
||||
|
||||
if(context.arguments & 0x1) {
|
||||
location.add<0>(context.arguments & 0x4 ? -1 : 1);
|
||||
advance_axis<0>();
|
||||
} else {
|
||||
location.add<1>(context.arguments & 0x8 ? -1 : 1);
|
||||
advance_axis<1>();
|
||||
}
|
||||
}
|
||||
|
||||
location = context.destination;
|
||||
}
|
||||
|
||||
private:
|
||||
@ -177,7 +189,7 @@ struct PointSet: public Command {
|
||||
return done_;
|
||||
}
|
||||
|
||||
void advance() final {
|
||||
void advance(int) final {
|
||||
done_ = true;
|
||||
}
|
||||
|
||||
@ -199,7 +211,7 @@ struct LogicalMoveFromCPU: public Command {
|
||||
location = context.destination;
|
||||
}
|
||||
|
||||
void advance() final {
|
||||
void advance(int) final {
|
||||
switch(access) {
|
||||
default: break;
|
||||
|
||||
@ -212,7 +224,7 @@ struct LogicalMoveFromCPU: public Command {
|
||||
case AccessType::PlotPoint:
|
||||
cycles = 0;
|
||||
access = AccessType::WaitForColourReceipt;
|
||||
context.destination.add<0>(context.arguments & 0x4 ? -1 : 1);
|
||||
advance_axis<0>();
|
||||
--context.size.v[0];
|
||||
|
||||
if(!context.size.v[0]) {
|
||||
@ -220,7 +232,7 @@ struct LogicalMoveFromCPU: public Command {
|
||||
context.size.v[0] = width_;
|
||||
context.destination.v[0] = start_x_;
|
||||
|
||||
context.destination.add<1>(context.arguments & 0x8 ? -1 : 1);
|
||||
advance_axis<1>();
|
||||
--context.size.v[1];
|
||||
}
|
||||
break;
|
||||
@ -235,6 +247,43 @@ struct LogicalMoveFromCPU: public Command {
|
||||
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 {
|
||||
|
||||
/// 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() {}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user