mirror of
https://github.com/TomHarte/CLK.git
synced 2025-01-11 08:30:55 +00:00
commit
327dc51ece
@ -18,7 +18,7 @@ struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Ta
|
||||
ReflectableEnum(VideoAdaptor,
|
||||
MDA,
|
||||
CGA);
|
||||
VideoAdaptor adaptor = VideoAdaptor::MDA;
|
||||
VideoAdaptor adaptor = VideoAdaptor::CGA;
|
||||
|
||||
Target() : Analyser::Static::Target(Machine::PCCompatible) {
|
||||
if(needs_declare()) {
|
||||
|
@ -159,7 +159,7 @@ void imul(
|
||||
FI;
|
||||
*/
|
||||
using sIntT = typename std::make_signed<IntT>::type;
|
||||
destination_high = (sIntT(destination_low) * sIntT(source)) >> (8 * sizeof(IntT));
|
||||
destination_high = IntT((sIntT(destination_low) * sIntT(source)) >> (8 * sizeof(IntT)));
|
||||
destination_low = IntT(sIntT(destination_low) * sIntT(source));
|
||||
|
||||
const auto sign_extension = (destination_low & Numeric::top_bit<IntT>()) ? IntT(~0) : 0;
|
||||
@ -216,7 +216,7 @@ void div(
|
||||
}
|
||||
|
||||
// TEMPORARY HACK. Will not work with DWords.
|
||||
const uint32_t dividend = (destination_high << (8 * sizeof(IntT))) + destination_low;
|
||||
const uint32_t dividend = uint32_t((destination_high << (8 * sizeof(IntT))) + destination_low);
|
||||
const auto result = dividend / source;
|
||||
if(IntT(result) != result) {
|
||||
interrupt(Interrupt::DivideError, context);
|
||||
@ -293,7 +293,7 @@ void idiv(
|
||||
}
|
||||
|
||||
destination_low = IntT(result);
|
||||
destination_high = dividend % sIntT(source);
|
||||
destination_high = IntT(dividend % sIntT(source));
|
||||
}
|
||||
|
||||
template <typename IntT, typename ContextT>
|
||||
|
@ -54,15 +54,16 @@ void rcl(
|
||||
case 0: break;
|
||||
case Numeric::bit_size<IntT>(): {
|
||||
const IntT temp_carry = destination & 1;
|
||||
destination = (destination >> 1) | (carry << (Numeric::bit_size<IntT>() - 1));
|
||||
destination = IntT((destination >> 1) | (carry << (Numeric::bit_size<IntT>() - 1)));
|
||||
carry = temp_carry;
|
||||
} break;
|
||||
default: {
|
||||
const IntT temp_carry = destination & (Numeric::top_bit<IntT>() >> (temp_count - 1));
|
||||
destination =
|
||||
destination = IntT(
|
||||
(destination << temp_count) |
|
||||
(destination >> (Numeric::bit_size<IntT>() + 1 - temp_count)) |
|
||||
(carry << (temp_count - 1));
|
||||
(carry << (temp_count - 1))
|
||||
);
|
||||
carry = temp_carry ? 1 : 0;
|
||||
} break;
|
||||
}
|
||||
@ -103,15 +104,16 @@ void rcr(
|
||||
case 0: break;
|
||||
case Numeric::bit_size<IntT>(): {
|
||||
const IntT temp_carry = destination & Numeric::top_bit<IntT>();
|
||||
destination = (destination << 1) | carry;
|
||||
destination = IntT((destination << 1) | carry);
|
||||
carry = temp_carry;
|
||||
} break;
|
||||
default: {
|
||||
const IntT temp_carry = destination & (1 << (temp_count - 1));
|
||||
destination =
|
||||
destination = IntT(
|
||||
(destination >> temp_count) |
|
||||
(destination << (Numeric::bit_size<IntT>() + 1 - temp_count)) |
|
||||
(carry << (Numeric::bit_size<IntT>() - temp_count));
|
||||
(carry << (Numeric::bit_size<IntT>() - temp_count))
|
||||
);
|
||||
carry = temp_carry;
|
||||
} break;
|
||||
}
|
||||
@ -159,9 +161,10 @@ void rol(
|
||||
return;
|
||||
}
|
||||
if(temp_count) {
|
||||
destination =
|
||||
destination = IntT(
|
||||
(destination << temp_count) |
|
||||
(destination >> (Numeric::bit_size<IntT>() - temp_count));
|
||||
(destination >> (Numeric::bit_size<IntT>() - temp_count))
|
||||
);
|
||||
}
|
||||
|
||||
context.flags.template set_from<Flag::Carry>(destination & 1);
|
||||
@ -210,9 +213,10 @@ void ror(
|
||||
return;
|
||||
}
|
||||
if(temp_count) {
|
||||
destination =
|
||||
destination = IntT(
|
||||
(destination >> temp_count) |
|
||||
(destination << (Numeric::bit_size<IntT>() - temp_count));
|
||||
(destination << (Numeric::bit_size<IntT>() - temp_count))
|
||||
);
|
||||
}
|
||||
|
||||
context.flags.template set_from<Flag::Carry>(destination & Numeric::top_bit<IntT>());
|
||||
@ -298,9 +302,9 @@ void sal(
|
||||
context.flags.template set_from<Flag::Carry>(
|
||||
destination & mask
|
||||
);
|
||||
context.flags.template set_from<Flag::Overflow>(
|
||||
context.flags.template set_from<Flag::Overflow>(IntT(
|
||||
(destination ^ (destination << 1)) & mask
|
||||
);
|
||||
));
|
||||
destination <<= count;
|
||||
}
|
||||
break;
|
||||
@ -323,7 +327,7 @@ void sar(
|
||||
destination = sign ? IntT(~0) : IntT(0);
|
||||
context.flags.template set_from<Flag::Carry>(sign);
|
||||
} else {
|
||||
const IntT mask = 1 << (count - 1);
|
||||
const auto mask = IntT(1 << (count - 1));
|
||||
context.flags.template set_from<Flag::Carry>(destination & mask);
|
||||
destination = (destination >> count) | (sign ? ~(IntT(~0) >> count) : 0);
|
||||
}
|
||||
@ -349,7 +353,7 @@ void shr(
|
||||
context.flags.template set_from<Flag::Carry>(0);
|
||||
destination = 0;
|
||||
} else {
|
||||
const IntT mask = 1 << (count - 1);
|
||||
const auto mask = IntT(1 << (count - 1));
|
||||
context.flags.template set_from<Flag::Carry>(destination & mask);
|
||||
destination >>= count;
|
||||
}
|
||||
|
377
Machines/PCCompatible/CGA.hpp
Normal file
377
Machines/PCCompatible/CGA.hpp
Normal file
@ -0,0 +1,377 @@
|
||||
//
|
||||
// CGA.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 05/12/2023.
|
||||
// Copyright © 2023 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef CGA_h
|
||||
#define CGA_h
|
||||
|
||||
#include "../../Components/6845/CRTC6845.hpp"
|
||||
#include "../../Outputs/CRT/CRT.hpp"
|
||||
#include "../../Machines/Utility/ROMCatalogue.hpp"
|
||||
|
||||
namespace PCCompatible {
|
||||
|
||||
class CGA {
|
||||
public:
|
||||
CGA() : crtc_(Motorola::CRTC::Personality::HD6845S, outputter_) {}
|
||||
|
||||
static constexpr uint32_t BaseAddress = 0xb'8000;
|
||||
static constexpr auto FontROM = ROM::Name::PCCompatibleCGAFont;
|
||||
|
||||
void set_source(const uint8_t *ram, std::vector<uint8_t> font) {
|
||||
outputter_.ram = ram;
|
||||
outputter_.font = font;
|
||||
}
|
||||
|
||||
void run_for(Cycles cycles) {
|
||||
// Input rate is the PIT rate of 1,193,182 Hz.
|
||||
// CGA is clocked at the real oscillator rate of 14 times that.
|
||||
// But there's also an internal divide by 8 to align to the fetch clock.
|
||||
full_clock_ += 7 * cycles.as<int>();
|
||||
|
||||
const int modulo = 4 * outputter_.clock_divider;
|
||||
crtc_.run_for(Cycles(full_clock_ / modulo));
|
||||
full_clock_ %= modulo;
|
||||
}
|
||||
|
||||
template <int address>
|
||||
void write(uint8_t value) {
|
||||
switch(address) {
|
||||
case 0: case 2: case 4: case 6:
|
||||
crtc_.select_register(value);
|
||||
break;
|
||||
case 1: case 3: case 5: case 7:
|
||||
crtc_.set_register(value);
|
||||
break;
|
||||
|
||||
case 0x8: outputter_.set_mode(value); break;
|
||||
case 0x9: outputter_.set_colours(value); break;
|
||||
}
|
||||
}
|
||||
|
||||
template <int address>
|
||||
uint8_t read() {
|
||||
switch(address) {
|
||||
case 1: case 3: case 5: case 7:
|
||||
return crtc_.get_register();
|
||||
|
||||
case 0xa:
|
||||
return
|
||||
// b3: 1 => in vsync; 0 => not;
|
||||
// b2: 1 => light pen switch is off;
|
||||
// b1: 1 => positive edge from light pen has set trigger;
|
||||
// b0: 1 => safe to write to VRAM now without causing snow.
|
||||
(crtc_.get_bus_state().vsync ? 0b1001 : 0b0000) |
|
||||
(crtc_.get_bus_state().hsync ? 0b0001 : 0b0000) |
|
||||
0b0100;
|
||||
|
||||
default: return 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Display type configuration.
|
||||
|
||||
void set_display_type(Outputs::Display::DisplayType display_type) {
|
||||
outputter_.crt.set_display_type(display_type);
|
||||
}
|
||||
Outputs::Display::DisplayType get_display_type() const {
|
||||
return outputter_.crt.get_display_type();
|
||||
}
|
||||
|
||||
// MARK: - Call-ins for ScanProducer.
|
||||
|
||||
void set_scan_target(Outputs::Display::ScanTarget *scan_target) {
|
||||
outputter_.crt.set_scan_target(scan_target);
|
||||
}
|
||||
|
||||
Outputs::Display::ScanStatus get_scaled_scan_status() const {
|
||||
return outputter_.crt.get_scaled_scan_status() * 4.0f / (7.0f * 8.0f);
|
||||
}
|
||||
|
||||
private:
|
||||
struct CRTCOutputter {
|
||||
enum class OutputState {
|
||||
Sync, Pixels, Border, ColourBurst
|
||||
};
|
||||
|
||||
CRTCOutputter() :
|
||||
crt(910, 8, Outputs::Display::Type::NTSC60, Outputs::Display::InputDataType::Red2Green2Blue2)
|
||||
{
|
||||
crt.set_visible_area(Outputs::Display::Rect(0.097f, 0.095f, 0.82f, 0.82f));
|
||||
// crt.set_display_type(Outputs::Display::DisplayType::CompositeColour); // TODO: needs to be a user option.
|
||||
crt.set_display_type(Outputs::Display::DisplayType::RGB); // TODO: needs to be a user option.
|
||||
}
|
||||
|
||||
void set_mode(uint8_t control) {
|
||||
// b5: enable blink
|
||||
// b4: 1 => 640x200 graphics
|
||||
// b3: video enable
|
||||
// b2: 1 => monochrome
|
||||
// b1: 1 => 320x200 graphics; 0 => text
|
||||
// b0: 1 => 80-column text; 0 => 40
|
||||
|
||||
control_ = control; // To capture blink, monochrome and video enable bits.
|
||||
|
||||
if(control & 0x2) {
|
||||
mode_ = (control & 0x10) ? Mode::Pixels640 : Mode::Pixels320;
|
||||
pixels_per_tick = (mode_ == Mode::Pixels640) ? 16 : 8;
|
||||
} else {
|
||||
mode_ = Mode::Text;
|
||||
pixels_per_tick = 8;
|
||||
}
|
||||
clock_divider = 1 + !(control & 0x01);
|
||||
}
|
||||
|
||||
void set_colours(uint8_t value) {
|
||||
// b5: 320x200 palette.
|
||||
if(value & 0x20) {
|
||||
palette320[1] = 0b00'10'10;
|
||||
palette320[2] = 0b10'00'10;
|
||||
palette320[3] = 0b10'10'10;
|
||||
} else {
|
||||
palette320[1] = 0b00'10'00;
|
||||
palette320[2] = 0b10'00'00;
|
||||
palette320[3] = 0b10'10'00; // TODO: brown, here and elsewhere.
|
||||
}
|
||||
|
||||
// b4: set 320x200 palette into high intensity.
|
||||
if(value & 0x10) {
|
||||
palette320[1] |= palette320[1] >> 1;
|
||||
palette320[2] |= palette320[2] >> 1;
|
||||
palette320[3] |= palette320[3] >> 1;
|
||||
}
|
||||
|
||||
// b3–b0: set background, border, monochrome colour.
|
||||
palette320[0] = uint8_t(
|
||||
((value & 0x01) << 1) |
|
||||
((value & 0x02) << 2) |
|
||||
((value & 0x04) << 3)
|
||||
);
|
||||
if(value & 0x08) {
|
||||
palette320[0] |= palette320[0] >> 1;
|
||||
}
|
||||
palette640[1] = palette320[0];
|
||||
}
|
||||
|
||||
uint8_t control() {
|
||||
return control_;
|
||||
}
|
||||
|
||||
void update_hsync(bool new_hsync) {
|
||||
if(new_hsync == previous_hsync) {
|
||||
cycles_since_hsync += clock_divider;
|
||||
} else {
|
||||
cycles_since_hsync = 0;
|
||||
previous_hsync = new_hsync;
|
||||
}
|
||||
}
|
||||
|
||||
OutputState implied_state(const Motorola::CRTC::BusState &state) const {
|
||||
OutputState new_state;
|
||||
|
||||
if(state.hsync || state.vsync) {
|
||||
new_state = OutputState::Sync;
|
||||
} else if(!state.display_enable || !(control_&0x08)) {
|
||||
new_state = OutputState::Border;
|
||||
|
||||
// TODO: I'm pretty sure this isn't correct for colour burst positioning, though
|
||||
// it happens to fool the particular CRT I've implemented.
|
||||
if(!(control_&4) && cycles_since_hsync <= 4) {
|
||||
new_state = OutputState::ColourBurst;
|
||||
}
|
||||
} else {
|
||||
new_state = OutputState::Pixels;
|
||||
}
|
||||
|
||||
return new_state;
|
||||
}
|
||||
|
||||
void perform_bus_cycle_phase1(const Motorola::CRTC::BusState &state) {
|
||||
// Determine new output state.
|
||||
update_hsync(state.hsync);
|
||||
const OutputState new_state = implied_state(state);
|
||||
|
||||
// Upon either a state change or just having accumulated too much local time...
|
||||
if(new_state != output_state || active_pixels_per_tick != pixels_per_tick || active_clock_divider != clock_divider || count > 912) {
|
||||
// (1) flush preexisting state.
|
||||
if(count) {
|
||||
switch(output_state) {
|
||||
case OutputState::Sync: crt.output_sync(count * active_clock_divider); break;
|
||||
case OutputState::Border: crt.output_blank(count * active_clock_divider); break;
|
||||
case OutputState::ColourBurst: crt.output_colour_burst(count * active_clock_divider, 0); break;
|
||||
case OutputState::Pixels: flush_pixels(); break;
|
||||
}
|
||||
}
|
||||
|
||||
// (2) adopt new state.
|
||||
output_state = new_state;
|
||||
active_pixels_per_tick = pixels_per_tick;
|
||||
active_clock_divider = clock_divider;
|
||||
count = 0;
|
||||
}
|
||||
|
||||
// Collect pixels if applicable.
|
||||
if(output_state == OutputState::Pixels) {
|
||||
if(!pixels) {
|
||||
pixel_pointer = pixels = crt.begin_data(DefaultAllocationSize);
|
||||
|
||||
// Flush any period where pixels weren't recorded due to back pressure.
|
||||
if(pixels && count) {
|
||||
crt.output_blank(count * active_clock_divider);
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if(pixels) {
|
||||
if(state.cursor) {
|
||||
std::fill(pixel_pointer, pixel_pointer + pixels_per_tick, 0x3f); // i.e. white.
|
||||
} else {
|
||||
if(mode_ == Mode::Text) {
|
||||
serialise_text(state);
|
||||
} else {
|
||||
serialise_pixels(state);
|
||||
}
|
||||
}
|
||||
pixel_pointer += active_pixels_per_tick;
|
||||
}
|
||||
}
|
||||
|
||||
// Advance.
|
||||
count += 8;
|
||||
|
||||
// Output pixel row prematurely if storage is exhausted.
|
||||
if(output_state == OutputState::Pixels && pixel_pointer == pixels + DefaultAllocationSize) {
|
||||
flush_pixels();
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
void perform_bus_cycle_phase2(const Motorola::CRTC::BusState &) {}
|
||||
|
||||
void flush_pixels() {
|
||||
crt.output_data(count * active_clock_divider, size_t((count * active_pixels_per_tick) / 8));
|
||||
pixels = pixel_pointer = nullptr;
|
||||
}
|
||||
|
||||
void serialise_pixels(const Motorola::CRTC::BusState &state) {
|
||||
// This is what I think is happenings:
|
||||
//
|
||||
// Refresh address is still shifted left one and two bytes are fetched, just as if it were
|
||||
// character code + attributes except that these are two bytes worth of graphics.
|
||||
//
|
||||
// Meanwhile, row address is used to invent a 15th address line.
|
||||
const auto base_address = ((state.refresh_address << 1) + (state.row_address << 13)) & 0x3fff;
|
||||
const uint8_t bitmap[] = {
|
||||
ram[base_address],
|
||||
ram[base_address + 1],
|
||||
};
|
||||
|
||||
if(mode_ == Mode::Pixels320) {
|
||||
pixel_pointer[0] = palette320[(bitmap[0] & 0xc0) >> 6];
|
||||
pixel_pointer[1] = palette320[(bitmap[0] & 0x30) >> 4];
|
||||
pixel_pointer[2] = palette320[(bitmap[0] & 0x0c) >> 2];
|
||||
pixel_pointer[3] = palette320[(bitmap[0] & 0x03) >> 0];
|
||||
pixel_pointer[4] = palette320[(bitmap[1] & 0xc0) >> 6];
|
||||
pixel_pointer[5] = palette320[(bitmap[1] & 0x30) >> 4];
|
||||
pixel_pointer[6] = palette320[(bitmap[1] & 0x0c) >> 2];
|
||||
pixel_pointer[7] = palette320[(bitmap[1] & 0x03) >> 0];
|
||||
} else {
|
||||
pixel_pointer[0x0] = palette640[(bitmap[0] & 0x80) >> 7];
|
||||
pixel_pointer[0x1] = palette640[(bitmap[0] & 0x40) >> 6];
|
||||
pixel_pointer[0x2] = palette640[(bitmap[0] & 0x20) >> 5];
|
||||
pixel_pointer[0x3] = palette640[(bitmap[0] & 0x10) >> 4];
|
||||
pixel_pointer[0x4] = palette640[(bitmap[0] & 0x08) >> 3];
|
||||
pixel_pointer[0x5] = palette640[(bitmap[0] & 0x04) >> 2];
|
||||
pixel_pointer[0x6] = palette640[(bitmap[0] & 0x02) >> 1];
|
||||
pixel_pointer[0x7] = palette640[(bitmap[0] & 0x01) >> 0];
|
||||
pixel_pointer[0x8] = palette640[(bitmap[1] & 0x80) >> 7];
|
||||
pixel_pointer[0x9] = palette640[(bitmap[1] & 0x40) >> 6];
|
||||
pixel_pointer[0xa] = palette640[(bitmap[1] & 0x20) >> 5];
|
||||
pixel_pointer[0xb] = palette640[(bitmap[1] & 0x10) >> 4];
|
||||
pixel_pointer[0xc] = palette640[(bitmap[1] & 0x08) >> 3];
|
||||
pixel_pointer[0xd] = palette640[(bitmap[1] & 0x04) >> 2];
|
||||
pixel_pointer[0xe] = palette640[(bitmap[1] & 0x02) >> 1];
|
||||
pixel_pointer[0xf] = palette640[(bitmap[1] & 0x01) >> 0];
|
||||
}
|
||||
}
|
||||
|
||||
void serialise_text(const Motorola::CRTC::BusState &state) {
|
||||
const uint8_t attributes = ram[((state.refresh_address << 1) + 1) & 0xfff];
|
||||
const uint8_t glyph = ram[((state.refresh_address << 1) + 0) & 0xfff];
|
||||
const uint8_t row = font[(glyph * 8) + state.row_address];
|
||||
|
||||
uint8_t colours[2] = {
|
||||
uint8_t(((attributes & 0x40) >> 1) | ((attributes & 0x20) >> 2) | ((attributes & 0x10) >> 3)),
|
||||
uint8_t(((attributes & 0x04) << 3) | ((attributes & 0x02) << 2) | ((attributes & 0x01) << 1)),
|
||||
};
|
||||
|
||||
// Apply foreground intensity.
|
||||
if(attributes & 0x08) {
|
||||
colours[1] |= colours[1] >> 1;
|
||||
}
|
||||
|
||||
// Apply blink or background intensity.
|
||||
if(control_ & 0x20) {
|
||||
if((attributes & 0x80) && (state.field_count & 16)) {
|
||||
std::swap(colours[0], colours[1]);
|
||||
}
|
||||
} else {
|
||||
if(attributes & 0x80) {
|
||||
colours[0] |= colours[0] >> 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Draw according to ROM contents.
|
||||
pixel_pointer[0] = (row & 0x80) ? colours[1] : colours[0];
|
||||
pixel_pointer[1] = (row & 0x40) ? colours[1] : colours[0];
|
||||
pixel_pointer[2] = (row & 0x20) ? colours[1] : colours[0];
|
||||
pixel_pointer[3] = (row & 0x10) ? colours[1] : colours[0];
|
||||
pixel_pointer[4] = (row & 0x08) ? colours[1] : colours[0];
|
||||
pixel_pointer[5] = (row & 0x04) ? colours[1] : colours[0];
|
||||
pixel_pointer[6] = (row & 0x02) ? colours[1] : colours[0];
|
||||
pixel_pointer[7] = (row & 0x01) ? colours[1] : colours[0];
|
||||
}
|
||||
|
||||
Outputs::CRT::CRT crt;
|
||||
static constexpr size_t DefaultAllocationSize = 320;
|
||||
|
||||
// Current output stream.
|
||||
uint8_t *pixels = nullptr;
|
||||
uint8_t *pixel_pointer = nullptr;
|
||||
int active_pixels_per_tick = 8;
|
||||
int active_clock_divider = 1;
|
||||
|
||||
// Source data.
|
||||
const uint8_t *ram = nullptr;
|
||||
std::vector<uint8_t> font;
|
||||
|
||||
// CRTC state tracking, for CRT serialisation.
|
||||
OutputState output_state = OutputState::Sync;
|
||||
int count = 0;
|
||||
|
||||
bool previous_hsync = false;
|
||||
int cycles_since_hsync = 0;
|
||||
|
||||
// Current Programmer-set parameters.
|
||||
int clock_divider = 1;
|
||||
int pixels_per_tick = 8;
|
||||
uint8_t control_ = 0;
|
||||
enum class Mode {
|
||||
Pixels640, Pixels320, Text,
|
||||
} mode_ = Mode::Text;
|
||||
|
||||
uint8_t palette320[4]{};
|
||||
uint8_t palette640[2]{};
|
||||
|
||||
} outputter_;
|
||||
Motorola::CRTC::CRTC6845<CRTCOutputter, Motorola::CRTC::CursorType::MDA> crtc_;
|
||||
|
||||
int full_clock_ = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* CGA_h */
|
236
Machines/PCCompatible/MDA.hpp
Normal file
236
Machines/PCCompatible/MDA.hpp
Normal file
@ -0,0 +1,236 @@
|
||||
//
|
||||
// MDA.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 05/12/2023.
|
||||
// Copyright © 2023 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef MDA_h
|
||||
#define MDA_h
|
||||
|
||||
#include "../../Components/6845/CRTC6845.hpp"
|
||||
#include "../../Outputs/CRT/CRT.hpp"
|
||||
#include "../../Machines/Utility/ROMCatalogue.hpp"
|
||||
|
||||
namespace PCCompatible {
|
||||
|
||||
class MDA {
|
||||
public:
|
||||
MDA() : crtc_(Motorola::CRTC::Personality::HD6845S, outputter_) {}
|
||||
|
||||
static constexpr uint32_t BaseAddress = 0xb'0000;
|
||||
static constexpr auto FontROM = ROM::Name::PCCompatibleMDAFont;
|
||||
|
||||
void set_source(const uint8_t *ram, std::vector<uint8_t> font) {
|
||||
outputter_.ram = ram;
|
||||
outputter_.font = font;
|
||||
}
|
||||
|
||||
void run_for(Cycles cycles) {
|
||||
// Input rate is the PIT rate of 1,193,182 Hz.
|
||||
// MDA is actually clocked at 16.257 MHz; which is treated internally as 1,806,333.333333333333333... Hz
|
||||
//
|
||||
// The GCD of those two numbers is... 2. Oh well.
|
||||
full_clock_ += 8'128'500 * cycles.as<int>();
|
||||
crtc_.run_for(Cycles(full_clock_ / (596'591 * 9)));
|
||||
full_clock_ %= (596'591 * 9);
|
||||
}
|
||||
|
||||
template <int address>
|
||||
void write(uint8_t value) {
|
||||
switch(address) {
|
||||
case 0: case 2: case 4: case 6:
|
||||
crtc_.select_register(value);
|
||||
break;
|
||||
case 1: case 3: case 5: case 7:
|
||||
crtc_.set_register(value);
|
||||
break;
|
||||
|
||||
case 0x8:
|
||||
outputter_.set_control(value);
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
template <int address>
|
||||
uint8_t read() {
|
||||
switch(address) {
|
||||
case 1: case 3: case 5: case 7:
|
||||
return crtc_.get_register();
|
||||
|
||||
case 0x8: return outputter_.control();
|
||||
|
||||
default: return 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Call-ins for ScanProducer.
|
||||
|
||||
void set_scan_target(Outputs::Display::ScanTarget *scan_target) {
|
||||
outputter_.crt.set_scan_target(scan_target);
|
||||
}
|
||||
Outputs::Display::ScanStatus get_scaled_scan_status() const {
|
||||
return outputter_.crt.get_scaled_scan_status() * 596591.0f / 8128500.0f;
|
||||
}
|
||||
|
||||
// MARK: - Display type configuration.
|
||||
|
||||
void set_display_type(Outputs::Display::DisplayType) {}
|
||||
Outputs::Display::DisplayType get_display_type() const {
|
||||
return outputter_.crt.get_display_type();
|
||||
}
|
||||
|
||||
private:
|
||||
struct CRTCOutputter {
|
||||
CRTCOutputter() :
|
||||
crt(882, 9, 370, 3, Outputs::Display::InputDataType::Red2Green2Blue2)
|
||||
// TODO: really this should be a Luminance8 and set an appropriate modal tint colour;
|
||||
// consider whether that's worth building into the scan target.
|
||||
{
|
||||
// crt.set_visible_area(Outputs::Display::Rect(0.1072f, 0.1f, 0.842105263157895f, 0.842105263157895f));
|
||||
crt.set_display_type(Outputs::Display::DisplayType::RGB);
|
||||
}
|
||||
|
||||
void set_control(uint8_t control) {
|
||||
// b0: 'high resolution' (probably breaks if not 1)
|
||||
// b3: video enable
|
||||
// b5: enable blink
|
||||
control_ = control;
|
||||
}
|
||||
|
||||
uint8_t control() {
|
||||
return control_;
|
||||
}
|
||||
|
||||
void perform_bus_cycle_phase1(const Motorola::CRTC::BusState &state) {
|
||||
// Determine new output state.
|
||||
const OutputState new_state =
|
||||
(state.hsync | state.vsync) ? OutputState::Sync :
|
||||
((state.display_enable && control_&0x08) ? OutputState::Pixels : OutputState::Border);
|
||||
|
||||
// Upon either a state change or just having accumulated too much local time...
|
||||
if(new_state != output_state || count > 882) {
|
||||
// (1) flush preexisting state.
|
||||
if(count) {
|
||||
switch(output_state) {
|
||||
case OutputState::Sync: crt.output_sync(count); break;
|
||||
case OutputState::Border: crt.output_blank(count); break;
|
||||
case OutputState::Pixels:
|
||||
crt.output_data(count);
|
||||
pixels = pixel_pointer = nullptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// (2) adopt new state.
|
||||
output_state = new_state;
|
||||
count = 0;
|
||||
}
|
||||
|
||||
// Collect pixels if applicable.
|
||||
if(output_state == OutputState::Pixels) {
|
||||
if(!pixels) {
|
||||
pixel_pointer = pixels = crt.begin_data(DefaultAllocationSize);
|
||||
|
||||
// Flush any period where pixels weren't recorded due to back pressure.
|
||||
if(pixels && count) {
|
||||
crt.output_blank(count);
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if(pixels) {
|
||||
static constexpr uint8_t high_intensity = 0x0d;
|
||||
static constexpr uint8_t low_intensity = 0x09;
|
||||
static constexpr uint8_t off = 0x00;
|
||||
|
||||
if(state.cursor) {
|
||||
pixel_pointer[0] = pixel_pointer[1] = pixel_pointer[2] = pixel_pointer[3] =
|
||||
pixel_pointer[4] = pixel_pointer[5] = pixel_pointer[6] = pixel_pointer[7] =
|
||||
pixel_pointer[8] = low_intensity;
|
||||
} else {
|
||||
const uint8_t attributes = ram[((state.refresh_address << 1) + 1) & 0xfff];
|
||||
const uint8_t glyph = ram[((state.refresh_address << 1) + 0) & 0xfff];
|
||||
uint8_t row = font[(glyph * 14) + state.row_address];
|
||||
|
||||
const uint8_t intensity = (attributes & 0x08) ? high_intensity : low_intensity;
|
||||
uint8_t blank = off;
|
||||
|
||||
// Handle irregular attributes.
|
||||
// Cf. http://www.seasip.info/VintagePC/mda.html#memmap
|
||||
switch(attributes) {
|
||||
case 0x00: case 0x08: case 0x80: case 0x88:
|
||||
row = 0;
|
||||
break;
|
||||
case 0x70: case 0x78: case 0xf0: case 0xf8:
|
||||
row ^= 0xff;
|
||||
blank = intensity;
|
||||
break;
|
||||
}
|
||||
|
||||
// Apply blink if enabled.
|
||||
if((control_ & 0x20) && (attributes & 0x80) && (state.field_count & 16)) {
|
||||
row ^= 0xff;
|
||||
blank = (blank == off) ? intensity : off;
|
||||
}
|
||||
|
||||
if(((attributes & 7) == 1) && state.row_address == 13) {
|
||||
// Draw as underline.
|
||||
std::fill(pixel_pointer, pixel_pointer + 9, intensity);
|
||||
} else {
|
||||
// Draw according to ROM contents, possibly duplicating final column.
|
||||
pixel_pointer[0] = (row & 0x80) ? intensity : off;
|
||||
pixel_pointer[1] = (row & 0x40) ? intensity : off;
|
||||
pixel_pointer[2] = (row & 0x20) ? intensity : off;
|
||||
pixel_pointer[3] = (row & 0x10) ? intensity : off;
|
||||
pixel_pointer[4] = (row & 0x08) ? intensity : off;
|
||||
pixel_pointer[5] = (row & 0x04) ? intensity : off;
|
||||
pixel_pointer[6] = (row & 0x02) ? intensity : off;
|
||||
pixel_pointer[7] = (row & 0x01) ? intensity : off;
|
||||
pixel_pointer[8] = (glyph >= 0xc0 && glyph < 0xe0) ? pixel_pointer[7] : blank;
|
||||
}
|
||||
}
|
||||
pixel_pointer += 9;
|
||||
}
|
||||
}
|
||||
|
||||
// Advance.
|
||||
count += 9;
|
||||
|
||||
// Output pixel row prematurely if storage is exhausted.
|
||||
if(output_state == OutputState::Pixels && pixel_pointer == pixels + DefaultAllocationSize) {
|
||||
crt.output_data(count);
|
||||
count = 0;
|
||||
|
||||
pixels = pixel_pointer = nullptr;
|
||||
}
|
||||
}
|
||||
void perform_bus_cycle_phase2(const Motorola::CRTC::BusState &) {}
|
||||
|
||||
Outputs::CRT::CRT crt;
|
||||
|
||||
enum class OutputState {
|
||||
Sync, Pixels, Border
|
||||
} output_state = OutputState::Sync;
|
||||
int count = 0;
|
||||
|
||||
uint8_t *pixels = nullptr;
|
||||
uint8_t *pixel_pointer = nullptr;
|
||||
static constexpr size_t DefaultAllocationSize = 720;
|
||||
|
||||
const uint8_t *ram = nullptr;
|
||||
std::vector<uint8_t> font;
|
||||
|
||||
uint8_t control_ = 0;
|
||||
} outputter_;
|
||||
Motorola::CRTC::CRTC6845<CRTCOutputter, Motorola::CRTC::CursorType::MDA> crtc_;
|
||||
|
||||
int full_clock_ = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* MDA_h */
|
@ -8,18 +8,19 @@
|
||||
|
||||
#include "PCCompatible.hpp"
|
||||
|
||||
#include "CGA.hpp"
|
||||
#include "DMA.hpp"
|
||||
#include "KeyboardMapper.hpp"
|
||||
#include "MDA.hpp"
|
||||
#include "Memory.hpp"
|
||||
#include "PIC.hpp"
|
||||
#include "PIT.hpp"
|
||||
#include "DMA.hpp"
|
||||
#include "Memory.hpp"
|
||||
|
||||
#include "../../InstructionSets/x86/Decoder.hpp"
|
||||
#include "../../InstructionSets/x86/Flags.hpp"
|
||||
#include "../../InstructionSets/x86/Instruction.hpp"
|
||||
#include "../../InstructionSets/x86/Perform.hpp"
|
||||
|
||||
#include "../../Components/6845/CRTC6845.hpp"
|
||||
#include "../../Components/8255/i8255.hpp"
|
||||
#include "../../Components/8272/CommandDecoder.hpp"
|
||||
#include "../../Components/8272/Results.hpp"
|
||||
@ -41,27 +42,33 @@
|
||||
#include "../ScanProducer.hpp"
|
||||
#include "../TimedMachine.hpp"
|
||||
|
||||
#include "../../Analyser/Static/PCCompatible/Target.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <iostream>
|
||||
|
||||
namespace PCCompatible {
|
||||
|
||||
//bool log = false;
|
||||
//std::string previous;
|
||||
using VideoAdaptor = Analyser::Static::PCCompatible::Target::VideoAdaptor;
|
||||
|
||||
template <VideoAdaptor adaptor> struct Adaptor;
|
||||
template <> struct Adaptor<VideoAdaptor::MDA> {
|
||||
using type = MDA;
|
||||
};
|
||||
template <> struct Adaptor<VideoAdaptor::CGA> {
|
||||
using type = CGA;
|
||||
};
|
||||
|
||||
class FloppyController {
|
||||
public:
|
||||
FloppyController(PIC &pic, DMA &dma) : pic_(pic), dma_(dma) {
|
||||
FloppyController(PIC &pic, DMA &dma, int drive_count) : pic_(pic), dma_(dma) {
|
||||
// Default: one floppy drive only.
|
||||
drives_[0].exists = true;
|
||||
drives_[1].exists = false;
|
||||
drives_[2].exists = false;
|
||||
drives_[3].exists = false;
|
||||
for(int c = 0; c < 4; c++) {
|
||||
drives_[c].exists = drive_count > c;
|
||||
}
|
||||
}
|
||||
|
||||
void set_digital_output(uint8_t control) {
|
||||
// printf("FDC DOR: %02x\n", control);
|
||||
|
||||
// b7, b6, b5, b4: enable motor for drive 4, 3, 2, 1;
|
||||
// b3: 1 => enable DMA; 0 => disable;
|
||||
// b2: 1 => enable FDC; 0 => hold at reset;
|
||||
@ -92,7 +99,6 @@ class FloppyController {
|
||||
}
|
||||
|
||||
uint8_t status() const {
|
||||
// printf("FDC: read status %02x\n", status_.main());
|
||||
return status_.main();
|
||||
}
|
||||
|
||||
@ -138,6 +144,7 @@ class FloppyController {
|
||||
switch(access_result) {
|
||||
default: break;
|
||||
case AccessResult::NotAccepted:
|
||||
complete = true;
|
||||
wrote_in_full = false;
|
||||
break;
|
||||
case AccessResult::AcceptedWithEOP:
|
||||
@ -182,7 +189,6 @@ class FloppyController {
|
||||
} break;
|
||||
|
||||
case Command::Recalibrate:
|
||||
printf("FDC: Recalibrate\n");
|
||||
drives_[decoder_.target().drive].track = 0;
|
||||
|
||||
drives_[decoder_.target().drive].raised_interrupt = true;
|
||||
@ -190,7 +196,6 @@ class FloppyController {
|
||||
pic_.apply_edge<6>(true);
|
||||
break;
|
||||
case Command::Seek:
|
||||
printf("FDC: Seek %d:%d to %d\n", decoder_.target().drive, decoder_.target().head, decoder_.seek_target());
|
||||
drives_[decoder_.target().drive].track = decoder_.seek_target();
|
||||
|
||||
drives_[decoder_.target().drive].raised_interrupt = true;
|
||||
@ -199,8 +204,6 @@ class FloppyController {
|
||||
break;
|
||||
|
||||
case Command::SenseInterruptStatus: {
|
||||
printf("FDC: SenseInterruptStatus\n");
|
||||
|
||||
int c = 0;
|
||||
for(; c < 4; c++) {
|
||||
if(drives_[c].raised_interrupt) {
|
||||
@ -219,14 +222,12 @@ class FloppyController {
|
||||
}
|
||||
} break;
|
||||
case Command::Specify:
|
||||
printf("FDC: Specify\n");
|
||||
specify_specs_ = decoder_.specify_specs();
|
||||
break;
|
||||
// case Command::SenseDriveStatus: {
|
||||
// } break;
|
||||
|
||||
case Command::Invalid:
|
||||
printf("FDC: Invalid\n");
|
||||
results_.serialise_none();
|
||||
break;
|
||||
}
|
||||
@ -251,11 +252,9 @@ class FloppyController {
|
||||
status_.set(MainStatus::DataIsToProcessor, false);
|
||||
status_.set(MainStatus::CommandInProgress, false);
|
||||
}
|
||||
// printf("FDC read: %02x\n", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
printf("FDC read?\n");
|
||||
return 0x80;
|
||||
}
|
||||
|
||||
@ -458,203 +457,6 @@ class KeyboardController {
|
||||
int reset_delay_ = 0;
|
||||
};
|
||||
|
||||
class MDA {
|
||||
public:
|
||||
MDA() : crtc_(Motorola::CRTC::Personality::HD6845S, outputter_) {}
|
||||
|
||||
void set_source(const uint8_t *ram, std::vector<uint8_t> font) {
|
||||
outputter_.ram = ram;
|
||||
outputter_.font = font;
|
||||
}
|
||||
|
||||
void run_for(Cycles cycles) {
|
||||
// I _think_ the MDA's CRTC is clocked at 14/9ths the PIT clock.
|
||||
// Do that conversion here.
|
||||
full_clock_ += 14 * cycles.as<int>();
|
||||
crtc_.run_for(Cycles(full_clock_ / 9));
|
||||
full_clock_ %= 9;
|
||||
}
|
||||
|
||||
template <int address>
|
||||
void write(uint8_t value) {
|
||||
if constexpr (address & 0x8) {
|
||||
outputter_.set_control(value);
|
||||
} else {
|
||||
if constexpr (address & 0x1) {
|
||||
crtc_.set_register(value);
|
||||
} else {
|
||||
crtc_.select_register(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <int address>
|
||||
uint8_t read() {
|
||||
if constexpr (address & 0x8) {
|
||||
return outputter_.control();
|
||||
} else {
|
||||
return crtc_.get_register();
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Call-ins for ScanProducer.
|
||||
|
||||
void set_scan_target(Outputs::Display::ScanTarget *scan_target) {
|
||||
outputter_.crt.set_scan_target(scan_target);
|
||||
}
|
||||
|
||||
Outputs::Display::ScanStatus get_scaled_scan_status() const {
|
||||
return outputter_.crt.get_scaled_scan_status() * 9.0f / 14.0f;
|
||||
}
|
||||
|
||||
private:
|
||||
struct CRTCOutputter {
|
||||
CRTCOutputter() :
|
||||
crt(882, 9, 382, 3, Outputs::Display::InputDataType::Red2Green2Blue2)
|
||||
// TODO: really this should be a Luminance8 and set an appropriate modal tint colour;
|
||||
// consider whether that's worth building into the scan target.
|
||||
{
|
||||
// crt.set_visible_area(Outputs::Display::Rect(0.1072f, 0.1f, 0.842105263157895f, 0.842105263157895f));
|
||||
crt.set_display_type(Outputs::Display::DisplayType::RGB);
|
||||
}
|
||||
|
||||
void set_control(uint8_t control) {
|
||||
// b0: 'high resolution' (probably breaks if not 1)
|
||||
// b3: video enable
|
||||
// b5: enable blink
|
||||
control_ = control;
|
||||
}
|
||||
|
||||
uint8_t control() {
|
||||
return control_;
|
||||
}
|
||||
|
||||
void perform_bus_cycle_phase1(const Motorola::CRTC::BusState &state) {
|
||||
// Determine new output state.
|
||||
const OutputState new_state =
|
||||
(state.hsync | state.vsync) ? OutputState::Sync :
|
||||
((state.display_enable && control_&0x08) ? OutputState::Pixels : OutputState::Border);
|
||||
|
||||
// Upon either a state change or just having accumulated too much local time...
|
||||
if(new_state != output_state || count > 882) {
|
||||
// (1) flush preexisting state.
|
||||
if(count) {
|
||||
switch(output_state) {
|
||||
case OutputState::Sync: crt.output_sync(count); break;
|
||||
case OutputState::Border: crt.output_blank(count); break;
|
||||
case OutputState::Pixels:
|
||||
crt.output_data(count);
|
||||
pixels = pixel_pointer = nullptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// (2) adopt new state.
|
||||
output_state = new_state;
|
||||
count = 0;
|
||||
}
|
||||
|
||||
// Collect pixels if applicable.
|
||||
if(output_state == OutputState::Pixels) {
|
||||
if(!pixels) {
|
||||
pixel_pointer = pixels = crt.begin_data(DefaultAllocationSize);
|
||||
|
||||
// Flush any period where pixels weren't recorded due to back pressure.
|
||||
if(pixels && count) {
|
||||
crt.output_blank(count);
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if(pixels) {
|
||||
static constexpr uint8_t high_intensity = 0x0d;
|
||||
static constexpr uint8_t low_intensity = 0x09;
|
||||
static constexpr uint8_t off = 0x00;
|
||||
|
||||
if(state.cursor) {
|
||||
pixel_pointer[0] = pixel_pointer[1] = pixel_pointer[2] = pixel_pointer[3] =
|
||||
pixel_pointer[4] = pixel_pointer[5] = pixel_pointer[6] = pixel_pointer[7] =
|
||||
pixel_pointer[8] = low_intensity;
|
||||
} else {
|
||||
const uint8_t attributes = ram[((state.refresh_address << 1) + 1) & 0xfff];
|
||||
const uint8_t glyph = ram[((state.refresh_address << 1) + 0) & 0xfff];
|
||||
uint8_t row = font[(glyph * 14) + state.row_address];
|
||||
|
||||
const uint8_t intensity = (attributes & 0x08) ? high_intensity : low_intensity;
|
||||
uint8_t blank = off;
|
||||
|
||||
// Handle irregular attributes.
|
||||
// Cf. http://www.seasip.info/VintagePC/mda.html#memmap
|
||||
switch(attributes) {
|
||||
case 0x00: case 0x08: case 0x80: case 0x88:
|
||||
row = 0;
|
||||
break;
|
||||
case 0x70: case 0x78: case 0xf0: case 0xf8:
|
||||
row ^= 0xff;
|
||||
blank = intensity;
|
||||
break;
|
||||
}
|
||||
|
||||
// Apply blink if enabled.
|
||||
if((control_ & 0x20) && (attributes & 0x80) && (state.field_count & 16)) {
|
||||
row ^= 0xff;
|
||||
blank = (blank == off) ? intensity : off;
|
||||
}
|
||||
|
||||
if(((attributes & 7) == 1) && state.row_address == 13) {
|
||||
// Draw as underline.
|
||||
std::fill(pixel_pointer, pixel_pointer + 9, intensity);
|
||||
} else {
|
||||
// Draw according to ROM contents, possibly duplicating final column.
|
||||
pixel_pointer[0] = (row & 0x80) ? intensity : off;
|
||||
pixel_pointer[1] = (row & 0x40) ? intensity : off;
|
||||
pixel_pointer[2] = (row & 0x20) ? intensity : off;
|
||||
pixel_pointer[3] = (row & 0x10) ? intensity : off;
|
||||
pixel_pointer[4] = (row & 0x08) ? intensity : off;
|
||||
pixel_pointer[5] = (row & 0x04) ? intensity : off;
|
||||
pixel_pointer[6] = (row & 0x02) ? intensity : off;
|
||||
pixel_pointer[7] = (row & 0x01) ? intensity : off;
|
||||
pixel_pointer[8] = (glyph >= 0xc0 && glyph < 0xe0) ? pixel_pointer[7] : blank;
|
||||
}
|
||||
}
|
||||
pixel_pointer += 9;
|
||||
}
|
||||
}
|
||||
|
||||
// Advance.
|
||||
count += 9;
|
||||
|
||||
// Output pixel row prematurely if storage is exhausted.
|
||||
if(output_state == OutputState::Pixels && pixel_pointer == pixels + DefaultAllocationSize) {
|
||||
crt.output_data(count);
|
||||
count = 0;
|
||||
|
||||
pixels = pixel_pointer = nullptr;
|
||||
}
|
||||
}
|
||||
void perform_bus_cycle_phase2(const Motorola::CRTC::BusState &) {}
|
||||
|
||||
Outputs::CRT::CRT crt;
|
||||
|
||||
enum class OutputState {
|
||||
Sync, Pixels, Border
|
||||
} output_state = OutputState::Sync;
|
||||
int count = 0;
|
||||
|
||||
uint8_t *pixels = nullptr;
|
||||
uint8_t *pixel_pointer = nullptr;
|
||||
static constexpr size_t DefaultAllocationSize = 720;
|
||||
|
||||
const uint8_t *ram = nullptr;
|
||||
std::vector<uint8_t> font;
|
||||
|
||||
uint8_t control_ = 0;
|
||||
} outputter_;
|
||||
Motorola::CRTC::CRTC6845<CRTCOutputter, Motorola::CRTC::CursorType::MDA> crtc_;
|
||||
|
||||
int full_clock_;
|
||||
};
|
||||
|
||||
struct PCSpeaker {
|
||||
PCSpeaker() :
|
||||
toggle(queue),
|
||||
@ -724,10 +526,28 @@ class PITObserver {
|
||||
using PIT = i8253<false, PITObserver>;
|
||||
|
||||
class i8255PortHandler : public Intel::i8255::PortHandler {
|
||||
// Likely to be helpful: https://github.com/tmk/tmk_keyboard/wiki/IBM-PC-XT-Keyboard-Protocol
|
||||
public:
|
||||
i8255PortHandler(PCSpeaker &speaker, KeyboardController &keyboard) :
|
||||
speaker_(speaker), keyboard_(keyboard) {}
|
||||
i8255PortHandler(PCSpeaker &speaker, KeyboardController &keyboard, VideoAdaptor adaptor, int drive_count) :
|
||||
speaker_(speaker), keyboard_(keyboard) {
|
||||
// High switches:
|
||||
//
|
||||
// b3, b2: drive count; 00 = 1, 01 = 2, etc
|
||||
// b1, b0: video mode (00 = ROM; 01 = CGA40; 10 = CGA80; 11 = MDA)
|
||||
switch(adaptor) {
|
||||
default: break;
|
||||
case VideoAdaptor::MDA: high_switches_ |= 0b11; break;
|
||||
case VideoAdaptor::CGA: high_switches_ |= 0b10; break; // Assume 80 columns.
|
||||
}
|
||||
high_switches_ |= uint8_t(drive_count << 2);
|
||||
|
||||
// Low switches:
|
||||
//
|
||||
// b3, b2: RAM on motherboard (64 * bit pattern)
|
||||
// b1: 1 => FPU present; 0 => absent;
|
||||
// b0: 1 => floppy drive present; 0 => absent.
|
||||
low_switches_ |= 0b1100; // Assume maximum RAM.
|
||||
if(drive_count) low_switches_ |= 0xb0001;
|
||||
}
|
||||
|
||||
void set_value(int port, uint8_t value) {
|
||||
switch(port) {
|
||||
@ -742,43 +562,35 @@ class i8255PortHandler : public Intel::i8255::PortHandler {
|
||||
enable_keyboard_ = !(value & 0x80);
|
||||
keyboard_.set_mode(value >> 6);
|
||||
|
||||
high_switches_ = value & 0x08;
|
||||
use_high_switches_ = value & 0x08;
|
||||
speaker_.set_control(value & 0x01, value & 0x02);
|
||||
break;
|
||||
}
|
||||
// printf("PPI: %02x to %d\n", value, port);
|
||||
}
|
||||
|
||||
uint8_t get_value(int port) {
|
||||
switch(port) {
|
||||
case 0:
|
||||
// printf("PPI: from keyboard\n");
|
||||
return enable_keyboard_ ? keyboard_.read() : 0b0011'1101;
|
||||
return enable_keyboard_ ? keyboard_.read() : uint8_t((high_switches_ << 4) | low_switches_);
|
||||
// Guesses that switches is high and low combined as below.
|
||||
|
||||
case 2:
|
||||
// Common:
|
||||
//
|
||||
// b7: 1 => memory parity error; 0 => none;
|
||||
// b6: 1 => IO channel error; 0 => none;
|
||||
// b5: timer 2 output; [TODO]
|
||||
// b4: cassette data input; [TODO]
|
||||
// b3...b0: whichever of the high and low switches is selected.
|
||||
return
|
||||
high_switches_ ?
|
||||
// b3, b2: drive count; 00 = 1, 01 = 2, etc
|
||||
// b1, b0: video mode (00 = ROM; 01 = CGA40; 10 = CGA80; 11 = MDA)
|
||||
0b0000'0011
|
||||
:
|
||||
// b3, b2: RAM on motherboard (64 * bit pattern)
|
||||
// b1: 1 => FPU present; 0 => absent;
|
||||
// b0: 1 => floppy drive present; 0 => absent.
|
||||
0b0000'1101;
|
||||
use_high_switches_ ? high_switches_ : low_switches_;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
private:
|
||||
bool high_switches_ = false;
|
||||
uint8_t high_switches_ = 0;
|
||||
uint8_t low_switches_ = 0;
|
||||
|
||||
bool use_high_switches_ = false;
|
||||
PCSpeaker &speaker_;
|
||||
KeyboardController &keyboard_;
|
||||
|
||||
@ -786,12 +598,16 @@ class i8255PortHandler : public Intel::i8255::PortHandler {
|
||||
};
|
||||
using PPI = Intel::i8255::i8255<i8255PortHandler>;
|
||||
|
||||
template <VideoAdaptor video>
|
||||
class IO {
|
||||
public:
|
||||
IO(PIT &pit, DMA &dma, PPI &ppi, PIC &pic, MDA &mda, FloppyController &fdc) :
|
||||
pit_(pit), dma_(dma), ppi_(ppi), pic_(pic), mda_(mda), fdc_(fdc) {}
|
||||
IO(PIT &pit, DMA &dma, PPI &ppi, PIC &pic, typename Adaptor<video>::type &card, FloppyController &fdc) :
|
||||
pit_(pit), dma_(dma), ppi_(ppi), pic_(pic), video_(card), fdc_(fdc) {}
|
||||
|
||||
template <typename IntT> void out(uint16_t port, IntT value) {
|
||||
static constexpr uint16_t crtc_base =
|
||||
video == VideoAdaptor::MDA ? 0x03b0 : 0x03d0;
|
||||
|
||||
switch(port) {
|
||||
default:
|
||||
if constexpr (std::is_same_v<IntT, uint8_t>) {
|
||||
@ -847,34 +663,30 @@ class IO {
|
||||
case 0x0086: dma_.pages.set_page<6>(uint8_t(value)); break;
|
||||
case 0x0087: dma_.pages.set_page<7>(uint8_t(value)); break;
|
||||
|
||||
case 0x03b0: case 0x03b2: case 0x03b4: case 0x03b6:
|
||||
//
|
||||
// CRTC access block, with slightly laboured 16-bit to 8-bit mapping.
|
||||
//
|
||||
case crtc_base + 0: case crtc_base + 2:
|
||||
case crtc_base + 4: case crtc_base + 6:
|
||||
if constexpr (std::is_same_v<IntT, uint16_t>) {
|
||||
mda_.write<0>(uint8_t(value));
|
||||
mda_.write<1>(uint8_t(value >> 8));
|
||||
video_.template write<0>(uint8_t(value));
|
||||
video_.template write<1>(uint8_t(value >> 8));
|
||||
} else {
|
||||
mda_.write<0>(value);
|
||||
video_.template write<0>(value);
|
||||
}
|
||||
break;
|
||||
case crtc_base + 1: case crtc_base + 3:
|
||||
case crtc_base + 5: case crtc_base + 7:
|
||||
if constexpr (std::is_same_v<IntT, uint16_t>) {
|
||||
video_.template write<1>(uint8_t(value));
|
||||
video_.template write<0>(uint8_t(value >> 8));
|
||||
} else {
|
||||
video_.template write<1>(value);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x03b1: case 0x03b3: case 0x03b5: case 0x03b7:
|
||||
if constexpr (std::is_same_v<IntT, uint16_t>) {
|
||||
mda_.write<1>(uint8_t(value));
|
||||
mda_.write<0>(uint8_t(value >> 8));
|
||||
} else {
|
||||
mda_.write<1>(value);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x03b8:
|
||||
mda_.write<8>(uint8_t(value));
|
||||
break;
|
||||
|
||||
case 0x03d0: case 0x03d1: case 0x03d2: case 0x03d3:
|
||||
case 0x03d4: case 0x03d5: case 0x03d6: case 0x03d7:
|
||||
case 0x03d8: case 0x03d9: case 0x03da: case 0x03db:
|
||||
case 0x03dc: case 0x03dd: case 0x03de: case 0x03df:
|
||||
// Ignore CGA accesses.
|
||||
break;
|
||||
case crtc_base + 0x8: video_.template write<0x8>(uint8_t(value)); break;
|
||||
case crtc_base + 0x9: video_.template write<0x9>(uint8_t(value)); break;
|
||||
|
||||
case 0x03f2:
|
||||
fdc_.set_digital_output(uint8_t(value));
|
||||
@ -959,7 +771,17 @@ class IO {
|
||||
case 0x03f4: return fdc_.status();
|
||||
case 0x03f5: return fdc_.read();
|
||||
|
||||
case 0x03b8: return mda_.read<8>();
|
||||
case 0x03b8:
|
||||
if constexpr (video == VideoAdaptor::MDA) {
|
||||
return video_.template read<0x8>();
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x3da:
|
||||
if constexpr (video == VideoAdaptor::CGA) {
|
||||
return video_.template read<0xa>();
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x02e8: case 0x02e9: case 0x02ea: case 0x02eb:
|
||||
case 0x02ec: case 0x02ed: case 0x02ee: case 0x02ef:
|
||||
@ -972,7 +794,7 @@ class IO {
|
||||
// Ignore serial port accesses.
|
||||
break;
|
||||
}
|
||||
return IntT(~0);
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
private:
|
||||
@ -980,7 +802,7 @@ class IO {
|
||||
DMA &dma_;
|
||||
PPI &ppi_;
|
||||
PIC &pic_;
|
||||
MDA &mda_;
|
||||
typename Adaptor<video>::type &video_;
|
||||
FloppyController &fdc_;
|
||||
};
|
||||
|
||||
@ -1033,6 +855,7 @@ class FlowController {
|
||||
bool halted_ = false;
|
||||
};
|
||||
|
||||
template <VideoAdaptor video>
|
||||
class ConcreteMachine:
|
||||
public Machine,
|
||||
public MachineTypes::TimedMachine,
|
||||
@ -1040,20 +863,24 @@ class ConcreteMachine:
|
||||
public MachineTypes::MappedKeyboardMachine,
|
||||
public MachineTypes::MediaTarget,
|
||||
public MachineTypes::ScanProducer,
|
||||
public Activity::Source
|
||||
public Activity::Source,
|
||||
public Configurable::Device
|
||||
{
|
||||
static constexpr int DriveCount = 1;
|
||||
using Video = typename Adaptor<video>::type;
|
||||
|
||||
public:
|
||||
ConcreteMachine(
|
||||
[[maybe_unused]] const Analyser::Static::Target &target,
|
||||
const Analyser::Static::PCCompatible::Target &target,
|
||||
const ROMMachine::ROMFetcher &rom_fetcher
|
||||
) :
|
||||
keyboard_(pic_),
|
||||
fdc_(pic_, dma_),
|
||||
fdc_(pic_, dma_, DriveCount),
|
||||
pit_observer_(pic_, speaker_),
|
||||
ppi_handler_(speaker_, keyboard_),
|
||||
ppi_handler_(speaker_, keyboard_, video, DriveCount),
|
||||
pit_(pit_observer_),
|
||||
ppi_(ppi_handler_),
|
||||
context(pit_, dma_, ppi_, pic_, mda_, fdc_)
|
||||
context(pit_, dma_, ppi_, pic_, video_, fdc_)
|
||||
{
|
||||
// Set up DMA source/target.
|
||||
dma_.set_memory(&context.memory);
|
||||
@ -1065,7 +892,7 @@ class ConcreteMachine:
|
||||
|
||||
// Fetch the BIOS. [8088 only, for now]
|
||||
const auto bios = ROM::Name::PCCompatibleGLaBIOS;
|
||||
const auto font = ROM::Name::PCCompatibleMDAFont;
|
||||
const auto font = Video::FontROM;
|
||||
|
||||
ROM::Request request = ROM::Request(bios) && ROM::Request(font);
|
||||
auto roms = rom_fetcher(request);
|
||||
@ -1076,9 +903,9 @@ class ConcreteMachine:
|
||||
const auto &bios_contents = roms.find(bios)->second;
|
||||
context.memory.install(0x10'0000 - bios_contents.size(), bios_contents.data(), bios_contents.size());
|
||||
|
||||
// Give the MDA something to read from.
|
||||
// Give the video card something to read from.
|
||||
const auto &font_contents = roms.find(font)->second;
|
||||
mda_.set_source(context.memory.at(0xb'0000), font_contents);
|
||||
video_.set_source(context.memory.at(Video::BaseAddress), font_contents);
|
||||
|
||||
// ... and insert media.
|
||||
insert_media(target.media);
|
||||
@ -1113,7 +940,7 @@ class ConcreteMachine:
|
||||
//
|
||||
// Advance CRTC at a more approximate rate.
|
||||
//
|
||||
mda_.run_for(Cycles(3));
|
||||
video_.run_for(Cycles(3));
|
||||
|
||||
//
|
||||
// Perform one CPU instruction every three PIT cycles.
|
||||
@ -1123,7 +950,7 @@ class ConcreteMachine:
|
||||
keyboard_.run_for(Cycles(1));
|
||||
|
||||
// Query for interrupts and apply if pending.
|
||||
if(pic_.pending() && context.flags.flag<InstructionSet::x86::Flag::Interrupt>()) {
|
||||
if(pic_.pending() && context.flags.template flag<InstructionSet::x86::Flag::Interrupt>()) {
|
||||
// Regress the IP if a REP is in-progress so as to resume it later.
|
||||
if(context.flow_controller.should_repeat()) {
|
||||
context.registers.ip() = decoded_ip_;
|
||||
@ -1192,10 +1019,10 @@ class ConcreteMachine:
|
||||
|
||||
// MARK: - ScanProducer.
|
||||
void set_scan_target(Outputs::Display::ScanTarget *scan_target) override {
|
||||
mda_.set_scan_target(scan_target);
|
||||
video_.set_scan_target(scan_target);
|
||||
}
|
||||
Outputs::Display::ScanStatus get_scaled_scan_status() const override {
|
||||
return mda_.get_scaled_scan_status();
|
||||
return video_.get_scaled_scan_status();
|
||||
}
|
||||
|
||||
// MARK: - AudioProducer.
|
||||
@ -1235,11 +1062,31 @@ class ConcreteMachine:
|
||||
fdc_.set_activity_observer(observer);
|
||||
}
|
||||
|
||||
// MARK: - Configuration options.
|
||||
std::unique_ptr<Reflection::Struct> get_options() override {
|
||||
auto options = std::make_unique<Options>(Configurable::OptionsType::UserFriendly);
|
||||
options->output = get_video_signal_configurable();
|
||||
return options;
|
||||
}
|
||||
|
||||
void set_options(const std::unique_ptr<Reflection::Struct> &str) override {
|
||||
const auto options = dynamic_cast<Options *>(str.get());
|
||||
set_video_signal_configurable(options->output);
|
||||
}
|
||||
|
||||
void set_display_type(Outputs::Display::DisplayType display_type) override {
|
||||
video_.set_display_type(display_type);
|
||||
}
|
||||
|
||||
Outputs::Display::DisplayType get_display_type() const override {
|
||||
return video_.get_display_type();
|
||||
}
|
||||
|
||||
private:
|
||||
PIC pic_;
|
||||
DMA dma_;
|
||||
PCSpeaker speaker_;
|
||||
MDA mda_;
|
||||
Video video_;
|
||||
|
||||
KeyboardController keyboard_;
|
||||
FloppyController fdc_;
|
||||
@ -1252,11 +1099,11 @@ class ConcreteMachine:
|
||||
PCCompatible::KeyboardMapper keyboard_mapper_;
|
||||
|
||||
struct Context {
|
||||
Context(PIT &pit, DMA &dma, PPI &ppi, PIC &pic, MDA &mda, FloppyController &fdc) :
|
||||
Context(PIT &pit, DMA &dma, PPI &ppi, PIC &pic, typename Adaptor<video>::type &card, FloppyController &fdc) :
|
||||
segments(registers),
|
||||
memory(registers, segments),
|
||||
flow_controller(registers, segments),
|
||||
io(pit, dma, ppi, pic, mda, fdc)
|
||||
io(pit, dma, ppi, pic, card, fdc)
|
||||
{
|
||||
reset();
|
||||
}
|
||||
@ -1271,7 +1118,7 @@ class ConcreteMachine:
|
||||
Segments segments;
|
||||
Memory memory;
|
||||
FlowController flow_controller;
|
||||
IO io;
|
||||
IO<video> io;
|
||||
static constexpr auto model = InstructionSet::x86::Model::i8086;
|
||||
} context;
|
||||
|
||||
@ -1293,7 +1140,14 @@ using namespace PCCompatible;
|
||||
|
||||
// See header; constructs and returns an instance of the Amstrad CPC.
|
||||
Machine *Machine::PCCompatible(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher) {
|
||||
return new PCCompatible::ConcreteMachine(*target, rom_fetcher);
|
||||
using Target = Analyser::Static::PCCompatible::Target;
|
||||
const Target *const pc_target = dynamic_cast<const Target *>(target);
|
||||
|
||||
switch(pc_target->adaptor) {
|
||||
case VideoAdaptor::MDA: return new PCCompatible::ConcreteMachine<VideoAdaptor::MDA>(*pc_target, rom_fetcher);
|
||||
case VideoAdaptor::CGA: return new PCCompatible::ConcreteMachine<VideoAdaptor::CGA>(*pc_target, rom_fetcher);
|
||||
default: return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
Machine::~Machine() {}
|
||||
|
@ -9,6 +9,8 @@
|
||||
#ifndef PCCompatible_hpp
|
||||
#define PCCompatible_hpp
|
||||
|
||||
#include "../../Configurable/Configurable.hpp"
|
||||
#include "../../Configurable/StandardOptions.hpp"
|
||||
#include "../../Analyser/Static/StaticAnalyser.hpp"
|
||||
#include "../ROMMachine.hpp"
|
||||
|
||||
@ -26,6 +28,23 @@ class Machine {
|
||||
const Analyser::Static::Target *target,
|
||||
const ROMMachine::ROMFetcher &rom_fetcher
|
||||
);
|
||||
|
||||
/// Defines the runtime options [sometimes] available for a PC.
|
||||
class Options:
|
||||
public Reflection::StructImpl<Options>,
|
||||
public Configurable::DisplayOption<Options>
|
||||
{
|
||||
friend Configurable::DisplayOption<Options>;
|
||||
public:
|
||||
Options(Configurable::OptionsType) :
|
||||
Configurable::DisplayOption<Options>(Configurable::Display::RGB)
|
||||
{
|
||||
if(needs_declare()) {
|
||||
declare_display_option();
|
||||
limit_enum(&output, Configurable::Display::RGB, Configurable::Display::CompositeColour, -1);
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -222,6 +222,7 @@ std::map<std::string, std::unique_ptr<Reflection::Struct>> Machine::AllOptionsBy
|
||||
Emplace(MasterSystem, Sega::MasterSystem::Machine);
|
||||
Emplace(MSX, MSX::Machine);
|
||||
Emplace(Oric, Oric::Machine);
|
||||
Emplace(PCCompatible, PCCompatible::Machine);
|
||||
Emplace(Vic20, Commodore::Vic20::Machine);
|
||||
Emplace(ZX8081, Sinclair::ZX8081::Machine);
|
||||
Emplace(ZXSpectrum, Sinclair::ZXSpectrum::Machine);
|
||||
|
@ -573,6 +573,9 @@ Description::Description(Name name) {
|
||||
case Name::PCCompatiblePhoenix80286BIOS:
|
||||
*this = Description(name, "PCCompatible", "Phoenix 80286 BIOS 3.05", "Phoenix 80286 ROM BIOS Version 3.05.bin", 32 * 1024, 0x8d0d318au);
|
||||
break;
|
||||
case Name::PCCompatibleCGAFont:
|
||||
*this = Description(name, "PCCompatible", "IBM's CGA font", "CGA.F08", 8 * 256, 0xa362ffe6u);
|
||||
break;
|
||||
case Name::PCCompatibleMDAFont:
|
||||
*this = Description(name, "PCCompatible", "IBM's MDA font", "EUMDA9.F14", 14 * 256, 0x7754882au);
|
||||
break;
|
||||
|
@ -136,6 +136,7 @@ enum Name {
|
||||
PCCompatibleGLaBIOS,
|
||||
PCCompatiblePhoenix80286BIOS,
|
||||
PCCompatibleMDAFont,
|
||||
PCCompatibleCGAFont,
|
||||
|
||||
// Sinclair QL.
|
||||
SinclairQLJS,
|
||||
|
@ -1168,6 +1168,8 @@
|
||||
428168392A37AFB4008ECD27 /* DispatcherTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = DispatcherTests.mm; sourceTree = "<group>"; };
|
||||
429B135E2B1F7BDA006BB4CB /* StaticAnalyser.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = StaticAnalyser.hpp; sourceTree = "<group>"; };
|
||||
429B135F2B1F7BDA006BB4CB /* StaticAnalyser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StaticAnalyser.cpp; sourceTree = "<group>"; };
|
||||
429B13622B1FCA96006BB4CB /* MDA.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = MDA.hpp; sourceTree = "<group>"; };
|
||||
429B13632B20234B006BB4CB /* CGA.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = CGA.hpp; sourceTree = "<group>"; };
|
||||
42A5E80B2ABBE04600A0DD5D /* NeskellTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NeskellTests.swift; sourceTree = "<group>"; };
|
||||
42A5E8332ABBE16F00A0DD5D /* illegal_rmw_test.bin */ = {isa = PBXFileReference; lastKnownFileType = archive.macbinary; path = illegal_rmw_test.bin; sourceTree = "<group>"; };
|
||||
42A5E8342ABBE16F00A0DD5D /* arr_bcd_test.bin */ = {isa = PBXFileReference; lastKnownFileType = archive.macbinary; path = arr_bcd_test.bin; sourceTree = "<group>"; };
|
||||
@ -2394,8 +2396,10 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
425739372B051EA800B7D1E4 /* PCCompatible.cpp */,
|
||||
429B13632B20234B006BB4CB /* CGA.hpp */,
|
||||
4267A9C92B0D4F17008A59BB /* DMA.hpp */,
|
||||
4267A9CA2B111ED2008A59BB /* KeyboardMapper.hpp */,
|
||||
429B13622B1FCA96006BB4CB /* MDA.hpp */,
|
||||
423820132B1A235200964EFE /* Memory.hpp */,
|
||||
425739362B051EA800B7D1E4 /* PCCompatible.hpp */,
|
||||
4267A9C82B0D4EC2008A59BB /* PIC.hpp */,
|
||||
|
@ -127,6 +127,11 @@ typedef NS_ENUM(NSInteger, CSPCCompatibleModel) {
|
||||
CSPCCompatibleModelTurboXT,
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSInteger, CSPCCompatibleVideoAdaptor) {
|
||||
CSPCCompatibleVideoAdaptorMDA,
|
||||
CSPCCompatibleVideoAdaptorCGA,
|
||||
};
|
||||
|
||||
typedef int Kilobytes;
|
||||
|
||||
@interface CSStaticAnalyser : NSObject
|
||||
@ -147,7 +152,7 @@ typedef int Kilobytes;
|
||||
- (instancetype)initWithVic20Region:(CSMachineVic20Region)region memorySize:(Kilobytes)memorySize hasC1540:(BOOL)hasC1540;
|
||||
- (instancetype)initWithZX80MemorySize:(Kilobytes)memorySize useZX81ROM:(BOOL)useZX81ROM;
|
||||
- (instancetype)initWithZX81MemorySize:(Kilobytes)memorySize;
|
||||
- (instancetype)initWithPCCompatibleModel:(CSPCCompatibleModel)model;
|
||||
- (instancetype)initWithPCCompatibleModel:(CSPCCompatibleModel)model videoAdaptor:(CSPCCompatibleVideoAdaptor)adaptor;
|
||||
|
||||
@property(nonatomic, readonly, nullable) NSString *optionsNibName;
|
||||
@property(nonatomic, readonly) NSString *displayName;
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include "../../../../../Analyser/Static/Macintosh/Target.hpp"
|
||||
#include "../../../../../Analyser/Static/MSX/Target.hpp"
|
||||
#include "../../../../../Analyser/Static/Oric/Target.hpp"
|
||||
#include "../../../../../Analyser/Static/PCCompatible/Target.hpp"
|
||||
#include "../../../../../Analyser/Static/ZX8081/Target.hpp"
|
||||
#include "../../../../../Analyser/Static/ZXSpectrum/Target.hpp"
|
||||
|
||||
@ -272,11 +273,15 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithPCCompatibleModel:(CSPCCompatibleModel)model {
|
||||
- (instancetype)initWithPCCompatibleModel:(CSPCCompatibleModel)model videoAdaptor:(CSPCCompatibleVideoAdaptor)adaptor {
|
||||
self = [super init];
|
||||
if(self) {
|
||||
using Target = Analyser::Static::Target;
|
||||
auto target = std::make_unique<Target>(Analyser::Machine::PCCompatible);
|
||||
using Target = Analyser::Static::PCCompatible::Target;
|
||||
auto target = std::make_unique<Target>();
|
||||
switch(adaptor) {
|
||||
case CSPCCompatibleVideoAdaptorMDA: target->adaptor = Target::VideoAdaptor::MDA; break;
|
||||
case CSPCCompatibleVideoAdaptorCGA: target->adaptor = Target::VideoAdaptor::CGA; break;
|
||||
}
|
||||
_targets.push_back(std::move(target));
|
||||
}
|
||||
return self;
|
||||
@ -375,6 +380,7 @@ static Analyser::Static::ZX8081::Target::MemoryModel ZX8081MemoryModelFromSize(K
|
||||
case Analyser::Machine::MasterSystem: return @"CompositeOptions";
|
||||
case Analyser::Machine::MSX: return @"QuickLoadCompositeOptions";
|
||||
case Analyser::Machine::Oric: return @"OricOptions";
|
||||
case Analyser::Machine::PCCompatible: return @"CompositeOptions";
|
||||
case Analyser::Machine::Vic20: return @"QuickLoadCompositeOptions";
|
||||
case Analyser::Machine::ZX8081: return @"ZX8081Options";
|
||||
case Analyser::Machine::ZXSpectrum: return @"QuickLoadCompositeOptions"; // TODO: @"ZXSpectrumOptions";
|
||||
|
@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="22155" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="21701" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="22155"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="21701"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
@ -18,7 +18,7 @@
|
||||
<windowStyleMask key="styleMask" titled="YES" documentModal="YES"/>
|
||||
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
||||
<rect key="contentRect" x="196" y="240" width="590" height="367"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1440"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="1800" height="1131"/>
|
||||
<view key="contentView" wantsLayer="YES" id="EiT-Mj-1SZ">
|
||||
<rect key="frame" x="0.0" y="0.0" width="590" height="367"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
@ -49,7 +49,7 @@ Gw
|
||||
<action selector="cancelCreateMachine:" target="-2" id="lf8-PM-c0m"/>
|
||||
</connections>
|
||||
</button>
|
||||
<textField focusRingType="none" horizontalHuggingPriority="1" verticalHuggingPriority="1" horizontalCompressionResistancePriority="250" setsMaxLayoutWidthAtFirstLayout="YES" translatesAutoresizingMaskIntoConstraints="NO" id="9YM-5x-pc0">
|
||||
<textField horizontalHuggingPriority="1" verticalHuggingPriority="1" horizontalCompressionResistancePriority="250" setsMaxLayoutWidthAtFirstLayout="YES" translatesAutoresizingMaskIntoConstraints="NO" id="9YM-5x-pc0">
|
||||
<rect key="frame" x="18" y="14" width="405" height="32"/>
|
||||
<textFieldCell key="cell" allowsUndo="NO" sendsActionOnEndEditing="YES" id="xTm-Oy-oz5">
|
||||
<font key="font" metaFont="system"/>
|
||||
@ -81,7 +81,7 @@ Gw
|
||||
</menu>
|
||||
</popUpButtonCell>
|
||||
</popUpButton>
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="P6K-dt-stj">
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="P6K-dt-stj">
|
||||
<rect key="frame" x="18" y="198" width="89" height="16"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Chip Memory:" id="FIO-ZR-rsA">
|
||||
<font key="font" metaFont="system"/>
|
||||
@ -89,7 +89,7 @@ Gw
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="YD0-OJ-2bY">
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="YD0-OJ-2bY">
|
||||
<rect key="frame" x="18" y="168" width="87" height="16"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Fast Memory:" id="Rpz-39-jyt">
|
||||
<font key="font" metaFont="system"/>
|
||||
@ -148,7 +148,7 @@ Gw
|
||||
</menu>
|
||||
</popUpButtonCell>
|
||||
</popUpButton>
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="c3g-96-b3x">
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="c3g-96-b3x">
|
||||
<rect key="frame" x="18" y="184" width="46" height="16"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Model:" id="53v-92-jmf">
|
||||
<font key="font" metaFont="system"/>
|
||||
@ -172,7 +172,7 @@ Gw
|
||||
<rect key="frame" x="10" y="7" width="400" height="222"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="V5Z-dX-Ns4">
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="V5Z-dX-Ns4">
|
||||
<rect key="frame" x="18" y="184" width="46" height="16"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Model:" id="qV3-2P-3JW">
|
||||
<font key="font" metaFont="system"/>
|
||||
@ -180,9 +180,9 @@ Gw
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="WnO-ef-IC6">
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="WnO-ef-IC6">
|
||||
<rect key="frame" x="18" y="154" width="96" height="16"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Disk controller:" id="kbf-rc-Y4M">
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Disk Controller:" id="kbf-rc-Y4M">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
@ -238,7 +238,7 @@ Gw
|
||||
<rect key="frame" x="10" y="7" width="400" height="222"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="0d9-IG-gKU">
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="0d9-IG-gKU">
|
||||
<rect key="frame" x="18" y="184" width="46" height="16"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Model:" id="kiv-1P-FWc">
|
||||
<font key="font" metaFont="system"/>
|
||||
@ -246,9 +246,9 @@ Gw
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="LES-76-Ovz">
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="LES-76-Ovz">
|
||||
<rect key="frame" x="18" y="154" width="85" height="16"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Memory size:" id="OLJ-nC-yyj">
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Memory Size:" id="OLJ-nC-yyj">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
@ -303,7 +303,7 @@ Gw
|
||||
<rect key="frame" x="10" y="7" width="400" height="222"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="dKg-qC-BBF">
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="dKg-qC-BBF">
|
||||
<rect key="frame" x="18" y="184" width="58" height="16"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Memory:" id="ZBF-0r-RNK">
|
||||
<font key="font" metaFont="system"/>
|
||||
@ -461,7 +461,7 @@ Gw
|
||||
</menu>
|
||||
</popUpButtonCell>
|
||||
</popUpButton>
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ykc-W1-YaS">
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ykc-W1-YaS">
|
||||
<rect key="frame" x="18" y="124" width="43" height="16"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="EXOS:" id="gUC-PN-zVL">
|
||||
<font key="font" metaFont="system"/>
|
||||
@ -469,7 +469,7 @@ Gw
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="frx-nk-c3P">
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="frx-nk-c3P">
|
||||
<rect key="frame" x="18" y="184" width="59" height="16"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Machine:" id="uTv-hH-mIC">
|
||||
<font key="font" metaFont="system"/>
|
||||
@ -477,7 +477,7 @@ Gw
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="dzd-tH-BjX">
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="dzd-tH-BjX">
|
||||
<rect key="frame" x="18" y="94" width="46" height="16"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="BASIC:" id="ai1-oR-X6Y">
|
||||
<font key="font" metaFont="system"/>
|
||||
@ -485,7 +485,7 @@ Gw
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="pxr-Bq-yh0">
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="pxr-Bq-yh0">
|
||||
<rect key="frame" x="18" y="64" width="36" height="16"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="DOS:" id="NFk-cp-DfS">
|
||||
<font key="font" metaFont="system"/>
|
||||
@ -493,7 +493,7 @@ Gw
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="rHr-bh-QMV">
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="rHr-bh-QMV">
|
||||
<rect key="frame" x="17" y="154" width="47" height="16"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Speed:" id="sAw-C9-Sf7">
|
||||
<font key="font" metaFont="system"/>
|
||||
@ -539,7 +539,7 @@ Gw
|
||||
<rect key="frame" x="10" y="7" width="400" height="222"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ZOY-4E-Cfl">
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ZOY-4E-Cfl">
|
||||
<rect key="frame" x="18" y="184" width="46" height="16"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Model:" id="h9r-i6-66j">
|
||||
<font key="font" metaFont="system"/>
|
||||
@ -599,7 +599,7 @@ Gw
|
||||
</menu>
|
||||
</popUpButtonCell>
|
||||
</popUpButton>
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ZaD-7v-rMS">
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ZaD-7v-rMS">
|
||||
<rect key="frame" x="18" y="154" width="50" height="16"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Region:" id="x4m-eh-Nif">
|
||||
<font key="font" metaFont="system"/>
|
||||
@ -607,7 +607,7 @@ Gw
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="gFV-RB-7dB">
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="gFV-RB-7dB">
|
||||
<rect key="frame" x="18" y="184" width="46" height="16"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Model:" id="r7C-zE-gyj">
|
||||
<font key="font" metaFont="system"/>
|
||||
@ -662,7 +662,7 @@ Gw
|
||||
<rect key="frame" x="10" y="7" width="400" height="222"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="0ct-tf-uRH">
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="0ct-tf-uRH">
|
||||
<rect key="frame" x="18" y="184" width="46" height="16"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Model:" id="Xm1-7x-YVl">
|
||||
<font key="font" metaFont="system"/>
|
||||
@ -700,9 +700,9 @@ Gw
|
||||
</menu>
|
||||
</popUpButtonCell>
|
||||
</popUpButton>
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="okM-ZI-NbF">
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="okM-ZI-NbF">
|
||||
<rect key="frame" x="18" y="154" width="92" height="16"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Disk interface:" id="SFK-hS-tFC">
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Disk Interface:" id="SFK-hS-tFC">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
@ -730,22 +730,21 @@ Gw
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="stw-i3-ikG">
|
||||
<rect key="frame" x="108" y="192" width="116" height="25"/>
|
||||
<popUpButtonCell key="cell" type="push" title="Unexpanded" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="axesIndependently" inset="2" selectedItem="oIy-If-5bQ" id="xz8-mu-ynU">
|
||||
<rect key="frame" x="116" y="192" width="68" height="25"/>
|
||||
<popUpButtonCell key="cell" type="push" title="MDA" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="axesIndependently" inset="2" selectedItem="oIy-If-5bQ" id="xz8-mu-ynU">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
<menu key="menu" id="dju-ZY-2DH">
|
||||
<items>
|
||||
<menuItem title="Unexpanded" state="on" id="oIy-If-5bQ"/>
|
||||
<menuItem title="16 kb" tag="16" id="igW-oa-8YI"/>
|
||||
<menuItem title="64 kb" tag="64" hidden="YES" enabled="NO" id="Tcz-UB-hbn"/>
|
||||
<menuItem title="MDA" state="on" id="oIy-If-5bQ"/>
|
||||
<menuItem title="CGA" tag="1" id="igW-oa-8YI"/>
|
||||
</items>
|
||||
</menu>
|
||||
</popUpButtonCell>
|
||||
</popUpButton>
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="uhf-1k-ibT">
|
||||
<rect key="frame" x="18" y="198" width="87" height="16"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Memory Size:" id="ROV-EU-T3W">
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="uhf-1k-ibT">
|
||||
<rect key="frame" x="18" y="198" width="95" height="16"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Video Adaptor:" id="ROV-EU-T3W">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
@ -797,7 +796,7 @@ Gw
|
||||
</menu>
|
||||
</popUpButtonCell>
|
||||
</popUpButton>
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="MTh-9p-FqC">
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="MTh-9p-FqC">
|
||||
<rect key="frame" x="18" y="184" width="50" height="16"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Region:" id="F3g-Ya-ypU">
|
||||
<font key="font" metaFont="system"/>
|
||||
@ -805,7 +804,7 @@ Gw
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="gRS-DK-rIy">
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="gRS-DK-rIy">
|
||||
<rect key="frame" x="18" y="154" width="87" height="16"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Memory Size:" id="a4I-vG-yCp">
|
||||
<font key="font" metaFont="system"/>
|
||||
@ -858,7 +857,7 @@ Gw
|
||||
</menu>
|
||||
</popUpButtonCell>
|
||||
</popUpButton>
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="NCX-4e-lSu">
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="NCX-4e-lSu">
|
||||
<rect key="frame" x="18" y="184" width="87" height="16"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Memory Size:" id="e6x-TE-OC5">
|
||||
<font key="font" metaFont="system"/>
|
||||
@ -906,7 +905,7 @@ Gw
|
||||
</menu>
|
||||
</popUpButtonCell>
|
||||
</popUpButton>
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="8tU-73-XEE">
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="8tU-73-XEE">
|
||||
<rect key="frame" x="18" y="184" width="87" height="16"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Memory Size:" id="z4b-oR-Yl2">
|
||||
<font key="font" metaFont="system"/>
|
||||
@ -947,7 +946,7 @@ Gw
|
||||
</menu>
|
||||
</popUpButtonCell>
|
||||
</popUpButton>
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="fJ3-ma-Byy">
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="fJ3-ma-Byy">
|
||||
<rect key="frame" x="18" y="184" width="46" height="16"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Model:" id="JId-Tp-LrE">
|
||||
<font key="font" metaFont="system"/>
|
||||
@ -1012,7 +1011,7 @@ Gw
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</scroller>
|
||||
</scrollView>
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="VAc-6N-O7q">
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="VAc-6N-O7q">
|
||||
<rect key="frame" x="18" y="320" width="554" height="27"/>
|
||||
<textFieldCell key="cell" lineBreakMode="clipping" title="Choose a machine" id="32m-Vs-dPO">
|
||||
<font key="font" textStyle="title2" name=".SFNS-Regular"/>
|
||||
@ -1070,6 +1069,7 @@ Gw
|
||||
<outlet property="msxRegionButton" destination="LG6-mP-SeG" id="3a9-VG-6Wf"/>
|
||||
<outlet property="oricDiskInterfaceButton" destination="fYL-p6-wyn" id="aAt-wM-hRZ"/>
|
||||
<outlet property="oricModelTypeButton" destination="ENP-hI-BVZ" id="n9i-Ym-miE"/>
|
||||
<outlet property="pcVideoAdaptorButton" destination="stw-i3-ikG" id="6VO-Q9-4kV"/>
|
||||
<outlet property="spectrumModelTypeButton" destination="gFZ-d4-WFv" id="tdX-Cv-Swe"/>
|
||||
<outlet property="vic20HasC1540Button" destination="Lrf-gL-6EI" id="21g-dJ-mOo"/>
|
||||
<outlet property="vic20MemorySizeButton" destination="2eV-Us-eEv" id="5j4-jw-89d"/>
|
||||
|
@ -63,6 +63,9 @@ class MachinePicker: NSObject, NSTableViewDataSource, NSTableViewDelegate {
|
||||
@IBOutlet var oricModelTypeButton: NSPopUpButton!
|
||||
@IBOutlet var oricDiskInterfaceButton: NSPopUpButton!
|
||||
|
||||
// MARK: - PC compatible properties
|
||||
@IBOutlet var pcVideoAdaptorButton: NSPopUpButton!
|
||||
|
||||
// MARK: - Spectrum properties
|
||||
@IBOutlet var spectrumModelTypeButton: NSPopUpButton!
|
||||
|
||||
@ -150,6 +153,9 @@ class MachinePicker: NSObject, NSTableViewDataSource, NSTableViewDelegate {
|
||||
oricDiskInterfaceButton.selectItem(withTag: standardUserDefaults.integer(forKey: "new.oricDiskInterface"))
|
||||
oricModelTypeButton.selectItem(withTag: standardUserDefaults.integer(forKey: "new.oricModel"))
|
||||
|
||||
// PC settings
|
||||
pcVideoAdaptorButton.selectItem(withTag: standardUserDefaults.integer(forKey: "new.pcVideoAdaptor"))
|
||||
|
||||
// Spectrum settings
|
||||
spectrumModelTypeButton.selectItem(withTag: standardUserDefaults.integer(forKey: "new.spectrumModel"))
|
||||
|
||||
@ -216,6 +222,9 @@ class MachinePicker: NSObject, NSTableViewDataSource, NSTableViewDelegate {
|
||||
standardUserDefaults.set(oricDiskInterfaceButton.selectedTag(), forKey: "new.oricDiskInterface")
|
||||
standardUserDefaults.set(oricModelTypeButton.selectedTag(), forKey: "new.oricModel")
|
||||
|
||||
// PC settings
|
||||
standardUserDefaults.set(pcVideoAdaptorButton.selectedTag(), forKey: "new.pcVideoAdaptor")
|
||||
|
||||
// Spectrum settings
|
||||
standardUserDefaults.set(spectrumModelTypeButton.selectedTag(), forKey: "new.spectrumModel")
|
||||
|
||||
@ -402,7 +411,12 @@ class MachinePicker: NSObject, NSTableViewDataSource, NSTableViewDelegate {
|
||||
return CSStaticAnalyser(oricModel: model, diskInterface: diskInterface)
|
||||
|
||||
case "pc":
|
||||
return CSStaticAnalyser(pcCompatibleModel: .turboXT)
|
||||
var videoAdaptor: CSPCCompatibleVideoAdaptor = .MDA
|
||||
switch pcVideoAdaptorButton.selectedTag() {
|
||||
case 1: videoAdaptor = .CGA
|
||||
default: break
|
||||
}
|
||||
return CSStaticAnalyser(pcCompatibleModel: .turboXT, videoAdaptor: videoAdaptor)
|
||||
|
||||
case "spectrum":
|
||||
var model: CSMachineSpectrumModel = .plus2a
|
||||
|
BIN
ROMImages/PCCompatible/CGA.F08
Normal file
BIN
ROMImages/PCCompatible/CGA.F08
Normal file
Binary file not shown.
@ -3,8 +3,9 @@ Expected files:
|
||||
GLABIOS_0.2.5_8T.ROM — the 8088 GlaBIOS ROM.
|
||||
Phoenix 80286 ROM BIOS Version 3.05.bin — Phoenix's 80286 AT-clone BIOS.
|
||||
EUMDA9.F14 — a dump of the MDA font.
|
||||
CGA.F08 — a dump of the CGA font.
|
||||
|
||||
|
||||
GlaBIOS is an open-source GPLv3 alternative BIOS for XT clones, available from https://glabios.org/
|
||||
|
||||
The MDA font is in the form offered at https://github.com/viler-int10h/vga-text-mode-fonts i.e. it's 256 lots of 14 bytes, the first 14 being the content of character 0, the next 14 being the content of character 1, etc.
|
||||
The MDA and CGA fonts are in the form offered at https://github.com/viler-int10h/vga-text-mode-fonts i.e. it's 256 lots of 14 bytes, the first 14 being the content of character 0, the next 14 being the content of character 1, etc.
|
Loading…
x
Reference in New Issue
Block a user