diff --git a/Analyser/Static/MSX/Target.hpp b/Analyser/Static/MSX/Target.hpp index 081220440..fa4e379bc 100644 --- a/Analyser/Static/MSX/Target.hpp +++ b/Analyser/Static/MSX/Target.hpp @@ -19,6 +19,12 @@ namespace MSX { struct Target: public ::Analyser::Static::Target { bool has_disk_drive = false; std::string loading_command; + + enum class Region { + Japan, + USA, + Europe + } region = Region::USA; }; } diff --git a/Machines/MSX/MSX.cpp b/Machines/MSX/MSX.cpp index 755675da0..e286fde05 100644 --- a/Machines/MSX/MSX.cpp +++ b/Machines/MSX/MSX.cpp @@ -146,7 +146,9 @@ class ConcreteMachine: public ClockingHint::Observer, public Activity::Source { public: - ConcreteMachine(const Analyser::Static::MSX::Target &target, const ROMMachine::ROMFetcher &rom_fetcher): + using Target = Analyser::Static::MSX::Target; + + ConcreteMachine(const Target &target, const ROMMachine::ROMFetcher &rom_fetcher): z80_(*this), vdp_(TI::TMS::TMS9918A), i8255_(i8255_port_handler_), @@ -169,19 +171,69 @@ class ConcreteMachine: // Set the AY to 50% of available volume, the toggle to 10% and leave 40% for an SCC. mixer_.set_relative_volumes({0.5f, 0.1f, 0.4f}); - // Fetch the necessary ROMs. + // Install the proper TV standard and select an ideal BIOS name. std::vector rom_names = {"msx.rom"}; + + bool is_ntsc = true; + uint8_t character_generator = 1; /* 0 = Japan, 1 = USA, etc, 2 = USSR */ + uint8_t date_format = 1; /* 0 = Y/M/D, 1 = M/D/Y, 2 = D/M/Y */ + uint8_t keyboard = 1; /* 0 = Japan, 1 = USA, 2 = France, 3 = UK, 4 = Germany, 5 = USSR, 6 = Spain */ + + switch(target.region) { + case Target::Region::Japan: + rom_names.push_back("msx-japanese.rom"); + vdp_.set_tv_standard(TI::TMS::TVStandard::NTSC); + + is_ntsc = true; + character_generator = 0; + date_format = 0; + break; + case Target::Region::USA: + rom_names.push_back("msx-american.rom"); + vdp_.set_tv_standard(TI::TMS::TVStandard::NTSC); + + is_ntsc = true; + character_generator = 1; + date_format = 1; + break; + case Target::Region::Europe: + rom_names.push_back("msx-european.rom"); + vdp_.set_tv_standard(TI::TMS::TVStandard::PAL); + + is_ntsc = false; + character_generator = 1; + date_format = 2; + break; + } + + // Fetch the necessary ROMs; try the region-specific ROM first, + // but failing that fall back on patching the main one. if(target.has_disk_drive) { rom_names.push_back("disk.rom"); } const auto roms = rom_fetcher("MSX", rom_names); - if(!roms[0] || (target.has_disk_drive && !roms[1])) { + if((!roms[0] && !roms[1]) || (target.has_disk_drive && !roms[2])) { throw ROMMachine::Error::MissingROMs; } - memory_slots_[0].source = std::move(*roms[0]); - memory_slots_[0].source.resize(32768); + // Figure out which BIOS to use, either a specific one or the generic + // one appropriately patched. + if(roms[1]) { + memory_slots_[0].source = std::move(*roms[1]); + memory_slots_[0].source.resize(32768); + } else { + memory_slots_[0].source = std::move(*roms[0]); + memory_slots_[0].source.resize(32768); + + + memory_slots_[0].source[0x2b] = uint8_t( + (is_ntsc ? 0x00 : 0x80) | + (date_format << 4) | + character_generator + ); + memory_slots_[0].source[0x2c] = keyboard; + } for(size_t c = 0; c < 8; ++c) { for(size_t slot = 0; slot < 3; ++slot) { diff --git a/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.h b/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.h index b8b5cc4b5..0316568c3 100644 --- a/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.h +++ b/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.h @@ -49,6 +49,12 @@ typedef NS_ENUM(NSInteger, CSMachineVic20Region) { CSMachineVic20RegionJapanese, }; +typedef NS_ENUM(NSInteger, CSMachineMSXRegion) { + CSMachineMSXRegionAmerican, + CSMachineMSXRegionEuropean, + CSMachineMSXRegionJapanese, +}; + typedef int Kilobytes; @interface CSStaticAnalyser : NSObject @@ -57,7 +63,7 @@ typedef int Kilobytes; - (instancetype)initWithElectronDFS:(BOOL)dfs adfs:(BOOL)adfs; - (instancetype)initWithAmstradCPCModel:(CSMachineCPCModel)model; -- (instancetype)initWithMSXHasDiskDrive:(BOOL)hasDiskDrive; +- (instancetype)initWithMSXRegion:(CSMachineMSXRegion)region hasDiskDrive:(BOOL)hasDiskDrive; - (instancetype)initWithOricModel:(CSMachineOricModel)model diskInterface:(CSMachineOricDiskInterface)diskInterface; - (instancetype)initWithVic20Region:(CSMachineVic20Region)region memorySize:(Kilobytes)memorySize hasC1540:(BOOL)hasC1540; - (instancetype)initWithZX80MemorySize:(Kilobytes)memorySize useZX81ROM:(BOOL)useZX81ROM; diff --git a/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm b/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm index cee116c1e..ea8725b9a 100644 --- a/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm +++ b/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm @@ -69,13 +69,18 @@ return self; } -- (instancetype)initWithMSXHasDiskDrive:(BOOL)hasDiskDrive { +- (instancetype)initWithMSXRegion:(CSMachineMSXRegion)region hasDiskDrive:(BOOL)hasDiskDrive { self = [super init]; if(self) { using Target = Analyser::Static::MSX::Target; std::unique_ptr target(new Target); target->machine = Analyser::Machine::MSX; target->has_disk_drive = !!hasDiskDrive; + switch(region) { + case CSMachineMSXRegionAmerican: target->region = Analyser::Static::MSX::Target::Region::USA; break; + case CSMachineMSXRegionEuropean: target->region = Analyser::Static::MSX::Target::Region::Europe; break; + case CSMachineMSXRegionJapanese: target->region = Analyser::Static::MSX::Target::Region::Japan; break; + } _targets.push_back(std::move(target)); } return self; diff --git a/OSBindings/Mac/Clock Signal/MachinePicker/Base.lproj/MachinePicker.xib b/OSBindings/Mac/Clock Signal/MachinePicker/Base.lproj/MachinePicker.xib index 1ab907199..458589a8d 100644 --- a/OSBindings/Mac/Clock Signal/MachinePicker/Base.lproj/MachinePicker.xib +++ b/OSBindings/Mac/Clock Signal/MachinePicker/Base.lproj/MachinePicker.xib @@ -1,8 +1,8 @@ - + - + @@ -14,7 +14,7 @@ - + @@ -84,7 +84,7 @@ Gw - + @@ -99,7 +99,7 @@ Gw - + @@ -134,7 +134,7 @@ Gw - + @@ -199,32 +199,58 @@ Gw - + + + + + + + + + + + + + + + + + + + + + + + - + + + + + - + - + @@ -232,7 +258,7 @@ Gw - + @@ -246,7 +272,7 @@ Gw - + @@ -260,7 +286,7 @@ Gw - + @@ -289,7 +315,7 @@ Gw - + @@ -305,7 +331,7 @@ Gw - + @@ -366,7 +392,7 @@ Gw - + @@ -414,7 +440,7 @@ Gw - + @@ -475,6 +501,7 @@ Gw + diff --git a/OSBindings/Mac/Clock Signal/MachinePicker/MachinePicker.swift b/OSBindings/Mac/Clock Signal/MachinePicker/MachinePicker.swift index 743a05bd2..782cd365c 100644 --- a/OSBindings/Mac/Clock Signal/MachinePicker/MachinePicker.swift +++ b/OSBindings/Mac/Clock Signal/MachinePicker/MachinePicker.swift @@ -23,6 +23,7 @@ class MachinePicker: NSObject { @IBOutlet var cpcModelTypeButton: NSPopUpButton? // MARK: - MSX properties + @IBOutlet var msxRegionButton: NSPopUpButton? @IBOutlet var msxHasDiskDriveButton: NSButton? // MARK: - Oric properties @@ -62,6 +63,7 @@ class MachinePicker: NSObject { cpcModelTypeButton?.selectItem(withTag: standardUserDefaults.integer(forKey: "new.cpcModel")) // MSX settings + msxRegionButton?.selectItem(withTag: standardUserDefaults.integer(forKey: "new.msxRegion")) msxHasDiskDriveButton?.state = standardUserDefaults.bool(forKey: "new.msxDiskDrive") ? .on : .off // Oric settings @@ -99,6 +101,7 @@ class MachinePicker: NSObject { standardUserDefaults.set(cpcModelTypeButton!.selectedTag(), forKey: "new.cpcModel") // MSX settings + standardUserDefaults.set(msxRegionButton!.selectedTag(), forKey: "new.msxRegion") standardUserDefaults.set(msxHasDiskDriveButton?.state == .on, forKey: "new.msxDiskDrive") // Oric settings @@ -155,7 +158,16 @@ class MachinePicker: NSObject { } case "msx": - return CSStaticAnalyser(msxHasDiskDrive: msxHasDiskDriveButton!.state == .on) + let hasDiskDrive = msxHasDiskDriveButton!.state == .on + switch msxRegionButton!.selectedItem?.tag { + case 2: + return CSStaticAnalyser(msxRegion: .japanese, hasDiskDrive: hasDiskDrive) + case 1: + return CSStaticAnalyser(msxRegion: .american, hasDiskDrive: hasDiskDrive) + case 0: fallthrough + default: + return CSStaticAnalyser(msxRegion: .european, hasDiskDrive: hasDiskDrive) + } case "oric": var diskInterface: CSMachineOricDiskInterface = .none diff --git a/ROMImages/MSX/readme.txt b/ROMImages/MSX/readme.txt index bee52679d..f97c4d102 100644 --- a/ROMImages/MSX/readme.txt +++ b/ROMImages/MSX/readme.txt @@ -1,8 +1,16 @@ ROMs for the MSX go here; the copyright status of these is uncertain so they have not been included in this repository. -Expected files: +Minimum 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 +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. + +You may also provide one or more of: + +msx-japanese.rom +msx-european.com +msx-american.rom + +If one of these files exists, it will be used as the BIOS for machines of that region. If the file does not exist, the emulator will load msx.rom and alter its region identification bytes appropriately. \ No newline at end of file