1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-08-16 00:29:01 +00:00

Merge pull request #520 from TomHarte/EnhancedIIe

Adds Enhanced IIe emulation.
This commit is contained in:
Thomas Harte 2018-08-11 19:42:47 -04:00 committed by GitHub
commit 10c930a59d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 72 additions and 60 deletions

View File

@ -19,7 +19,8 @@ struct Target: public ::Analyser::Static::Target {
enum class Model { enum class Model {
II, II,
IIplus, IIplus,
IIe IIe,
EnhancedIIe
}; };
enum class DiskController { enum class DiskController {
None, None,

View File

@ -34,7 +34,9 @@
namespace { namespace {
template <bool is_iie> class ConcreteMachine: #define is_iie() ((model == Analyser::Static::AppleII::Target::Model::IIe) || (model == Analyser::Static::AppleII::Target::Model::EnhancedIIe))
template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
public CRTMachine::Machine, public CRTMachine::Machine,
public MediaTarget::Machine, public MediaTarget::Machine,
public KeyboardMachine::Machine, public KeyboardMachine::Machine,
@ -62,7 +64,7 @@ template <bool is_iie> class ConcreteMachine:
CPU::MOS6502::Processor<ConcreteMachine, false> m6502_; CPU::MOS6502::Processor<ConcreteMachine, false> m6502_;
VideoBusHandler video_bus_handler_; VideoBusHandler video_bus_handler_;
std::unique_ptr<AppleII::Video::Video<VideoBusHandler, is_iie>> video_; std::unique_ptr<AppleII::Video::Video<VideoBusHandler, is_iie()>> video_;
int cycles_into_current_line_ = 0; int cycles_into_current_line_ = 0;
Cycles cycles_since_video_update_; Cycles cycles_since_video_update_;
@ -179,7 +181,7 @@ template <bool is_iie> class ConcreteMachine:
bool has_language_card_ = true; bool has_language_card_ = true;
void set_language_card_paging() { void set_language_card_paging() {
uint8_t *const ram = alternative_zero_page_ ? aux_ram_ : ram_; uint8_t *const ram = alternative_zero_page_ ? aux_ram_ : ram_;
uint8_t *const rom = is_iie ? &rom_[3840] : rom_.data(); uint8_t *const rom = is_iie() ? &rom_[3840] : rom_.data();
page(0xd0, 0xe0, page(0xd0, 0xe0,
language_card_.read ? &ram[language_card_.bank1 ? 0xd000 : 0xc000] : rom, language_card_.read ? &ram[language_card_.bank1 ? 0xd000 : 0xc000] : rom,
@ -298,7 +300,7 @@ template <bool is_iie> class ConcreteMachine:
public: public:
ConcreteMachine(const Analyser::Static::AppleII::Target &target, const ROMMachine::ROMFetcher &rom_fetcher): ConcreteMachine(const Analyser::Static::AppleII::Target &target, const ROMMachine::ROMFetcher &rom_fetcher):
m6502_(CPU::MOS6502::Personality::P6502, *this), m6502_((model == Analyser::Static::AppleII::Target::Model::EnhancedIIe) ? CPU::MOS6502::Personality::P65C02 : CPU::MOS6502::Personality::P6502, *this),
video_bus_handler_(ram_, aux_ram_), video_bus_handler_(ram_, aux_ram_),
audio_toggle_(audio_queue_), audio_toggle_(audio_queue_),
speaker_(audio_toggle_) { speaker_(audio_toggle_) {
@ -345,6 +347,11 @@ template <bool is_iie> class ConcreteMachine:
rom_names.push_back("apple2eu-character.rom"); rom_names.push_back("apple2eu-character.rom");
rom_names.push_back("apple2eu.rom"); rom_names.push_back("apple2eu.rom");
break; break;
case Target::Model::EnhancedIIe:
rom_size += 3840;
rom_names.push_back("apple2e-character.rom");
rom_names.push_back("apple2e.rom");
break;
} }
const auto roms = rom_fetcher("AppleII", rom_names); const auto roms = rom_fetcher("AppleII", rom_names);
@ -383,7 +390,7 @@ template <bool is_iie> class ConcreteMachine:
} }
void setup_output(float aspect_ratio) override { void setup_output(float aspect_ratio) override {
video_.reset(new AppleII::Video::Video<VideoBusHandler, is_iie>(video_bus_handler_)); video_.reset(new AppleII::Video::Video<VideoBusHandler, is_iie()>(video_bus_handler_));
video_->set_character_rom(character_rom_); video_->set_character_rom(character_rom_);
} }
@ -422,7 +429,7 @@ template <bool is_iie> class ConcreteMachine:
if(isReadOperation(operation)) *value = read_pages_[address >> 8][address & 0xff]; if(isReadOperation(operation)) *value = read_pages_[address >> 8][address & 0xff];
else if(write_pages_[address >> 8]) write_pages_[address >> 8][address & 0xff] = *value; else if(write_pages_[address >> 8]) write_pages_[address >> 8][address & 0xff] = *value;
if(is_iie && address >= 0xc300 && address < 0xd000) { if(is_iie() && address >= 0xc300 && address < 0xd000) {
bool internal_c8_rom = internal_c8_rom_; bool internal_c8_rom = internal_c8_rom_;
internal_c8_rom |= ((address >> 8) == 0xc3) && !slot_C3_rom_; internal_c8_rom |= ((address >> 8) == 0xc3) && !slot_C3_rom_;
internal_c8_rom &= (address != 0xcfff); internal_c8_rom &= (address != 0xcfff);
@ -468,7 +475,7 @@ template <bool is_iie> class ConcreteMachine:
*value &= 0x7f; *value &= 0x7f;
if( if(
static_cast<Joystick *>(joysticks_[0].get())->buttons[0] || static_cast<Joystick *>(joysticks_[1].get())->buttons[2] || static_cast<Joystick *>(joysticks_[0].get())->buttons[0] || static_cast<Joystick *>(joysticks_[1].get())->buttons[2] ||
(is_iie && open_apple_is_pressed_) (is_iie() && open_apple_is_pressed_)
) )
*value |= 0x80; *value |= 0x80;
break; break;
@ -476,7 +483,7 @@ template <bool is_iie> class ConcreteMachine:
*value &= 0x7f; *value &= 0x7f;
if( if(
static_cast<Joystick *>(joysticks_[0].get())->buttons[1] || static_cast<Joystick *>(joysticks_[1].get())->buttons[1] || static_cast<Joystick *>(joysticks_[0].get())->buttons[1] || static_cast<Joystick *>(joysticks_[1].get())->buttons[1] ||
(is_iie && closed_apple_is_pressed_) (is_iie() && closed_apple_is_pressed_)
) )
*value |= 0x80; *value |= 0x80;
break; break;
@ -498,26 +505,26 @@ template <bool is_iie> class ConcreteMachine:
} break; } break;
// The IIe-only state reads follow... // The IIe-only state reads follow...
case 0xc011: if(is_iie) *value = (*value & 0x7f) | (language_card_.bank1 ? 0x80 : 0x00); break; case 0xc011: if(is_iie()) *value = (*value & 0x7f) | (language_card_.bank1 ? 0x80 : 0x00); break;
case 0xc012: if(is_iie) *value = (*value & 0x7f) | (language_card_.read ? 0x80 : 0x00); break; case 0xc012: if(is_iie()) *value = (*value & 0x7f) | (language_card_.read ? 0x80 : 0x00); break;
case 0xc013: if(is_iie) *value = (*value & 0x7f) | (read_auxiliary_memory_ ? 0x80 : 0x00); break; case 0xc013: if(is_iie()) *value = (*value & 0x7f) | (read_auxiliary_memory_ ? 0x80 : 0x00); break;
case 0xc014: if(is_iie) *value = (*value & 0x7f) | (write_auxiliary_memory_ ? 0x80 : 0x00); break; case 0xc014: if(is_iie()) *value = (*value & 0x7f) | (write_auxiliary_memory_ ? 0x80 : 0x00); break;
case 0xc015: if(is_iie) *value = (*value & 0x7f) | (internal_CX_rom_ ? 0x80 : 0x00); break; case 0xc015: if(is_iie()) *value = (*value & 0x7f) | (internal_CX_rom_ ? 0x80 : 0x00); break;
case 0xc016: if(is_iie) *value = (*value & 0x7f) | (alternative_zero_page_ ? 0x80 : 0x00); break; case 0xc016: if(is_iie()) *value = (*value & 0x7f) | (alternative_zero_page_ ? 0x80 : 0x00); break;
case 0xc017: if(is_iie) *value = (*value & 0x7f) | (slot_C3_rom_ ? 0x80 : 0x00); break; case 0xc017: if(is_iie()) *value = (*value & 0x7f) | (slot_C3_rom_ ? 0x80 : 0x00); break;
case 0xc018: if(is_iie) *value = (*value & 0x7f) | (video_->get_80_store() ? 0x80 : 0x00); break; case 0xc018: if(is_iie()) *value = (*value & 0x7f) | (video_->get_80_store() ? 0x80 : 0x00); break;
case 0xc019: if(is_iie) *value = (*value & 0x7f) | (video_->get_is_vertical_blank(cycles_since_video_update_) ? 0x00 : 0x80); break; case 0xc019: if(is_iie()) *value = (*value & 0x7f) | (video_->get_is_vertical_blank(cycles_since_video_update_) ? 0x00 : 0x80); break;
case 0xc01a: if(is_iie) *value = (*value & 0x7f) | (video_->get_text() ? 0x80 : 0x00); break; case 0xc01a: if(is_iie()) *value = (*value & 0x7f) | (video_->get_text() ? 0x80 : 0x00); break;
case 0xc01b: if(is_iie) *value = (*value & 0x7f) | (video_->get_mixed() ? 0x80 : 0x00); break; case 0xc01b: if(is_iie()) *value = (*value & 0x7f) | (video_->get_mixed() ? 0x80 : 0x00); break;
case 0xc01c: if(is_iie) *value = (*value & 0x7f) | (video_->get_page2() ? 0x80 : 0x00); break; case 0xc01c: if(is_iie()) *value = (*value & 0x7f) | (video_->get_page2() ? 0x80 : 0x00); break;
case 0xc01d: if(is_iie) *value = (*value & 0x7f) | (video_->get_high_resolution() ? 0x80 : 0x00); break; case 0xc01d: if(is_iie()) *value = (*value & 0x7f) | (video_->get_high_resolution() ? 0x80 : 0x00); break;
case 0xc01e: if(is_iie) *value = (*value & 0x7f) | (video_->get_alternative_character_set() ? 0x80 : 0x00); break; case 0xc01e: if(is_iie()) *value = (*value & 0x7f) | (video_->get_alternative_character_set() ? 0x80 : 0x00); break;
case 0xc01f: if(is_iie) *value = (*value & 0x7f) | (video_->get_80_columns() ? 0x80 : 0x00); break; case 0xc01f: if(is_iie()) *value = (*value & 0x7f) | (video_->get_80_columns() ? 0x80 : 0x00); break;
case 0xc07f: if(is_iie) *value = (*value & 0x7f) | (video_->get_double_high_resolution() ? 0x80 : 0x00); break; case 0xc07f: if(is_iie()) *value = (*value & 0x7f) | (video_->get_double_high_resolution() ? 0x80 : 0x00); break;
} }
} else { } else {
// Write-only switches. All IIe as currently implemented. // Write-only switches. All IIe as currently implemented.
if(is_iie) { if(is_iie()) {
switch(address) { switch(address) {
default: printf("Write %04x?\n", address); break; default: printf("Write %04x?\n", address); break;
@ -612,7 +619,7 @@ template <bool is_iie> class ConcreteMachine:
case 0xc05e: case 0xc05e:
case 0xc05f: case 0xc05f:
if(is_iie) { if(is_iie()) {
update_video(); update_video();
video_->set_double_high_resolution(!(address&1)); video_->set_double_high_resolution(!(address&1));
} }
@ -626,7 +633,7 @@ template <bool is_iie> class ConcreteMachine:
} }
// On the IIe, reading C010 returns additional key info. // On the IIe, reading C010 returns additional key info.
if(is_iie && isReadOperation(operation)) { if(is_iie() && isReadOperation(operation)) {
*value = (key_is_down_ ? 0x80 : 0x00) | (keyboard_input_ & 0x7f); *value = (key_is_down_ ? 0x80 : 0x00) | (keyboard_input_ & 0x7f);
} }
break; break;
@ -769,7 +776,7 @@ template <bool is_iie> class ConcreteMachine:
} }
// Prior to the IIe, the keyboard could produce uppercase only. // Prior to the IIe, the keyboard could produce uppercase only.
if(!is_iie) value = static_cast<char>(toupper(value)); if(!is_iie()) value = static_cast<char>(toupper(value));
if(is_pressed) { if(is_pressed) {
keyboard_input_ = static_cast<uint8_t>(value | 0x80); keyboard_input_ = static_cast<uint8_t>(value | 0x80);
@ -818,10 +825,12 @@ using namespace AppleII;
Machine *Machine::AppleII(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher) { Machine *Machine::AppleII(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher) {
using Target = Analyser::Static::AppleII::Target; using Target = Analyser::Static::AppleII::Target;
const Target *const appleii_target = dynamic_cast<const Target *>(target); const Target *const appleii_target = dynamic_cast<const Target *>(target);
if(appleii_target->model == Target::Model::IIe) { switch(appleii_target->model) {
return new ConcreteMachine<true>(*appleii_target, rom_fetcher); default: return nullptr;
} else { case Target::Model::II: return new ConcreteMachine<Target::Model::II>(*appleii_target, rom_fetcher);
return new ConcreteMachine<false>(*appleii_target, rom_fetcher); case Target::Model::IIplus: return new ConcreteMachine<Target::Model::IIplus>(*appleii_target, rom_fetcher);
case Target::Model::IIe: return new ConcreteMachine<Target::Model::IIe>(*appleii_target, rom_fetcher);
case Target::Model::EnhancedIIe: return new ConcreteMachine<Target::Model::EnhancedIIe>(*appleii_target, rom_fetcher);
} }
} }

View File

@ -262,18 +262,20 @@ template <class BusHandler, bool is_iie> class Video: public VideoBase {
case GraphicsMode::Text: { case GraphicsMode::Text: {
const uint8_t inverses[] = { const uint8_t inverses[] = {
0xff, 0xff,
alternative_character_set_ ? static_cast<uint8_t>(0xff) : static_cast<uint8_t>((flash_ / flash_length) * 0xff), static_cast<uint8_t>((flash_ / flash_length) * 0xff),
0x00, 0x00,
0x00 0x00
}; };
const uint8_t masks[] = {
alternative_character_set_ ? static_cast<uint8_t>(0x7f) : static_cast<uint8_t>(0x3f),
is_iie ? 0x7f : 0x3f,
};
for(int c = column_; c < pixel_end; ++c) { for(int c = column_; c < pixel_end; ++c) {
const uint8_t character = bus_handler_.perform_read(static_cast<uint16_t>(text_address + c)); int character = bus_handler_.perform_read(static_cast<uint16_t>(text_address + c));
const uint8_t xor_mask = inverses[character >> 6]; if(is_iie) {
const std::size_t character_address = static_cast<std::size_t>(((character & masks[character >> 7]) << 3) + pixel_row); character |= alternative_character_set_ ? 0x100 : 0;
} else {
character &= 0x3f;
}
const uint8_t xor_mask = is_iie ? 0xff : inverses[character >> 6];
const std::size_t character_address = static_cast<std::size_t>((character << 3) + pixel_row);
const uint8_t character_pattern = character_rom_[character_address] ^ xor_mask; const uint8_t character_pattern = character_rom_[character_address] ^ xor_mask;
// The character ROM is output MSB to LSB rather than LSB to MSB. // The character ROM is output MSB to LSB rather than LSB to MSB.
@ -290,26 +292,21 @@ template <class BusHandler, bool is_iie> class Video: public VideoBase {
} break; } break;
case GraphicsMode::DoubleText: { case GraphicsMode::DoubleText: {
const uint8_t inverses[] = {
0xff,
alternative_character_set_ ? static_cast<uint8_t>(0xff) : static_cast<uint8_t>((flash_ / flash_length) * 0xff),
0x00,
0x00
};
const uint8_t masks[] = {
alternative_character_set_ ? static_cast<uint8_t>(0x7f) : static_cast<uint8_t>(0x3f),
is_iie ? 0x7f : 0x3f,
};
for(int c = column_; c < pixel_end; ++c) { for(int c = column_; c < pixel_end; ++c) {
const uint16_t characters = bus_handler_.perform_aux_read(static_cast<uint16_t>(text_address + c)); const uint16_t characters = bus_handler_.perform_aux_read(static_cast<uint16_t>(text_address + c));
const std::size_t character_addresses[2] = { const std::size_t character_addresses[2] = {
static_cast<std::size_t>((((characters >> 8) & masks[characters >> 15]) << 3) + pixel_row), static_cast<std::size_t>(
static_cast<std::size_t>(((characters & masks[(characters >> 7)&1]) << 3) + pixel_row), (((characters >> 8)) << 3) + pixel_row
),
static_cast<std::size_t>(
(characters << 3) + pixel_row
),
}; };
const size_t pattern_offset = alternative_character_set_ ? (256*8) : 0;
const uint8_t character_patterns[2] = { const uint8_t character_patterns[2] = {
static_cast<uint8_t>(character_rom_[character_addresses[0]] ^ inverses[(characters >> 14) & 3]), character_rom_[character_addresses[0] + pattern_offset],
static_cast<uint8_t>(character_rom_[character_addresses[1]] ^ inverses[(characters >> 6) & 3]), character_rom_[character_addresses[1] + pattern_offset],
}; };
// The character ROM is output MSB to LSB rather than LSB to MSB. // The character ROM is output MSB to LSB rather than LSB to MSB.

View File

@ -13,7 +13,8 @@
typedef NS_ENUM(NSInteger, CSMachineAppleIIModel) { typedef NS_ENUM(NSInteger, CSMachineAppleIIModel) {
CSMachineAppleIIModelAppleII, CSMachineAppleIIModelAppleII,
CSMachineAppleIIModelAppleIIPlus, CSMachineAppleIIModelAppleIIPlus,
CSMachineAppleIIModelAppleIIe CSMachineAppleIIModelAppleIIe,
CSMachineAppleIIModelAppleEnhancedIIe
}; };
typedef NS_ENUM(NSInteger, CSMachineAppleIIDiskController) { typedef NS_ENUM(NSInteger, CSMachineAppleIIDiskController) {

View File

@ -172,6 +172,7 @@ static Analyser::Static::ZX8081::Target::MemoryModel ZX8081MemoryModelFromSize(K
default: target->model = Target::Model::II; break; default: target->model = Target::Model::II; break;
case CSMachineAppleIIModelAppleIIPlus: target->model = Target::Model::IIplus; break; case CSMachineAppleIIModelAppleIIPlus: target->model = Target::Model::IIplus; break;
case CSMachineAppleIIModelAppleIIe: target->model = Target::Model::IIe; break; case CSMachineAppleIIModelAppleIIe: target->model = Target::Model::IIe; break;
case CSMachineAppleIIModelAppleEnhancedIIe: target->model = Target::Model::EnhancedIIe; break;
} }
switch(diskController) { switch(diskController) {
default: default:

View File

@ -84,7 +84,7 @@ Gw
</textFieldCell> </textFieldCell>
</textField> </textField>
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="jli-ac-Sij"> <popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="jli-ac-Sij">
<rect key="frame" x="65" y="67" width="91" height="26"/> <rect key="frame" x="65" y="67" width="115" height="26"/>
<popUpButtonCell key="cell" type="push" title="Apple II" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="VBQ-JG-AeM" id="U6V-us-O2F"> <popUpButtonCell key="cell" type="push" title="Apple II" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="VBQ-JG-AeM" id="U6V-us-O2F">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/> <behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="menu"/> <font key="font" metaFont="menu"/>
@ -93,6 +93,7 @@ Gw
<menuItem title="Apple II" state="on" id="VBQ-JG-AeM"/> <menuItem title="Apple II" state="on" id="VBQ-JG-AeM"/>
<menuItem title="Apple II+" tag="1" id="Yme-Wn-Obh"/> <menuItem title="Apple II+" tag="1" id="Yme-Wn-Obh"/>
<menuItem title="Apple IIe" tag="2" id="AMt-WU-a0H"/> <menuItem title="Apple IIe" tag="2" id="AMt-WU-a0H"/>
<menuItem title="Enhanced IIe" tag="3" id="kUz-FG-lqW"/>
</items> </items>
</menu> </menu>
</popUpButtonCell> </popUpButtonCell>

View File

@ -131,6 +131,7 @@ class MachinePicker: NSObject {
switch appleIIModelButton!.selectedTag() { switch appleIIModelButton!.selectedTag() {
case 1: model = .appleIIPlus case 1: model = .appleIIPlus
case 2: model = .appleIIe case 2: model = .appleIIe
case 3: model = .appleEnhancedIIe
case 0: fallthrough case 0: fallthrough
default: model = .appleII default: model = .appleII
} }

View File

@ -9,5 +9,6 @@ apple2eu.rom — as per apple2e.rom, but for the Unenhanced Apple II.
apple2-character.rom — a 2kb image of the Apple IIe's character ROM. apple2-character.rom — a 2kb image of the Apple IIe's character ROM.
apple2eu-character.rom — a 4kb image of the Unenhanced IIe's character ROM. apple2eu-character.rom — a 4kb image of the Unenhanced IIe's character ROM.
apple2e-character.rom — a 4kb image of the Enhanced IIe's character ROM.
Apologies for the wackiness around "at least xkb big", it's to allow for use of files such as those on ftp.apple.asimov.net, which tend to be a bunch of other things, then the system ROM. Apologies for the wackiness around "at least xkb big", it's to allow for use of files such as those on ftp.apple.asimov.net, which tend to be a bunch of other things, then the system ROM.