1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-12-24 12:30:17 +00:00

Merge pull request #331 from TomHarte/MSXFloppy

Adds floppy emulation for the MSX
This commit is contained in:
Thomas Harte 2018-01-07 19:25:11 -08:00 committed by GitHub
commit b36c917810
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 308 additions and 27 deletions

View File

@ -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<int>(Event1770::ForceInterrupt));
} else {
printf("!!!TODO: force interrupt!!!\n");
update_status([] (Status &status) {
status.type = Status::One;
});
}
} else {
command_ = value;
posit_event(static_cast<int>(Event1770::Command));
@ -169,13 +175,23 @@ void WD1770::posit_event(int new_event_type) {
}
}
if(!(interesting_event_mask_ & static_cast<int>(new_event_type))) return;
interesting_event_mask_ &= ~new_event_type;
if(new_event_type == static_cast<int>(Event1770::ForceInterrupt)) {
interesting_event_mask_ = 0;
resume_point_ = 0;
update_status([] (Status &status) {
status.type = Status::One;
status.data_request = false;
});
} else {
if(!(interesting_event_mask_ & static_cast<int>(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);
@ -460,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;

View File

@ -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_;

71
Machines/MSX/DiskROM.cpp Normal file
View File

@ -0,0 +1,71 @@
//
// 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<uint8_t> &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_]);
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);
} break;
}
}
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];
}
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<int>(controller_cycles_ / 715909)));
controller_cycles_ %= 715909;
}
void DiskROM::set_disk(std::shared_ptr<Storage::Disk::Disk> 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);
}

44
Machines/MSX/DiskROM.hpp Normal file
View File

@ -0,0 +1,44 @@
//
// 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 <cstdint>
#include <vector>
namespace MSX {
class DiskROM: public ROMSlotHandler, public WD::WD1770 {
public:
DiskROM(const std::vector<uint8_t> &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<Storage::Disk::Disk> disk, int drive);
private:
const std::vector<uint8_t> &rom_;
long int controller_cycles_ = 0;
int selected_drive_ = 0;
int selected_head_ = 0;
std::shared_ptr<Storage::Disk::Drive> drives_[4];
void set_head_load_request(bool head_load) override;
};
}
#endif /* DiskROM_hpp */

View File

@ -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<DiskROM *>(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_;

View File

@ -591,6 +591,10 @@
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 */; };
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 */; };
@ -1308,6 +1312,10 @@
4BEAC08C1E7E0DF800EE56B2 /* Unpaged.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Unpaged.hpp; sourceTree = "<group>"; };
4BEAC08D1E7E0E1A00EE56B2 /* Bus.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Bus.hpp; sourceTree = "<group>"; };
4BEAC08E1E7E110500EE56B2 /* Pitfall2.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Pitfall2.hpp; sourceTree = "<group>"; };
4BEBFB4B2002C4BF000708CC /* MSXDSK.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = MSXDSK.cpp; sourceTree = "<group>"; };
4BEBFB4C2002C4BF000708CC /* MSXDSK.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = MSXDSK.hpp; sourceTree = "<group>"; };
4BEBFB4F2002DB30000708CC /* DiskROM.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = DiskROM.cpp; path = MSX/DiskROM.cpp; sourceTree = "<group>"; };
4BEBFB502002DB30000708CC /* DiskROM.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = DiskROM.hpp; path = MSX/DiskROM.hpp; sourceTree = "<group>"; };
4BEE0A6A1D72496600532C7B /* Cartridge.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Cartridge.cpp; sourceTree = "<group>"; };
4BEE0A6B1D72496600532C7B /* Cartridge.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Cartridge.hpp; sourceTree = "<group>"; };
4BEE0A6D1D72496600532C7B /* PRG.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PRG.cpp; sourceTree = "<group>"; };
@ -1776,20 +1784,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 */,
);
@ -2013,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 */,
@ -3349,6 +3361,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 */,
@ -3369,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 */,
@ -3483,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 */,
@ -3542,6 +3557,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 */,

View File

@ -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.

View File

@ -213,6 +213,9 @@ void StaticAnalyser::MSX::AddTargets(const Media &media, std::list<Target> &dest
}
}
// Blindly accept disks for now.
target.media.disks = media.disks;
if(!target.media.empty()) {
target.machine = Target::MSX;
target.probability = 1.0;

View File

@ -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<Storage::Disk::D64>, TargetPlatform::Commodore) // D64
Format("dsd", result.disks, Disk::DiskImageHolder<Storage::Disk::SSD>, TargetPlatform::Acorn) // DSD
Format("dsk", result.disks, Disk::DiskImageHolder<Storage::Disk::CPCDSK>, TargetPlatform::AmstradCPC) // DSK (Amstrad CPC)
Format("dsk", result.disks, Disk::DiskImageHolder<Storage::Disk::MSXDSK>, TargetPlatform::MSX) // DSK (MSX)
Format("dsk", result.disks, Disk::DiskImageHolder<Storage::Disk::OricMFMDSK>, TargetPlatform::Oric) // DSK (Oric)
Format("g64", result.disks, Disk::DiskImageHolder<Storage::Disk::G64>, TargetPlatform::Commodore) // G64
Format("hfe", result.disks, Disk::DiskImageHolder<Storage::Disk::HFE>, TargetPlatform::AmstradCPC) // HFE (TODO: plus other target platforms)

View File

@ -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() {

View File

@ -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<Track> MFMSectorDump::get_track_at_position(Track::Address address) {
@ -32,7 +33,7 @@ std::shared_ptr<Track> MFMSectorDump::get_track_at_position(Track::Address addre
file_.read(sectors, sizeof(sectors));
}
return track_for_sectors(sectors, static_cast<uint8_t>(address.position), static_cast<uint8_t>(address.head), 0, sector_size_, is_double_density_);
return track_for_sectors(sectors, static_cast<uint8_t>(address.position), static_cast<uint8_t>(address.head), first_sector_, sector_size_, is_double_density_);
}
void MFMSectorDump::set_tracks(const std::map<Track::Address, std::shared_ptr<Track>> &tracks) {
@ -43,7 +44,7 @@ void MFMSectorDump::set_tracks(const std::map<Track::Address, std::shared_ptr<Tr
for(auto &track : tracks) {
// Assumption here: sector IDs will run from 0.
decode_sectors(*track.second, parsed_track, 0, static_cast<uint8_t>(sectors_per_track_-1), sector_size_, is_double_density_);
decode_sectors(*track.second, parsed_track, first_sector_, first_sector_ + static_cast<uint8_t>(sectors_per_track_-1), sector_size_, is_double_density_);
long file_offset = get_file_offset_for_position(track.first);
std::lock_guard<std::mutex> lock_guard(file_.get_file_access_mutex());

View File

@ -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<Track::Address, std::shared_ptr<Track>> &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;
};
}

View File

@ -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<int>(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, 1, 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*2 + address.head) * 512 * 9;
}

View File

@ -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 */

View File

@ -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;

View File

@ -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() {

View File

@ -28,7 +28,7 @@ std::shared_ptr<Track> 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;