2019-03-10 22:40:12 +00:00
|
|
|
//
|
|
|
|
// EmuTOSTests.m
|
|
|
|
// Clock SignalTests
|
|
|
|
//
|
|
|
|
// Created by Thomas Harte on 10/03/2019.
|
|
|
|
// Copyright © 2019 Thomas Harte. All rights reserved.
|
|
|
|
//
|
|
|
|
|
|
|
|
#import <XCTest/XCTest.h>
|
|
|
|
|
2019-03-12 02:47:37 +00:00
|
|
|
#include <array>
|
2019-03-10 22:40:12 +00:00
|
|
|
#include <cassert>
|
2019-03-12 02:47:37 +00:00
|
|
|
|
2019-05-28 19:17:03 +00:00
|
|
|
//#define LOG_TRACE
|
2019-05-28 19:05:42 +00:00
|
|
|
|
2019-03-10 22:40:12 +00:00
|
|
|
#include "68000.hpp"
|
2019-04-29 20:55:21 +00:00
|
|
|
#include "Comparative68000.hpp"
|
2019-03-10 22:40:12 +00:00
|
|
|
#include "CSROMFetcher.hpp"
|
|
|
|
|
2019-04-29 20:55:21 +00:00
|
|
|
class EmuTOS: public ComparativeBusHandler {
|
2019-03-10 22:40:12 +00:00
|
|
|
public:
|
2019-04-29 20:55:21 +00:00
|
|
|
EmuTOS(const std::vector<uint8_t> &emuTOS, const char *trace_name) : ComparativeBusHandler(trace_name), m68000_(*this) {
|
2019-03-10 22:40:12 +00:00
|
|
|
assert(!(emuTOS.size() & 1));
|
|
|
|
emuTOS_.resize(emuTOS.size() / 2);
|
|
|
|
|
|
|
|
for(size_t c = 0; c < emuTOS_.size(); ++c) {
|
|
|
|
emuTOS_[c] = (emuTOS[c << 1] << 8) | emuTOS[(c << 1) + 1];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void run_for(HalfCycles cycles) {
|
|
|
|
m68000_.run_for(cycles);
|
|
|
|
}
|
|
|
|
|
2020-01-24 03:57:51 +00:00
|
|
|
CPU::MC68000::ProcessorState get_state() final {
|
2019-04-29 20:55:21 +00:00
|
|
|
return m68000_.get_state();
|
|
|
|
}
|
|
|
|
|
2019-03-10 22:40:12 +00:00
|
|
|
HalfCycles perform_bus_operation(const CPU::MC68000::Microcycle &cycle, int is_supervisor) {
|
2019-04-07 00:00:34 +00:00
|
|
|
const uint32_t address = cycle.word_address();
|
|
|
|
uint32_t word_address = address;
|
2019-03-12 02:47:37 +00:00
|
|
|
|
|
|
|
// As much about the Atari ST's memory map as is relevant here: the ROM begins
|
|
|
|
// at 0xfc0000, and the first eight bytes are mirrored to the first four memory
|
|
|
|
// addresses in order for /RESET to work properly. RAM otherwise fills the first
|
|
|
|
// 512kb of the address space. Trying to write to ROM raises a bus error.
|
|
|
|
|
2019-04-29 21:27:44 +00:00
|
|
|
const bool is_rom = (word_address >= (0xfc0000 >> 1) && word_address < (0xff0000 >> 1)) || word_address < 4;
|
|
|
|
const bool is_ram = word_address < ram_.size();
|
|
|
|
const bool is_peripheral = !is_rom && !is_ram;
|
|
|
|
|
2019-03-12 02:47:37 +00:00
|
|
|
uint16_t *const base = is_rom ? emuTOS_.data() : ram_.data();
|
|
|
|
if(is_rom) {
|
2019-04-29 20:55:21 +00:00
|
|
|
word_address %= emuTOS_.size();
|
2019-03-12 02:47:37 +00:00
|
|
|
} else {
|
2019-04-29 20:55:21 +00:00
|
|
|
word_address %= ram_.size();
|
2019-03-12 02:47:37 +00:00
|
|
|
}
|
|
|
|
|
2019-03-16 21:54:58 +00:00
|
|
|
using Microcycle = CPU::MC68000::Microcycle;
|
|
|
|
if(cycle.data_select_active()) {
|
2019-04-07 00:00:34 +00:00
|
|
|
uint16_t peripheral_result = 0xffff;
|
|
|
|
if(is_peripheral) {
|
|
|
|
switch(address & 0x7ff) {
|
|
|
|
// A hard-coded value for TIMER B.
|
|
|
|
case (0xa21 >> 1):
|
|
|
|
peripheral_result = 0x00000001;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-16 21:54:58 +00:00
|
|
|
switch(cycle.operation & (Microcycle::SelectWord | Microcycle::SelectByte | Microcycle::Read)) {
|
|
|
|
default: break;
|
|
|
|
|
|
|
|
case Microcycle::SelectWord | Microcycle::Read:
|
2019-04-07 00:00:34 +00:00
|
|
|
cycle.value->full = is_peripheral ? peripheral_result : base[word_address];
|
2019-03-16 21:54:58 +00:00
|
|
|
break;
|
|
|
|
case Microcycle::SelectByte | Microcycle::Read:
|
2019-04-07 00:00:34 +00:00
|
|
|
cycle.value->halves.low = (is_peripheral ? peripheral_result : base[word_address]) >> cycle.byte_shift();
|
2019-03-16 21:54:58 +00:00
|
|
|
break;
|
|
|
|
case Microcycle::SelectWord:
|
|
|
|
base[word_address] = cycle.value->full;
|
|
|
|
break;
|
|
|
|
case Microcycle::SelectByte:
|
2019-04-17 20:39:10 +00:00
|
|
|
base[word_address] = (cycle.value->halves.low << cycle.byte_shift()) | (base[word_address] & (0xffff ^ cycle.byte_mask()));
|
2019-03-16 21:54:58 +00:00
|
|
|
break;
|
|
|
|
}
|
2019-03-10 22:40:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return HalfCycles(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2019-04-29 20:55:21 +00:00
|
|
|
CPU::MC68000::Processor<EmuTOS, true, true> m68000_;
|
2019-03-10 22:40:12 +00:00
|
|
|
|
|
|
|
std::vector<uint16_t> emuTOS_;
|
2019-03-12 02:47:37 +00:00
|
|
|
std::array<uint16_t, 256*1024> ram_;
|
2019-03-10 22:40:12 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
@interface EmuTOSTests : XCTestCase
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation EmuTOSTests {
|
|
|
|
std::unique_ptr<EmuTOS> _machine;
|
|
|
|
}
|
|
|
|
|
2019-04-29 22:04:57 +00:00
|
|
|
- (void)testImage:(NSString *)image trace:(NSString *)trace length:(int)length {
|
2019-07-24 16:01:30 +00:00
|
|
|
const std::vector<ROMMachine::ROM> rom_names = {{"AtariST", "", image.UTF8String, 0, 0 }};
|
2020-05-30 04:37:06 +00:00
|
|
|
const auto roms = CSROMFetcher()(rom_names);
|
2019-04-29 22:04:57 +00:00
|
|
|
NSString *const traceLocation = [[NSBundle bundleForClass:[self class]] pathForResource:trace ofType:@"trace.txt.gz"];
|
2020-05-30 04:37:06 +00:00
|
|
|
_machine = std::make_unique<EmuTOS>(*roms[0], traceLocation.UTF8String);
|
|
|
|
_machine->run_for(HalfCycles(length));
|
2019-04-29 22:04:57 +00:00
|
|
|
}
|
2019-04-29 21:56:49 +00:00
|
|
|
|
2019-04-29 22:04:57 +00:00
|
|
|
- (void)testEmuTOSStartup {
|
2019-04-30 02:08:37 +00:00
|
|
|
[self testImage:@"etos192uk.img" trace:@"etos192uk" length:313490];
|
2020-05-30 04:37:06 +00:00
|
|
|
// TODO: assert that machine is now STOPped.
|
2019-03-10 22:40:12 +00:00
|
|
|
}
|
|
|
|
|
2019-04-29 22:04:57 +00:00
|
|
|
- (void)testTOSStartup {
|
|
|
|
[self testImage:@"tos100.img" trace:@"tos100" length:54011091];
|
|
|
|
}
|
|
|
|
|
2019-03-10 22:40:12 +00:00
|
|
|
@end
|