From 999a0c22d4f7b246c173295ec54e4c4e7a94e19d Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 7 Jan 2018 16:35:57 -0500 Subject: [PATCH 1/7] Adds superficial support for MSX .DSK. In the sense that the file format itself is properly parsed, but the MSX doesn't actually yet have disk hardware. --- .../Clock Signal.xcodeproj/project.pbxproj | 22 ++++--- ROMImages/MSX/readme.txt | 1 + StaticAnalyser/MSX/StaticAnalyser.cpp | 3 + StaticAnalyser/StaticAnalyser.cpp | 2 + Storage/Disk/DiskImage/Formats/MSXDSK.cpp | 59 +++++++++++++++++++ Storage/Disk/DiskImage/Formats/MSXDSK.hpp | 42 +++++++++++++ Storage/Disk/DiskImage/Formats/OricMFMDSK.hpp | 4 +- 7 files changed, 124 insertions(+), 9 deletions(-) create mode 100644 Storage/Disk/DiskImage/Formats/MSXDSK.cpp create mode 100644 Storage/Disk/DiskImage/Formats/MSXDSK.hpp diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index b5f02182b..014c2eb39 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -591,6 +591,8 @@ 4BEA525E1DF33323007E74F2 /* Tape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEA525D1DF33323007E74F2 /* Tape.cpp */; }; 4BEA52631DF339D7007E74F2 /* SoundGenerator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEA52611DF339D7007E74F2 /* SoundGenerator.cpp */; }; 4BEA52661DF3472B007E74F2 /* TIASound.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEA52641DF3472B007E74F2 /* TIASound.cpp */; }; + 4BEBFB4D2002C4BF000708CC /* MSXDSK.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEBFB4B2002C4BF000708CC /* MSXDSK.cpp */; }; + 4BEBFB4E2002C4BF000708CC /* MSXDSK.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEBFB4B2002C4BF000708CC /* MSXDSK.cpp */; }; 4BEE0A6F1D72496600532C7B /* Cartridge.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEE0A6A1D72496600532C7B /* Cartridge.cpp */; }; 4BEE0A701D72496600532C7B /* PRG.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEE0A6D1D72496600532C7B /* PRG.cpp */; }; 4BEF6AAA1D35CE9E00E73575 /* DigitalPhaseLockedLoopBridge.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BEF6AA91D35CE9E00E73575 /* DigitalPhaseLockedLoopBridge.mm */; }; @@ -1308,6 +1310,8 @@ 4BEAC08C1E7E0DF800EE56B2 /* Unpaged.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Unpaged.hpp; sourceTree = ""; }; 4BEAC08D1E7E0E1A00EE56B2 /* Bus.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Bus.hpp; sourceTree = ""; }; 4BEAC08E1E7E110500EE56B2 /* Pitfall2.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Pitfall2.hpp; sourceTree = ""; }; + 4BEBFB4B2002C4BF000708CC /* MSXDSK.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = MSXDSK.cpp; sourceTree = ""; }; + 4BEBFB4C2002C4BF000708CC /* MSXDSK.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = MSXDSK.hpp; sourceTree = ""; }; 4BEE0A6A1D72496600532C7B /* Cartridge.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Cartridge.cpp; sourceTree = ""; }; 4BEE0A6B1D72496600532C7B /* Cartridge.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Cartridge.hpp; sourceTree = ""; }; 4BEE0A6D1D72496600532C7B /* PRG.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PRG.cpp; sourceTree = ""; }; @@ -1776,20 +1780,22 @@ isa = PBXGroup; children = ( 4B45188D1F75FD1B00926311 /* AcornADF.cpp */, - 4B45188E1F75FD1B00926311 /* AcornADF.hpp */, 4B45188F1F75FD1B00926311 /* CPCDSK.cpp */, - 4B4518901F75FD1B00926311 /* CPCDSK.hpp */, 4B4518911F75FD1B00926311 /* D64.cpp */, - 4B4518921F75FD1B00926311 /* D64.hpp */, 4B4518931F75FD1B00926311 /* G64.cpp */, - 4B4518941F75FD1B00926311 /* G64.hpp */, 4B4518951F75FD1B00926311 /* HFE.cpp */, - 4B4518961F75FD1B00926311 /* HFE.hpp */, 4B58601C1F806AB200AEE2E3 /* MFMSectorDump.cpp */, - 4B58601D1F806AB200AEE2E3 /* MFMSectorDump.hpp */, + 4BEBFB4B2002C4BF000708CC /* MSXDSK.cpp */, 4B4518971F75FD1B00926311 /* OricMFMDSK.cpp */, - 4B4518981F75FD1B00926311 /* OricMFMDSK.hpp */, 4B4518991F75FD1B00926311 /* SSD.cpp */, + 4B45188E1F75FD1B00926311 /* AcornADF.hpp */, + 4B4518901F75FD1B00926311 /* CPCDSK.hpp */, + 4B4518921F75FD1B00926311 /* D64.hpp */, + 4B4518941F75FD1B00926311 /* G64.hpp */, + 4B4518961F75FD1B00926311 /* HFE.hpp */, + 4B58601D1F806AB200AEE2E3 /* MFMSectorDump.hpp */, + 4BEBFB4C2002C4BF000708CC /* MSXDSK.hpp */, + 4B4518981F75FD1B00926311 /* OricMFMDSK.hpp */, 4B45189A1F75FD1B00926311 /* SSD.hpp */, 4BFDD7891F7F2DB4008579B9 /* Utility */, ); @@ -3349,6 +3355,7 @@ 4B055AED1FAE9BA20060FFFF /* Z80Storage.cpp in Sources */, 4B055AD11FAE9B030060FFFF /* Video.cpp in Sources */, 4B055AA21FAE85DA0060FFFF /* SSD.cpp in Sources */, + 4BEBFB4E2002C4BF000708CC /* MSXDSK.cpp in Sources */, 4B055ADD1FAE9B460060FFFF /* i8272.cpp in Sources */, 4B055AC51FAE9AEE0060FFFF /* Atari2600.cpp in Sources */, 4B055A9C1FAE85DA0060FFFF /* CPCDSK.cpp in Sources */, @@ -3542,6 +3549,7 @@ 4BCA6CC81D9DD9F000C2D7B2 /* CommodoreROM.cpp in Sources */, 4BA22B071D8817CE0008C640 /* Disk.cpp in Sources */, 4BEA52661DF3472B007E74F2 /* TIASound.cpp in Sources */, + 4BEBFB4D2002C4BF000708CC /* MSXDSK.cpp in Sources */, 4BBFBB6C1EE8401E00C01E7A /* ZX8081.cpp in Sources */, 4B83348A1F5DB94B0097E338 /* IRQDelegatePortHandler.cpp in Sources */, 4B7136891F78725F008B8ED9 /* Shifter.cpp in Sources */, diff --git a/ROMImages/MSX/readme.txt b/ROMImages/MSX/readme.txt index d5b4a4325..bee52679d 100644 --- a/ROMImages/MSX/readme.txt +++ b/ROMImages/MSX/readme.txt @@ -3,5 +3,6 @@ ROMs for the MSX go here; the copyright status of these is uncertain so they hav Expected files: msx.rom +disk.rom These names match those offered for download at http://fms.komkon.org/fMSX/ (albeit in lowercase), and the emulator has been tested against those images. \ No newline at end of file diff --git a/StaticAnalyser/MSX/StaticAnalyser.cpp b/StaticAnalyser/MSX/StaticAnalyser.cpp index 22d8d9aab..6120d5f32 100644 --- a/StaticAnalyser/MSX/StaticAnalyser.cpp +++ b/StaticAnalyser/MSX/StaticAnalyser.cpp @@ -213,6 +213,9 @@ void StaticAnalyser::MSX::AddTargets(const Media &media, std::list &dest } } + // Blindly accept disks for now. + target.media.disks = media.disks; + if(!target.media.empty()) { target.machine = Target::MSX; target.probability = 1.0; diff --git a/StaticAnalyser/StaticAnalyser.cpp b/StaticAnalyser/StaticAnalyser.cpp index 36750ea75..2d71973a6 100644 --- a/StaticAnalyser/StaticAnalyser.cpp +++ b/StaticAnalyser/StaticAnalyser.cpp @@ -30,6 +30,7 @@ #include "../Storage/Disk/DiskImage/Formats/D64.hpp" #include "../Storage/Disk/DiskImage/Formats/G64.hpp" #include "../Storage/Disk/DiskImage/Formats/HFE.hpp" +#include "../Storage/Disk/DiskImage/Formats/MSXDSK.hpp" #include "../Storage/Disk/DiskImage/Formats/OricMFMDSK.hpp" #include "../Storage/Disk/DiskImage/Formats/SSD.hpp" @@ -91,6 +92,7 @@ static Media GetMediaAndPlatforms(const char *file_name, TargetPlatform::IntType Format("d64", result.disks, Disk::DiskImageHolder, TargetPlatform::Commodore) // D64 Format("dsd", result.disks, Disk::DiskImageHolder, TargetPlatform::Acorn) // DSD Format("dsk", result.disks, Disk::DiskImageHolder, TargetPlatform::AmstradCPC) // DSK (Amstrad CPC) + Format("dsk", result.disks, Disk::DiskImageHolder, TargetPlatform::MSX) // DSK (MSX) Format("dsk", result.disks, Disk::DiskImageHolder, TargetPlatform::Oric) // DSK (Oric) Format("g64", result.disks, Disk::DiskImageHolder, TargetPlatform::Commodore) // G64 Format("hfe", result.disks, Disk::DiskImageHolder, TargetPlatform::AmstradCPC) // HFE (TODO: plus other target platforms) diff --git a/Storage/Disk/DiskImage/Formats/MSXDSK.cpp b/Storage/Disk/DiskImage/Formats/MSXDSK.cpp new file mode 100644 index 000000000..1f04ec686 --- /dev/null +++ b/Storage/Disk/DiskImage/Formats/MSXDSK.cpp @@ -0,0 +1,59 @@ +// +// MSXDSK.cpp +// Clock Signal +// +// Created by Thomas Harte on 07/01/2018. +// Copyright © 2018 Thomas Harte. All rights reserved. +// + +#include "MSXDSK.hpp" + +#include "Utility/ImplicitSectors.hpp" + +namespace { + static const int sectors_per_track = 9; + static const int sector_size = 2; +} + +using namespace Storage::Disk; + +MSXDSK::MSXDSK(const char *file_name) : + MFMSectorDump(file_name) { + // The only sanity check here is whether a sensible + // geometry can be guessed. + off_t file_size = file_.stats().st_size; + const off_t track_size = 512*9; + + // Throw if there would seemingly be an incomplete track. + if(file_size % track_size) throw ErrorNotMSXDSK; + + track_count_ = static_cast(file_size / track_size); + head_count_ = 1; + + // Throw if too large or too small or too large for single sided and + // clearly not double sided. + if(track_count_ < 40) throw ErrorNotMSXDSK; + if(track_count_ > 82*2) throw ErrorNotMSXDSK; + if(track_count_ > 82 && track_count_&1) throw ErrorNotMSXDSK; + + // The below effectively prefers the idea of a single-sided 80-track disk + // to a double-sided 40-track disk. Emulators have to guess. + if(track_count_ > 82) { + track_count_ /= 2; + head_count_ = 2; + } + + set_geometry(sectors_per_track, sector_size, true); +} + +int MSXDSK::get_head_position_count() { + return track_count_; +} + +int MSXDSK::get_head_count() { + return head_count_; +} + +long MSXDSK::get_file_offset_for_position(Track::Address address) { + return (address.position + address.head*track_count_) * 512 * 9; +} diff --git a/Storage/Disk/DiskImage/Formats/MSXDSK.hpp b/Storage/Disk/DiskImage/Formats/MSXDSK.hpp new file mode 100644 index 000000000..ae64158e4 --- /dev/null +++ b/Storage/Disk/DiskImage/Formats/MSXDSK.hpp @@ -0,0 +1,42 @@ +// +// MSXDSK.hpp +// Clock Signal +// +// Created by Thomas Harte on 07/01/2018. +// Copyright © 2018 Thomas Harte. All rights reserved. +// + +#ifndef MSXDSK_hpp +#define MSXDSK_hpp + +#include "MFMSectorDump.hpp" + +namespace Storage { +namespace Disk { + +/*! + Provides a @c Disk containing an MSX-style disk image: + a sector dump of appropriate proportions. +*/ +class MSXDSK: public MFMSectorDump { + public: + MSXDSK(const char *file_name); + + enum { + ErrorNotMSXDSK, + }; + + int get_head_position_count() override; + int get_head_count() override; + + private: + long get_file_offset_for_position(Track::Address address) override; + + int head_count_; + int track_count_; +}; + +} +} + +#endif /* MSXDSK_hpp */ diff --git a/Storage/Disk/DiskImage/Formats/OricMFMDSK.hpp b/Storage/Disk/DiskImage/Formats/OricMFMDSK.hpp index 40d350636..0e00eb31f 100644 --- a/Storage/Disk/DiskImage/Formats/OricMFMDSK.hpp +++ b/Storage/Disk/DiskImage/Formats/OricMFMDSK.hpp @@ -16,7 +16,7 @@ namespace Storage { namespace Disk { /*! - Provies a @c Disk containing an Oric MFM-stype disk image — a stream of the MFM data bits with clocks omitted. + Provides a @c Disk containing an Oric MFM-stype disk image — a stream of the MFM data bits with clocks omitted. */ class OricMFMDSK: public DiskImage { public: @@ -31,7 +31,7 @@ class OricMFMDSK: public DiskImage { ErrorNotOricMFMDSK, }; - // implemented to satisfy @c Disk + // implemented to satisfy @c DiskImage int get_head_position_count() override; int get_head_count() override; bool get_is_read_only() override; From 513c067f943938bd285ea295d546779fe89f737f Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 7 Jan 2018 19:12:52 -0500 Subject: [PATCH 2/7] Makes an attempt to rope in the WD1770 for MSX disk ROM emulation. --- Machines/MSX/DiskROM.cpp | 69 +++++++++++++++++++ Machines/MSX/DiskROM.hpp | 45 ++++++++++++ Machines/MSX/MSX.cpp | 30 +++++++- .../Clock Signal.xcodeproj/project.pbxproj | 8 +++ 4 files changed, 149 insertions(+), 3 deletions(-) create mode 100644 Machines/MSX/DiskROM.cpp create mode 100644 Machines/MSX/DiskROM.hpp diff --git a/Machines/MSX/DiskROM.cpp b/Machines/MSX/DiskROM.cpp new file mode 100644 index 000000000..139002dbe --- /dev/null +++ b/Machines/MSX/DiskROM.cpp @@ -0,0 +1,69 @@ +// +// DiskROM.cpp +// Clock Signal +// +// Created by Thomas Harte on 07/01/2018. +// Copyright © 2018 Thomas Harte. All rights reserved. +// + +#include "DiskROM.hpp" + +using namespace MSX; + +DiskROM::DiskROM(const std::vector &rom) : + WD1770(P1793), + rom_(rom) { + set_is_double_density(true); +} + +void DiskROM::write(uint16_t address, uint8_t value) { + switch(address) { + case 0x7ff8: case 0x7ff9: case 0x7ffa: case 0x7ffb: + set_register(address, value); + break; + case 0x7ffc: + selected_head_ = value & 1; + if(drives_[0]) drives_[0]->set_head(selected_head_); + if(drives_[1]) drives_[1]->set_head(selected_head_); + break; + case 0x7ffd: + selected_drive_ = value & 1; + set_drive(drives_[selected_drive_]); + set_motor_on(!!(value & 0x80)); + break; + } +} + +uint8_t DiskROM::read(uint16_t address) { + if(address >= 0x7ff8 && address < 0x7ffc) { + return get_register(address); + } + return rom_[address & 0x3fff]; +} + +void DiskROM::run_for(HalfCycles half_cycles) { + // Input clock is going to be 7159090/2 Mhz, but the drive controller + // needs an 8Mhz clock, so scale up. 8000000/7159090 simplifies to + // 800000/715909. + controller_cycles_ += 800000 * half_cycles.as_int(); + WD::WD1770::run_for(Cycles(static_cast(controller_cycles_ / 715909))); + controller_cycles_ %= 715909; +} + +void DiskROM::set_disk(std::shared_ptr disk, int drive) { + if(!drives_[drive]) { + drives_[drive].reset(new Storage::Disk::Drive(8000000, 300, 2)); + drives_[drive]->set_head(selected_head_); + if(drive == selected_drive_) set_drive(drives_[drive]); + } + drives_[drive]->set_disk(disk); +} + +void DiskROM::set_head_load_request(bool head_load) { + // Magic! + set_head_loaded(head_load); +} + +bool DiskROM::get_drive_is_ready() { + return true; +} diff --git a/Machines/MSX/DiskROM.hpp b/Machines/MSX/DiskROM.hpp new file mode 100644 index 000000000..fcdeac05c --- /dev/null +++ b/Machines/MSX/DiskROM.hpp @@ -0,0 +1,45 @@ +// +// DiskROM.hpp +// Clock Signal +// +// Created by Thomas Harte on 07/01/2018. +// Copyright © 2018 Thomas Harte. All rights reserved. +// + +#ifndef DiskROM_hpp +#define DiskROM_hpp + +#include "ROMSlotHandler.hpp" + +#include "../../Components/1770/1770.hpp" + +#include +#include + +namespace MSX { + +class DiskROM: public ROMSlotHandler, public WD::WD1770 { + public: + DiskROM(const std::vector &rom); + + void write(uint16_t address, uint8_t value) override; + uint8_t read(uint16_t address) override; + void run_for(HalfCycles half_cycles) override; + + void set_disk(std::shared_ptr disk, int drive); + + private: + const std::vector &rom_; + + long int controller_cycles_ = 0; + int selected_drive_ = 0; + int selected_head_ = 0; + std::shared_ptr drives_[4]; + + void set_head_load_request(bool head_load); + bool get_drive_is_ready(); +}; + +} + +#endif /* DiskROM_hpp */ diff --git a/Machines/MSX/MSX.cpp b/Machines/MSX/MSX.cpp index f3a493871..ccdbb52f9 100644 --- a/Machines/MSX/MSX.cpp +++ b/Machines/MSX/MSX.cpp @@ -8,6 +8,7 @@ #include "MSX.hpp" +#include "DiskROM.hpp" #include "Keyboard.hpp" #include "ROMSlotHandler.hpp" @@ -156,19 +157,28 @@ class ConcreteMachine: } void configure_as_target(const StaticAnalyser::Target &target) override { + // Add a disk cartridge if any disks were supplied. + if(!target.media.disks.empty()) { + map(2, 0, 0x4000, 0x2000); + unmap(2, 0x6000, 0x2000); + memory_slots_[2].set_handler(new DiskROM(memory_slots_[2].source)); + } + + // Insert the media. insert_media(target.media); + // Type whatever has been requested. if(target.loading_command.length()) { type_string(target.loading_command); } + // Attach the hardware necessary for a game cartridge, if any. switch(target.msx.cartridge_type) { default: break; case StaticAnalyser::MSXCartridgeType::Konami: memory_slots_[1].set_handler(new Cartridge::KonamiROMSlotHandler(*this, 1)); break; case StaticAnalyser::MSXCartridgeType::KonamiWithSCC: - // TODO: enable an SCC. memory_slots_[1].set_handler(new Cartridge::KonamiWithSCCROMSlotHandler(*this, 1, scc_)); break; case StaticAnalyser::MSXCartridgeType::ASCII8kb: @@ -191,6 +201,16 @@ class ConcreteMachine: tape_player_.set_tape(media.tapes.front()); } + if(!media.disks.empty()) { + DiskROM *disk_rom = dynamic_cast(memory_slots_[2].handler.get()); + int drive = 0; + for(auto &disk : media.disks) { + disk_rom->set_disk(disk, drive); + drive++; + if(drive == 2) break; + } + } + return true; } @@ -439,14 +459,18 @@ class ConcreteMachine: auto roms = roms_with_names( "MSX", { - "msx.rom" + "msx.rom", + "disk.rom" }); - if(!roms[0]) return false; + if(!roms[0] || !roms[1]) return false; memory_slots_[0].source = std::move(*roms[0]); memory_slots_[0].source.resize(32768); + memory_slots_[2].source = std::move(*roms[1]); + memory_slots_[2].source.resize(16384); + for(size_t c = 0; c < 8; ++c) { for(size_t slot = 0; slot < 3; ++slot) { memory_slots_[slot].read_pointers[c] = unpopulated_; diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 014c2eb39..de6b2ed0d 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -593,6 +593,8 @@ 4BEA52661DF3472B007E74F2 /* TIASound.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEA52641DF3472B007E74F2 /* TIASound.cpp */; }; 4BEBFB4D2002C4BF000708CC /* MSXDSK.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEBFB4B2002C4BF000708CC /* MSXDSK.cpp */; }; 4BEBFB4E2002C4BF000708CC /* MSXDSK.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEBFB4B2002C4BF000708CC /* MSXDSK.cpp */; }; + 4BEBFB512002DB30000708CC /* DiskROM.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEBFB4F2002DB30000708CC /* DiskROM.cpp */; }; + 4BEBFB522002DB30000708CC /* DiskROM.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEBFB4F2002DB30000708CC /* DiskROM.cpp */; }; 4BEE0A6F1D72496600532C7B /* Cartridge.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEE0A6A1D72496600532C7B /* Cartridge.cpp */; }; 4BEE0A701D72496600532C7B /* PRG.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEE0A6D1D72496600532C7B /* PRG.cpp */; }; 4BEF6AAA1D35CE9E00E73575 /* DigitalPhaseLockedLoopBridge.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BEF6AA91D35CE9E00E73575 /* DigitalPhaseLockedLoopBridge.mm */; }; @@ -1312,6 +1314,8 @@ 4BEAC08E1E7E110500EE56B2 /* Pitfall2.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Pitfall2.hpp; sourceTree = ""; }; 4BEBFB4B2002C4BF000708CC /* MSXDSK.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = MSXDSK.cpp; sourceTree = ""; }; 4BEBFB4C2002C4BF000708CC /* MSXDSK.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = MSXDSK.hpp; sourceTree = ""; }; + 4BEBFB4F2002DB30000708CC /* DiskROM.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = DiskROM.cpp; path = MSX/DiskROM.cpp; sourceTree = ""; }; + 4BEBFB502002DB30000708CC /* DiskROM.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = DiskROM.hpp; path = MSX/DiskROM.hpp; sourceTree = ""; }; 4BEE0A6A1D72496600532C7B /* Cartridge.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Cartridge.cpp; sourceTree = ""; }; 4BEE0A6B1D72496600532C7B /* Cartridge.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Cartridge.hpp; sourceTree = ""; }; 4BEE0A6D1D72496600532C7B /* PRG.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PRG.cpp; sourceTree = ""; }; @@ -2019,8 +2023,10 @@ 4B79A4FC1FC8FF9800EEDAD5 /* MSX */ = { isa = PBXGroup; children = ( + 4BEBFB4F2002DB30000708CC /* DiskROM.cpp */, 4B12C0EB1FCFA98D005BFD93 /* Keyboard.cpp */, 4B79A4FF1FC913C900EEDAD5 /* MSX.cpp */, + 4BEBFB502002DB30000708CC /* DiskROM.hpp */, 4B12C0EC1FCFA98D005BFD93 /* Keyboard.hpp */, 4B79A5001FC913C900EEDAD5 /* MSX.hpp */, 4B70EF6A1FFDCDF400A3494E /* ROMSlotHandler.hpp */, @@ -3376,6 +3382,7 @@ 4B055A7F1FAE852F0060FFFF /* StaticAnalyser.cpp in Sources */, 4B055ADB1FAE9B460060FFFF /* 6560.cpp in Sources */, 4B055AA01FAE85DA0060FFFF /* MFMSectorDump.cpp in Sources */, + 4BEBFB522002DB30000708CC /* DiskROM.cpp in Sources */, 4B055AA11FAE85DA0060FFFF /* OricMFMDSK.cpp in Sources */, 4B055A951FAE85BB0060FFFF /* BitReverse.cpp in Sources */, 4B055A891FAE85580060FFFF /* StaticAnalyser.cpp in Sources */, @@ -3490,6 +3497,7 @@ 4BF829661D8F732B001BAE39 /* Disk.cpp in Sources */, 4B448E811F1C45A00009ABD6 /* TZX.cpp in Sources */, 4B1BA08A1FD4967800CB4ADA /* CSMSX.mm in Sources */, + 4BEBFB512002DB30000708CC /* DiskROM.cpp in Sources */, 4BEA52631DF339D7007E74F2 /* SoundGenerator.cpp in Sources */, 4BC5E4921D7ED365008CF980 /* StaticAnalyser.cpp in Sources */, 4BC830D11D6E7C690000A26F /* Tape.cpp in Sources */, From 793ef6820632f7a2bc4a41e460981456f75479e1 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 7 Jan 2018 19:42:38 -0500 Subject: [PATCH 3/7] Implements unconditional force interrupt for the WD. --- Components/1770/1770.cpp | 27 +++++++++++++++++++++------ Components/1770/1770.hpp | 3 ++- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/Components/1770/1770.cpp b/Components/1770/1770.cpp index 272d7b4d0..d8c21c4e3 100644 --- a/Components/1770/1770.cpp +++ b/Components/1770/1770.cpp @@ -23,10 +23,16 @@ void WD1770::set_register(int address, uint8_t value) { switch(address&3) { case 0: { if((value&0xf0) == 0xd0) { - printf("!!!TODO: force interrupt!!!\n"); - update_status([] (Status &status) { - status.type = Status::One; - }); + if(value == 0xd0) { + // Force interrupt **immediately**. + printf("Force interrupt immediately\n"); + posit_event(static_cast(Event1770::ForceInterrupt)); + } else { + printf("!!!TODO: force interrupt!!!\n"); + update_status([] (Status &status) { + status.type = Status::One; + }); + } } else { command_ = value; posit_event(static_cast(Event1770::Command)); @@ -169,13 +175,22 @@ void WD1770::posit_event(int new_event_type) { } } - if(!(interesting_event_mask_ & static_cast(new_event_type))) return; - interesting_event_mask_ &= ~new_event_type; + if(new_event_type == static_cast(Event1770::ForceInterrupt)) { + interesting_event_mask_ = 0; + resume_point_ = 0; + update_status([] (Status &status) { + status.type = Status::One; + }); + } else { + if(!(interesting_event_mask_ & static_cast(new_event_type))) return; + interesting_event_mask_ &= ~new_event_type; + } Status new_status; BEGIN_SECTION() // Wait for a new command, branch to the appropriate handler. + case 0: wait_for_command: printf("Idle...\n"); set_data_mode(DataMode::Scanning); diff --git a/Components/1770/1770.hpp b/Components/1770/1770.hpp index aa15a5259..52d544824 100644 --- a/Components/1770/1770.hpp +++ b/Components/1770/1770.hpp @@ -116,7 +116,8 @@ class WD1770: public Storage::Disk::MFMController { Command = (1 << 3), // Indicates receipt of a new command. HeadLoad = (1 << 4), // Indicates the head has been loaded (1973 only). Timer = (1 << 5), // Indicates that the delay_time_-powered timer has timed out. - IndexHoleTarget = (1 << 6) // Indicates that index_hole_count_ has reached index_hole_count_target_. + IndexHoleTarget = (1 << 6), // Indicates that index_hole_count_ has reached index_hole_count_target_. + ForceInterrupt = (1 << 7) // Indicates a forced interrupt. }; void posit_event(int type); int interesting_event_mask_; From 2f592263006e87964a7711ed6a18ec3783ec2549 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 7 Jan 2018 20:02:40 -0500 Subject: [PATCH 4/7] Fixes: DiskROM drive motor control, track_for_sectors' sides. --- Components/1770/1770.cpp | 3 ++- Machines/MSX/DiskROM.cpp | 13 ++++++++++--- .../DiskImage/Formats/Utility/ImplicitSectors.cpp | 2 +- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/Components/1770/1770.cpp b/Components/1770/1770.cpp index d8c21c4e3..7071d1eb8 100644 --- a/Components/1770/1770.cpp +++ b/Components/1770/1770.cpp @@ -180,6 +180,7 @@ void WD1770::posit_event(int new_event_type) { resume_point_ = 0; update_status([] (Status &status) { status.type = Status::One; + status.data_request = false; }); } else { if(!(interesting_event_mask_ & static_cast(new_event_type))) return; @@ -475,7 +476,7 @@ void WD1770::posit_event(int new_event_type) { sector_++; goto test_type2_write_protection; } - printf("Read sector %d\n", sector_); + printf("Finished reading sector %d\n", sector_); goto wait_for_command; } goto type2_check_crc; diff --git a/Machines/MSX/DiskROM.cpp b/Machines/MSX/DiskROM.cpp index 139002dbe..1af677d3e 100644 --- a/Machines/MSX/DiskROM.cpp +++ b/Machines/MSX/DiskROM.cpp @@ -25,12 +25,18 @@ void DiskROM::write(uint16_t address, uint8_t value) { selected_head_ = value & 1; if(drives_[0]) drives_[0]->set_head(selected_head_); if(drives_[1]) drives_[1]->set_head(selected_head_); + printf("HEAD: %d\n", selected_head_); break; - case 0x7ffd: + case 0x7ffd: { selected_drive_ = value & 1; set_drive(drives_[selected_drive_]); - set_motor_on(!!(value & 0x80)); - break; + + bool drive_motor = !!(value & 0x80); + if(drives_[0]) drives_[0]->set_motor_on(drive_motor); + if(drives_[1]) drives_[1]->set_motor_on(drive_motor); + printf("DRIVE: %d\n", selected_head_); + printf("MOTOR ON: %s\n", (value & 0x80) ? "on" : "off"); + } break; } } @@ -51,6 +57,7 @@ void DiskROM::run_for(HalfCycles half_cycles) { } void DiskROM::set_disk(std::shared_ptr disk, int drive) { + printf("Disk to %d\n", drive); if(!drives_[drive]) { drives_[drive].reset(new Storage::Disk::Drive(8000000, 300, 2)); drives_[drive]->set_head(selected_head_); diff --git a/Storage/Disk/DiskImage/Formats/Utility/ImplicitSectors.cpp b/Storage/Disk/DiskImage/Formats/Utility/ImplicitSectors.cpp index 7b1dc1382..bf94f0c5f 100644 --- a/Storage/Disk/DiskImage/Formats/Utility/ImplicitSectors.cpp +++ b/Storage/Disk/DiskImage/Formats/Utility/ImplicitSectors.cpp @@ -28,7 +28,7 @@ std::shared_ptr Storage::Disk::track_for_sectors(uint8_t *const source, u Storage::Encodings::MFM::Sector &new_sector = sectors.back(); new_sector.address.track = track; - new_sector.address.side = size; + new_sector.address.side = side; new_sector.address.sector = first_sector; first_sector++; new_sector.size = size; From 8be6cb827bd14c5e3620002ac0d4b942aaa4a2e2 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 7 Jan 2018 20:28:34 -0500 Subject: [PATCH 5/7] Implements MSX interrupt/data request reading register. The disk ROM now appears to accept on-disk bytes, but still announces an IO failure. --- Machines/MSX/DiskROM.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Machines/MSX/DiskROM.cpp b/Machines/MSX/DiskROM.cpp index 1af677d3e..9b90433f2 100644 --- a/Machines/MSX/DiskROM.cpp +++ b/Machines/MSX/DiskROM.cpp @@ -44,6 +44,9 @@ uint8_t DiskROM::read(uint16_t address) { if(address >= 0x7ff8 && address < 0x7ffc) { return get_register(address); } + if(address == 0x7fff) { + return (get_data_request_line() ? 0x00 : 0x80) | (get_interrupt_request_line() ? 0x00 : 0x40); + } return rom_[address & 0x3fff]; } From 0ccc104027c20427265ae26b8b102dc5102dee65 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 7 Jan 2018 21:59:18 -0500 Subject: [PATCH 6/7] Corrects start sector and track interleaving for MSX DSK. MSX DSKs start with sector 1; Acorn disks still begin with sector 0. Also it turns out that MSX DSKs are indeed interleaved. --- Storage/Disk/DiskImage/Formats/AcornADF.cpp | 2 +- Storage/Disk/DiskImage/Formats/MFMSectorDump.cpp | 7 ++++--- Storage/Disk/DiskImage/Formats/MFMSectorDump.hpp | 3 ++- Storage/Disk/DiskImage/Formats/MSXDSK.cpp | 4 ++-- Storage/Disk/DiskImage/Formats/SSD.cpp | 2 +- 5 files changed, 10 insertions(+), 8 deletions(-) diff --git a/Storage/Disk/DiskImage/Formats/AcornADF.cpp b/Storage/Disk/DiskImage/Formats/AcornADF.cpp index 569681e73..3ec74dbce 100644 --- a/Storage/Disk/DiskImage/Formats/AcornADF.cpp +++ b/Storage/Disk/DiskImage/Formats/AcornADF.cpp @@ -33,7 +33,7 @@ AcornADF::AcornADF(const char *file_name) : MFMSectorDump(file_name) { file_.read(bytes, 4); if(bytes[0] != 'H' || bytes[1] != 'u' || bytes[2] != 'g' || bytes[3] != 'o') throw ErrorNotAcornADF; - set_geometry(sectors_per_track, sector_size, true); + set_geometry(sectors_per_track, sector_size, 0, true); } int AcornADF::get_head_position_count() { diff --git a/Storage/Disk/DiskImage/Formats/MFMSectorDump.cpp b/Storage/Disk/DiskImage/Formats/MFMSectorDump.cpp index d73dd9f8a..1bdd1e5d6 100644 --- a/Storage/Disk/DiskImage/Formats/MFMSectorDump.cpp +++ b/Storage/Disk/DiskImage/Formats/MFMSectorDump.cpp @@ -14,10 +14,11 @@ using namespace Storage::Disk; MFMSectorDump::MFMSectorDump(const char *file_name) : file_(file_name) {} -void MFMSectorDump::set_geometry(int sectors_per_track, uint8_t sector_size, bool is_double_density) { +void MFMSectorDump::set_geometry(int sectors_per_track, uint8_t sector_size, uint8_t first_sector, bool is_double_density) { sectors_per_track_ = sectors_per_track; sector_size_ = sector_size; is_double_density_ = is_double_density; + first_sector_ = first_sector; } std::shared_ptr MFMSectorDump::get_track_at_position(Track::Address address) { @@ -32,7 +33,7 @@ std::shared_ptr MFMSectorDump::get_track_at_position(Track::Address addre file_.read(sectors, sizeof(sectors)); } - return track_for_sectors(sectors, static_cast(address.position), static_cast(address.head), 0, sector_size_, is_double_density_); + return track_for_sectors(sectors, static_cast(address.position), static_cast(address.head), first_sector_, sector_size_, is_double_density_); } void MFMSectorDump::set_tracks(const std::map> &tracks) { @@ -43,7 +44,7 @@ void MFMSectorDump::set_tracks(const std::map(sectors_per_track_-1), sector_size_, is_double_density_); + decode_sectors(*track.second, parsed_track, first_sector_, first_sector_ + static_cast(sectors_per_track_-1), sector_size_, is_double_density_); long file_offset = get_file_offset_for_position(track.first); std::lock_guard lock_guard(file_.get_file_access_mutex()); diff --git a/Storage/Disk/DiskImage/Formats/MFMSectorDump.hpp b/Storage/Disk/DiskImage/Formats/MFMSectorDump.hpp index 9fffcf677..428241f39 100644 --- a/Storage/Disk/DiskImage/Formats/MFMSectorDump.hpp +++ b/Storage/Disk/DiskImage/Formats/MFMSectorDump.hpp @@ -21,7 +21,7 @@ namespace Disk { class MFMSectorDump: public DiskImage { public: MFMSectorDump(const char *file_name); - void set_geometry(int sectors_per_track, uint8_t sector_size, bool is_double_density); + void set_geometry(int sectors_per_track, uint8_t sector_size, uint8_t first_sector, bool is_double_density); bool get_is_read_only() override; void set_tracks(const std::map> &tracks) override; @@ -36,6 +36,7 @@ class MFMSectorDump: public DiskImage { int sectors_per_track_ = 0; uint8_t sector_size_ = 0; bool is_double_density_ = true; + uint8_t first_sector_ = 0; }; } diff --git a/Storage/Disk/DiskImage/Formats/MSXDSK.cpp b/Storage/Disk/DiskImage/Formats/MSXDSK.cpp index 1f04ec686..014845ed3 100644 --- a/Storage/Disk/DiskImage/Formats/MSXDSK.cpp +++ b/Storage/Disk/DiskImage/Formats/MSXDSK.cpp @@ -43,7 +43,7 @@ MSXDSK::MSXDSK(const char *file_name) : head_count_ = 2; } - set_geometry(sectors_per_track, sector_size, true); + set_geometry(sectors_per_track, sector_size, 1, true); } int MSXDSK::get_head_position_count() { @@ -55,5 +55,5 @@ int MSXDSK::get_head_count() { } long MSXDSK::get_file_offset_for_position(Track::Address address) { - return (address.position + address.head*track_count_) * 512 * 9; + return (address.position*2 + address.head) * 512 * 9; } diff --git a/Storage/Disk/DiskImage/Formats/SSD.cpp b/Storage/Disk/DiskImage/Formats/SSD.cpp index f8c91d969..31acee2c8 100644 --- a/Storage/Disk/DiskImage/Formats/SSD.cpp +++ b/Storage/Disk/DiskImage/Formats/SSD.cpp @@ -33,7 +33,7 @@ SSD::SSD(const char *file_name) : MFMSectorDump(file_name) { if(track_count_ < 40) track_count_ = 40; else if(track_count_ < 80) track_count_ = 80; - set_geometry(sectors_per_track, sector_size, false); + set_geometry(sectors_per_track, sector_size, 0, false); } int SSD::get_head_position_count() { From a5ac8c824e891000eafbdb6ac35a221d6d529a90 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 7 Jan 2018 21:59:59 -0500 Subject: [PATCH 7/7] Removes logging and unnecessary `get_drive_is_ready`. --- Machines/MSX/DiskROM.cpp | 8 -------- Machines/MSX/DiskROM.hpp | 3 +-- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/Machines/MSX/DiskROM.cpp b/Machines/MSX/DiskROM.cpp index 9b90433f2..4382768f4 100644 --- a/Machines/MSX/DiskROM.cpp +++ b/Machines/MSX/DiskROM.cpp @@ -25,7 +25,6 @@ void DiskROM::write(uint16_t address, uint8_t value) { selected_head_ = value & 1; if(drives_[0]) drives_[0]->set_head(selected_head_); if(drives_[1]) drives_[1]->set_head(selected_head_); - printf("HEAD: %d\n", selected_head_); break; case 0x7ffd: { selected_drive_ = value & 1; @@ -34,8 +33,6 @@ void DiskROM::write(uint16_t address, uint8_t value) { bool drive_motor = !!(value & 0x80); if(drives_[0]) drives_[0]->set_motor_on(drive_motor); if(drives_[1]) drives_[1]->set_motor_on(drive_motor); - printf("DRIVE: %d\n", selected_head_); - printf("MOTOR ON: %s\n", (value & 0x80) ? "on" : "off"); } break; } } @@ -60,7 +57,6 @@ void DiskROM::run_for(HalfCycles half_cycles) { } void DiskROM::set_disk(std::shared_ptr disk, int drive) { - printf("Disk to %d\n", drive); if(!drives_[drive]) { drives_[drive].reset(new Storage::Disk::Drive(8000000, 300, 2)); drives_[drive]->set_head(selected_head_); @@ -73,7 +69,3 @@ void DiskROM::set_head_load_request(bool head_load) { // Magic! set_head_loaded(head_load); } - -bool DiskROM::get_drive_is_ready() { - return true; -} diff --git a/Machines/MSX/DiskROM.hpp b/Machines/MSX/DiskROM.hpp index fcdeac05c..b7cce1c1d 100644 --- a/Machines/MSX/DiskROM.hpp +++ b/Machines/MSX/DiskROM.hpp @@ -36,8 +36,7 @@ class DiskROM: public ROMSlotHandler, public WD::WD1770 { int selected_head_ = 0; std::shared_ptr drives_[4]; - void set_head_load_request(bool head_load); - bool get_drive_is_ready(); + void set_head_load_request(bool head_load) override; }; }