mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-26 08:49:37 +00:00
Merge branch 'MSX2' of github.com:TomHarte/CLK into MSX2
This commit is contained in:
commit
c9543d0b36
@ -37,6 +37,11 @@ static std::unique_ptr<Analyser::Static::Target> CartridgeTarget(
|
||||
auto target = std::make_unique<Analyser::Static::MSX::Target>();
|
||||
target->confidence = confidence;
|
||||
|
||||
// Observation: all ROMs of 48kb or less are from the MSX 1 era.
|
||||
if(segment.data.size() < 48*1024) {
|
||||
target->model = Analyser::Static::MSX::Target::Model::MSX1;
|
||||
}
|
||||
|
||||
if(type == Analyser::Static::MSX::Cartridge::Type::None) {
|
||||
target->media.cartridges.emplace_back(new Storage::Cartridge::Cartridge(output_segments));
|
||||
} else {
|
||||
@ -100,6 +105,7 @@ static Analyser::Static::TargetList CartridgeTargetsFrom(
|
||||
// TODO: check for a rational init address?
|
||||
|
||||
// If this ROM is less than 48kb in size then it's an ordinary ROM. Just emplace it and move on.
|
||||
// Bonus observation: all such ROMs are from the MSX 1 era.
|
||||
if(data_size <= 0xc000) {
|
||||
targets.emplace_back(CartridgeTarget(segment, start_address, Analyser::Static::MSX::Cartridge::Type::None, 1.0));
|
||||
continue;
|
||||
|
@ -48,8 +48,9 @@ Base<personality>::Base() :
|
||||
// Start at a random position.
|
||||
output_pointer_.row = rand() % 262;
|
||||
output_pointer_.column = rand() % (Timing<personality>::CyclesPerLine - output_lag);
|
||||
fetch_pointer_.row = fetch_pointer_.row;
|
||||
fetch_pointer_.column = output_pointer_.column + output_lag;
|
||||
|
||||
fetch_pointer_ = output_pointer_;
|
||||
fetch_pointer_.column += output_lag;
|
||||
}
|
||||
|
||||
template <Personality personality>
|
||||
@ -261,10 +262,11 @@ void TMS9918<personality>::run_for(const HalfCycles cycles) {
|
||||
// It is otherwise decremented.
|
||||
if constexpr (is_sega_vdp(personality)) {
|
||||
if(this->fetch_pointer_.row >= 0 && this->fetch_pointer_.row <= this->mode_timing_.pixel_lines) {
|
||||
--this->line_interrupt_counter_;
|
||||
if(this->line_interrupt_counter_ == 0xff) {
|
||||
if(!this->line_interrupt_counter_) {
|
||||
this->line_interrupt_pending_ = true;
|
||||
this->line_interrupt_counter_ = this->line_interrupt_target_;
|
||||
} else {
|
||||
--this->line_interrupt_counter_;
|
||||
}
|
||||
} else {
|
||||
this->line_interrupt_counter_ = this->line_interrupt_target_;
|
||||
@ -922,20 +924,20 @@ void Base<personality>::commit_register(int reg, uint8_t value) {
|
||||
default:
|
||||
case 0b0000: Storage<personality>::command_ = nullptr; break; // STOP.
|
||||
|
||||
case 0b0100: break; // TODO: point. [read a pixel colour]
|
||||
case 0b0101: Begin(PointSet); break; // PSET [plot a pixel].
|
||||
case 0b0100: Begin(Point<true>); break; // POINT [read a pixel colour].
|
||||
case 0b0101: Begin(Point<false>); break; // PSET [plot a pixel].
|
||||
case 0b0110: break; // TODO: srch. [search horizontally for a colour]
|
||||
case 0b0111: Begin(Line); break; // LINE [draw a Bresenham line].
|
||||
case 0b0111: Begin(Line); break; // LINE [draw a Bresenham line].
|
||||
|
||||
case 0b1000: Begin(LogicalFill); break; // LMMV [logical move, VDP to VRAM, i.e. solid-colour fill].
|
||||
case 0b1001: Begin(Move<MoveType::Logical>); break; // LMMM [logical move, VRAM to VRAM].
|
||||
case 0b1000: Begin(LogicalFill); break; // LMMV [logical move, VDP to VRAM, i.e. solid-colour fill].
|
||||
case 0b1001: Begin(Move<MoveType::Logical>); break; // LMMM [logical move, VRAM to VRAM].
|
||||
case 0b1010: break; // TODO: lmcm. [logical move, VRAM to CPU]
|
||||
case 0b1011: Begin(MoveFromCPU<true>); break; // LMMC [logical move, CPU to VRAM].
|
||||
case 0b1011: Begin(MoveFromCPU<true>); break; // LMMC [logical move, CPU to VRAM].
|
||||
|
||||
case 0b1100: Begin(HighSpeedFill); break; // HMMV [high-speed move, VDP to VRAM, i.e. single-byte fill].
|
||||
case 0b1101: Begin(Move<MoveType::HighSpeed>); break; // HMMM [high-speed move, VRAM to VRAM].
|
||||
case 0b1110: Begin(Move<MoveType::YOnly>); break; // YMMM [high-speed move, y only, VRAM to VRAM].
|
||||
case 0b1111: Begin(MoveFromCPU<false>); break; // HMMC [high-speed move, CPU to VRAM].
|
||||
case 0b1100: Begin(HighSpeedFill); break; // HMMV [high-speed move, VDP to VRAM, i.e. single-byte fill].
|
||||
case 0b1101: Begin(Move<MoveType::HighSpeed>); break; // HMMM [high-speed move, VRAM to VRAM].
|
||||
case 0b1110: Begin(Move<MoveType::YOnly>); break; // YMMM [high-speed move, y only, VRAM to VRAM].
|
||||
case 0b1111: Begin(MoveFromCPU<false>); break; // HMMC [high-speed move, CPU to VRAM].
|
||||
}
|
||||
#undef Begin
|
||||
|
||||
@ -1111,7 +1113,9 @@ uint8_t Base<personality>::read_register() {
|
||||
case 4: LOG("TODO: Yamaha status 4"); break;
|
||||
case 5: LOG("TODO: Yamaha status 5"); break;
|
||||
case 6: LOG("TODO: Yamaha status 6"); break;
|
||||
case 7: LOG("TODO: Yamaha status 7"); break;
|
||||
|
||||
case 7: return Storage<personality>::colour_status_;
|
||||
|
||||
case 8: LOG("TODO: Yamaha status 8"); break;
|
||||
case 9: LOG("TODO: Yamaha status 9"); break;
|
||||
}
|
||||
|
@ -390,6 +390,13 @@ template <Personality personality> struct Base: public Storage<personality> {
|
||||
case CommandStep::None:
|
||||
break;
|
||||
|
||||
case CommandStep::CopySourcePixelToStatus:
|
||||
Storage<personality>::colour_status_ = extract_colour(source[command_address(context.source, context.arguments & 0x10)], context.source);
|
||||
|
||||
Storage<personality>::command_->advance(pixels_per_byte(this->underlying_mode_));
|
||||
Storage<personality>::update_command_step(access_column);
|
||||
break;
|
||||
|
||||
case CommandStep::ReadSourcePixel:
|
||||
context.latched_colour.set(extract_colour(source[command_address(context.source, context.arguments & 0x10)], context.source));
|
||||
|
||||
|
@ -70,8 +70,9 @@ void Base<personality>::draw_sprites(LineBuffer &buffer, int start, int end, con
|
||||
) colour_buffer[c] = sprite_buffer[c];
|
||||
}
|
||||
|
||||
if(sprite_collision)
|
||||
if(sprite_collision) {
|
||||
status_ |= StatusSpriteCollision;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -475,7 +475,7 @@ struct CharacterSequencer {
|
||||
break;
|
||||
case 3:
|
||||
character_fetcher.fetch_pattern(block);
|
||||
character_fetcher.fetch_pattern(block);
|
||||
character_fetcher.fetch_colour(block);
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
|
@ -100,6 +100,9 @@ template <Personality personality> struct Storage<personality, std::enable_if_t<
|
||||
// Sprite collection state.
|
||||
bool sprites_enabled_ = true;
|
||||
|
||||
// Additional status.
|
||||
uint8_t colour_status_ = 0;
|
||||
|
||||
/// Resets line-ephemeral state for a new line.
|
||||
void begin_line(ScreenMode mode, bool is_refresh) {
|
||||
if(is_refresh) {
|
||||
@ -134,6 +137,8 @@ template <Personality personality> struct Storage<personality, std::enable_if_t<
|
||||
enum class CommandStep {
|
||||
None,
|
||||
|
||||
CopySourcePixelToStatus,
|
||||
|
||||
ReadSourcePixel,
|
||||
ReadDestinationPixel,
|
||||
WritePixel,
|
||||
@ -158,6 +163,10 @@ template <Personality personality> struct Storage<personality, std::enable_if_t<
|
||||
|
||||
minimum_command_column_ = current_column + command_->cycles;
|
||||
switch(command_->access) {
|
||||
case Command::AccessType::ReadPoint:
|
||||
next_command_step_ = CommandStep::CopySourcePixelToStatus;
|
||||
break;
|
||||
|
||||
case Command::AccessType::CopyPoint:
|
||||
next_command_step_ = CommandStep::ReadSourcePixel;
|
||||
break;
|
||||
|
@ -111,7 +111,9 @@ struct Command {
|
||||
/// being a read, a 24-cycle gap, then a write.
|
||||
CopyByte,
|
||||
|
||||
// ReadPoint,
|
||||
/// Copies a single pixel from @c source to the colour status register.
|
||||
ReadPoint,
|
||||
|
||||
// ReadByte,
|
||||
// WaitForColourSend,
|
||||
};
|
||||
@ -208,14 +210,14 @@ struct Line: public Command {
|
||||
|
||||
// MARK: - Single pixel manipulation.
|
||||
|
||||
/// Implements the PSET command, which plots a single pixel.
|
||||
/// Implements the PSET command, which plots a single pixel and POINT, which reads one.
|
||||
///
|
||||
/// No timings are documented, so this'll output as quickly as possible.
|
||||
struct PointSet: public Command {
|
||||
/// No timings are documented, so this'll output or input as quickly as possible.
|
||||
template <bool is_read> struct Point: public Command {
|
||||
public:
|
||||
PointSet(CommandContext &context) : Command(context) {
|
||||
Point(CommandContext &context) : Command(context) {
|
||||
cycles = 0; // TODO.
|
||||
access = AccessType::PlotPoint;
|
||||
access = is_read ? AccessType::ReadPoint : AccessType::PlotPoint;
|
||||
}
|
||||
|
||||
bool done() final {
|
||||
@ -230,8 +232,6 @@ struct PointSet: public Command {
|
||||
bool done_ = false;
|
||||
};
|
||||
|
||||
// TODO: point.
|
||||
|
||||
// MARK: - Rectangular base.
|
||||
|
||||
/// Useful base class for anything that does logical work in a rectangle.
|
||||
|
@ -175,19 +175,19 @@ class ConcreteMachine:
|
||||
}
|
||||
|
||||
void set_scan_target(Outputs::Display::ScanTarget *scan_target) final {
|
||||
vdp_->set_scan_target(scan_target);
|
||||
vdp_.last_valid()->set_scan_target(scan_target);
|
||||
}
|
||||
|
||||
Outputs::Display::ScanStatus get_scaled_scan_status() const final {
|
||||
return vdp_->get_scaled_scan_status();
|
||||
return vdp_.last_valid()->get_scaled_scan_status();
|
||||
}
|
||||
|
||||
void set_display_type(Outputs::Display::DisplayType display_type) final {
|
||||
vdp_->set_display_type(display_type);
|
||||
vdp_.last_valid()->set_display_type(display_type);
|
||||
}
|
||||
|
||||
Outputs::Display::DisplayType get_display_type() const final {
|
||||
return vdp_->get_display_type();
|
||||
return vdp_.last_valid()->get_display_type();
|
||||
}
|
||||
|
||||
Outputs::Speaker::Speaker *get_speaker() final {
|
||||
|
@ -181,24 +181,27 @@ template <Analyser::Static::Sega::Target::Model model> class ConcreteMachine:
|
||||
}
|
||||
|
||||
void set_scan_target(Outputs::Display::ScanTarget *scan_target) final {
|
||||
vdp_->set_tv_standard(
|
||||
vdp_.last_valid()->set_tv_standard(
|
||||
(region_ == Target::Region::Europe) ?
|
||||
TI::TMS::TVStandard::PAL : TI::TMS::TVStandard::NTSC);
|
||||
time_until_debounce_ = vdp_->get_time_until_line(-1);
|
||||
|
||||
vdp_->set_scan_target(scan_target);
|
||||
// Doing the following would be technically correct, but isn't
|
||||
// especially thread-safe and won't make a substantial difference.
|
||||
// time_until_debounce_ = vdp_->get_time_until_line(-1);
|
||||
|
||||
vdp_.last_valid()->set_scan_target(scan_target);
|
||||
}
|
||||
|
||||
Outputs::Display::ScanStatus get_scaled_scan_status() const final {
|
||||
return vdp_->get_scaled_scan_status();
|
||||
return vdp_.last_valid()->get_scaled_scan_status();
|
||||
}
|
||||
|
||||
void set_display_type(Outputs::Display::DisplayType display_type) final {
|
||||
vdp_->set_display_type(display_type);
|
||||
vdp_.last_valid()->set_display_type(display_type);
|
||||
}
|
||||
|
||||
Outputs::Display::DisplayType get_display_type() const final {
|
||||
return vdp_->get_display_type();
|
||||
return vdp_.last_valid()->get_display_type();
|
||||
}
|
||||
|
||||
Outputs::Speaker::Speaker *get_speaker() final {
|
||||
|
Loading…
Reference in New Issue
Block a user