1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-11-10 08:04:45 +00:00
CLK/StaticAnalyser/Acorn/StaticAnalyser.cpp

126 lines
4.0 KiB
C++

//
// AcornAnalyser.cpp
// Clock Signal
//
// Created by Thomas Harte on 29/08/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
//
#include "StaticAnalyser.hpp"
#include "Disk.hpp"
#include "Tape.hpp"
using namespace StaticAnalyser::Acorn;
static std::list<std::shared_ptr<Storage::Cartridge::Cartridge>>
AcornCartridgesFrom(const std::list<std::shared_ptr<Storage::Cartridge::Cartridge>> &cartridges) {
std::list<std::shared_ptr<Storage::Cartridge::Cartridge>> acorn_cartridges;
for(const auto &cartridge : cartridges) {
const auto &segments = cartridge->get_segments();
// only one mapped item is allowed
if(segments.size() != 1) continue;
// which must be 16 kb in size
Storage::Cartridge::Cartridge::Segment segment = segments.front();
if(segment.data.size() != 0x4000) continue;
// is a copyright string present?
uint8_t copyright_offset = segment.data[7];
if(
segment.data[copyright_offset] != 0x00 ||
segment.data[copyright_offset+1] != 0x28 ||
segment.data[copyright_offset+2] != 0x43 ||
segment.data[copyright_offset+3] != 0x29
) continue;
// is the language entry point valid?
if(!(
(segment.data[0] == 0x00 && segment.data[1] == 0x00 && segment.data[2] == 0x00) ||
(segment.data[0] != 0x00 && segment.data[2] >= 0x80 && segment.data[2] < 0xc0)
)) continue;
// is the service entry point valid?
if(!(segment.data[5] >= 0x80 && segment.data[5] < 0xc0)) continue;
// probability of a random binary blob that isn't an Acorn ROM proceeding to here:
// 1/(2^32) *
// ( ((2^24)-1)/(2^24)*(1/4) + 1/(2^24) ) *
// 1/4
// = something very improbable — around 1/16th of 1 in 2^32, but not exactly.
acorn_cartridges.push_back(cartridge);
}
return acorn_cartridges;
}
void StaticAnalyser::Acorn::AddTargets(const Media &media, std::list<Target> &destination) {
Target target;
target.machine = Target::Electron;
target.probability = 1.0; // TODO: a proper estimation
target.acorn.has_dfs = false;
target.acorn.has_adfs = false;
target.acorn.should_shift_restart = false;
// strip out inappropriate cartridges
target.media.cartridges = AcornCartridgesFrom(media.cartridges);
// if there are any tapes, attempt to get data from the first
if(media.tapes.size() > 0) {
std::shared_ptr<Storage::Tape::Tape> tape = media.tapes.front();
std::list<File> files = GetFiles(tape);
tape->reset();
// continue if there are any files
if(files.size()) {
bool is_basic = true;
// protected files are always for *RUNning only
if(files.front().is_protected) is_basic = false;
// check also for a continuous threading of BASIC lines; if none then this probably isn't BASIC code,
// so that's also justification to *RUN
std::size_t pointer = 0;
uint8_t *data = &files.front().data[0];
std::size_t data_size = files.front().data.size();
while(1) {
if(pointer >= data_size-1 || data[pointer] != 13) {
is_basic = false;
break;
}
if((data[pointer+1]&0x7f) == 0x7f) break;
pointer += data[pointer+3];
}
// Inspect first file. If it's protected or doesn't look like BASIC
// then the loading command is *RUN. Otherwise it's CHAIN"".
target.loadingCommand = is_basic ? "CHAIN\"\"\n" : "*RUN\n";
target.media.tapes = media.tapes;
}
}
if(media.disks.size() > 0) {
std::shared_ptr<Storage::Disk::Disk> disk = media.disks.front();
std::unique_ptr<Catalogue> dfs_catalogue, adfs_catalogue;
dfs_catalogue = GetDFSCatalogue(disk);
if(dfs_catalogue == nullptr) adfs_catalogue = GetADFSCatalogue(disk);
if(dfs_catalogue || adfs_catalogue) {
target.media.disks = media.disks;
target.acorn.has_dfs = !!dfs_catalogue;
target.acorn.has_adfs = !!adfs_catalogue;
Catalogue::BootOption bootOption = (dfs_catalogue ?: adfs_catalogue)->bootOption;
if(bootOption != Catalogue::BootOption::None)
target.acorn.should_shift_restart = true;
else
target.loadingCommand = "*CAT\n";
}
}
if(target.media.tapes.size() || target.media.disks.size() || target.media.cartridges.size())
destination.push_back(target);
}