mirror of
https://github.com/TomHarte/CLK.git
synced 2024-12-27 01:31:42 +00:00
Introduces [over-]analysis of cassette contents prior to starting the MSX, and simplifies ROM checking.
So a proper loading command is now known.
This commit is contained in:
parent
98a9d57c0b
commit
2cc1a2684a
@ -8,13 +8,17 @@
|
||||
|
||||
#include "StaticAnalyser.hpp"
|
||||
|
||||
#include "Tape.hpp"
|
||||
|
||||
/*
|
||||
DEFB "AB" ; expansion ROM header
|
||||
DEFW initcode ; start of the init code, 0 if no initcode
|
||||
DEFW callstat; pointer to CALL statement handler, 0 if no such handler
|
||||
DEFW device; pointer to expansion device handler, 0 if no such handler
|
||||
DEFW basic ; pointer to the start of a tokenized basicprogram, 0 if no basicprogram
|
||||
DEFS 6,0 ; room reserved for future extensions
|
||||
Expected standard cartridge format:
|
||||
|
||||
DEFB "AB" ; expansion ROM header
|
||||
DEFW initcode ; start of the init code, 0 if no initcode
|
||||
DEFW callstat; pointer to CALL statement handler, 0 if no such handler
|
||||
DEFW device; pointer to expansion device handler, 0 if no such handler
|
||||
DEFW basic ; pointer to the start of a tokenized basicprogram, 0 if no basicprogram
|
||||
DEFS 6,0 ; room reserved for future extensions
|
||||
*/
|
||||
static std::list<std::shared_ptr<Storage::Cartridge::Cartridge>>
|
||||
MSXCartridgesFrom(const std::list<std::shared_ptr<Storage::Cartridge::Cartridge>> &cartridges) {
|
||||
@ -29,31 +33,17 @@ static std::list<std::shared_ptr<Storage::Cartridge::Cartridge>>
|
||||
// Which must be a multiple of 16 kb in size.
|
||||
Storage::Cartridge::Cartridge::Segment segment = segments.front();
|
||||
const size_t data_size = segment.data.size();
|
||||
if(data_size < 0x4000 || data_size & 0x3fff) continue;
|
||||
if(data_size < 0x2000 || data_size & 0x3fff) continue;
|
||||
|
||||
// Check for a ROM header at address 0; TODO: if it's not found then try 0x4000
|
||||
// and consider swapping the image.
|
||||
|
||||
// Check for the expansion ROM header and the reserved bytes.
|
||||
if(segment.data[0] != 0x41 || segment.data[1] != 0x42) continue;
|
||||
bool all_zeroes = true;
|
||||
for(size_t c = 0; c < 6; ++c) {
|
||||
if(segment.data[10 + c] != 0) all_zeroes = false;
|
||||
}
|
||||
if(!all_zeroes) continue;
|
||||
|
||||
// Pick a paging address based on the four pointers.
|
||||
uint16_t start_address = 0xc000;
|
||||
for(size_t c = 0; c < 8; c += 2) {
|
||||
uint16_t code_pointer = static_cast<uint16_t>(segment.data[2 + c] | segment.data[3 + c] << 8);
|
||||
if(code_pointer) {
|
||||
start_address = std::min(static_cast<uint16_t>(code_pointer &~ 0x3fff), start_address);
|
||||
}
|
||||
}
|
||||
|
||||
// That'll do then, but apply the detected start address.
|
||||
// Apply the standard MSX start address.
|
||||
msx_cartridges.emplace_back(new Storage::Cartridge::Cartridge({
|
||||
Storage::Cartridge::Cartridge::Segment(start_address, segment.data)
|
||||
Storage::Cartridge::Cartridge::Segment(0x4000, segment.data)
|
||||
}));
|
||||
}
|
||||
|
||||
@ -63,10 +53,22 @@ static std::list<std::shared_ptr<Storage::Cartridge::Cartridge>>
|
||||
void StaticAnalyser::MSX::AddTargets(const Media &media, std::list<Target> &destination) {
|
||||
Target target;
|
||||
|
||||
// Obtain only those cartridges which it looks like an MSX would understand.
|
||||
target.media.cartridges = MSXCartridgesFrom(media.cartridges);
|
||||
|
||||
// TODO: tape parsing. Be dumb for now.
|
||||
target.media.tapes = media.tapes;
|
||||
// Check tapes for loadable files.
|
||||
for(const auto &tape : media.tapes) {
|
||||
std::vector<File> files_on_tape = GetFiles(tape);
|
||||
if(!files_on_tape.empty()) {
|
||||
switch(files_on_tape.front().type) {
|
||||
case File::Type::ASCII: target.loadingCommand = "RUN\"CAS:\n"; break;
|
||||
case File::Type::TokenisedBASIC: target.loadingCommand = "CLOAD\nRUN\n"; break;
|
||||
case File::Type::Binary: target.loadingCommand = "BLOAD\"CAS:\",R\n"; break;
|
||||
default: break;
|
||||
}
|
||||
target.media.tapes.push_back(tape);
|
||||
}
|
||||
}
|
||||
|
||||
if(!target.media.empty()) {
|
||||
target.machine = Target::MSX;
|
||||
|
163
StaticAnalyser/MSX/Tape.cpp
Normal file
163
StaticAnalyser/MSX/Tape.cpp
Normal file
@ -0,0 +1,163 @@
|
||||
//
|
||||
// Tape.cpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 25/12/2017.
|
||||
// Copyright © 2017 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "Tape.hpp"
|
||||
|
||||
#include "../../Storage/Tape/Parsers/MSX.hpp"
|
||||
|
||||
using namespace StaticAnalyser::MSX;
|
||||
|
||||
File::File(File &&rhs) :
|
||||
name(std::move(rhs.name)),
|
||||
type(rhs.type),
|
||||
data(std::move(rhs.data)),
|
||||
starting_address(rhs.starting_address),
|
||||
entry_address(rhs.entry_address) {}
|
||||
|
||||
File::File() :
|
||||
type(Type::Binary),
|
||||
starting_address(0),
|
||||
entry_address(0) {} // For the sake of initialising in a defined state.
|
||||
|
||||
std::vector<File> StaticAnalyser::MSX::GetFiles(const std::shared_ptr<Storage::Tape::Tape> &tape) {
|
||||
std::vector<File> files;
|
||||
|
||||
Storage::Tape::BinaryTapePlayer tape_player(1000000);
|
||||
tape_player.set_motor_control(true);
|
||||
tape_player.set_tape(tape);
|
||||
|
||||
using Parser = Storage::Tape::MSX::Parser;
|
||||
|
||||
// Get all recognisable files from the tape.
|
||||
while(!tape->is_at_end()) {
|
||||
// Try to locate and measure a header.
|
||||
std::unique_ptr<Parser::FileSpeed> file_speed = Parser::find_header(tape_player);
|
||||
if(!file_speed) continue;
|
||||
|
||||
// Check whether what follows is a recognisable file type.
|
||||
uint8_t header[10] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
|
||||
for(std::size_t c = 0; c < sizeof(header); ++c) {
|
||||
int next_byte = Parser::get_byte(*file_speed, tape_player);
|
||||
if(next_byte == -1) break;
|
||||
header[c] = static_cast<uint8_t>(next_byte);
|
||||
}
|
||||
|
||||
bool bytes_are_same = true;
|
||||
for(std::size_t c = 1; c < sizeof(header); ++c)
|
||||
bytes_are_same &= (header[c] == header[0]);
|
||||
|
||||
if(!bytes_are_same) continue;
|
||||
if(header[0] != 0xd0 && header[0] != 0xd3 && header[0] != 0xea) continue;
|
||||
|
||||
File file;
|
||||
|
||||
// Determine file type from information already collected.
|
||||
switch(header[0]) {
|
||||
case 0xd0: file.type = File::Type::Binary; break;
|
||||
case 0xd3: file.type = File::Type::TokenisedBASIC; break;
|
||||
case 0xea: file.type = File::Type::ASCII; break;
|
||||
default: break; // Unreachable.
|
||||
}
|
||||
|
||||
// Read file name.
|
||||
char name[7];
|
||||
for(std::size_t c = 1; c < 6; ++c)
|
||||
name[c] = static_cast<char>(Parser::get_byte(*file_speed, tape_player));
|
||||
name[6] = '\0';
|
||||
file.name = name;
|
||||
|
||||
// ASCII: Read 256-byte segments until one ends with an end-of-file character.
|
||||
if(file.type == File::Type::ASCII) {
|
||||
while(true) {
|
||||
file_speed = Parser::find_header(tape_player);
|
||||
if(!file_speed) break;
|
||||
int c = 256;
|
||||
while(c--) {
|
||||
int byte = Parser::get_byte(*file_speed, tape_player);
|
||||
if(byte == -1) break;
|
||||
file.data.push_back(static_cast<uint8_t>(byte));
|
||||
}
|
||||
if(c != -1) break;
|
||||
if(file.data.back() == 0x1a) {
|
||||
files.push_back(std::move(file));
|
||||
break;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Read a single additional segment, using the information at the begging to determine length.
|
||||
file_speed = Parser::find_header(tape_player);
|
||||
if(!file_speed) continue;
|
||||
|
||||
// Binary: read start address, end address, entry address, then that many bytes.
|
||||
if(file.type == File::Type::Binary) {
|
||||
uint8_t locations[6];
|
||||
uint16_t end_address;
|
||||
std::size_t c;
|
||||
for(c = 0; c < sizeof(locations); ++c) {
|
||||
int byte = Parser::get_byte(*file_speed, tape_player);
|
||||
if(byte == -1) break;
|
||||
locations[c] = static_cast<uint8_t>(byte);
|
||||
}
|
||||
if(c != sizeof(locations)) continue;
|
||||
|
||||
file.starting_address = static_cast<uint16_t>(locations[0] | (locations[1] << 8));
|
||||
end_address = static_cast<uint16_t>(locations[2] | (locations[3] << 8));
|
||||
file.entry_address = static_cast<uint16_t>(locations[4] | (locations[5] << 8));
|
||||
|
||||
if(end_address < file.starting_address) continue;
|
||||
|
||||
std::size_t length = end_address - file.starting_address;
|
||||
while(length--) {
|
||||
int byte = Parser::get_byte(*file_speed, tape_player);
|
||||
if(byte == -1) continue;
|
||||
file.data.push_back(static_cast<uint8_t>(byte));
|
||||
}
|
||||
|
||||
files.push_back(std::move(file));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Tokenised BASIC, then: keep following 'next line' links from a hypothetical start of
|
||||
// 0x8001, until finding the final line.
|
||||
uint16_t current_address = 0x8001;
|
||||
while(current_address) {
|
||||
int next_address_buffer[2];
|
||||
next_address_buffer[0] = Parser::get_byte(*file_speed, tape_player);
|
||||
next_address_buffer[1] = Parser::get_byte(*file_speed, tape_player);
|
||||
|
||||
if(next_address_buffer[0] == -1 || next_address_buffer[1] == -1) break;
|
||||
file.data.push_back(static_cast<uint8_t>(next_address_buffer[0]));
|
||||
file.data.push_back(static_cast<uint8_t>(next_address_buffer[1]));
|
||||
|
||||
uint16_t next_address = static_cast<uint16_t>(next_address_buffer[0] | (next_address_buffer[1] << 8));
|
||||
if(!next_address) {
|
||||
files.push_back(std::move(file));
|
||||
break;
|
||||
}
|
||||
if(next_address < current_address+2) break;
|
||||
|
||||
// This line makes sense, so push it all in.
|
||||
std::size_t length = next_address - current_address - 2;
|
||||
current_address = next_address;
|
||||
bool found_error = false;
|
||||
while(length--) {
|
||||
int byte = Parser::get_byte(*file_speed, tape_player);
|
||||
if(byte == -1) {
|
||||
found_error = true;
|
||||
break;
|
||||
}
|
||||
file.data.push_back(static_cast<uint8_t>(byte));
|
||||
}
|
||||
if(found_error) break;
|
||||
}
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
42
StaticAnalyser/MSX/Tape.hpp
Normal file
42
StaticAnalyser/MSX/Tape.hpp
Normal file
@ -0,0 +1,42 @@
|
||||
//
|
||||
// Tape.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 25/12/2017.
|
||||
// Copyright © 2017 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef StaticAnalyser_MSX_Tape_hpp
|
||||
#define StaticAnalyser_MSX_Tape_hpp
|
||||
|
||||
#include "../../Storage/Tape/Tape.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace StaticAnalyser {
|
||||
namespace MSX {
|
||||
|
||||
struct File {
|
||||
std::string name;
|
||||
enum Type {
|
||||
Binary,
|
||||
TokenisedBASIC,
|
||||
ASCII
|
||||
} type;
|
||||
|
||||
std::vector<uint8_t> data;
|
||||
|
||||
uint16_t starting_address; // Provided only for Type::Binary files.
|
||||
uint16_t entry_address; // Provided only for Type::Binary files.
|
||||
|
||||
File(File &&rhs);
|
||||
File();
|
||||
};
|
||||
|
||||
std::vector<File> GetFiles(const std::shared_ptr<Storage::Tape::Tape> &tape);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* StaticAnalyser_MSX_Tape_hpp */
|
Loading…
Reference in New Issue
Block a user