1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-12-01 11:49:58 +00:00

Started importing unit tests aplenty for the 6502.

This commit is contained in:
Thomas Harte 2015-07-16 20:15:30 -04:00
parent 6558ae1425
commit 65d60f065d
273 changed files with 1670 additions and 22 deletions

Binary file not shown.

View File

@ -0,0 +1,5 @@
//
// Use this file to import your target's public headers that you would like to expose to Swift.
//
#import "TestMachine.h"

View File

@ -9,27 +9,389 @@
import XCTest import XCTest
@testable import Clock_Signal @testable import Clock_Signal
class Clock_SignalTests: XCTestCase { class Clock_SignalTests: XCTestCase, CSTestMachineJamHandler {
override func setUp() { func testAllSuiteA() {
super.setUp() if let filename = NSBundle(forClass: self.dynamicType).pathForResource("AllSuiteA", ofType: "bin") {
// Put setup code here. This method is called before the invocation of each test method in the class. if let allSuiteA = NSData(contentsOfFile: filename) {
let machine = CSTestMachine()
machine.jamHandler = self
machine.setData(allSuiteA, atAddress: 0x4000)
machine.setValue(CSTestMachineJamOpcode, forAddress:0x45c0); // end
machine.setValue(0x4000, forRegister: CSTestMachineRegister.ProgramCounter)
while !machine.isJammed {
machine.runForNumberOfCycles(1000)
} }
override func tearDown() { if machine.valueForAddress(0x0210) != 0xff {
// Put teardown code here. This method is called after the invocation of each test method in the class. NSException(name: "Failed AllSuiteA", reason: "Failed test \(machine.valueForAddress(0x0210))", userInfo: nil).raise()
super.tearDown() }
}
}
} }
func testExample() { func testKlausDormann() {
// This is an example of a functional test case.
// Use XCTAssert and related functions to verify your tests produce the correct results. func errorForTrapAddress(address: UInt16) -> String? {
let hexAddress = String(format:"%04x", address)
switch address {
case 0x3399: return nil // success!
case 0x33a7: return "Decimal ADC result has wrong value"
case 0x3502: return "Binary SBC result has wrong value"
case 0x33b9: return "Decimal SBC result has wrong value"
case 0x33c0: return "Decimal SBC wrong carry flag"
case 0x36d1: return "BRK: unexpected BRK or IRQ"
case 0x36ac, 0x36f6: return "Improper JSR return address on stack"
case 0x36e5: return "BRK flag not set on stack"
case 0x26d2: return "ASL zpg,x produced incorrect flags"
default: return "Unknown error at \(hexAddress)"
}
} }
func testPerformanceExample() { if let filename = NSBundle(forClass: self.dynamicType).pathForResource("6502_functional_test", ofType: "bin") {
// This is an example of a performance test case. if let functionalTest = NSData(contentsOfFile: filename) {
self.measureBlock { let machine = CSTestMachine()
// Put the code you want to measure the time of here.
machine.setData(functionalTest, atAddress: 0)
machine.setValue(0x400, forRegister: CSTestMachineRegister.ProgramCounter)
while true {
let oldPC = machine.valueForRegister(CSTestMachineRegister.LastOperationAddress)
machine.runForNumberOfCycles(1000)
let newPC = machine.valueForRegister(CSTestMachineRegister.LastOperationAddress)
if newPC == oldPC {
let error = errorForTrapAddress(oldPC)
if let error = error {
NSException(name: "Failed test", reason: error, userInfo: nil).raise()
} else {
return
}
}
}
}
}
}
func testWolfgangLorenzStart() {
self.runWolfgangLorenzTest(" start")
}
func testWolfgangLorenzLDA() {
self.runWolfgangLorenzTest("lda", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"])
}
func testWolfgangLorenzSTA() {
self.runWolfgangLorenzTest("sta", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"])
}
func testWolfgangLorenzLDX() {
self.runWolfgangLorenzTest("ldx", suffixes: ["b", "z", "zy", "a", "ay"])
}
func testWolfgangLorenzSTX() {
self.runWolfgangLorenzTest("stx", suffixes: ["z", "zy", "a"])
}
func testWolfgangLorenzLDY() {
self.runWolfgangLorenzTest("ldy", suffixes: ["b", "z", "zx", "a", "ax"])
}
func testWolfgangLorenzSTY() {
self.runWolfgangLorenzTest("sty", suffixes: ["z", "zx", "a"])
}
func testWolfgangLorenzTransfers() {
self.runWolfgangLorenzTest("taxn")
self.runWolfgangLorenzTest("tayn")
self.runWolfgangLorenzTest("txan")
self.runWolfgangLorenzTest("tyan")
self.runWolfgangLorenzTest("tsxn")
self.runWolfgangLorenzTest("txsn")
}
func testWolfgangLorenzStack() {
self.runWolfgangLorenzTest("phan")
self.runWolfgangLorenzTest("plan")
self.runWolfgangLorenzTest("phpn")
self.runWolfgangLorenzTest("plpn")
}
func testWolfgangLorenzIncsAndDecs() {
self.runWolfgangLorenzTest("inxn")
self.runWolfgangLorenzTest("inyn")
self.runWolfgangLorenzTest("dexn")
self.runWolfgangLorenzTest("deyn")
self.runWolfgangLorenzTest("incz")
self.runWolfgangLorenzTest("inczx")
self.runWolfgangLorenzTest("inca")
self.runWolfgangLorenzTest("incax")
self.runWolfgangLorenzTest("decz")
self.runWolfgangLorenzTest("deczx")
self.runWolfgangLorenzTest("deca")
self.runWolfgangLorenzTest("decax")
}
func testWolfgangLorenzASL() {
self.runWolfgangLorenzTest("asl", suffixes: ["n", "z", "zx", "a", "ax"])
}
func testWolfgangLorenzLSR() {
self.runWolfgangLorenzTest("lsr", suffixes: ["n", "z", "zx", "a", "ax"])
}
func testWolfgangLorenzROL() {
self.runWolfgangLorenzTest("rol", suffixes: ["n", "z", "zx", "a", "ax"])
}
func testWolfgangLorenzROR() {
self.runWolfgangLorenzTest("ror", suffixes: ["n", "z", "zx", "a", "ax"])
}
func testWolfgangLorenzAND() {
self.runWolfgangLorenzTest("and", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"])
}
func testWolfgangLorenzORA() {
self.runWolfgangLorenzTest("ora", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"])
}
func testWolfgangLorenzEOR() {
self.runWolfgangLorenzTest("eor", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"])
}
func testWolfgangLorenzFlagManipulation() {
self.runWolfgangLorenzTest("clcn")
self.runWolfgangLorenzTest("secn")
self.runWolfgangLorenzTest("cldn")
self.runWolfgangLorenzTest("sedn")
self.runWolfgangLorenzTest("clin")
self.runWolfgangLorenzTest("sein")
self.runWolfgangLorenzTest("clvn")
}
func testWolfgangLorenzADC() {
self.runWolfgangLorenzTest("adc", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"])
}
func testWolfgangLorenzSBC() {
self.runWolfgangLorenzTest("sbc", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"])
}
func testWolfgangLorenzCompare() {
self.runWolfgangLorenzTest("cmp", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"])
self.runWolfgangLorenzTest("cpx", suffixes: ["b", "z", "a"])
self.runWolfgangLorenzTest("cpy", suffixes: ["b", "z", "a"])
}
func testWolfgangLorenzBIT() {
self.runWolfgangLorenzTest("bit", suffixes: ["z", "a"])
}
func testWolfgangLorenzFlow() {
self.runWolfgangLorenzTest("brkn")
self.runWolfgangLorenzTest("rtin")
self.runWolfgangLorenzTest("jsrw")
self.runWolfgangLorenzTest("rtsn")
self.runWolfgangLorenzTest("jmpw")
self.runWolfgangLorenzTest("jmpi")
}
func testWolfgangLorenzBranch() {
self.runWolfgangLorenzTest("beqr")
self.runWolfgangLorenzTest("bner")
self.runWolfgangLorenzTest("bmir")
self.runWolfgangLorenzTest("bplr")
self.runWolfgangLorenzTest("bcsr")
self.runWolfgangLorenzTest("bccr")
self.runWolfgangLorenzTest("bvsr")
self.runWolfgangLorenzTest("bvcr")
}
func testWolfgangLorenzNOP() {
self.runWolfgangLorenzTest("nop", suffixes: ["n", "b", "z", "zx", "a", "ax"])
}
func testWolfgangLorenzASO() {
self.runWolfgangLorenzTest("aso", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"])
}
func testWolfgangLorenzRLA() {
self.runWolfgangLorenzTest("rla", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"])
}
func testWolfgangLorenzLSE() {
self.runWolfgangLorenzTest("lse", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"])
}
func testWolfgangLorenzRRA() {
self.runWolfgangLorenzTest("rra", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"])
}
func testWolfgangLorenzDCM() {
self.runWolfgangLorenzTest("dcm", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"])
}
func testWolfgangLorenzINS() {
self.runWolfgangLorenzTest("ins", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"])
}
func testWolfgangLorenzLAX() {
self.runWolfgangLorenzTest("lax", suffixes: ["z", "zy", "a", "ay", "ix", "iy"])
}
func testWolfgangLorenzAXS() {
self.runWolfgangLorenzTest("axs", suffixes: ["z", "zy", "a", "ix"])
}
func testWolfgangLorenzALR() {
self.runWolfgangLorenzTest("alrb")
}
func testWolfgangLorenzARR() {
self.runWolfgangLorenzTest("arrb")
}
func testWolfgangLorenzSBX() {
self.runWolfgangLorenzTest("sbxb")
}
func testWolfgangLorenzSHA() {
self.runWolfgangLorenzTest("sha", suffixes: ["ay", "iy"])
}
func testWolfgangLorenzSHX() {
self.runWolfgangLorenzTest("shxay")
}
func testWolfgangLorenzSHY() {
self.runWolfgangLorenzTest("shyax")
}
func testWolfgangLorenzSHS() {
self.runWolfgangLorenzTest("shsay")
}
func testWolfgangLorenzLXA() {
self.runWolfgangLorenzTest("lxab")
}
func testWolfgangLorenzANE() {
self.runWolfgangLorenzTest("aneb")
}
func testWolfgangLorenzANC() {
self.runWolfgangLorenzTest("ancb")
}
func testWolfgangLorenzLAS() {
self.runWolfgangLorenzTest("lasay")
}
func testWolfgangLorenzSBCB() {
self.runWolfgangLorenzTest("sbcb(eb)")
}
private func runWolfgangLorenzTest(name: String, suffixes: [String]) {
for suffix in suffixes {
let testName = name + suffix
self.runWolfgangLorenzTest(testName)
}
}
private var output: String = ""
private func runWolfgangLorenzTest(name: String) {
var machine: CSTestMachine!
if let filename = NSBundle(forClass: self.dynamicType).pathForResource(name, ofType: nil) {
if let testData = NSData(contentsOfFile: filename) {
machine = CSTestMachine()
machine.jamHandler = self
// machine.logActivity = true
output = ""
let dataPointer = UnsafePointer<UInt8>(testData.bytes)
let loadAddress = UInt16(dataPointer[0]) | (UInt16(dataPointer[1]) << 8)
let contents = testData.subdataWithRange(NSMakeRange(2, testData.length - 2))
machine.setData(contents, atAddress: loadAddress)
machine.setValue(0x00, forAddress: 0x0002)
machine.setValue(0x00, forAddress: 0xa002)
machine.setValue(0x80, forAddress: 0xa003)
machine.setValue(0xff, forAddress: 0x01fe)
machine.setValue(0x7f, forAddress: 0x01ff)
machine.setValue(0x48, forAddress: 0xfffe)
machine.setValue(0xff, forAddress: 0xffff)
let irqHandler = NSData(bytes: [
0x48, 0x8a, 0x48, 0x98, 0x48, 0xba, 0xbd, 0x04, 0x01,
0x29, 0x10, 0xf0, 0x03, 0x6c, 0x16, 0x03, 0x6c, 0x14, 0x03
] as [UInt8], length: 19)
machine.setData( irqHandler, atAddress: 0xff48)
machine.setValue(CSTestMachineJamOpcode, forAddress:0xffd2); // print character
machine.setValue(CSTestMachineJamOpcode, forAddress:0xe16f); // load
machine.setValue(CSTestMachineJamOpcode, forAddress:0xffe4); // scan keyboard
machine.setValue(CSTestMachineJamOpcode, forAddress:0x8000); // exit
machine.setValue(CSTestMachineJamOpcode, forAddress:0xa474); // exit
machine.setValue(0x0801, forRegister: CSTestMachineRegister.ProgramCounter)
machine.setValue(0xfd, forRegister: CSTestMachineRegister.StackPointer)
machine.setValue(0x04, forRegister: CSTestMachineRegister.Flags)
}
}
if machine == nil {
NSException(name: "Failed Test", reason: "Couldn't load file \(name)", userInfo: nil).raise()
}
while !machine.isJammed {
machine.runForNumberOfCycles(1000)
}
let jammedPC = machine.valueForRegister(CSTestMachineRegister.LastOperationAddress)
if jammedPC != 0xe16f {
let hexAddress = String(format:"%04x", jammedPC)
NSException(name: "Failed Test", reason: "Processor jammed unexpectedly at \(hexAddress)", userInfo: nil).raise()
}
}
// MARK: MachineJamHandler
func petsciiToString(string: String) -> String {
let petsciiToCharCommon: [String] = [
"?", "?", "?", "[RUN/STOP]", "?", "[WHT]", "?", "?", "[SHIFT DISABLE]", "[SHIFT ENABLE]", "?", "?", "?", "\r", "[TEXT MODE]", "?",
"?", "\n", "[RVS ON]", "[HOME]", "[DEL]", "?", "?", "?", "?", "?", "?", "?", "[RED]", "[RIGHT]", "[GRN]", "[BLU]",
" ", "!", "\"", "#", "$", "%", "&", "'", "(", ")", "*", "+", ",", "-", ".", "/",
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ":", ";", "<", "=", ">", "?",
];
let petsciiToCharRegular: [String] = [
"@", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o",
"p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "[", "£", "]", "", "",
]
let petsciiToCharInverse: [String] = [
"@", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O",
"P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "[", "£", "]", "", "",
]
var result: String = ""
for character in string.utf16 {
let charInt: Int = Int(character)
var stringToAppend = ""
if charInt&0x7f < petsciiToCharCommon.count {
stringToAppend = petsciiToCharCommon[charInt&0x7f]
} else {
let lookupTable = (charInt > 0x80) ? petsciiToCharInverse : petsciiToCharRegular
let lookupIndex = (charInt&0x7f) - petsciiToCharCommon.count
if lookupIndex < lookupTable.count {
stringToAppend = lookupTable[lookupIndex]
} else {
stringToAppend += "!"
}
}
result += stringToAppend
}
return result
}
func testMachine(machine: CSTestMachine!, didJamAtAddress address: UInt16) {
switch address {
case 0xffd2:
machine.setValue(0x00, forAddress: 0x030c)
let character = machine.valueForRegister(CSTestMachineRegister.A)
output.append(Character(UnicodeScalar(character)))
machine.returnFromSubroutine()
case 0xffe4:
machine.setValue(0x3, forRegister:CSTestMachineRegister.A)
machine.returnFromSubroutine()
case 0x8000, 0xa474:
NSException(name: "Failed test", reason: self.petsciiToString(output), userInfo: nil).raise()
case 0x0000:
NSException(name: "Failed test", reason: "Execution hit 0000", userInfo: nil).raise()
case 0xe16f, 0x45c0: // Wolfgang Lorenz load next (which we consider to be success)
break;
default:
let hexAddress = String(format:"%04x", address)
NSException(name: "Failed Test", reason: "Processor jammed unexpectedly at \(hexAddress)", userInfo: nil).raise()
} }
} }

View File

@ -0,0 +1,42 @@
//
// Machine.h
// ElectrEm
//
// Created by Thomas Harte on 29/06/2015.
// Copyright © 2015 Thomas Harte. All rights reserved.
//
#import <Foundation/Foundation.h>
typedef NS_ENUM(NSInteger, CSTestMachineRegister) {
CSTestMachineRegisterLastOperationAddress,
CSTestMachineRegisterProgramCounter,
CSTestMachineRegisterStackPointer,
CSTestMachineRegisterFlags,
CSTestMachineRegisterA
};
extern const uint8_t CSTestMachineJamOpcode;
@class CSTestMachine;
@protocol CSTestMachineJamHandler <NSObject>
- (void)testMachine:(CSTestMachine *)machine didJamAtAddress:(uint16_t)address;
@end
@interface CSTestMachine : NSObject
- (void)setData:(NSData *)data atAddress:(uint16_t)startAddress;
- (void)runForNumberOfCycles:(int)cycles;
- (void)setValue:(uint8_t)value forAddress:(uint16_t)address;
- (uint8_t)valueForAddress:(uint16_t)address;
- (void)setValue:(uint16_t)value forRegister:(CSTestMachineRegister)reg;
- (uint16_t)valueForRegister:(CSTestMachineRegister)reg;
- (void)reset;
- (void)returnFromSubroutine;
@property (nonatomic, readonly) BOOL isJammed;
@property (nonatomic, weak) id <CSTestMachineJamHandler> jamHandler;
@end

View File

@ -0,0 +1,98 @@
//
// Machine.m
// ElectrEm
//
// Created by Thomas Harte on 29/06/2015.
// Copyright © 2015 Thomas Harte. All rights reserved.
//
#import "TestMachine.h"
#include <stdint.h>
#include "CPU6502AllRAM.hpp"
const uint8_t CSTestMachineJamOpcode = CPU6502::JamOpcode;
class MachineJamHandler: public CPU6502::AllRAMProcessor::JamHandler {
public:
MachineJamHandler(CSTestMachine *targetMachine) {
_targetMachine = targetMachine;
}
void processor_did_jam(CPU6502::AllRAMProcessor::Processor *processor, uint16_t address) override {
[_targetMachine.jamHandler testMachine:_targetMachine didJamAtAddress:address];
}
private:
CSTestMachine *_targetMachine;
};
@implementation CSTestMachine {
CPU6502::AllRAMProcessor _processor;
MachineJamHandler *_cppJamHandler;
}
- (uint8_t)valueForAddress:(uint16_t)address {
uint8_t value;
_processor.perform_bus_operation(CPU6502::BusOperation::Read, address, &value);
return value;
}
- (void)setValue:(uint8_t)value forAddress:(uint16_t)address {
_processor.perform_bus_operation(CPU6502::BusOperation::Write, address, &value);
}
- (void)returnFromSubroutine {
_processor.return_from_subroutine();
}
- (CPU6502::Register)registerForRegister:(CSTestMachineRegister)reg {
switch (reg) {
case CSTestMachineRegisterProgramCounter: return CPU6502::Register::ProgramCounter;
case CSTestMachineRegisterLastOperationAddress: return CPU6502::Register::LastOperationAddress;
case CSTestMachineRegisterFlags: return CPU6502::Register::Flags;
case CSTestMachineRegisterA: return CPU6502::Register::A;
case CSTestMachineRegisterStackPointer: return CPU6502::Register::S;
default: break;
}
}
- (void)setValue:(uint16_t)value forRegister:(CSTestMachineRegister)reg {
_processor.set_value_of_register([self registerForRegister:reg], value);
}
- (uint16_t)valueForRegister:(CSTestMachineRegister)reg {
return _processor.get_value_of_register([self registerForRegister:reg]);
}
- (void)setData:(NSData *)data atAddress:(uint16_t)startAddress {
_processor.set_data_at_address(startAddress, data.length, (const uint8_t *)data.bytes);
}
- (void)reset {
_processor.reset();
}
- (void)runForNumberOfCycles:(int)cycles {
_processor.run_for_cycles(cycles);
}
- (BOOL)isJammed {
return _processor.is_jammed();
}
- (instancetype)init {
self = [super init];
if (self) {
_cppJamHandler = new MachineJamHandler(self);
_processor.set_jam_handler(_cppJamHandler);
}
return self;
}
- (void)dealloc {
delete _cppJamHandler;
}
@end

Some files were not shown because too many files have changed in this diff Show More