1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-06 01:28:57 +00:00

Merge pull request #611 from TomHarte/68000

Adds an Initial Emulation of the 68000
This commit is contained in:
Thomas Harte 2019-05-03 15:08:24 -04:00 committed by GitHub
commit 9c3c2192dd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
43 changed files with 9893 additions and 350 deletions

View File

@ -62,6 +62,7 @@ class ConcreteMachine:
set_clock_rate(2000000);
speaker_.set_input_rate(2000000 / SoundGenerator::clock_rate_divider);
speaker_.set_high_frequency_cutoff(7000);
std::vector<std::string> rom_names = {"basic.rom", "os.rom"};
if(target.has_adfs) {

View File

@ -150,7 +150,6 @@
4B2A332D1DB86821002876E3 /* OricOptions.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4B2A332B1DB86821002876E3 /* OricOptions.xib */; };
4B2A539F1D117D36003C6002 /* CSAudioQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B2A53911D117D36003C6002 /* CSAudioQueue.m */; };
4B2A53A01D117D36003C6002 /* CSMachine.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B2A53961D117D36003C6002 /* CSMachine.mm */; };
4B2AF8691E513FC20027EE29 /* TIATests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B2AF8681E513FC20027EE29 /* TIATests.mm */; };
4B2B3A4B1F9B8FA70062DABF /* Typer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B2B3A471F9B8FA70062DABF /* Typer.cpp */; };
4B2B3A4C1F9B8FA70062DABF /* MemoryFuzzer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B2B3A481F9B8FA70062DABF /* MemoryFuzzer.cpp */; };
4B2BFC5F1D613E0200BA3AA9 /* TapePRG.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B2BFC5D1D613E0200BA3AA9 /* TapePRG.cpp */; };
@ -198,7 +197,6 @@
4B4B1A3D200198CA00A0F866 /* KonamiSCC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4B1A3A200198C900A0F866 /* KonamiSCC.cpp */; };
4B4DC8211D2C2425003C5BF8 /* Vic20.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4DC81F1D2C2425003C5BF8 /* Vic20.cpp */; };
4B4DC82B1D2C27A4003C5BF8 /* SerialBus.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4DC8291D2C27A4003C5BF8 /* SerialBus.cpp */; };
4B50730A1DDFCFDF00C48FBD /* ArrayBuilderTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B5073091DDFCFDF00C48FBD /* ArrayBuilderTests.mm */; };
4B54C0BC1F8D8E790050900F /* KeyboardMachine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B54C0BB1F8D8E790050900F /* KeyboardMachine.cpp */; };
4B54C0BF1F8D8F450050900F /* Keyboard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B54C0BD1F8D8F450050900F /* Keyboard.cpp */; };
4B54C0C21F8D91CD0050900F /* Keyboard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B54C0C11F8D91CD0050900F /* Keyboard.cpp */; };
@ -248,6 +246,8 @@
4B83348A1F5DB94B0097E338 /* IRQDelegatePortHandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B8334891F5DB94B0097E338 /* IRQDelegatePortHandler.cpp */; };
4B83348C1F5DB99C0097E338 /* 6522Base.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B83348B1F5DB99C0097E338 /* 6522Base.cpp */; };
4B8334951F5E25B60097E338 /* C1540.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B8334941F5E25B60097E338 /* C1540.cpp */; };
4B85322D227793CB00F26553 /* etos192uk.trace.txt.gz in Resources */ = {isa = PBXBuildFile; fileRef = 4B85322C227793CA00F26553 /* etos192uk.trace.txt.gz */; };
4B85322F2277ABDE00F26553 /* tos100.trace.txt.gz in Resources */ = {isa = PBXBuildFile; fileRef = 4B85322E2277ABDD00F26553 /* tos100.trace.txt.gz */; };
4B86E25B1F8C628F006FAA45 /* Keyboard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B86E2591F8C628F006FAA45 /* Keyboard.cpp */; };
4B8805F01DCFC99C003085B1 /* Acorn.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B8805EE1DCFC99C003085B1 /* Acorn.cpp */; };
4B8805F41DCFD22A003085B1 /* Commodore.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B8805F21DCFD22A003085B1 /* Commodore.cpp */; };
@ -308,6 +308,9 @@
4B98A1CE1FFADEC500ADF63B /* MSX ROMs in Resources */ = {isa = PBXBuildFile; fileRef = 4B98A1CD1FFADEC400ADF63B /* MSX ROMs */; };
4B9BE400203A0C0600FFAE60 /* MultiSpeaker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B9BE3FE203A0C0600FFAE60 /* MultiSpeaker.cpp */; };
4B9BE401203A0C0600FFAE60 /* MultiSpeaker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B9BE3FE203A0C0600FFAE60 /* MultiSpeaker.cpp */; };
4B9F11C92272375400701480 /* qltrace.txt.gz in Resources */ = {isa = PBXBuildFile; fileRef = 4B9F11C82272375400701480 /* qltrace.txt.gz */; };
4B9F11CA2272433900701480 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B69FB451C4D950F00B5F0AA /* libz.tbd */; };
4B9F11CC22729B3600701480 /* OPCLOGR2.BIN in Resources */ = {isa = PBXBuildFile; fileRef = 4B9F11CB22729B3500701480 /* OPCLOGR2.BIN */; };
4BA0F68E1EEA0E8400E9489E /* ZX8081.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BA0F68C1EEA0E8400E9489E /* ZX8081.cpp */; };
4BA61EB01D91515900B3C876 /* NSData+StdVector.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BA61EAF1D91515900B3C876 /* NSData+StdVector.mm */; };
4BA91E1D216D85BA00F79557 /* MasterSystemVDPTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BA91E1C216D85BA00F79557 /* MasterSystemVDPTests.mm */; };
@ -618,6 +621,7 @@
4BCF1FA41DADC3DD0039D2E7 /* Oric.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BCF1FA21DADC3DD0039D2E7 /* Oric.cpp */; };
4BD191F42191180E0042E144 /* ScanTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD191F22191180E0042E144 /* ScanTarget.cpp */; };
4BD191F52191180E0042E144 /* ScanTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD191F22191180E0042E144 /* ScanTarget.cpp */; };
4BD388882239E198002D14B5 /* 68000Tests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BD388872239E198002D14B5 /* 68000Tests.mm */; };
4BD3A30B1EE755C800B5B501 /* Video.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD3A3091EE755C800B5B501 /* Video.cpp */; };
4BD424DF2193B5340097291A /* TextureTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD424DD2193B5340097291A /* TextureTarget.cpp */; };
4BD424E02193B5340097291A /* TextureTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD424DD2193B5340097291A /* TextureTarget.cpp */; };
@ -638,6 +642,7 @@
4BDB61EB2032806E0048AF91 /* CSAtari2600.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B2A539A1D117D36003C6002 /* CSAtari2600.mm */; };
4BDB61EC203285AE0048AF91 /* Atari2600OptionsPanel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B8FE21F1DA19D7C0090D3CE /* Atari2600OptionsPanel.swift */; };
4BDDBA991EF3451200347E61 /* Z80MachineCycleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BDDBA981EF3451200347E61 /* Z80MachineCycleTests.swift */; };
4BE76CF922641ED400ACD6FA /* QLTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BE76CF822641ED300ACD6FA /* QLTests.mm */; };
4BE7C9181E3D397100A5496D /* TIA.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BE7C9161E3D397100A5496D /* TIA.cpp */; };
4BE9A6B11EDE293000CBCB47 /* zexdoc.com in Resources */ = {isa = PBXBuildFile; fileRef = 4BE9A6B01EDE293000CBCB47 /* zexdoc.com */; };
4BEA525E1DF33323007E74F2 /* Tape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEA525D1DF33323007E74F2 /* Tape.cpp */; };
@ -661,6 +666,9 @@
4BFDD78C1F7F2DB4008579B9 /* ImplicitSectors.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BFDD78B1F7F2DB4008579B9 /* ImplicitSectors.cpp */; };
4BFE7B871FC39BF100160B38 /* StandardOptions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BFE7B851FC39BF100160B38 /* StandardOptions.cpp */; };
4BFE7B881FC39D8900160B38 /* StandardOptions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BFE7B851FC39BF100160B38 /* StandardOptions.cpp */; };
4BFF1D3922337B0300838EA1 /* 68000Storage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BFF1D3822337B0300838EA1 /* 68000Storage.cpp */; };
4BFF1D3A22337B0300838EA1 /* 68000Storage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BFF1D3822337B0300838EA1 /* 68000Storage.cpp */; };
4BFF1D3D2235C3C100838EA1 /* EmuTOSTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BFF1D3C2235C3C100838EA1 /* EmuTOSTests.mm */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@ -869,7 +877,6 @@
4B4DC8271D2C2470003C5BF8 /* C1540.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = C1540.hpp; sourceTree = "<group>"; };
4B4DC8291D2C27A4003C5BF8 /* SerialBus.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SerialBus.cpp; sourceTree = "<group>"; };
4B4DC82A1D2C27A4003C5BF8 /* SerialBus.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = SerialBus.hpp; sourceTree = "<group>"; };
4B5073091DDFCFDF00C48FBD /* ArrayBuilderTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ArrayBuilderTests.mm; sourceTree = "<group>"; };
4B51F70920A521D700AFA2C1 /* Source.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Source.hpp; sourceTree = "<group>"; };
4B51F70A20A521D700AFA2C1 /* Observer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Observer.hpp; sourceTree = "<group>"; };
4B54C0BB1F8D8E790050900F /* KeyboardMachine.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = KeyboardMachine.cpp; sourceTree = "<group>"; };
@ -958,6 +965,9 @@
4B83348E1F5DBA6E0097E338 /* 6522Storage.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = 6522Storage.hpp; path = Implementation/6522Storage.hpp; sourceTree = "<group>"; };
4B8334911F5E24FF0097E338 /* C1540Base.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = C1540Base.hpp; path = Implementation/C1540Base.hpp; sourceTree = "<group>"; };
4B8334941F5E25B60097E338 /* C1540.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = C1540.cpp; path = Implementation/C1540.cpp; sourceTree = "<group>"; };
4B85322922778E4200F26553 /* Comparative68000.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Comparative68000.hpp; sourceTree = "<group>"; };
4B85322C227793CA00F26553 /* etos192uk.trace.txt.gz */ = {isa = PBXFileReference; lastKnownFileType = archive.gzip; path = etos192uk.trace.txt.gz; sourceTree = "<group>"; };
4B85322E2277ABDD00F26553 /* tos100.trace.txt.gz */ = {isa = PBXFileReference; lastKnownFileType = archive.gzip; path = tos100.trace.txt.gz; sourceTree = "<group>"; };
4B86E2591F8C628F006FAA45 /* Keyboard.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Keyboard.cpp; sourceTree = "<group>"; };
4B86E25A1F8C628F006FAA45 /* Keyboard.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Keyboard.hpp; sourceTree = "<group>"; };
4B8805EE1DCFC99C003085B1 /* Acorn.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Acorn.cpp; path = Parsers/Acorn.cpp; sourceTree = "<group>"; };
@ -1035,6 +1045,8 @@
4B98A1CD1FFADEC400ADF63B /* MSX ROMs */ = {isa = PBXFileReference; lastKnownFileType = folder; path = "MSX ROMs"; sourceTree = "<group>"; };
4B9BE3FE203A0C0600FFAE60 /* MultiSpeaker.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = MultiSpeaker.cpp; sourceTree = "<group>"; };
4B9BE3FF203A0C0600FFAE60 /* MultiSpeaker.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = MultiSpeaker.hpp; sourceTree = "<group>"; };
4B9F11C82272375400701480 /* qltrace.txt.gz */ = {isa = PBXFileReference; lastKnownFileType = archive.gzip; path = qltrace.txt.gz; sourceTree = "<group>"; };
4B9F11CB22729B3500701480 /* OPCLOGR2.BIN */ = {isa = PBXFileReference; lastKnownFileType = archive.macbinary; name = OPCLOGR2.BIN; path = "68000 Coverage/OPCLOGR2.BIN"; sourceTree = "<group>"; };
4BA0F68C1EEA0E8400E9489E /* ZX8081.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ZX8081.cpp; path = Data/ZX8081.cpp; sourceTree = "<group>"; };
4BA0F68D1EEA0E8400E9489E /* ZX8081.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = ZX8081.hpp; path = Data/ZX8081.hpp; sourceTree = "<group>"; };
4BA141C12073100800A31EC9 /* Target.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Target.hpp; sourceTree = "<group>"; };
@ -1375,6 +1387,7 @@
4BD191F22191180E0042E144 /* ScanTarget.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ScanTarget.cpp; sourceTree = "<group>"; };
4BD191F32191180E0042E144 /* ScanTarget.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ScanTarget.hpp; sourceTree = "<group>"; };
4BD388411FE34E010042B588 /* 9918Base.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = 9918Base.hpp; path = 9918/Implementation/9918Base.hpp; sourceTree = "<group>"; };
4BD388872239E198002D14B5 /* 68000Tests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = 68000Tests.mm; sourceTree = "<group>"; };
4BD3A3091EE755C800B5B501 /* Video.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Video.cpp; path = ZX8081/Video.cpp; sourceTree = "<group>"; };
4BD3A30A1EE755C800B5B501 /* Video.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Video.hpp; path = ZX8081/Video.hpp; sourceTree = "<group>"; };
4BD424DD2193B5340097291A /* TextureTarget.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TextureTarget.cpp; sourceTree = "<group>"; };
@ -1404,6 +1417,7 @@
4BE3231520532AA7006EF799 /* Target.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Target.hpp; sourceTree = "<group>"; };
4BE3231620532BED006EF799 /* Target.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Target.hpp; sourceTree = "<group>"; };
4BE3231720532CC0006EF799 /* Target.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Target.hpp; sourceTree = "<group>"; };
4BE76CF822641ED300ACD6FA /* QLTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = QLTests.mm; sourceTree = "<group>"; };
4BE7C9161E3D397100A5496D /* TIA.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TIA.cpp; sourceTree = "<group>"; };
4BE7C9171E3D397100A5496D /* TIA.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TIA.hpp; sourceTree = "<group>"; };
4BE845201F2FF7F100A5EA22 /* CRTC6845.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = CRTC6845.hpp; path = 6845/CRTC6845.hpp; sourceTree = "<group>"; };
@ -1459,6 +1473,11 @@
4BFDD78B1F7F2DB4008579B9 /* ImplicitSectors.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ImplicitSectors.cpp; sourceTree = "<group>"; };
4BFE7B851FC39BF100160B38 /* StandardOptions.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = StandardOptions.cpp; sourceTree = "<group>"; };
4BFE7B861FC39BF100160B38 /* StandardOptions.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = StandardOptions.hpp; sourceTree = "<group>"; };
4BFF1D342233778C00838EA1 /* 68000.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = 68000.hpp; sourceTree = "<group>"; };
4BFF1D37223379D500838EA1 /* 68000Storage.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = 68000Storage.hpp; sourceTree = "<group>"; };
4BFF1D3822337B0300838EA1 /* 68000Storage.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = 68000Storage.cpp; sourceTree = "<group>"; };
4BFF1D3B2235714900838EA1 /* 68000Implementation.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = 68000Implementation.hpp; sourceTree = "<group>"; };
4BFF1D3C2235C3C100838EA1 /* EmuTOSTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = EmuTOSTests.mm; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -1486,6 +1505,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
4B9F11CA2272433900701480 /* libz.tbd in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -1551,13 +1571,16 @@
4B1414631B588A1100E04248 /* Test Binaries */ = {
isa = PBXGroup;
children = (
4B85322B227793CA00F26553 /* TOS Startup */,
4B9252CD1E74D28200B76AF1 /* Atari ROMs */,
4B44EBF81DC9898E00A7820C /* BCDTEST_beeb */,
4B98A1CD1FFADEC400ADF63B /* MSX ROMs */,
4B018B88211930DE002A3937 /* 65C02_extended_opcodes_test.bin */,
4B44EBF61DC9883B00A7820C /* 6502_functional_test.bin */,
4B44EBF41DC987AE00A7820C /* AllSuiteA.bin */,
4B9F11CB22729B3500701480 /* OPCLOGR2.BIN */,
4BBF49B41ED2881600AB3669 /* FUSE */,
4B9F11C72272375400701480 /* QL Startup */,
4BB297E41B587D8300A49093 /* Wolfgang Lorenz 6502 test suite */,
4BE9A6B21EDE294200CBCB47 /* Zexall */,
);
@ -2238,6 +2261,15 @@
name = Implementation;
sourceTree = "<group>";
};
4B85322B227793CA00F26553 /* TOS Startup */ = {
isa = PBXGroup;
children = (
4B85322E2277ABDD00F26553 /* tos100.trace.txt.gz */,
4B85322C227793CA00F26553 /* etos192uk.trace.txt.gz */,
);
path = "TOS Startup";
sourceTree = "<group>";
};
4B86E2581F8C628F006FAA45 /* Inputs */ = {
isa = PBXGroup;
children = (
@ -2435,6 +2467,14 @@
path = Implementation;
sourceTree = "<group>";
};
4B9F11C72272375400701480 /* QL Startup */ = {
isa = PBXGroup;
children = (
4B9F11C82272375400701480 /* qltrace.txt.gz */,
);
path = "QL Startup";
sourceTree = "<group>";
};
4BAB62AA1D3272D200DF5BA0 /* Disk */ = {
isa = PBXGroup;
children = (
@ -2817,13 +2857,16 @@
4BB73EB51B587A5100552FC2 /* Clock SignalTests */ = {
isa = PBXGroup;
children = (
4B5073091DDFCFDF00C48FBD /* ArrayBuilderTests.mm */,
4B85322922778E4200F26553 /* Comparative68000.hpp */,
4BD388872239E198002D14B5 /* 68000Tests.mm */,
4B924E981E74D22700B76AF1 /* AtariStaticAnalyserTests.mm */,
4BB2A9AE1E13367E001A5C23 /* CRCTests.mm */,
4BFF1D3C2235C3C100838EA1 /* EmuTOSTests.mm */,
4BA91E1C216D85BA00F79557 /* MasterSystemVDPTests.mm */,
4B98A0601FFADCDE00ADF63B /* MSXStaticAnalyserTests.mm */,
4B121F9A1E06293F00BFDA12 /* PCMSegmentEventSourceTests.mm */,
4BD4A8CF1E077FD20020D856 /* PCMTrackTests.mm */,
4BE76CF822641ED300ACD6FA /* QLTests.mm */,
4B2AF8681E513FC20027EE29 /* TIATests.mm */,
4B1D08051E0F7A1100763741 /* TimeTests.mm */,
4BB73EB81B587A5100552FC2 /* Info.plist */,
@ -2886,11 +2929,12 @@
4BB73EDD1B587CA500552FC2 /* Processors */ = {
isa = PBXGroup;
children = (
4B1414561B58879D00E04248 /* 6502 */,
4B77069E1EC9045B0053B588 /* Z80 */,
4B2C455C1EC9442600FC74DD /* RegisterSizes.hpp */,
4BFCA1211ECBDCAF00AC40C1 /* AllRAMProcessor.cpp */,
4BFCA1221ECBDCAF00AC40C1 /* AllRAMProcessor.hpp */,
4B2C455C1EC9442600FC74DD /* RegisterSizes.hpp */,
4B1414561B58879D00E04248 /* 6502 */,
4BFF1D332233778C00838EA1 /* 68000 */,
4B77069E1EC9045B0053B588 /* Z80 */,
);
name = Processors;
path = ../../Processors;
@ -3196,6 +3240,25 @@
path = Utility;
sourceTree = "<group>";
};
4BFF1D332233778C00838EA1 /* 68000 */ = {
isa = PBXGroup;
children = (
4BFF1D342233778C00838EA1 /* 68000.hpp */,
4BFF1D36223379D500838EA1 /* Implementation */,
);
path = 68000;
sourceTree = "<group>";
};
4BFF1D36223379D500838EA1 /* Implementation */ = {
isa = PBXGroup;
children = (
4BFF1D37223379D500838EA1 /* 68000Storage.hpp */,
4BFF1D3822337B0300838EA1 /* 68000Storage.cpp */,
4BFF1D3B2235714900838EA1 /* 68000Implementation.hpp */,
);
path = Implementation;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@ -3285,7 +3348,7 @@
};
4BB73E9D1B587A5100552FC2 = {
CreatedOnToolsVersion = 7.0;
LastSwiftMigration = 0900;
LastSwiftMigration = 1020;
SystemCapabilities = {
com.apple.Sandbox = {
enabled = 1;
@ -3294,12 +3357,12 @@
};
4BB73EB11B587A5100552FC2 = {
CreatedOnToolsVersion = 7.0;
LastSwiftMigration = 0900;
LastSwiftMigration = 1020;
TestTargetID = 4BB73E9D1B587A5100552FC2;
};
4BB73EBC1B587A5100552FC2 = {
CreatedOnToolsVersion = 7.0;
LastSwiftMigration = 0900;
LastSwiftMigration = 1020;
TestTargetID = 4BB73E9D1B587A5100552FC2;
};
};
@ -3408,6 +3471,7 @@
4BB298FB1B587D8400A49093 /* ancb in Resources */,
4BB299431B587D8400A49093 /* dcma in Resources */,
4BB298FD1B587D8400A49093 /* andax in Resources */,
4B85322D227793CB00F26553 /* etos192uk.trace.txt.gz in Resources */,
4BB299401B587D8400A49093 /* cpya in Resources */,
4BB299BE1B587D8400A49093 /* rraix in Resources */,
4BB299E41B587D8400A49093 /* tayn in Resources */,
@ -3484,6 +3548,7 @@
4BB2997D1B587D8400A49093 /* ldxay in Resources */,
4BB299D71B587D8400A49093 /* staax in Resources */,
4B98A1CE1FFADEC500ADF63B /* MSX ROMs in Resources */,
4B9F11CC22729B3600701480 /* OPCLOGR2.BIN in Resources */,
4BB2990C1B587D8400A49093 /* asoax in Resources */,
4BB299191B587D8400A49093 /* bita in Resources */,
4BB2992A1B587D8400A49093 /* cia2ta in Resources */,
@ -3501,6 +3566,7 @@
4BB299C41B587D8400A49093 /* sbca in Resources */,
4BB298F41B587D8400A49093 /* adcay in Resources */,
4B44EBF51DC987AF00A7820C /* AllSuiteA.bin in Resources */,
4B85322F2277ABDE00F26553 /* tos100.trace.txt.gz in Resources */,
4BB299C61B587D8400A49093 /* sbcay in Resources */,
4BB299601B587D8400A49093 /* insa in Resources */,
4BB299951B587D8400A49093 /* mmufetch in Resources */,
@ -3602,6 +3668,7 @@
4BB299A41B587D8400A49093 /* oraz in Resources */,
4BB299611B587D8400A49093 /* insax in Resources */,
4BB299351B587D8400A49093 /* cmpix in Resources */,
4B9F11C92272375400701480 /* qltrace.txt.gz in Resources */,
4BB299041B587D8400A49093 /* aneb in Resources */,
4BB299BB1B587D8400A49093 /* rraa in Resources */,
4BB299091B587D8400A49093 /* aslz in Resources */,
@ -3687,6 +3754,7 @@
4B05401F219D1618001BF69C /* ScanTarget.cpp in Sources */,
4B055AE81FAE9B7B0060FFFF /* FIRFilter.cpp in Sources */,
4B055A901FAE85A90060FFFF /* TimedEventLoop.cpp in Sources */,
4BFF1D3A22337B0300838EA1 /* 68000Storage.cpp in Sources */,
4B055AC71FAE9AEE0060FFFF /* TIA.cpp in Sources */,
4B055AD21FAE9B0B0060FFFF /* Keyboard.cpp in Sources */,
4B89451B201967B4007DE474 /* ConfidenceSummary.cpp in Sources */,
@ -3967,6 +4035,7 @@
4B8334821F5D9FF70097E338 /* PartialMachineCycle.cpp in Sources */,
4BD424E72193B5830097291A /* Rectangle.cpp in Sources */,
4B1B88C0202E3DB200B67DFF /* MultiConfigurable.cpp in Sources */,
4BFF1D3922337B0300838EA1 /* 68000Storage.cpp in Sources */,
4B54C0BC1F8D8E790050900F /* KeyboardMachine.cpp in Sources */,
4BB73EA21B587A5100552FC2 /* AppDelegate.swift in Sources */,
4B894534201967B4007DE474 /* AddressMapper.cpp in Sources */,
@ -3979,6 +4048,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
4BFF1D3D2235C3C100838EA1 /* EmuTOSTests.mm in Sources */,
4B1E85811D176468001EF87D /* 6532Tests.swift in Sources */,
4BDDBA991EF3451200347E61 /* Z80MachineCycleTests.swift in Sources */,
4B98A05F1FFAD62400ADF63B /* CSROMFetcher.mm in Sources */,
@ -3989,18 +4059,18 @@
4B7BC7F51F58F27800D1B1B4 /* 6502AllRAM.cpp in Sources */,
4B08A2751EE35D56008B7065 /* Z80InterruptTests.swift in Sources */,
4BFCA1241ECBDCB400AC40C1 /* AllRAMProcessor.cpp in Sources */,
4B50730A1DDFCFDF00C48FBD /* ArrayBuilderTests.mm in Sources */,
4BBF49AF1ED2880200AB3669 /* FUSETests.swift in Sources */,
4B2AF8691E513FC20027EE29 /* TIATests.mm in Sources */,
4B3BA0CE1D318B44005DD7A7 /* C1540Bridge.mm in Sources */,
4B3BA0D11D318B44005DD7A7 /* TestMachine6502.mm in Sources */,
4B92EACA1B7C112B00246143 /* 6502TimingTests.swift in Sources */,
4BB73EB71B587A5100552FC2 /* AllSuiteATests.swift in Sources */,
4B01A6881F22F0DB001FD6E3 /* Z80MemptrTests.swift in Sources */,
4B121F9B1E06293F00BFDA12 /* PCMSegmentEventSourceTests.mm in Sources */,
4BD388882239E198002D14B5 /* 68000Tests.mm in Sources */,
4BA91E1D216D85BA00F79557 /* MasterSystemVDPTests.mm in Sources */,
4B98A0611FFADCDE00ADF63B /* MSXStaticAnalyserTests.mm in Sources */,
4BEF6AAC1D35D1C400E73575 /* DPLLTests.swift in Sources */,
4BE76CF922641ED400ACD6FA /* QLTests.mm in Sources */,
4B3BA0CF1D318B44005DD7A7 /* MOS6522Bridge.mm in Sources */,
4BC751B21D157E61006C31D9 /* 6522Tests.swift in Sources */,
4BFCA12B1ECBE7C400AC40C1 /* ZexallTests.swift in Sources */,
@ -4313,8 +4383,7 @@
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Clock Signal/ClockSignal-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
SWIFT_VERSION = 4.0;
SWIFT_VERSION = 5.0;
};
name = Debug;
};
@ -4355,8 +4424,7 @@
PRODUCT_BUNDLE_IDENTIFIER = "TH.Clock-Signal";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Clock Signal/ClockSignal-Bridging-Header.h";
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
SWIFT_VERSION = 4.0;
SWIFT_VERSION = 5.0;
};
name = Release;
};
@ -4372,8 +4440,7 @@
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Clock SignalTests/Bridges/Clock SignalTests-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
SWIFT_VERSION = 4.0;
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Clock Signal.app/Contents/MacOS/Clock Signal";
};
name = Debug;
@ -4389,8 +4456,7 @@
PRODUCT_BUNDLE_IDENTIFIER = "TH.Clock-SignalTests";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Clock SignalTests/Bridges/Clock SignalTests-Bridging-Header.h";
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
SWIFT_VERSION = 4.0;
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Clock Signal.app/Contents/MacOS/Clock Signal";
};
name = Release;
@ -4403,8 +4469,7 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "TH.Clock-SignalUITests";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
SWIFT_VERSION = 4.0;
SWIFT_VERSION = 5.0;
TEST_TARGET_NAME = "Clock Signal";
USES_XCTRUNNER = YES;
};
@ -4418,8 +4483,7 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "TH.Clock-SignalUITests";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
SWIFT_VERSION = 4.0;
SWIFT_VERSION = 5.0;
TEST_TARGET_NAME = "Clock Signal";
USES_XCTRUNNER = YES;
};

View File

@ -26,6 +26,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
disableMainThreadChecker = "YES"
codeCoverageEnabled = "YES"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>

View File

@ -49,7 +49,7 @@ class MachineDocument:
fileprivate var bestEffortUpdater: CSBestEffortUpdater?
override var windowNibName: NSNib.Name? {
return NSNib.Name(rawValue: "MachineDocument")
return "MachineDocument"
}
override func windowControllerDidLoadNib(_ aController: NSWindowController) {
@ -64,7 +64,7 @@ class MachineDocument:
func windowDidUpdate(_ notification: Notification) {
if self.shouldShowNewMachinePanel {
self.shouldShowNewMachinePanel = false
Bundle.main.loadNibNamed(NSNib.Name(rawValue: "MachinePicker"), owner: self, topLevelObjects: nil)
Bundle.main.loadNibNamed("MachinePicker", owner: self, topLevelObjects: nil)
self.machinePicker?.establishStoredOptions()
self.windowControllers[0].window?.beginSheet(self.machinePickerPanel!, completionHandler: nil)
}
@ -80,7 +80,7 @@ class MachineDocument:
// attach an options panel if one is available
if let optionsPanelNibName = self.optionsPanelNibName {
Bundle.main.loadNibNamed(NSNib.Name(rawValue: optionsPanelNibName), owner: self, topLevelObjects: nil)
Bundle.main.loadNibNamed(optionsPanelNibName, owner: self, topLevelObjects: nil)
self.optionsPanel.machine = machine
self.optionsPanel?.establishStoredOptions()
showOptions(self)
@ -372,7 +372,7 @@ class MachineDocument:
func setupActivityDisplay() {
var leds = machine.leds
if leds.count > 0 {
Bundle.main.loadNibNamed(NSNib.Name(rawValue: "Activity"), owner: self, topLevelObjects: nil)
Bundle.main.loadNibNamed("Activity", owner: self, topLevelObjects: nil)
showActivity(nil)
// Inspect the activity panel for indicators.

Binary file not shown.

View File

@ -0,0 +1,5 @@
This file provides a record of which opcodes are valid and which are invalid on a
real 68000. It was generated by AtariZoll and made available via
http://www.atari-forum.com/viewtopic.php?f=68&t=26820 .
No licence was specified.

View File

@ -0,0 +1,518 @@
//
// 68000Tests.m
// Clock SignalTests
//
// Created by Thomas Harte on 13/03/2019.
// Copyright © 2019 Thomas Harte. All rights reserved.
//
#import <XCTest/XCTest.h>
#include <array>
#include <cassert>
#include "68000.hpp"
/*!
Provides a 68000 with 64kb of RAM in its low address space;
/RESET will put the supervisor stack pointer at 0xFFFF and
begin execution at 0x0400.
*/
class RAM68000: public CPU::MC68000::BusHandler {
public:
RAM68000() : m68000_(*this) {
ram_.resize(256*1024);
// Setup the /RESET vector.
ram_[0] = 0;
ram_[1] = 0xffff;
ram_[2] = 0;
ram_[3] = 0x1000;
}
void set_program(const std::vector<uint16_t> &program) {
memcpy(&ram_[0x1000 >> 1], program.data(), program.size() * sizeof(uint16_t));
}
void will_perform(uint32_t address, uint16_t opcode) {
--instructions_remaining_;
}
void run_for_instructions(int count) {
instructions_remaining_ = count;
while(instructions_remaining_) {
run_for(HalfCycles(2));
}
}
void run_for(HalfCycles cycles) {
m68000_.run_for(cycles);
}
uint16_t *ram_at(uint32_t address) {
return &ram_[address >> 1];
}
HalfCycles perform_bus_operation(const CPU::MC68000::Microcycle &cycle, int is_supervisor) {
const uint32_t word_address = cycle.word_address();
using Microcycle = CPU::MC68000::Microcycle;
if(cycle.data_select_active()) {
if(cycle.operation & Microcycle::InterruptAcknowledge) {
cycle.value->halves.low = 10;
} else {
switch(cycle.operation & (Microcycle::SelectWord | Microcycle::SelectByte | Microcycle::Read)) {
default: break;
case Microcycle::SelectWord | Microcycle::Read:
cycle.value->full = ram_[word_address];
printf("r %04x from %08x \n", cycle.value->full, *cycle.address);
break;
case Microcycle::SelectByte | Microcycle::Read:
cycle.value->halves.low = ram_[word_address] >> cycle.byte_shift();
break;
case Microcycle::SelectWord:
printf("w %08x of %04x\n", *cycle.address, cycle.value->full);
ram_[word_address] = cycle.value->full;
break;
case Microcycle::SelectByte:
ram_[word_address] = (cycle.value->full & cycle.byte_mask()) | (ram_[word_address] & (0xffff ^ cycle.byte_mask()));
break;
}
}
}
return HalfCycles(0);
}
CPU::MC68000::Processor<RAM68000, true>::State get_processor_state() {
return m68000_.get_state();
}
void set_processor_state(const CPU::MC68000::Processor<RAM68000, true>::State &state) {
m68000_.set_state(state);
}
CPU::MC68000::Processor<RAM68000, true, true> &processor() {
return m68000_;
}
private:
CPU::MC68000::Processor<RAM68000, true, true> m68000_;
std::vector<uint16_t> ram_;
int instructions_remaining_;
};
class CPU::MC68000::ProcessorStorageTests {
public:
ProcessorStorageTests(const CPU::MC68000::ProcessorStorage &storage, const char *coverage_file_name) {
false_valids_ = [NSMutableSet set];
false_invalids_ = [NSMutableSet set];
FILE *source = fopen(coverage_file_name, "rb");
// The file format here is [2 bytes opcode][2 ASCII characters:VA for valid, IN for invalid]...
// The file terminates with four additional bytes that begin with two zero bytes.
//
// The version of the file I grabbed seems to cover all opcodes, making their enumeration
// arguably redundant; the code below nevertheless uses the codes from the file.
//
// Similarly, I'm testing for exactly the strings VA or IN to ensure no further
// types creep into any updated version of the table that I then deal with incorrectly.
uint16_t last_observed = 0;
while(true) {
// Fetch opcode number.
uint16_t next_opcode = fgetc(source) << 8;
next_opcode |= fgetc(source);
if(next_opcode < last_observed) break;
last_observed = next_opcode;
// Determine whether it's meant to be valid.
char type[3];
type[0] = fgetc(source);
type[1] = fgetc(source);
type[2] = '\0';
// TEMPORARY: factor out A- and F-line exceptions.
if((next_opcode&0xf000) == 0xa000) continue;
if((next_opcode&0xf000) == 0xf000) continue;
if(!strcmp(type, "VA")) {
// Test for validity.
if(!storage.instructions[next_opcode].micro_operations) {
[false_invalids_ addObject:@(next_opcode)];
}
continue;
}
if(!strcmp(type, "IN")) {
// Test for invalidity.
if(storage.instructions[next_opcode].micro_operations) {
[false_valids_ addObject:@(next_opcode)];
}
continue;
}
assert(false);
}
fclose(source);
}
NSSet<NSNumber *> *false_valids() const {
return false_valids_;
}
NSSet<NSNumber *> *false_invalids() const {
return false_invalids_;
}
private:
NSMutableSet<NSNumber *> *false_invalids_;
NSMutableSet<NSNumber *> *false_valids_;
};
@interface NSSet (CSHexDump)
- (NSString *)hexDump;
@end
@implementation NSSet (CSHexDump)
- (NSString *)hexDump {
NSMutableArray<NSString *> *components = [NSMutableArray array];
for(NSNumber *number in [[self allObjects] sortedArrayUsingSelector:@selector(compare:)]) {
[components addObject:[NSString stringWithFormat:@"%04x", number.intValue]];
}
return [components componentsJoinedByString:@" "];
}
@end
@interface M68000Tests : XCTestCase
@end
@implementation M68000Tests {
std::unique_ptr<RAM68000> _machine;
}
- (void)setUp {
_machine.reset(new RAM68000());
}
- (void)tearDown {
_machine.reset();
}
- (void)testABCD {
for(int d = 0; d < 100; ++d) {
_machine.reset(new RAM68000());
_machine->set_program({
0xc100 // ABCD D0, D0
});
auto state = _machine->get_processor_state();
const uint8_t bcd_d = ((d / 10) * 16) + (d % 10);
state.data[0] = bcd_d;
_machine->set_processor_state(state);
_machine->run_for_instructions(1);
state = _machine->get_processor_state();
const uint8_t double_d = (d * 2) % 100;
const uint8_t bcd_double_d = ((double_d / 10) * 16) + (double_d % 10);
XCTAssert(state.data[0] == bcd_double_d, "%02x + %02x = %02x; should equal %02x", bcd_d, bcd_d, state.data[0], bcd_double_d);
}
}
- (void)testDivideByZero {
_machine->set_program({
0x7000, // MOVE #0, D0; location 0x400
0x3200, // MOVE D0, D1; location 0x402
0x82C0, // DIVU; location 0x404
/* Next instruction would be at 0x406 */
});
auto state = _machine->get_processor_state();
state.supervisor_stack_pointer = 0x1000;
_machine->set_processor_state(state);
_machine->run_for_instructions(4);
state = _machine->get_processor_state();
XCTAssert(state.supervisor_stack_pointer == 0x1000 - 6, @"Exception information should have been pushed to stack.");
const uint16_t *stack_top = _machine->ram_at(state.supervisor_stack_pointer);
XCTAssert(stack_top[1] == 0x0000 && stack_top[2] == 0x1006, @"Return address should point to instruction after DIVU.");
}
- (void)testMOVE {
_machine->set_program({
0x303c, 0xfb2e, // MOVE #fb2e, D0
0x3200, // MOVE D0, D1
0x3040, // MOVEA D0, A0
0x3278, 0x1000, // MOVEA.w (0x1000), A1
0x387c, 0x1000, // MOVE #$1000, A4
0x2414, // MOVE.l (A4), D2
});
// run_for_instructions technically runs up to the next instruction
// fetch; therefore run for '1' to get past the implied RESET.
_machine->run_for_instructions(1);
// Perform MOVE #fb2e, D0
_machine->run_for_instructions(1);
auto state = _machine->get_processor_state();
XCTAssert(state.data[0] == 0xfb2e);
// Perform MOVE D0, D1
_machine->run_for_instructions(1);
state = _machine->get_processor_state();
XCTAssert(state.data[1] == 0xfb2e);
// Perform MOVEA D0, A0
_machine->run_for_instructions(1);
state = _machine->get_processor_state();
XCTAssert(state.address[0] == 0xfffffb2e, "A0 was %08x instead of 0xfffffb2e", state.address[0]);
// Perform MOVEA.w (0x1000), A1
_machine->run_for_instructions(1);
state = _machine->get_processor_state();
XCTAssert(state.address[1] == 0x0000303c, "A1 was %08x instead of 0x0000303c", state.address[1]);
// Perform MOVE #$400, A4; MOVE.l (A4), D2
_machine->run_for_instructions(2);
state = _machine->get_processor_state();
XCTAssert(state.address[4] == 0x1000, "A4 was %08x instead of 0x00001000", state.address[4]);
XCTAssert(state.data[2] == 0x303cfb2e, "D2 was %08x instead of 0x303cfb2e", state.data[2]);
}
- (void)testVectoredInterrupt {
_machine->set_program({
0x46fc, 0x2000, // MOVE.w #$2000, SR
0x4e71, // NOP
0x4e71, // NOP
0x4e71, // NOP
0x4e71, // NOP
0x4e71, // NOP
});
// Set the vector that will be supplied back to the start of the
// program; this will ensure no further exceptions following
// the interrupt.
const auto vector = _machine->ram_at(40);
vector[0] = 0x0000;
vector[1] = 0x1004;
_machine->run_for_instructions(3);
_machine->processor().set_interrupt_level(1);
_machine->run_for_instructions(1);
const auto state = _machine->processor().get_state();
XCTAssert(state.program_counter == 0x1008); // i.e. the interrupt happened, the instruction performed was the one at 1004, and therefore
// by the wonders of prefetch the program counter is now at 1008.
}
- (void)testOpcodeCoverage {
// Perform an audit of implemented instructions.
CPU::MC68000::ProcessorStorageTests storage_tests(
_machine->processor(),
[[NSBundle bundleForClass:[self class]] pathForResource:@"OPCLOGR2" ofType:@"BIN"].UTF8String
);
// This is a list of instructions nominated as valid with OPCLOGR2.BIN but with no obvious decoding —
// the disassemblers I tried couldn't figure them out, and I didn't spot them anywhere in the PRM.
NSSet<NSNumber *> *const undecodables = [NSSet setWithArray:@[
// These look like malformed MOVEs.
@(0x2e7d), @(0x2e7e), @(0x2e7f), @(0x2efd), @(0x2efe), @(0x2eff), @(0x2f7d), @(0x2f7e),
@(0x2f7f), @(0x2fc0), @(0x2fc1), @(0x2fc2), @(0x2fc3), @(0x2fc4), @(0x2fc5), @(0x2fc6),
@(0x2fc7), @(0x2fc8), @(0x2fc9), @(0x2fca), @(0x2fcb), @(0x2fcc), @(0x2fcd), @(0x2fce),
@(0x2fcf), @(0x2fd0), @(0x2fd1), @(0x2fd2), @(0x2fd3), @(0x2fd4), @(0x2fd5), @(0x2fd6),
@(0x2fd7), @(0x2fd8), @(0x2fd9), @(0x2fda), @(0x2fdb), @(0x2fdc), @(0x2fdd), @(0x2fde),
@(0x2fdf), @(0x2fe0), @(0x2fe1), @(0x2fe2), @(0x2fe3), @(0x2fe4), @(0x2fe5), @(0x2fe6),
@(0x2fe7), @(0x2fe8), @(0x2fe9), @(0x2fea), @(0x2feb), @(0x2fec), @(0x2fed), @(0x2fee),
@(0x2fef), @(0x2ff0), @(0x2ff1), @(0x2ff2), @(0x2ff3), @(0x2ff4), @(0x2ff5), @(0x2ff6),
@(0x2ff7), @(0x2ff8), @(0x2ff9), @(0x2ffa), @(0x2ffb), @(0x2ffc), @(0x2ffd), @(0x2ffe),
@(0x2fff),
@(0x3e7d), @(0x3e7e), @(0x3e7f), @(0x3efd), @(0x3efe), @(0x3eff), @(0x3f7d), @(0x3f7e),
@(0x3f7f), @(0x3fc0), @(0x3fc1), @(0x3fc2), @(0x3fc3), @(0x3fc4), @(0x3fc5), @(0x3fc6),
@(0x3fc7), @(0x3fc8), @(0x3fc9), @(0x3fca), @(0x3fcb), @(0x3fcc), @(0x3fcd), @(0x3fce),
@(0x3fcf), @(0x3fd0), @(0x3fd1), @(0x3fd2), @(0x3fd3), @(0x3fd4), @(0x3fd5), @(0x3fd6),
@(0x3fd7), @(0x3fd8), @(0x3fd9), @(0x3fda), @(0x3fdb), @(0x3fdc), @(0x3fdd), @(0x3fde),
@(0x3fdf), @(0x3fe0), @(0x3fe1), @(0x3fe2), @(0x3fe3), @(0x3fe4), @(0x3fe5), @(0x3fe6),
@(0x3fe7), @(0x3fe8), @(0x3fe9), @(0x3fea), @(0x3feb), @(0x3fec), @(0x3fed), @(0x3fee),
@(0x3fef), @(0x3ff0), @(0x3ff1), @(0x3ff2), @(0x3ff3), @(0x3ff4), @(0x3ff5), @(0x3ff6),
@(0x3ff7), @(0x3ff8), @(0x3ff9), @(0x3ffa), @(0x3ffb), @(0x3ffc), @(0x3ffd), @(0x3ffe),
@(0x3fff),
@(0x46c8), @(0x46c9), @(0x46ca), @(0x46cb), @(0x46cc), @(0x46cd), @(0x46ce), @(0x46cf),
@(0x46fd), @(0x46fe), @(0x46ff), @(0x47c0), @(0x47c1), @(0x47c2), @(0x47c3), @(0x47c4),
@(0x47c5), @(0x47c6), @(0x47c7), @(0x47c8), @(0x47c9), @(0x47ca), @(0x47cb), @(0x47cc),
@(0x47cd), @(0x47ce), @(0x47cf), @(0x47d8), @(0x47d9), @(0x47da), @(0x47db), @(0x47dc),
@(0x47dd), @(0x47de), @(0x47df), @(0x47e0), @(0x47e1), @(0x47e2), @(0x47e3), @(0x47e4),
@(0x47e5), @(0x47e6), @(0x47e7), @(0x47fc), @(0x47fd), @(0x47fe), @(0x47ff), @(0x4e80),
@(0x4e81), @(0x4e82), @(0x4e83), @(0x4e84), @(0x4e85), @(0x4e86), @(0x4e87), @(0x4e88),
@(0x4e89), @(0x4e8a), @(0x4e8b), @(0x4e8c), @(0x4e8d), @(0x4e8e), @(0x4e8f), @(0x4e98),
@(0x4e99), @(0x4e9a), @(0x4e9b), @(0x4e9c), @(0x4e9d), @(0x4e9e), @(0x4e9f), @(0x4ea0),
@(0x4ea1), @(0x4ea2), @(0x4ea3), @(0x4ea4), @(0x4ea5), @(0x4ea6), @(0x4ea7), @(0x4ebc),
@(0x4ebd), @(0x4ebe), @(0x4ebf), @(0x4ec0), @(0x4ec1), @(0x4ec2), @(0x4ec3), @(0x4ec4),
@(0x4ec5), @(0x4ec6), @(0x4ec7), @(0x4ec8), @(0x4ec9), @(0x4eca), @(0x4ecb), @(0x4ecc),
@(0x4ecd), @(0x4ece), @(0x4ecf), @(0x4ed8), @(0x4ed9), @(0x4eda), @(0x4edb), @(0x4edc),
@(0x4edd), @(0x4ede), @(0x4edf), @(0x4ee0), @(0x4ee1), @(0x4ee2), @(0x4ee3), @(0x4ee4),
@(0x4ee5), @(0x4ee6), @(0x4ee7), @(0x4efc), @(0x4efd), @(0x4efe), @(0x4eff), @(0x4f88),
@(0x4f89), @(0x4f8a), @(0x4f8b), @(0x4f8c), @(0x4f8d), @(0x4f8e), @(0x4f8f), @(0x4fbd),
@(0x4fbe), @(0x4fbf), @(0x4fc0), @(0x4fc1), @(0x4fc2), @(0x4fc3), @(0x4fc4), @(0x4fc5),
@(0x4fc6), @(0x4fc7), @(0x4fc8), @(0x4fc9), @(0x4fca), @(0x4fcb), @(0x4fcc), @(0x4fcd),
@(0x4fce), @(0x4fcf), @(0x4fd8), @(0x4fd9), @(0x4fda), @(0x4fdb), @(0x4fdc), @(0x4fdd),
@(0x4fde), @(0x4fdf), @(0x4fe0), @(0x4fe1), @(0x4fe2), @(0x4fe3), @(0x4fe4), @(0x4fe5),
@(0x4fe6), @(0x4fe7), @(0x4ffc), @(0x4ffd), @(0x4ffe), @(0x4fff),
@(0x50fa), @(0x50fb), @(0x50fc), @(0x50fd), @(0x50fe), @(0x50ff), @(0x51fa), @(0x51fb),
@(0x51fc), @(0x51fd), @(0x51fe), @(0x51ff), @(0x52fa), @(0x52fb), @(0x52fc), @(0x52fd),
@(0x52fe), @(0x52ff), @(0x53fa), @(0x53fb), @(0x53fc), @(0x53fd), @(0x53fe), @(0x53ff),
@(0x54fa), @(0x54fb), @(0x54fc), @(0x54fd), @(0x54fe), @(0x54ff), @(0x55fa), @(0x55fb),
@(0x55fc), @(0x55fd), @(0x55fe), @(0x55ff), @(0x56fa), @(0x56fb), @(0x56fc), @(0x56fd),
@(0x56fe), @(0x56ff), @(0x57fa), @(0x57fb), @(0x57fc), @(0x57fd), @(0x57fe), @(0x57ff),
@(0x58fa), @(0x58fb), @(0x58fc), @(0x58fd), @(0x58fe), @(0x58ff), @(0x59fa), @(0x59fb),
@(0x59fc), @(0x59fd), @(0x59fe), @(0x59ff), @(0x5afa), @(0x5afb), @(0x5afc), @(0x5afd),
@(0x5afe), @(0x5aff), @(0x5bfa), @(0x5bfb), @(0x5bfc), @(0x5bfd), @(0x5bfe), @(0x5bff),
@(0x5cfa), @(0x5cfb), @(0x5cfc), @(0x5cfd), @(0x5cfe), @(0x5cff), @(0x5dfa), @(0x5dfb),
@(0x5dfc), @(0x5dfd), @(0x5dfe), @(0x5dff), @(0x5eba), @(0x5ebb), @(0x5ebc), @(0x5ebd),
@(0x5ebe), @(0x5ebf), @(0x5efa), @(0x5efb), @(0x5efc), @(0x5efd), @(0x5efe), @(0x5eff),
@(0x5fba), @(0x5fbb), @(0x5fbc), @(0x5fbd), @(0x5fbe), @(0x5fbf), @(0x5ffa), @(0x5ffb),
@(0x5ffc), @(0x5ffd), @(0x5ffe), @(0x5fff),
// These are almost MOVEQs if only bit 8 weren't set.
@(0x71c8), @(0x71c9), @(0x71ca), @(0x71cb), @(0x71cc), @(0x71cd), @(0x71ce), @(0x71cf),
@(0x71d8), @(0x71d9), @(0x71da), @(0x71db), @(0x71dc), @(0x71dd), @(0x71de), @(0x71df),
@(0x71e8), @(0x71e9), @(0x71ea), @(0x71eb), @(0x71ec), @(0x71ed), @(0x71ee), @(0x71ef),
@(0x71f8), @(0x71f9), @(0x71fa), @(0x71fb), @(0x71fc), @(0x71fd), @(0x71fe), @(0x71ff),
@(0x73c8), @(0x73c9), @(0x73ca), @(0x73cb), @(0x73cc), @(0x73cd), @(0x73ce), @(0x73cf),
@(0x73d8), @(0x73d9), @(0x73da), @(0x73db), @(0x73dc), @(0x73dd), @(0x73de), @(0x73df),
@(0x73e8), @(0x73e9), @(0x73ea), @(0x73eb), @(0x73ec), @(0x73ed), @(0x73ee), @(0x73ef),
@(0x73f8), @(0x73f9), @(0x73fa), @(0x73fb), @(0x73fc), @(0x73fd), @(0x73fe), @(0x73ff),
@(0x75c8), @(0x75c9), @(0x75ca), @(0x75cb), @(0x75cc), @(0x75cd), @(0x75ce), @(0x75cf),
@(0x75d8), @(0x75d9), @(0x75da), @(0x75db), @(0x75dc), @(0x75dd), @(0x75de), @(0x75df),
@(0x75e8), @(0x75e9), @(0x75ea), @(0x75eb), @(0x75ec), @(0x75ed), @(0x75ee), @(0x75ef),
@(0x75f8), @(0x75f9), @(0x75fa), @(0x75fb), @(0x75fc), @(0x75fd), @(0x75fe), @(0x75ff),
@(0x77c0), @(0x77c1), @(0x77c2), @(0x77c3), @(0x77c4), @(0x77c5), @(0x77c6), @(0x77c7),
@(0x77c8), @(0x77c9), @(0x77ca), @(0x77cb), @(0x77cc), @(0x77cd), @(0x77ce), @(0x77cf),
@(0x77d0), @(0x77d1), @(0x77d2), @(0x77d3), @(0x77d4), @(0x77d5), @(0x77d6), @(0x77d7),
@(0x77d8), @(0x77d9), @(0x77da), @(0x77db), @(0x77dc), @(0x77dd), @(0x77de), @(0x77df),
@(0x77e0), @(0x77e1), @(0x77e2), @(0x77e3), @(0x77e4), @(0x77e5), @(0x77e6), @(0x77e7),
@(0x77e8), @(0x77e9), @(0x77ea), @(0x77eb), @(0x77ec), @(0x77ed), @(0x77ee), @(0x77ef),
@(0x77f0), @(0x77f1), @(0x77f2), @(0x77f3), @(0x77f4), @(0x77f5), @(0x77f6), @(0x77f7),
@(0x77f8), @(0x77f9), @(0x77fa), @(0x77fb), @(0x77fc), @(0x77fd), @(0x77fe), @(0x77ff),
@(0x79c8), @(0x79c9), @(0x79ca), @(0x79cb), @(0x79cc), @(0x79cd), @(0x79ce), @(0x79cf),
@(0x79d8), @(0x79d9), @(0x79da), @(0x79db), @(0x79dc), @(0x79dd), @(0x79de), @(0x79df),
@(0x79e8), @(0x79e9), @(0x79ea), @(0x79eb), @(0x79ec), @(0x79ed), @(0x79ee), @(0x79ef),
@(0x79f8), @(0x79f9), @(0x79fa), @(0x79fb), @(0x79fc), @(0x79fd), @(0x79fe), @(0x79ff),
@(0x7bc8), @(0x7bc9), @(0x7bca), @(0x7bcb), @(0x7bcc), @(0x7bcd), @(0x7bce), @(0x7bcf),
@(0x7bd8), @(0x7bd9), @(0x7bda), @(0x7bdb), @(0x7bdc), @(0x7bdd), @(0x7bde), @(0x7bdf),
@(0x7be8), @(0x7be9), @(0x7bea), @(0x7beb), @(0x7bec), @(0x7bed), @(0x7bee), @(0x7bef),
@(0x7bf8), @(0x7bf9), @(0x7bfa), @(0x7bfb), @(0x7bfc), @(0x7bfd), @(0x7bfe), @(0x7bff),
@(0x7dc8), @(0x7dc9), @(0x7dca), @(0x7dcb), @(0x7dcc), @(0x7dcd), @(0x7dce), @(0x7dcf),
@(0x7dd8), @(0x7dd9), @(0x7dda), @(0x7ddb), @(0x7ddc), @(0x7ddd), @(0x7dde), @(0x7ddf),
@(0x7de8), @(0x7de9), @(0x7dea), @(0x7deb), @(0x7dec), @(0x7ded), @(0x7dee), @(0x7def),
@(0x7df8), @(0x7df9), @(0x7dfa), @(0x7dfb), @(0x7dfc), @(0x7dfd), @(0x7dfe), @(0x7dff),
@(0x7f40), @(0x7f41), @(0x7f42), @(0x7f43), @(0x7f44), @(0x7f45), @(0x7f46), @(0x7f47),
@(0x7f48), @(0x7f49), @(0x7f4a), @(0x7f4b), @(0x7f4c), @(0x7f4d), @(0x7f4e), @(0x7f4f),
@(0x7f50), @(0x7f51), @(0x7f52), @(0x7f53), @(0x7f54), @(0x7f55), @(0x7f56), @(0x7f57),
@(0x7f58), @(0x7f59), @(0x7f5a), @(0x7f5b), @(0x7f5c), @(0x7f5d), @(0x7f5e), @(0x7f5f),
@(0x7f60), @(0x7f61), @(0x7f62), @(0x7f63), @(0x7f64), @(0x7f65), @(0x7f66), @(0x7f67),
@(0x7f68), @(0x7f69), @(0x7f6a), @(0x7f6b), @(0x7f6c), @(0x7f6d), @(0x7f6e), @(0x7f6f),
@(0x7f70), @(0x7f71), @(0x7f72), @(0x7f73), @(0x7f74), @(0x7f75), @(0x7f76), @(0x7f77),
@(0x7f78), @(0x7f79), @(0x7f7a), @(0x7f7b), @(0x7f7c), @(0x7f7d), @(0x7f7e), @(0x7f7f),
@(0x7f80), @(0x7f81), @(0x7f82), @(0x7f83), @(0x7f84), @(0x7f85), @(0x7f86), @(0x7f87),
@(0x7f88), @(0x7f89), @(0x7f8a), @(0x7f8b), @(0x7f8c), @(0x7f8d), @(0x7f8e), @(0x7f8f),
@(0x7f90), @(0x7f91), @(0x7f92), @(0x7f93), @(0x7f94), @(0x7f95), @(0x7f96), @(0x7f97),
@(0x7f98), @(0x7f99), @(0x7f9a), @(0x7f9b), @(0x7f9c), @(0x7f9d), @(0x7f9e), @(0x7f9f),
@(0x7fa0), @(0x7fa1), @(0x7fa2), @(0x7fa3), @(0x7fa4), @(0x7fa5), @(0x7fa6), @(0x7fa7),
@(0x7fa8), @(0x7fa9), @(0x7faa), @(0x7fab), @(0x7fac), @(0x7fad), @(0x7fae), @(0x7faf),
@(0x7fb0), @(0x7fb1), @(0x7fb2), @(0x7fb3), @(0x7fb4), @(0x7fb5), @(0x7fb6), @(0x7fb7),
@(0x7fb8), @(0x7fb9), @(0x7fba), @(0x7fbb), @(0x7fbc), @(0x7fbd), @(0x7fbe), @(0x7fbf),
@(0x7fc0), @(0x7fc1), @(0x7fc2), @(0x7fc3), @(0x7fc4), @(0x7fc5), @(0x7fc6), @(0x7fc7),
@(0x7fc8), @(0x7fc9), @(0x7fca), @(0x7fcb), @(0x7fcc), @(0x7fcd), @(0x7fce), @(0x7fcf),
@(0x7fd0), @(0x7fd1), @(0x7fd2), @(0x7fd3), @(0x7fd4), @(0x7fd5), @(0x7fd6), @(0x7fd7),
@(0x7fd8), @(0x7fd9), @(0x7fda), @(0x7fdb), @(0x7fdc), @(0x7fdd), @(0x7fde), @(0x7fdf),
@(0x7fe0), @(0x7fe1), @(0x7fe2), @(0x7fe3), @(0x7fe4), @(0x7fe5), @(0x7fe6), @(0x7fe7),
@(0x7fe8), @(0x7fe9), @(0x7fea), @(0x7feb), @(0x7fec), @(0x7fed), @(0x7fee), @(0x7fef),
@(0x7ff0), @(0x7ff1), @(0x7ff2), @(0x7ff3), @(0x7ff4), @(0x7ff5), @(0x7ff6), @(0x7ff7),
@(0x7ff8), @(0x7ff9), @(0x7ffa), @(0x7ffb), @(0x7ffc), @(0x7ffd), @(0x7ffe), @(0x7fff),
@(0xbe7d), @(0xbe7e), @(0xbe7f), @(0xbefd), @(0xbefe), @(0xbeff), @(0xbf7a), @(0xbf7b),
@(0xbf7c), @(0xbf7d), @(0xbf7e), @(0xbf7f), @(0xbffd), @(0xbffe), @(0xbfff),
//
@(0xc6c8), @(0xc6c9), @(0xc6ca), @(0xc6cb), @(0xc6cc), @(0xc6cd), @(0xc6ce), @(0xc6cf),
@(0xc6fd), @(0xc6fe), @(0xc6ff), @(0xc7c8), @(0xc7c9), @(0xc7ca), @(0xc7cb), @(0xc7cc),
@(0xc7cd), @(0xc7ce), @(0xc7cf), @(0xc7fd), @(0xc7fe), @(0xc7ff), @(0xce88), @(0xce89),
@(0xce8a), @(0xce8b), @(0xce8c), @(0xce8d), @(0xce8e), @(0xce8f), @(0xcebd), @(0xcebe),
@(0xcebf), @(0xcec8), @(0xcec9), @(0xceca), @(0xcecb), @(0xcecc), @(0xcecd), @(0xcece),
@(0xcecf), @(0xcefd), @(0xcefe), @(0xceff), @(0xcf80), @(0xcf81), @(0xcf82), @(0xcf83),
@(0xcf84), @(0xcf85), @(0xcf86), @(0xcf87), @(0xcfba), @(0xcfbb), @(0xcfbc), @(0xcfbd),
@(0xcfbe), @(0xcfbf), @(0xcfc8), @(0xcfc9), @(0xcfca), @(0xcfcb), @(0xcfcc), @(0xcfcd),
@(0xcfce), @(0xcfcf), @(0xcffd), @(0xcffe), @(0xcfff),
// These are from the Bcc/BRA/BSR page.
@(0xd0fd), @(0xd0fe), @(0xd0ff), @(0xd1fd), @(0xd1fe), @(0xd1ff), @(0xd2fd), @(0xd2fe),
@(0xd2ff), @(0xd3fd), @(0xd3fe), @(0xd3ff), @(0xd4fd), @(0xd4fe), @(0xd4ff), @(0xd5fd),
@(0xd5fe), @(0xd5ff), @(0xd6fd), @(0xd6fe), @(0xd6ff), @(0xd7fd), @(0xd7fe), @(0xd7ff),
@(0xd8fd), @(0xd8fe), @(0xd8ff), @(0xd9fd), @(0xd9fe), @(0xd9ff), @(0xdafd), @(0xdafe),
@(0xdaff), @(0xdbfd), @(0xdbfe), @(0xdbff), @(0xdcfd), @(0xdcfe), @(0xdcff), @(0xddfd),
@(0xddfe), @(0xddff), @(0xdebd), @(0xdebe), @(0xdebf), @(0xdefd), @(0xdefe), @(0xdeff),
@(0xdfba), @(0xdfbb), @(0xdfbc), @(0xdfbd), @(0xdfbe), @(0xdfbf), @(0xdffd), @(0xdffe),
@(0xdfff),
// The E line is for shifts and rolls; none of the those listed below appear to nominate valid
// addressing modes.
@(0xe6c0), @(0xe6c1), @(0xe6c2), @(0xe6c3), @(0xe6c4), @(0xe6c5), @(0xe6c6), @(0xe6c7),
@(0xe6c8), @(0xe6c9), @(0xe6ca), @(0xe6cb), @(0xe6cc), @(0xe6cd), @(0xe6ce), @(0xe6cf),
@(0xe6fa), @(0xe6fb), @(0xe6fc), @(0xe6fd), @(0xe6fe), @(0xe6ff), @(0xe7c0), @(0xe7c1),
@(0xe7c2), @(0xe7c3), @(0xe7c4), @(0xe7c5), @(0xe7c6), @(0xe7c7), @(0xe7c8), @(0xe7c9),
@(0xe7ca), @(0xe7cb), @(0xe7cc), @(0xe7cd), @(0xe7ce), @(0xe7cf), @(0xe7fa), @(0xe7fb),
@(0xe7fc), @(0xe7fd), @(0xe7fe), @(0xe7ff), @(0xeec0), @(0xeec1), @(0xeec2), @(0xeec3),
@(0xeec4), @(0xeec5), @(0xeec6), @(0xeec7), @(0xeec8), @(0xeec9), @(0xeeca), @(0xeecb),
@(0xeecc), @(0xeecd), @(0xeece), @(0xeecf), @(0xeed0), @(0xeed1), @(0xeed2), @(0xeed3),
@(0xeed4), @(0xeed5), @(0xeed6), @(0xeed7), @(0xeed8), @(0xeed9), @(0xeeda), @(0xeedb),
@(0xeedc), @(0xeedd), @(0xeede), @(0xeedf), @(0xeee0), @(0xeee1), @(0xeee2), @(0xeee3),
@(0xeee4), @(0xeee5), @(0xeee6), @(0xeee7), @(0xeee8), @(0xeee9), @(0xeeea), @(0xeeeb),
@(0xeeec), @(0xeeed), @(0xeeee), @(0xeeef), @(0xeef0), @(0xeef1), @(0xeef2), @(0xeef3),
@(0xeef4), @(0xeef5), @(0xeef6), @(0xeef7), @(0xeef8), @(0xeef9), @(0xeefa), @(0xeefb),
@(0xeefc), @(0xeefd), @(0xeefe), @(0xeeff), @(0xefc0), @(0xefc1), @(0xefc2), @(0xefc3),
@(0xefc4), @(0xefc5), @(0xefc6), @(0xefc7), @(0xefc8), @(0xefc9), @(0xefca), @(0xefcb),
@(0xefcc), @(0xefcd), @(0xefce), @(0xefcf), @(0xefd0), @(0xefd1), @(0xefd2), @(0xefd3),
@(0xefd4), @(0xefd5), @(0xefd6), @(0xefd7), @(0xefd8), @(0xefd9), @(0xefda), @(0xefdb),
@(0xefdc), @(0xefdd), @(0xefde), @(0xefdf), @(0xefe0), @(0xefe1), @(0xefe2), @(0xefe3),
@(0xefe4), @(0xefe5), @(0xefe6), @(0xefe7), @(0xefe8), @(0xefe9), @(0xefea), @(0xefeb),
@(0xefec), @(0xefed), @(0xefee), @(0xefef), @(0xeff0), @(0xeff1), @(0xeff2), @(0xeff3),
@(0xeff4), @(0xeff5), @(0xeff6), @(0xeff7), @(0xeff8), @(0xeff9), @(0xeffa), @(0xeffb),
@(0xeffc), @(0xeffd), @(0xeffe), @(0xefff)
]];
NSSet<NSNumber *> *const falseValids = storage_tests.false_valids();
NSSet<NSNumber *> *const falseInvalids = storage_tests.false_invalids();
XCTAssert(!falseValids.count, "%@ opcodes should be invalid but aren't: %@", @(falseValids.count), falseValids.hexDump);
NSMutableSet<NSNumber *> *const decodedUndecodables = [undecodables mutableCopy];
[decodedUndecodables minusSet:falseInvalids];
XCTAssert(!decodedUndecodables.count, "This test considers these undecodable but they were decoded: %@", decodedUndecodables.hexDump);
NSMutableSet<NSNumber *> *const trimmedInvalids = [falseInvalids mutableCopy];
[trimmedInvalids minusSet:undecodables];
XCTAssert(!trimmedInvalids.count, "%@ opcodes should be valid but aren't: %@", @(trimmedInvalids.count), trimmedInvalids.hexDump);
// XCTAssert(!falseInvalids.count, "%@ opcodes should be valid but aren't: %@", @(falseInvalids.count), falseInvalids.hexDump);
}
@end

View File

@ -1,140 +0,0 @@
//
// ArrayBuilderTests.m
// Clock Signal
//
// Created by Thomas Harte on 19/11/2016.
// Copyright 2016 Thomas Harte. All rights reserved.
//
#import <XCTest/XCTest.h>
#include "ArrayBuilder.hpp"
static NSData *inputData, *outputData;
static void setData(bool is_input, uint8_t *data, size_t size)
{
NSData *dataObject = [NSData dataWithBytes:data length:size];
if(is_input) inputData = dataObject; else outputData = dataObject;
}
@interface ArrayBuilderTests : XCTestCase
@end
@implementation ArrayBuilderTests
+ (void)setUp
{
inputData = nil;
outputData = nil;
}
- (void)assertMonotonicForInputSize:(size_t)inputSize outputSize:(size_t)outputSize
{
XCTAssert(inputData != nil, @"Should have received some input data");
XCTAssert(outputData != nil, @"Should have received some output data");
XCTAssert(inputData.length == inputSize, @"Input data should be %lu bytes long, was %lu", inputSize, (unsigned long)inputData.length);
XCTAssert(outputData.length == outputSize, @"Output data should be %lu bytes long, was %lu", outputSize, (unsigned long)outputData.length);
if(inputData.length == inputSize && outputData.length == outputSize)
{
uint8_t *input = (uint8_t *)inputData.bytes;
uint8_t *output = (uint8_t *)outputData.bytes;
for(int c = 0; c < inputSize; c++) XCTAssert(input[c] == c, @"Input item %d should be %d, was %d", c, c, input[c]);
for(int c = 0; c < outputSize; c++) XCTAssert(output[c] == c + 0x80, @"Output item %d should be %d, was %d", c, c+0x80, output[c]);
}
}
- (std::function<void(uint8_t *input, size_t input_size, uint8_t *output, size_t output_size)>)emptyFlushFunction
{
return [=] (uint8_t *input, size_t input_size, uint8_t *output, size_t output_size) {};
}
- (void)testSingleWriteSingleFlush
{
Outputs::CRT::ArrayBuilder arrayBuilder(200, 100, setData);
uint8_t *input = arrayBuilder.get_input_storage(5);
uint8_t *output = arrayBuilder.get_output_storage(3);
for(int c = 0; c < 5; c++) input[c] = c;
for(int c = 0; c < 3; c++) output[c] = c + 0x80;
arrayBuilder.flush(self.emptyFlushFunction);
arrayBuilder.submit();
[self assertMonotonicForInputSize:5 outputSize:3];
}
- (void)testDoubleWriteSingleFlush
{
Outputs::CRT::ArrayBuilder arrayBuilder(200, 100, setData);
uint8_t *input;
uint8_t *output;
input = arrayBuilder.get_input_storage(2);
output = arrayBuilder.get_output_storage(2);
for(int c = 0; c < 2; c++) input[c] = c;
for(int c = 0; c < 2; c++) output[c] = c + 0x80;
input = arrayBuilder.get_input_storage(2);
output = arrayBuilder.get_output_storage(2);
for(int c = 0; c < 2; c++) input[c] = c+2;
for(int c = 0; c < 2; c++) output[c] = c+2 + 0x80;
arrayBuilder.flush(self.emptyFlushFunction);
arrayBuilder.submit();
[self assertMonotonicForInputSize:4 outputSize:4];
}
- (void)testSubmitWithoutFlush
{
Outputs::CRT::ArrayBuilder arrayBuilder(200, 100, setData);
arrayBuilder.get_input_storage(5);
arrayBuilder.get_input_storage(8);
arrayBuilder.get_output_storage(6);
arrayBuilder.get_input_storage(12);
arrayBuilder.get_output_storage(3);
arrayBuilder.submit();
XCTAssert(inputData.length == 0, @"No input data should have been received; %lu bytes were received", (unsigned long)inputData.length);
XCTAssert(outputData.length == 0, @"No output data should have been received; %lu bytes were received", (unsigned long)outputData.length);
arrayBuilder.flush(self.emptyFlushFunction);
arrayBuilder.submit();
XCTAssert(inputData.length == 25, @"All input data should have been received; %lu bytes were received", (unsigned long)inputData.length);
XCTAssert(outputData.length == 9, @"All output data should have been received; %lu bytes were received", (unsigned long)outputData.length);
}
- (void)testSubmitContinuity
{
Outputs::CRT::ArrayBuilder arrayBuilder(200, 100, setData);
arrayBuilder.get_input_storage(5);
arrayBuilder.get_output_storage(5);
arrayBuilder.flush(self.emptyFlushFunction);
uint8_t *input = arrayBuilder.get_input_storage(5);
uint8_t *output = arrayBuilder.get_output_storage(5);
arrayBuilder.submit();
for(int c = 0; c < 5; c++) input[c] = c;
for(int c = 0; c < 5; c++) output[c] = c + 0x80;
arrayBuilder.flush(self.emptyFlushFunction);
arrayBuilder.submit();
[self assertMonotonicForInputSize:5 outputSize:5];
}
@end

View File

@ -0,0 +1,59 @@
//
// Comparative68000.hpp
// Clock SignalTests
//
// Created by Thomas Harte on 29/04/2019.
// Copyright © 2019 Thomas Harte. All rights reserved.
//
#ifndef Comparative68000_hpp
#define Comparative68000_hpp
#include <zlib.h>
#include "68000.hpp"
class ComparativeBusHandler: public CPU::MC68000::BusHandler {
public:
ComparativeBusHandler(const char *trace_name) {
trace = gzopen(trace_name, "rt");
}
~ComparativeBusHandler() {
gzclose(trace);
}
void will_perform(uint32_t address, uint16_t opcode) {
// Obtain the next line from the trace file.
char correct_state[300] = "\n";
gzgets(trace, correct_state, sizeof(correct_state));
++line_count;
// Generate state locally.
const auto state = get_state();
char local_state[300];
sprintf(local_state, "%04x: %02x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x\n",
address,
state.status,
state.data[0], state.data[1], state.data[2], state.data[3], state.data[4], state.data[5], state.data[6], state.data[7],
state.address[0], state.address[1], state.address[2], state.address[3], state.address[4], state.address[5], state.address[6],
(state.status & 0x2000) ? state.supervisor_stack_pointer : state.user_stack_pointer
);
// Check that the two coincide.
if(strcmp(correct_state, local_state)) {
fprintf(stderr, "Diverges at line %d\n", line_count);
fprintf(stderr, "Good: %s", correct_state);
fprintf(stderr, "Bad: %s", local_state);
assert(false);
}
}
virtual CPU::MC68000::ProcessorState get_state() = 0;
private:
int line_count = 0;
gzFile trace;
};
#endif /* Comparative68000_hpp */

View File

@ -0,0 +1,120 @@
//
// EmuTOSTests.m
// Clock SignalTests
//
// Created by Thomas Harte on 10/03/2019.
// Copyright © 2019 Thomas Harte. All rights reserved.
//
#import <XCTest/XCTest.h>
#include <array>
#include <cassert>
#include "68000.hpp"
#include "Comparative68000.hpp"
#include "CSROMFetcher.hpp"
class EmuTOS: public ComparativeBusHandler {
public:
EmuTOS(const std::vector<uint8_t> &emuTOS, const char *trace_name) : ComparativeBusHandler(trace_name), m68000_(*this) {
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);
}
CPU::MC68000::ProcessorState get_state() override {
return m68000_.get_state();
}
HalfCycles perform_bus_operation(const CPU::MC68000::Microcycle &cycle, int is_supervisor) {
const uint32_t address = cycle.word_address();
uint32_t word_address = address;
// 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.
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;
uint16_t *const base = is_rom ? emuTOS_.data() : ram_.data();
if(is_rom) {
word_address %= emuTOS_.size();
} else {
word_address %= ram_.size();
}
using Microcycle = CPU::MC68000::Microcycle;
if(cycle.data_select_active()) {
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;
}
}
switch(cycle.operation & (Microcycle::SelectWord | Microcycle::SelectByte | Microcycle::Read)) {
default: break;
case Microcycle::SelectWord | Microcycle::Read:
cycle.value->full = is_peripheral ? peripheral_result : base[word_address];
break;
case Microcycle::SelectByte | Microcycle::Read:
cycle.value->halves.low = (is_peripheral ? peripheral_result : base[word_address]) >> cycle.byte_shift();
break;
case Microcycle::SelectWord:
base[word_address] = cycle.value->full;
break;
case Microcycle::SelectByte:
base[word_address] = (cycle.value->halves.low << cycle.byte_shift()) | (base[word_address] & (0xffff ^ cycle.byte_mask()));
break;
}
}
return HalfCycles(0);
}
private:
CPU::MC68000::Processor<EmuTOS, true, true> m68000_;
std::vector<uint16_t> emuTOS_;
std::array<uint16_t, 256*1024> ram_;
};
@interface EmuTOSTests : XCTestCase
@end
@implementation EmuTOSTests {
std::unique_ptr<EmuTOS> _machine;
}
- (void)testImage:(NSString *)image trace:(NSString *)trace length:(int)length {
const auto roms = CSROMFetcher()("AtariST", { image.UTF8String });
NSString *const traceLocation = [[NSBundle bundleForClass:[self class]] pathForResource:trace ofType:@"trace.txt.gz"];
_machine.reset(new EmuTOS(*roms[0], traceLocation.UTF8String));
_machine->run_for(HalfCycles(length));
}
- (void)testEmuTOSStartup {
[self testImage:@"etos192uk.img" trace:@"etos192uk" length:313490];
// TODO: assert that machine is now STOPped.
}
- (void)testTOSStartup {
[self testImage:@"tos100.img" trace:@"tos100" length:54011091];
}
@end

View File

@ -0,0 +1,112 @@
//
// EmuTOSTests.m
// Clock SignalTests
//
// Created by Thomas Harte on 10/03/2019.
// Copyright © 2019 Thomas Harte. All rights reserved.
//
#import <XCTest/XCTest.h>
#include <array>
#include <cassert>
#include <iostream>
#include <fstream>
#include <zlib.h>
#include "68000.hpp"
#include "Comparative68000.hpp"
#include "CSROMFetcher.hpp"
class QL: public ComparativeBusHandler {
public:
QL(const std::vector<uint8_t> &rom, const char *trace_name) : ComparativeBusHandler(trace_name), m68000_(*this) {
assert(!(rom.size() & 1));
rom_.resize(rom.size() / 2);
for(size_t c = 0; c < rom_.size(); ++c) {
rom_[c] = (rom[c << 1] << 8) | rom[(c << 1) + 1];
}
}
void run_for(HalfCycles cycles) {
m68000_.run_for(cycles);
}
CPU::MC68000::ProcessorState get_state() override {
return m68000_.get_state();
}
HalfCycles perform_bus_operation(const CPU::MC68000::Microcycle &cycle, int is_supervisor) {
const uint32_t address = cycle.word_address();
uint32_t word_address = address;
// QL memory map: ROM is in the lowest area; RAM is from 0x20000.
const bool is_rom = word_address < rom_.size();
const bool is_ram = word_address >= 0x10000 && word_address < 0x10000+ram_.size();
const bool is_peripheral = !is_ram && !is_rom;
uint16_t *const base = is_rom ? rom_.data() : ram_.data();
if(is_rom) {
word_address %= rom_.size();
}
if(is_ram) {
word_address %= ram_.size();
}
using Microcycle = CPU::MC68000::Microcycle;
if(cycle.data_select_active()) {
uint16_t peripheral_result = 0xffff;
switch(cycle.operation & (Microcycle::SelectWord | Microcycle::SelectByte | Microcycle::Read)) {
default: break;
case Microcycle::SelectWord | Microcycle::Read:
cycle.value->full = is_peripheral ? peripheral_result : base[word_address];
break;
case Microcycle::SelectByte | Microcycle::Read:
cycle.value->halves.low = (is_peripheral ? peripheral_result : base[word_address]) >> cycle.byte_shift();
break;
case Microcycle::SelectWord:
assert(!(is_rom && !is_peripheral));
if(!is_peripheral) base[word_address] = cycle.value->full;
break;
case Microcycle::SelectByte:
assert(!(is_rom && !is_peripheral));
if(!is_peripheral) base[word_address] = (cycle.value->halves.low << cycle.byte_shift()) | (base[word_address] & (0xffff ^ cycle.byte_mask()));
break;
}
}
return HalfCycles(0);
}
private:
CPU::MC68000::Processor<QL, true, true> m68000_;
std::vector<uint16_t> rom_;
std::array<uint16_t, 64*1024> ram_;
};
@interface QLTests : XCTestCase
@end
@implementation QLTests {
std::unique_ptr<QL> _machine;
}
/*!
Tests the progression of Clock Signal's 68000 through the Sinclair QL's ROM against a known-good trace.
*/
- (void)testStartup {
const auto roms = CSROMFetcher()("SinclairQL", {"js.rom"});
NSString *const traceLocation = [[NSBundle bundleForClass:[self class]] pathForResource:@"qltrace" ofType:@".txt.gz"];
_machine.reset(new QL(*roms[0], traceLocation.UTF8String));
// This is how many cycles it takes to exhaust the supplied trace file.
_machine->run_for(HalfCycles(23923180));
}
@end

View File

@ -24,6 +24,7 @@ class Z80MachineCycleTests: XCTestCase {
case .portRead: opName = "i"
case .portWrite: opName = "o"
case .internalOperation: opName = "iop"
default: opName = "?"
}
return "\(opName) \(length)"
}
@ -35,7 +36,7 @@ class Z80MachineCycleTests: XCTestCase {
// Create a machine and install the supplied program at address 0, setting the PC to run from there
let machine = CSTestMachineZ80()
machine.setValue(0x0000, for: .programCounter)
machine.setData(Data(bytes: program), atAddress: 0x0000)
machine.setData(Data(_: program), atAddress: 0x0000)
// Figure out the total number of cycles implied by the bus cycles
var totalCycles: Int32 = 0

View File

@ -14,7 +14,7 @@ class Z80MemptrTests: XCTestCase {
private func test(program : [UInt8], length : Int32, initialValue : UInt16) -> UInt16 {
// Create a machine and install the supplied program at address 0, setting the PC to run from there
machine.setValue(0x0000, for: .programCounter)
machine.setData(Data(bytes: program), atAddress: 0x0000)
machine.setData(Data(_: program), atAddress: 0x0000)
// Set the initial value of memptr, run for the requested number of cycles,
// return the new value
@ -316,7 +316,7 @@ class Z80MemptrTests: XCTestCase {
let program: [UInt8] = [
0xed, 0xa1
]
machine.setData(Data(bytes: program), atAddress: 0x0000)
machine.setData(Data(_: program), atAddress: 0x0000)
machine.setValue(0, for: .memPtr)
for c in 1 ..< 65536 {
@ -332,7 +332,7 @@ class Z80MemptrTests: XCTestCase {
let program: [UInt8] = [
0xed, 0xa9
]
machine.setData(Data(bytes: program), atAddress: 0x0000)
machine.setData(Data(_: program), atAddress: 0x0000)
machine.setValue(0, for: .memPtr)
for c in 1 ..< 65536 {

View File

@ -67,6 +67,7 @@ SOURCES += glob.glob('../../Outputs/OpenGL/*.cpp')
SOURCES += glob.glob('../../Outputs/OpenGL/Primitives/*.cpp')
SOURCES += glob.glob('../../Processors/6502/Implementation/*.cpp')
SOURCES += glob.glob('../../Processors/68000/Implementation/*.cpp')
SOURCES += glob.glob('../../Processors/Z80/Implementation/*.cpp')
SOURCES += glob.glob('../../SignalProcessing/*.cpp')

View File

@ -23,8 +23,8 @@
#include <ios>
#include <iomanip>
#define PADHEX(n) std::hex << std::setw(n) << std::right << std::setfill('0')
#define PADDEC(n) std::dec << std::setw(n) << std::right << std::setfill('0')
#define PADHEX(n) std::hex << std::setfill('0') << std::setw(n)
#define PADDEC(n) std::dec << std::setfill('0') << std::setw(n)
#define LOG(x) std::cout << x << std::endl
#define LOGNBR(x) std::cout << x

View File

@ -28,7 +28,7 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
// These plus program below act to give the compiler permission to update these values
// without touching the class storage (i.e. it explicitly says they need be completely up
// to date in this stack frame only); which saves some complicated addressing
RegisterPair nextAddress = next_address_;
RegisterPair16 nextAddress = next_address_;
BusOperation nextBusOperation = next_bus_operation_;
uint16_t busAddress = bus_address_;
uint8_t *busValue = bus_value_;
@ -147,8 +147,8 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
}
case CycleIncPCPushPCH: pc_.full++; // deliberate fallthrough
case CyclePushPCH: push(pc_.bytes.high); break;
case CyclePushPCL: push(pc_.bytes.low); break;
case CyclePushPCH: push(pc_.halves.high); break;
case CyclePushPCL: push(pc_.halves.low); break;
case CyclePushOperand: push(operand_); break;
case CyclePushA: push(a_); break;
case CyclePushX: push(x_); break;
@ -175,8 +175,8 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
continue;
case OperationNMIPickVector: nextAddress.full = 0xfffa; continue;
case OperationRSTPickVector: nextAddress.full = 0xfffc; continue;
case CycleReadVectorLow: read_mem(pc_.bytes.low, nextAddress.full); break;
case CycleReadVectorHigh: read_mem(pc_.bytes.high, nextAddress.full+1); break;
case CycleReadVectorLow: read_mem(pc_.halves.low, nextAddress.full); break;
case CycleReadVectorHigh: read_mem(pc_.halves.high, nextAddress.full+1); break;
case OperationSetIRQFlags:
inverse_interrupt_flag_ = 0;
if(is_65c02(personality)) decimal_flag_ = false;
@ -185,8 +185,8 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
if(is_65c02(personality)) decimal_flag_ = false;
continue;
case CyclePullPCL: s_++; read_mem(pc_.bytes.low, s_ | 0x100); break;
case CyclePullPCH: s_++; read_mem(pc_.bytes.high, s_ | 0x100); break;
case CyclePullPCL: s_++; read_mem(pc_.halves.low, s_ | 0x100); break;
case CyclePullPCH: s_++; read_mem(pc_.halves.high, s_ | 0x100); break;
case CyclePullA: s_++; read_mem(a_, s_ | 0x100); break;
case CyclePullX: s_++; read_mem(x_, s_ | 0x100); break;
case CyclePullY: s_++; read_mem(y_, s_ | 0x100); break;
@ -198,11 +198,11 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
case OperationSetFlagsFromX: zero_result_ = negative_result_ = x_; continue;
case OperationSetFlagsFromY: zero_result_ = negative_result_ = y_; continue;
case CycleIncrementPCAndReadStack: pc_.full++; throwaway_read(s_ | 0x100); break;
case CycleReadPCLFromAddress: read_mem(pc_.bytes.low, address_.full); break;
case CycleReadPCHFromAddressLowInc: address_.bytes.low++; read_mem(pc_.bytes.high, address_.full); break;
case CycleReadPCHFromAddressFixed: if(!address_.bytes.low) address_.bytes.high++; read_mem(pc_.bytes.high, address_.full); break;
case CycleReadPCHFromAddressInc: address_.full++; read_mem(pc_.bytes.high, address_.full); break;
case CycleIncrementPCAndReadStack: pc_.full++; throwaway_read(s_ | 0x100); break;
case CycleReadPCLFromAddress: read_mem(pc_.halves.low, address_.full); break;
case CycleReadPCHFromAddressLowInc: address_.halves.low++; read_mem(pc_.halves.high, address_.full); break;
case CycleReadPCHFromAddressFixed: if(!address_.halves.low) address_.halves.high++; read_mem(pc_.halves.high, address_.full); break;
case CycleReadPCHFromAddressInc: address_.full++; read_mem(pc_.halves.high, address_.full); break;
case CycleReadAndIncrementPC: {
uint16_t oldPC = pc_.full;
@ -244,10 +244,10 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
case OperationSTY: operand_ = y_; continue;
case OperationSTZ: operand_ = 0; continue;
case OperationSAX: operand_ = a_ & x_; continue;
case OperationSHA: operand_ = a_ & x_ & (address_.bytes.high+1); continue;
case OperationSHX: operand_ = x_ & (address_.bytes.high+1); continue;
case OperationSHY: operand_ = y_ & (address_.bytes.high+1); continue;
case OperationSHS: s_ = a_ & x_; operand_ = s_ & (address_.bytes.high+1); continue;
case OperationSHA: operand_ = a_ & x_ & (address_.halves.high+1); continue;
case OperationSHX: operand_ = x_ & (address_.halves.high+1); continue;
case OperationSHY: operand_ = y_ & (address_.halves.high+1); continue;
case OperationSHS: s_ = a_ & x_; operand_ = s_ & (address_.halves.high+1); continue;
case OperationLXA:
a_ = x_ = (a_ | 0xee) & operand_;
@ -475,28 +475,28 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
case CycleAddXToAddressLow:
nextAddress.full = address_.full + x_;
address_.bytes.low = nextAddress.bytes.low;
if(address_.bytes.high != nextAddress.bytes.high) {
address_.halves.low = nextAddress.halves.low;
if(address_.halves.high != nextAddress.halves.high) {
page_crossing_stall_read();
break;
}
continue;
case CycleAddXToAddressLowRead:
nextAddress.full = address_.full + x_;
address_.bytes.low = nextAddress.bytes.low;
address_.halves.low = nextAddress.halves.low;
page_crossing_stall_read();
break;
case CycleAddYToAddressLow:
nextAddress.full = address_.full + y_;
address_.bytes.low = nextAddress.bytes.low;
if(address_.bytes.high != nextAddress.bytes.high) {
address_.halves.low = nextAddress.halves.low;
if(address_.halves.high != nextAddress.halves.high) {
page_crossing_stall_read();
break;
}
continue;
case CycleAddYToAddressLowRead:
nextAddress.full = address_.full + y_;
address_.bytes.low = nextAddress.bytes.low;
address_.halves.low = nextAddress.halves.low;
page_crossing_stall_read();
break;
@ -507,37 +507,37 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
continue;
case CycleIncrementPCFetchAddressLowFromOperand:
pc_.full++;
read_mem(address_.bytes.low, operand_);
read_mem(address_.halves.low, operand_);
break;
case CycleAddXToOperandFetchAddressLow:
operand_ += x_;
read_mem(address_.bytes.low, operand_);
read_mem(address_.halves.low, operand_);
break;
case CycleFetchAddressLowFromOperand:
read_mem(address_.bytes.low, operand_);
read_mem(address_.halves.low, operand_);
break;
case CycleIncrementOperandFetchAddressHigh:
operand_++;
read_mem(address_.bytes.high, operand_);
read_mem(address_.halves.high, operand_);
break;
case CycleIncrementPCReadPCHLoadPCL: // deliberate fallthrough
pc_.full++;
case CycleReadPCHLoadPCL: {
uint16_t oldPC = pc_.full;
pc_.bytes.low = operand_;
read_mem(pc_.bytes.high, oldPC);
pc_.halves.low = operand_;
read_mem(pc_.halves.high, oldPC);
} break;
case CycleReadAddressHLoadAddressL:
address_.bytes.low = operand_; pc_.full++;
read_mem(address_.bytes.high, pc_.full);
address_.halves.low = operand_; pc_.full++;
read_mem(address_.halves.high, pc_.full);
break;
case CycleLoadAddressAbsolute: {
uint16_t nextPC = pc_.full+1;
pc_.full += 2;
address_.bytes.low = operand_;
read_mem(address_.bytes.high, nextPC);
address_.halves.low = operand_;
read_mem(address_.halves.high, nextPC);
} break;
case OperationLoadAddressZeroPage:
@ -583,8 +583,8 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
case CycleAddSignedOperandToPC:
nextAddress.full = static_cast<uint16_t>(pc_.full + (int8_t)operand_);
pc_.bytes.low = nextAddress.bytes.low;
if(nextAddress.bytes.high != pc_.bytes.high) {
pc_.halves.low = nextAddress.halves.low;
if(nextAddress.halves.high != pc_.halves.high) {
uint16_t halfUpdatedPc = pc_.full;
pc_.full = nextAddress.full;
throwaway_read(halfUpdatedPc);
@ -598,7 +598,7 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
continue;
case CycleFetchFromHalfUpdatedPC: {
uint16_t halfUpdatedPc = static_cast<uint16_t>(((pc_.bytes.low + (int8_t)operand_) & 0xff) | (pc_.bytes.high << 8));
uint16_t halfUpdatedPc = static_cast<uint16_t>(((pc_.halves.low + (int8_t)operand_) & 0xff) | (pc_.halves.high << 8));
throwaway_read(halfUpdatedPc);
} break;

View File

@ -205,7 +205,7 @@ class ProcessorStorage {
/*
Storage for the 6502 registers; F is stored as individual flags.
*/
RegisterPair pc_, last_operation_pc_;
RegisterPair16 pc_, last_operation_pc_;
uint8_t a_, x_, y_, s_ = 0;
uint8_t carry_flag_, negative_result_, zero_result_, decimal_flag_, overflow_flag_, inverse_interrupt_flag_ = 0;
@ -213,7 +213,7 @@ class ProcessorStorage {
Temporary state for the micro programs.
*/
uint8_t operation_, operand_;
RegisterPair address_, next_address_;
RegisterPair16 address_, next_address_;
/*
Temporary storage allowing a common dispatch point for calling perform_bus_operation;

294
Processors/68000/68000.hpp Normal file
View File

@ -0,0 +1,294 @@
//
// 68000.hpp
// Clock Signal
//
// Created by Thomas Harte on 08/03/2019.
// Copyright © 2019 Thomas Harte. All rights reserved.
//
#ifndef MC68000_h
#define MC68000_h
#include <cstdint>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <ostream>
#include <vector>
#include "../../ClockReceiver/ClockReceiver.hpp"
#include "../RegisterSizes.hpp"
namespace CPU {
namespace MC68000 {
/*!
A microcycle is an atomic unit of 68000 bus activity it is a single item large enough
fully to specify a sequence of bus events that occur without any possible interruption.
Concretely, a standard read cycle breaks down into at least two microcycles:
1) a 4 half-cycle length microcycle in which the address strobe is signalled; and
2) a 4 half-cycle length microcycle in which at least one of the data strobes is
signalled, and the data bus is sampled.
That is, assuming DTack were signalled when microcycle (1) ended. If not then additional
wait state microcycles would fall between those two parts.
The 68000 data sheet defines when the address becomes valid during microcycle (1), and
when the address strobe is actually asserted. But those timings are fixed. So simply
telling you that this was a microcycle during which the address trobe was signalled is
sufficient fully to describe the bus activity.
(Aside: see the 68000 template's definition for options re: implicit DTack; if your
68000 owner can always predict exactly how long it will hold DTack following observation
of an address-strobing microcycle, it can just supply those periods for accounting and
avoid the runtime cost of actual DTack emulation. But such as the bus allows.)
*/
struct Microcycle {
/// A NewAddress cycle is one in which the address strobe is initially low but becomes high;
/// this correlates to states 0 to 5 of a standard read/write cycle.
static const int NewAddress = 1 << 0;
/// A SameAddress cycle is one in which the address strobe is continuously asserted, but neither
/// of the data strobes are.
static const int SameAddress = 1 << 1;
/// A Reset cycle is one in which the RESET output is asserted.
static const int Reset = 1 << 2;
/// Indicates that the address and both data select strobes are active.
static const int SelectWord = 1 << 3;
/// Indicates that the address strobe and exactly one of the data strobes are active; you can determine
/// which by inspecting the low bit of the provided address. The RW line indicates a read.
static const int SelectByte = 1 << 4;
/// If set, indicates a read. Otherwise, a write.
static const int Read = 1 << 5;
/// Contains the value of line FC0 if it is not implicit via InterruptAcknowledge.
static const int IsData = 1 << 6;
/// Contains the value of line FC1 if it is not implicit via InterruptAcknowledge.
static const int IsProgram = 1 << 7;
/// The interrupt acknowledge cycle is that during which the 68000 seeks to obtain the vector for
/// an interrupt it plans to observe. Noted on a real 68000 by all FCs being set to 1.
static const int InterruptAcknowledge = 1 << 8;
/// Represents the state of the 68000's valid memory address line — indicating whether this microcycle
/// is synchronised with the E clock to satisfy a valid peripheral address request.
static const int IsPeripheral = 1 << 9;
/// Contains a valid combination of the various static const int flags, describing the operation
/// performed by this Microcycle.
int operation = 0;
/// Describes the duration of this Microcycle.
HalfCycles length = HalfCycles(4);
/*!
For expediency, this provides a full 32-bit byte-resolution address  e.g.
if reading indirectly via an address register, this will indicate the full
value of the address register.
The receiver should ignore bits 0 and 24+. Use word_address() automatically
to obtain the only the 68000's real address lines, giving a 23-bit address
at word resolution.
*/
const uint32_t *address = nullptr;
/*!
If this is a write cycle, dereference value to get the value loaded onto
the data bus.
If this is a read cycle, write the value on the data bus to it.
Otherwise, this value is undefined.
Byte values are provided via @c value.halves.low. @c value.halves.high is undefined.
This is true regardless of whether the upper or lower byte of a word is being
accessed.
Word values occupy the entirety of @c value.full.
*/
RegisterPair16 *value = nullptr;
/// @returns @c true if two Microcycles are equal; @c false otherwise.
bool operator ==(const Microcycle &rhs) const {
if(value != rhs.value) return false;
if(address != rhs.address) return false;
if(length != rhs.length) return false;
if(operation != rhs.operation) return false;
return true;
}
// Various inspectors.
/*! @returns true if any data select line is active; @c false otherwise. */
inline bool data_select_active() const {
return bool(operation & (SelectWord | SelectByte | InterruptAcknowledge));
}
/*!
@returns 0 if this byte access wants the low part of a 16-bit word; 8 if it wants the high part.
*/
inline unsigned int byte_shift() const {
return (((*address) & 1) << 3) ^ 8;
}
/*!
Obtains the mask to apply to a word that will leave only the byte this microcycle is selecting.
@returns 0x00ff if this byte access wants the low part of a 16-bit word; 0xff00 if it wants the high part.
*/
inline uint16_t byte_mask() const {
return uint16_t(0xff00) >> (((*address) & 1) << 3);
}
/*!
Obtains the mask to apply to a word that will leave only the byte this microcycle **isn't** selecting.
i.e. this is the part of a word that should be untouched by this microcycle.
@returns 0xff00 if this byte access wants the low part of a 16-bit word; 0x00ff if it wants the high part.
*/
inline uint16_t untouched_byte_mask() const {
return uint16_t(uint16_t(0xff) << (((*address) & 1) << 3));
}
/*!
Assuming this cycle is a byte write, mutates @c destination by writing the byte to the proper upper or
lower part, retaining the other half.
*/
uint16_t write_byte(uint16_t destination) const {
return uint16_t((destination & untouched_byte_mask()) | (value->halves.low << byte_shift()));
}
/*!
@returns non-zero if this is a byte read and 68000 LDS is asserted.
*/
inline int lower_data_select() const {
return (operation & SelectByte) & ((*address & 1) << 3);
}
/*!
@returns non-zero if this is a byte read and 68000 UDS is asserted.
*/
inline int upper_data_select() const {
return (operation & SelectByte) & ~((*address & 1) << 3);
}
/*!
@returns the address being accessed at the precision a 68000 supplies it
only 24 address bit precision, with the low bit shifted out. So it's the
68000 address at word precision: address 0 is the first word in the address
space, address 1 is the second word (i.e. the third and fourth bytes) in
the address space, etc.
*/
uint32_t word_address() const {
return (address ? (*address) & 0x00fffffe : 0) >> 1;
}
};
/*!
This is the prototype for a 68000 bus handler; real bus handlers can descend from this
in order to get default implementations of any changes that may occur in the expected interface.
*/
class BusHandler {
public:
/*!
Provides the bus handler with a single Microcycle to 'perform'.
FC0 and FC1 are provided inside the microcycle as the IsData and IsProgram
flags; FC2 is provided here as is_supervisor it'll be either 0 or 1.
*/
HalfCycles perform_bus_operation(const Microcycle &cycle, int is_supervisor) {
return HalfCycles(0);
}
void flush() {}
/*!
Provides information about the path of execution if enabled via the template.
*/
void will_perform(uint32_t address, uint16_t opcode) {}
};
#include "Implementation/68000Storage.hpp"
class ProcessorBase: public ProcessorStorage {
};
struct ProcessorState {
uint32_t data[8];
uint32_t address[7];
uint32_t user_stack_pointer, supervisor_stack_pointer;
uint32_t program_counter;
uint16_t status;
// TODO: More state needed to indicate current instruction, the processor's
// progress through it, and anything it has fetched so far.
// uint16_t current_instruction;
};
template <class T, bool dtack_is_implicit, bool signal_will_perform = false> class Processor: public ProcessorBase {
public:
Processor(T &bus_handler) : ProcessorBase(), bus_handler_(bus_handler) {}
void run_for(HalfCycles duration);
using State = ProcessorState;
/// @returns The current processor state.
State get_state();
/// Sets the processor to the supplied state.
void set_state(const State &);
/// Sets the DTack line — @c true for active, @c false for inactive.
inline void set_dtack(bool dtack) {
dtack_ = dtack;
}
/// Sets the VPA (valid peripheral address) line — @c true for active, @c false for inactive.
inline void set_is_peripheral_address(bool is_peripheral_address) {
is_peripheral_address_ = is_peripheral_address;
}
/// Sets the bus error line — @c true for active, @c false for inactive.
void set_bus_error(bool bus_error) {
bus_error_ = bus_error;
}
/// Sets the interrupt lines, IPL0, IPL1 and IPL2.
void set_interrupt_level(int interrupt_level) {
bus_interrupt_level_ = interrupt_level;
}
/// Sets the bus request line.
/// This are of functionality is TODO.
void set_bus_request(bool bus_request) {
bus_request_ = bus_request;
}
/// Sets the bus acknowledge line.
/// This are of functionality is TODO.
void set_bus_acknowledge(bool bus_acknowledge) {
bus_acknowledge_ = bus_acknowledge;
}
/// Sets the halt line.
void set_halt(bool halt) {
halt_ = halt;
}
private:
T &bus_handler_;
};
#include "Implementation/68000Implementation.hpp"
}
}
#endif /* MC68000_h */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,498 @@
//
// 68000Storage.hpp
// Clock Signal
//
// Created by Thomas Harte on 08/03/2019.
// Copyright © 2019 Thomas Harte. All rights reserved.
//
#ifndef MC68000Storage_h
#define MC68000Storage_h
class ProcessorStorage {
public:
ProcessorStorage();
protected:
RegisterPair32 data_[8];
RegisterPair32 address_[8];
RegisterPair32 program_counter_;
RegisterPair32 stack_pointers_[2]; // [0] = user stack pointer; [1] = supervisor; the values from here
// are copied into/out of address_[7] upon mode switches.
RegisterPair32 prefetch_queue_; // Each word will go into the low part of the word, then proceed upward.
enum class ExecutionState {
/// The normal mode, this means the 68000 is expending processing effort.
Executing,
/// The 68000 is in a holding loop, waiting for either DTack or to be notified of a bus error.
WaitingForDTack,
/// Occurs after executing a STOP instruction; the processor will idle waiting for an interrupt or reset.
Stopped,
/// Occurs at the end of the current bus cycle after detection of the HALT input, continuing until
/// HALT is no longer signalled.
Halted,
/// Signals a transition from some other straight directly to cueing up an interrupt.
BeginInterrupt,
} execution_state_ = ExecutionState::Executing;
Microcycle dtack_cycle_;
Microcycle stop_cycle_;
// Various status bits.
int is_supervisor_;
int interrupt_level_;
uint_fast32_t zero_result_; // The zero flag is set if this value is zero.
uint_fast32_t carry_flag_; // The carry flag is set if this value is non-zero.
uint_fast32_t extend_flag_; // The extend flag is set if this value is non-zero.
uint_fast32_t overflow_flag_; // The overflow flag is set if this value is non-zero.
uint_fast32_t negative_flag_; // The negative flag is set if this value is non-zero.
uint_fast32_t trace_flag_; // The trace flag is set if this value is non-zero.
// Bus inputs.
int bus_interrupt_level_ = 0;
bool dtack_ = false;
bool is_peripheral_address_ = false;
bool bus_error_ = false;
bool bus_request_ = false;
bool bus_acknowledge_ = false;
bool halt_ = false;
int accepted_interrupt_level_ = 0;
bool is_starting_interrupt_ = false;
// Generic sources and targets for memory operations;
// by convention: [0] = source, [1] = destination.
RegisterPair32 effective_address_[2];
RegisterPair32 source_bus_data_[1];
RegisterPair32 destination_bus_data_[1];
HalfCycles half_cycles_left_to_run_;
HalfCycles e_clock_phase_;
enum class Operation {
None,
ABCD, SBCD, NBCD,
ADDb, ADDw, ADDl,
ADDQb, ADDQw, ADDQl,
ADDAw, ADDAl,
ADDQAw, ADDQAl,
ADDXb, ADDXw, ADDXl,
SUBb, SUBw, SUBl,
SUBQb, SUBQw, SUBQl,
SUBAw, SUBAl,
SUBQAw, SUBQAl,
SUBXb, SUBXw, SUBXl,
MOVEb, MOVEw, MOVEl, MOVEq,
MOVEAw, MOVEAl,
MOVEtoSR, MOVEfromSR,
MOVEtoCCR,
ORItoSR, ORItoCCR,
ANDItoSR, ANDItoCCR,
EORItoSR, EORItoCCR,
BTSTb, BTSTl,
BCLRl, BCLRb,
CMPb, CMPw, CMPl,
TSTb, TSTw, TSTl,
JMP,
BRA, Bcc,
DBcc,
Scc,
CLRb, CLRw, CLRl,
NEGXb, NEGXw, NEGXl,
NEGb, NEGw, NEGl,
ASLb, ASLw, ASLl, ASLm,
ASRb, ASRw, ASRl, ASRm,
LSLb, LSLw, LSLl, LSLm,
LSRb, LSRw, LSRl, LSRm,
ROLb, ROLw, ROLl, ROLm,
RORb, RORw, RORl, RORm,
ROXLb, ROXLw, ROXLl, ROXLm,
ROXRb, ROXRw, ROXRl, ROXRm,
MOVEMtoRl, MOVEMtoRw,
MOVEMtoMl, MOVEMtoMw,
MOVEPtoRl, MOVEPtoRw,
MOVEPtoMl, MOVEPtoMw,
ANDb, ANDw, ANDl,
EORb, EORw, EORl,
NOTb, NOTw, NOTl,
ORb, ORw, ORl,
MULU, MULS,
DIVU, DIVS,
RTE_RTR,
TRAP, TRAPV,
CHK,
EXG, SWAP,
BCHGl, BCHGb,
BSETl, BSETb,
TAS,
EXTbtow, EXTwtol,
LINK, UNLINK,
STOP,
};
/*!
Bus steps are sequences of things to communicate to the bus.
Standard behaviour is: (i) perform microcycle; (ii) perform action.
*/
struct BusStep {
Microcycle microcycle;
enum class Action {
None,
/// Performs effective_address_[0] += 2.
IncrementEffectiveAddress0,
/// Performs effective_address_[1] += 2.
IncrementEffectiveAddress1,
/// Performs effective_address_[0] -= 2.
DecrementEffectiveAddress0,
/// Performs effective_address_[1] -= 2.
DecrementEffectiveAddress1,
/// Performs program_counter_ += 2.
IncrementProgramCounter,
/// Copies prefetch_queue_[1] to prefetch_queue_[0].
AdvancePrefetch,
/*!
Terminates an atomic program; if nothing else is pending, schedules the next instruction.
This action is special in that it usurps any included microcycle. So any Step with this
as its action acts as an end-of-list sentinel.
*/
ScheduleNextProgram
} action = Action::None;
inline bool operator ==(const BusStep &rhs) const {
if(action != rhs.action) return false;
return microcycle == rhs.microcycle;
}
inline bool is_terminal() const {
return action == Action::ScheduleNextProgram;
}
};
/*!
A micro-op is: (i) an action to take; and (ii) a sequence of bus operations
to perform after taking the action.
NOTE: this therefore has the opposite order of behaviour compared to a BusStep,
the action occurs BEFORE the bus operations, not after.
A nullptr bus_program terminates a sequence of micro operations; the is_terminal
test should be used to query for that. The action on the final operation will
be performed.
*/
struct MicroOp {
enum class Action: int {
None,
/// Does whatever this instruction says is the main operation.
PerformOperation,
/*
All of the below will honour the source and destination masks
in deciding where to apply their actions.
*/
/// Subtracts 1 from the [source/destination]_address.
Decrement1,
/// Subtracts 2 from the [source/destination]_address.
Decrement2,
/// Subtracts 4 from the [source/destination]_address.
Decrement4,
/// Adds 1 from the [source/destination]_address.
Increment1,
/// Adds 2 from the [source/destination]_address.
Increment2,
/// Adds 4 from the [source/destination]_address.
Increment4,
/// Copies the source and/or destination to effective_address_.
CopyToEffectiveAddress,
/// Peeking into the end of the prefetch queue, calculates the proper target of (d16,An) addressing.
CalcD16An,
/// Peeking into the end of the prefetch queue, calculates the proper target of (d8,An,Xn) addressing.
CalcD8AnXn,
/// Peeking into the prefetch queue, calculates the proper target of (d16,PC) addressing,
/// adjusting as though it had been performed after the proper PC fetches. The source
/// and destination mask flags affect only the destination of the result.
CalcD16PC,
/// Peeking into the prefetch queue, calculates the proper target of (d8,An,Xn) addressing,
/// adjusting as though it had been performed after the proper PC fetches. The source
/// and destination mask flags affect only the destination of the result.
CalcD8PCXn,
/// Sets the high word according to the MSB of the low word.
SignExtendWord,
/// Sets the high three bytes according to the MSB of the low byte.
SignExtendByte,
/// From the next word in the prefetch queue assembles a sign-extended long word in either or
/// both of effective_address_[0] and effective_address_[1].
AssembleWordAddressFromPrefetch,
/// From the next word in the prefetch queue assembles a 0-padded 32-bit long word in either or
/// both of bus_data_[0] and bus_data_[1].
AssembleWordDataFromPrefetch,
/// Copies the next two prefetch words into one of the effective_address_.
AssembleLongWordAddressFromPrefetch,
/// Copies the next two prefetch words into one of the bus_data_.
AssembleLongWordDataFromPrefetch,
/// Copies the low part of the prefetch queue into next_word_.
CopyNextWord,
/// Performs write-back of post-increment address and/or sign extensions as necessary.
MOVEMtoRComplete,
/// Performs write-back of pre-decrement address.
MOVEMtoMComplete,
// (i) inspects the prefetch queue to determine the length of this instruction and copies the next PC to destination_bus_data_;
// (ii) copies the stack pointer minus 4 to effective_address_[1];
// (iii) decrements the stack pointer by four.
PrepareJSR,
PrepareBSR,
// (i) copies the stack pointer to effective_address_[0];
// (ii) increments the stack pointer by four.
PrepareRTS,
// (i) fills in the proper stack addresses to the bus steps for this micro-op; and
// (ii) adjusts the stack pointer appropriately.
PrepareRTE_RTR,
// Performs the necessary status word substitution for the current interrupt level,
// and does the first part of initialising the trap steps.
PrepareINT,
// Observes the bus_error_, valid_peripheral_address_ and/or the value currently in
// source_bus_data_ to determine an interrupt vector, and fills in the final trap
// steps detail appropriately.
PrepareINTVector,
};
static const int SourceMask = 1 << 30;
static const int DestinationMask = 1 << 29;
int action = int(Action::None);
BusStep *bus_program = nullptr;
MicroOp() {}
MicroOp(int action) : action(action) {}
MicroOp(int action, BusStep *bus_program) : action(action), bus_program(bus_program) {}
MicroOp(Action action) : MicroOp(int(action)) {}
MicroOp(Action action, BusStep *bus_program) : MicroOp(int(action), bus_program) {}
inline bool is_terminal() const {
return bus_program == nullptr;
}
};
/*!
A program represents the implementation of a particular opcode, as a sequence
of micro-ops and, separately, the operation to perform plus whatever other
fields the operation requires.
*/
struct Program {
MicroOp *micro_operations = nullptr;
RegisterPair32 *source = nullptr;
RegisterPair32 *destination = nullptr;
RegisterPair32 *source_address = nullptr;
RegisterPair32 *destination_address = nullptr;
Operation operation;
bool requires_supervisor = false;
void set_source(ProcessorStorage &storage, int mode, int reg) {
source_address = &storage.address_[reg];
switch(mode) {
case 0: source = &storage.data_[reg]; break;
case 1: source = &storage.address_[reg]; break;
default: source = &storage.source_bus_data_[0]; break;
}
}
void set_destination(ProcessorStorage &storage, int mode, int reg) {
destination_address = &storage.address_[reg];
switch(mode) {
case 0: destination = &storage.data_[reg]; break;
case 1: destination = &storage.address_[reg]; break;
default: destination = &storage.destination_bus_data_[0]; break;
}
}
};
// Storage for all the sequences of bus steps and micro-ops used throughout
// the 68000.
std::vector<BusStep> all_bus_steps_;
std::vector<MicroOp> all_micro_ops_;
// A lookup table from instructions to implementations.
Program instructions[65536];
// Special steps and programs for exception handlers.
BusStep *reset_bus_steps_;
MicroOp *long_exception_micro_ops_; // i.e. those that leave 14 bytes on the stack — bus error and address error.
MicroOp *short_exception_micro_ops_; // i.e. those that leave 6 bytes on the stack — everything else (other than interrupts).
MicroOp *interrupt_micro_ops_;
// Special micro-op sequences and storage for conditionals.
BusStep *branch_taken_bus_steps_;
BusStep *branch_byte_not_taken_bus_steps_;
BusStep *branch_word_not_taken_bus_steps_;
BusStep *bsr_bus_steps_;
uint32_t dbcc_false_address_;
BusStep *dbcc_condition_true_steps_;
BusStep *dbcc_condition_false_no_branch_steps_;
BusStep *dbcc_condition_false_branch_steps_;
BusStep *movem_read_steps_;
BusStep *movem_write_steps_;
BusStep *trap_steps_;
BusStep *bus_error_steps_;
// Current bus step pointer, and outer program pointer.
Program *active_program_ = nullptr;
MicroOp *active_micro_op_ = nullptr;
BusStep *active_step_ = nullptr;
RegisterPair16 decoded_instruction_ = 0;
uint16_t next_word_ = 0;
/// Copies address_[7] to the proper stack pointer based on current mode.
void write_back_stack_pointer();
/// Sets or clears the supervisor flag, ensuring the stack pointer is properly updated.
void set_is_supervisor(bool);
// Transient storage for MOVEM, TRAP and others.
uint32_t precomputed_addresses_[65];
RegisterPair16 throwaway_value_;
uint32_t movem_final_address_;
/*!
Evaluates the conditional described by @c code and returns @c true or @c false to
indicate the result of that evaluation.
*/
inline bool evaluate_condition(uint8_t code) {
switch(code & 0xf) {
default:
case 0x00: return true; // true
case 0x01: return false; // false
case 0x02: return zero_result_ && !carry_flag_; // high
case 0x03: return !zero_result_ || carry_flag_; // low or same
case 0x04: return !carry_flag_; // carry clear
case 0x05: return carry_flag_; // carry set
case 0x06: return zero_result_; // not equal
case 0x07: return !zero_result_; // equal
case 0x08: return !overflow_flag_; // overflow clear
case 0x09: return overflow_flag_; // overflow set
case 0x0a: return !negative_flag_; // positive
case 0x0b: return negative_flag_; // negative
case 0x0c: // greater than or equal
return (negative_flag_ && overflow_flag_) || (!negative_flag_ && !overflow_flag_);
case 0x0d: // less than
return (negative_flag_ && !overflow_flag_) || (!negative_flag_ && overflow_flag_);
case 0x0e: // greater than
return zero_result_ && ((negative_flag_ && overflow_flag_) || (!negative_flag_ && !overflow_flag_));
case 0x0f: // less than or equal
return !zero_result_ || (negative_flag_ && !overflow_flag_) || (!negative_flag_ && overflow_flag_);
}
}
/*!
Fills in the appropriate addresses and values to complete the TRAP steps those
representing a short-form exception and mutates the status register as if one
were beginning.
*/
inline void populate_trap_steps(uint32_t vector, uint16_t status) {
// Fill in the status word value.
destination_bus_data_[0].full = status;
// Switch to supervisor mode, disable the trace bit.
set_is_supervisor(true);
trace_flag_ = 0;
// Pick a vector.
effective_address_[0].full = vector << 2;
// Schedule the proper stack activity.
precomputed_addresses_[0] = address_[7].full - 2; // PC.l
precomputed_addresses_[1] = address_[7].full - 6; // status word (in destination_bus_data_[0])
precomputed_addresses_[2] = address_[7].full - 4; // PC.h
address_[7].full -= 6;
// Set the default timing.
trap_steps_->microcycle.length = HalfCycles(8);
}
inline void populate_bus_error_steps(uint32_t vector, uint16_t status, uint16_t bus_status, RegisterPair32 faulting_address) {
// Fill in the status word value.
destination_bus_data_[0].halves.low.full = status;
destination_bus_data_[0].halves.high.full = bus_status;
effective_address_[1] = faulting_address;
// Switch to supervisor mode, disable the trace bit.
set_is_supervisor(true);
trace_flag_ = 0;
// Pick a vector.
effective_address_[0].full = vector << 2;
// Schedule the proper stack activity.
precomputed_addresses_[0] = address_[7].full - 2; // PC.l
precomputed_addresses_[1] = address_[7].full - 6; // status word
precomputed_addresses_[2] = address_[7].full - 4; // PC.h
precomputed_addresses_[3] = address_[7].full - 8; // current instruction
precomputed_addresses_[4] = address_[7].full - 10; // fault address.l
precomputed_addresses_[5] = address_[7].full - 14; // bus cycle status word
precomputed_addresses_[6] = address_[7].full - 12; // fault address.h
address_[7].full -= 14;
}
private:
friend class ProcessorStorageConstructor;
friend class ProcessorStorageTests;
};
#endif /* MC68000Storage_h */

View File

@ -13,16 +13,27 @@
namespace CPU {
union RegisterPair {
RegisterPair(uint16_t v) : full(v) {}
template <typename Full, typename Half> union RegisterPair {
RegisterPair(Full v) : full(v) {}
RegisterPair() {}
uint16_t full;
Full full;
#pragma pack(push, 1)
#if TARGET_RT_BIG_ENDIAN
struct {
uint8_t low, high;
} bytes;
Half high, low;
} halves;
#else
struct {
Half low, high;
} halves;
#endif
#pragma pack(pop)
};
typedef RegisterPair<uint16_t, uint8_t> RegisterPair16;
typedef RegisterPair<uint32_t, RegisterPair16> RegisterPair32;
}
#endif /* RegisterSizes_hpp */

View File

@ -23,38 +23,38 @@ uint16_t ProcessorBase::get_value_of_register(Register r) {
case Register::A: return a_;
case Register::Flags: return get_flags();
case Register::AF: return static_cast<uint16_t>((a_ << 8) | get_flags());
case Register::B: return bc_.bytes.high;
case Register::C: return bc_.bytes.low;
case Register::B: return bc_.halves.high;
case Register::C: return bc_.halves.low;
case Register::BC: return bc_.full;
case Register::D: return de_.bytes.high;
case Register::E: return de_.bytes.low;
case Register::D: return de_.halves.high;
case Register::E: return de_.halves.low;
case Register::DE: return de_.full;
case Register::H: return hl_.bytes.high;
case Register::L: return hl_.bytes.low;
case Register::H: return hl_.halves.high;
case Register::L: return hl_.halves.low;
case Register::HL: return hl_.full;
case Register::ADash: return afDash_.bytes.high;
case Register::FlagsDash: return afDash_.bytes.low;
case Register::ADash: return afDash_.halves.high;
case Register::FlagsDash: return afDash_.halves.low;
case Register::AFDash: return afDash_.full;
case Register::BDash: return bcDash_.bytes.high;
case Register::CDash: return bcDash_.bytes.low;
case Register::BDash: return bcDash_.halves.high;
case Register::CDash: return bcDash_.halves.low;
case Register::BCDash: return bcDash_.full;
case Register::DDash: return deDash_.bytes.high;
case Register::EDash: return deDash_.bytes.low;
case Register::DDash: return deDash_.halves.high;
case Register::EDash: return deDash_.halves.low;
case Register::DEDash: return deDash_.full;
case Register::HDash: return hlDash_.bytes.high;
case Register::LDash: return hlDash_.bytes.low;
case Register::HDash: return hlDash_.halves.high;
case Register::LDash: return hlDash_.halves.low;
case Register::HLDash: return hlDash_.full;
case Register::IXh: return ix_.bytes.high;
case Register::IXl: return ix_.bytes.low;
case Register::IXh: return ix_.halves.high;
case Register::IXl: return ix_.halves.low;
case Register::IX: return ix_.full;
case Register::IYh: return iy_.bytes.high;
case Register::IYl: return iy_.bytes.low;
case Register::IYh: return iy_.halves.high;
case Register::IYl: return iy_.halves.low;
case Register::IY: return iy_.full;
case Register::R: return ir_.bytes.low;
case Register::I: return ir_.bytes.high;
case Register::R: return ir_.halves.low;
case Register::I: return ir_.halves.high;
case Register::Refresh: return ir_.full;
case Register::IFF1: return iff1_ ? 1 : 0;
@ -76,38 +76,38 @@ void ProcessorBase::set_value_of_register(Register r, uint16_t value) {
case Register::AF: a_ = static_cast<uint8_t>(value >> 8); // deliberate fallthrough...
case Register::Flags: set_flags(static_cast<uint8_t>(value)); break;
case Register::B: bc_.bytes.high = static_cast<uint8_t>(value); break;
case Register::C: bc_.bytes.low = static_cast<uint8_t>(value); break;
case Register::B: bc_.halves.high = static_cast<uint8_t>(value); break;
case Register::C: bc_.halves.low = static_cast<uint8_t>(value); break;
case Register::BC: bc_.full = value; break;
case Register::D: de_.bytes.high = static_cast<uint8_t>(value); break;
case Register::E: de_.bytes.low = static_cast<uint8_t>(value); break;
case Register::D: de_.halves.high = static_cast<uint8_t>(value); break;
case Register::E: de_.halves.low = static_cast<uint8_t>(value); break;
case Register::DE: de_.full = value; break;
case Register::H: hl_.bytes.high = static_cast<uint8_t>(value); break;
case Register::L: hl_.bytes.low = static_cast<uint8_t>(value); break;
case Register::H: hl_.halves.high = static_cast<uint8_t>(value); break;
case Register::L: hl_.halves.low = static_cast<uint8_t>(value); break;
case Register::HL: hl_.full = value; break;
case Register::ADash: afDash_.bytes.high = static_cast<uint8_t>(value); break;
case Register::FlagsDash: afDash_.bytes.low = static_cast<uint8_t>(value); break;
case Register::ADash: afDash_.halves.high = static_cast<uint8_t>(value); break;
case Register::FlagsDash: afDash_.halves.low = static_cast<uint8_t>(value); break;
case Register::AFDash: afDash_.full = value; break;
case Register::BDash: bcDash_.bytes.high = static_cast<uint8_t>(value); break;
case Register::CDash: bcDash_.bytes.low = static_cast<uint8_t>(value); break;
case Register::BDash: bcDash_.halves.high = static_cast<uint8_t>(value); break;
case Register::CDash: bcDash_.halves.low = static_cast<uint8_t>(value); break;
case Register::BCDash: bcDash_.full = value; break;
case Register::DDash: deDash_.bytes.high = static_cast<uint8_t>(value); break;
case Register::EDash: deDash_.bytes.low = static_cast<uint8_t>(value); break;
case Register::DDash: deDash_.halves.high = static_cast<uint8_t>(value); break;
case Register::EDash: deDash_.halves.low = static_cast<uint8_t>(value); break;
case Register::DEDash: deDash_.full = value; break;
case Register::HDash: hlDash_.bytes.high = static_cast<uint8_t>(value); break;
case Register::LDash: hlDash_.bytes.low = static_cast<uint8_t>(value); break;
case Register::HDash: hlDash_.halves.high = static_cast<uint8_t>(value); break;
case Register::LDash: hlDash_.halves.low = static_cast<uint8_t>(value); break;
case Register::HLDash: hlDash_.full = value; break;
case Register::IXh: ix_.bytes.high = static_cast<uint8_t>(value); break;
case Register::IXl: ix_.bytes.low = static_cast<uint8_t>(value); break;
case Register::IXh: ix_.halves.high = static_cast<uint8_t>(value); break;
case Register::IXl: ix_.halves.low = static_cast<uint8_t>(value); break;
case Register::IX: ix_.full = value; break;
case Register::IYh: iy_.bytes.high = static_cast<uint8_t>(value); break;
case Register::IYl: iy_.bytes.low = static_cast<uint8_t>(value); break;
case Register::IYh: iy_.halves.high = static_cast<uint8_t>(value); break;
case Register::IYl: iy_.halves.low = static_cast<uint8_t>(value); break;
case Register::IY: iy_.full = value; break;
case Register::R: ir_.bytes.low = static_cast<uint8_t>(value); break;
case Register::I: ir_.bytes.high = static_cast<uint8_t>(value); break;
case Register::R: ir_.halves.low = static_cast<uint8_t>(value); break;
case Register::I: ir_.halves.high = static_cast<uint8_t>(value); break;
case Register::Refresh: ir_.full = value; break;
case Register::IFF1: iff1_ = !!value; break;

View File

@ -90,7 +90,7 @@ template < class T,
break;
case MicroOp::DecodeOperation:
refresh_addr_ = ir_;
ir_.bytes.low = (ir_.bytes.low & 0x80) | ((ir_.bytes.low + current_instruction_page_->r_step) & 0x7f);
ir_.halves.low = (ir_.halves.low & 0x80) | ((ir_.halves.low + current_instruction_page_->r_step) & 0x7f);
pc_.full += pc_increment_ & static_cast<uint16_t>(halt_mask_);
scheduled_program_counter_ = current_instruction_page_->instructions[operation_ & halt_mask_];
flag_adjustment_history_ <<= 1;
@ -108,13 +108,12 @@ template < class T,
case MicroOp::Move16: *static_cast<uint16_t *>(operation->destination) = *static_cast<uint16_t *>(operation->source); break;
case MicroOp::AssembleAF:
temp16_.bytes.high = a_;
temp16_.bytes.low = get_flags();
temp16_.halves.high = a_;
temp16_.halves.low = get_flags();
break;
case MicroOp::DisassembleAF:
a_ = temp16_.bytes.high;
set_flags(temp16_.bytes.low);
//
a_ = temp16_.halves.high;
set_flags(temp16_.halves.low);
break;
// MARK: - Logical
@ -179,8 +178,8 @@ template < class T,
// MARK: - Flow control
case MicroOp::DJNZ:
bc_.bytes.high--;
if(!bc_.bytes.high) {
bc_.halves.high--;
if(!bc_.halves.high) {
advance_operation();
}
break;
@ -460,10 +459,10 @@ template < class T,
case MicroOp::ExAFAFDash: {
const uint8_t a = a_;
const uint8_t f = get_flags();
set_flags(afDash_.bytes.low);
a_ = afDash_.bytes.high;
afDash_.bytes.high = a;
afDash_.bytes.low = f;
set_flags(afDash_.halves.low);
a_ = afDash_.halves.high;
afDash_.halves.high = a;
afDash_.halves.low = f;
} break;
case MicroOp::EXX: {
@ -554,13 +553,13 @@ template < class T,
#undef CPxR_STEP
#define INxR_STEP(dir) \
bc_.bytes.high--; \
bc_.halves.high--; \
hl_.full += dir; \
\
sign_result_ = zero_result_ = bit53_result_ = bc_.bytes.high; \
sign_result_ = zero_result_ = bit53_result_ = bc_.halves.high; \
subtract_flag_ = (temp8_ >> 6) & Flag::Subtract; \
\
const int next_bc = bc_.bytes.low + dir; \
const int next_bc = bc_.halves.low + dir; \
int summation = temp8_ + (next_bc&0xff); \
\
if(summation > 0xff) { \
@ -571,18 +570,18 @@ template < class T,
half_carry_result_ = 0; \
} \
\
summation = (summation&7) ^ bc_.bytes.high; \
summation = (summation&7) ^ bc_.halves.high; \
set_parity(summation); \
set_did_compute_flags();
case MicroOp::INDR: {
INxR_STEP(-1);
REPEAT(bc_.bytes.high);
REPEAT(bc_.halves.high);
} break;
case MicroOp::INIR: {
INxR_STEP(1);
REPEAT(bc_.bytes.high);
REPEAT(bc_.halves.high);
} break;
case MicroOp::IND: {
@ -598,13 +597,13 @@ template < class T,
#undef INxR_STEP
#define OUTxR_STEP(dir) \
bc_.bytes.high--; \
bc_.halves.high--; \
hl_.full += dir; \
\
sign_result_ = zero_result_ = bit53_result_ = bc_.bytes.high; \
sign_result_ = zero_result_ = bit53_result_ = bc_.halves.high; \
subtract_flag_ = (temp8_ >> 6) & Flag::Subtract; \
\
int summation = temp8_ + hl_.bytes.low; \
int summation = temp8_ + hl_.halves.low; \
if(summation > 0xff) { \
carry_result_ = Flag::Carry; \
half_carry_result_ = Flag::HalfCarry; \
@ -612,12 +611,12 @@ template < class T,
carry_result_ = half_carry_result_ = 0; \
} \
\
summation = (summation&7) ^ bc_.bytes.high; \
summation = (summation&7) ^ bc_.halves.high; \
set_parity(summation); \
set_did_compute_flags();
case MicroOp::OUT_R:
REPEAT(bc_.bytes.high);
REPEAT(bc_.halves.high);
break;
case MicroOp::OUTD: {
@ -638,7 +637,7 @@ template < class T,
const uint8_t result = *static_cast<uint8_t *>(operation->source) & (1 << ((operation_ >> 3)&7));
if(current_instruction_page_->is_indexed || ((operation_&0x07) == 6)) {
bit53_result_ = memptr_.bytes.high;
bit53_result_ = memptr_.halves.high;
} else {
bit53_result_ = *static_cast<uint8_t *>(operation->source);
}

View File

@ -86,48 +86,48 @@ ProcessorStorage::ProcessorStorage() {
#define Read5Inc(addr, val) Read5(addr, val), Inc16(addr)
#define WriteInc(addr, val) Write3(addr, val), {MicroOp::Increment16, &addr.full}
#define Read16Inc(addr, val) ReadInc(addr, val.bytes.low), ReadInc(addr, val.bytes.high)
#define Read16(addr, val) ReadInc(addr, val.bytes.low), Read3(addr, val.bytes.high)
#define Read16Inc(addr, val) ReadInc(addr, val.halves.low), ReadInc(addr, val.halves.high)
#define Read16(addr, val) ReadInc(addr, val.halves.low), Read3(addr, val.halves.high)
#define Write16(addr, val) WriteInc(addr, val.bytes.low), Write3(addr, val.bytes.high)
#define Write16(addr, val) WriteInc(addr, val.halves.low), Write3(addr, val.halves.high)
#define INDEX() {MicroOp::IndexedPlaceHolder}, ReadInc(pc_, temp8_), InternalOperation(10), {MicroOp::CalculateIndexAddress, &index}
#define FINDEX() {MicroOp::IndexedPlaceHolder}, ReadInc(pc_, temp8_), {MicroOp::CalculateIndexAddress, &index}
#define INDEX_ADDR() (add_offsets ? memptr_ : index)
#define Push(x) {MicroOp::Decrement16, &sp_.full}, Write3(sp_, x.bytes.high), {MicroOp::Decrement16, &sp_.full}, Write3(sp_, x.bytes.low)
#define Pop(x) Read3(sp_, x.bytes.low), {MicroOp::Increment16, &sp_.full}, Read3(sp_, x.bytes.high), {MicroOp::Increment16, &sp_.full}
#define Push(x) {MicroOp::Decrement16, &sp_.full}, Write3(sp_, x.halves.high), {MicroOp::Decrement16, &sp_.full}, Write3(sp_, x.halves.low)
#define Pop(x) Read3(sp_, x.halves.low), {MicroOp::Increment16, &sp_.full}, Read3(sp_, x.halves.high), {MicroOp::Increment16, &sp_.full}
#define Push8(x) {MicroOp::Decrement16, &sp_.full}, Write3(sp_, x.bytes.high), {MicroOp::Decrement16, &sp_.full}, Write5(sp_, x.bytes.low)
#define Pop7(x) Read3(sp_, x.bytes.low), {MicroOp::Increment16, &sp_.full}, Read4(sp_, x.bytes.high), {MicroOp::Increment16, &sp_.full}
#define Push8(x) {MicroOp::Decrement16, &sp_.full}, Write3(sp_, x.halves.high), {MicroOp::Decrement16, &sp_.full}, Write5(sp_, x.halves.low)
#define Pop7(x) Read3(sp_, x.halves.low), {MicroOp::Increment16, &sp_.full}, Read4(sp_, x.halves.high), {MicroOp::Increment16, &sp_.full}
/* The following are actual instructions */
#define NOP Sequence(BusOp(Refresh(4)))
#define JP(cc) StdInstr(Read16Inc(pc_, temp16_), {MicroOp::cc, nullptr}, {MicroOp::Move16, &temp16_.full, &pc_.full})
#define CALL(cc) StdInstr(ReadInc(pc_, temp16_.bytes.low), {MicroOp::cc, conditional_call_untaken_program_.data()}, Read4Inc(pc_, temp16_.bytes.high), Push(pc_), {MicroOp::Move16, &temp16_.full, &pc_.full})
#define CALL(cc) StdInstr(ReadInc(pc_, temp16_.halves.low), {MicroOp::cc, conditional_call_untaken_program_.data()}, Read4Inc(pc_, temp16_.halves.high), Push(pc_), {MicroOp::Move16, &temp16_.full, &pc_.full})
#define RET(cc) Instr(6, {MicroOp::cc, nullptr}, Pop(memptr_), {MicroOp::Move16, &memptr_.full, &pc_.full})
#define JR(cc) StdInstr(ReadInc(pc_, temp8_), {MicroOp::cc, nullptr}, InternalOperation(10), {MicroOp::CalculateIndexAddress, &pc_.full}, {MicroOp::Move16, &memptr_.full, &pc_.full})
#define RST() Instr(6, {MicroOp::CalculateRSTDestination}, Push(pc_), {MicroOp::Move16, &memptr_.full, &pc_.full})
#define LD(a, b) StdInstr({MicroOp::Move8, &b, &a})
#define LD_GROUP(r, ri) \
LD(r, bc_.bytes.high), LD(r, bc_.bytes.low), LD(r, de_.bytes.high), LD(r, de_.bytes.low), \
LD(r, index.bytes.high), LD(r, index.bytes.low), \
LD(r, bc_.halves.high), LD(r, bc_.halves.low), LD(r, de_.halves.high), LD(r, de_.halves.low), \
LD(r, index.halves.high), LD(r, index.halves.low), \
StdInstr(INDEX(), Read3(INDEX_ADDR(), temp8_), {MicroOp::Move8, &temp8_, &ri}), \
LD(r, a_)
#define READ_OP_GROUP(op) \
StdInstr({MicroOp::op, &bc_.bytes.high}), StdInstr({MicroOp::op, &bc_.bytes.low}), \
StdInstr({MicroOp::op, &de_.bytes.high}), StdInstr({MicroOp::op, &de_.bytes.low}), \
StdInstr({MicroOp::op, &index.bytes.high}), StdInstr({MicroOp::op, &index.bytes.low}), \
StdInstr({MicroOp::op, &bc_.halves.high}), StdInstr({MicroOp::op, &bc_.halves.low}), \
StdInstr({MicroOp::op, &de_.halves.high}), StdInstr({MicroOp::op, &de_.halves.low}), \
StdInstr({MicroOp::op, &index.halves.high}), StdInstr({MicroOp::op, &index.halves.low}), \
StdInstr(INDEX(), Read3(INDEX_ADDR(), temp8_), {MicroOp::op, &temp8_}), \
StdInstr({MicroOp::op, &a_})
#define READ_OP_GROUP_D(op) \
StdInstr({MicroOp::op, &bc_.bytes.high}), StdInstr({MicroOp::op, &bc_.bytes.low}), \
StdInstr({MicroOp::op, &de_.bytes.high}), StdInstr({MicroOp::op, &de_.bytes.low}), \
StdInstr({MicroOp::op, &index.bytes.high}), StdInstr({MicroOp::op, &index.bytes.low}), \
StdInstr({MicroOp::op, &bc_.halves.high}), StdInstr({MicroOp::op, &bc_.halves.low}), \
StdInstr({MicroOp::op, &de_.halves.high}), StdInstr({MicroOp::op, &de_.halves.low}), \
StdInstr({MicroOp::op, &index.halves.high}), StdInstr({MicroOp::op, &index.halves.low}), \
StdInstr(INDEX(), Read4Pre(INDEX_ADDR(), temp8_), {MicroOp::op, &temp8_}), \
StdInstr({MicroOp::op, &a_})
@ -135,19 +135,19 @@ ProcessorStorage::ProcessorStorage() {
#define RMWI(x, op, ...) StdInstr(Read4(INDEX_ADDR(), x), {MicroOp::op, &x}, Write3(INDEX_ADDR(), x))
#define MODIFY_OP_GROUP(op) \
StdInstr({MicroOp::op, &bc_.bytes.high}), StdInstr({MicroOp::op, &bc_.bytes.low}), \
StdInstr({MicroOp::op, &de_.bytes.high}), StdInstr({MicroOp::op, &de_.bytes.low}), \
StdInstr({MicroOp::op, &index.bytes.high}), StdInstr({MicroOp::op, &index.bytes.low}), \
StdInstr({MicroOp::op, &bc_.halves.high}), StdInstr({MicroOp::op, &bc_.halves.low}), \
StdInstr({MicroOp::op, &de_.halves.high}), StdInstr({MicroOp::op, &de_.halves.low}), \
StdInstr({MicroOp::op, &index.halves.high}), StdInstr({MicroOp::op, &index.halves.low}), \
RMW(temp8_, op), \
StdInstr({MicroOp::op, &a_})
#define IX_MODIFY_OP_GROUP(op) \
RMWI(bc_.bytes.high, op), \
RMWI(bc_.bytes.low, op), \
RMWI(de_.bytes.high, op), \
RMWI(de_.bytes.low, op), \
RMWI(hl_.bytes.high, op), \
RMWI(hl_.bytes.low, op), \
RMWI(bc_.halves.high, op), \
RMWI(bc_.halves.low, op), \
RMWI(de_.halves.high, op), \
RMWI(de_.halves.low, op), \
RMWI(hl_.halves.high, op), \
RMWI(hl_.halves.low, op), \
RMWI(temp8_, op), \
RMWI(a_, op)
@ -166,7 +166,7 @@ ProcessorStorage::ProcessorStorage() {
#define SBC16(d, s) StdInstr(InternalOperation(8), InternalOperation(6), {MicroOp::SBC16, &s.full, &d.full})
void ProcessorStorage::install_default_instruction_set() {
MicroOp conditional_call_untaken_program[] = Sequence(ReadInc(pc_, temp16_.bytes.high));
MicroOp conditional_call_untaken_program[] = Sequence(ReadInc(pc_, temp16_.halves.high));
copy_program(conditional_call_untaken_program, conditional_call_untaken_program_);
assemble_base_page(base_page_, hl_, false, cb_page_);
@ -225,12 +225,12 @@ void ProcessorStorage::install_default_instruction_set() {
};
MicroOp irq_mode2_program[] = {
{ MicroOp::BeginIRQ },
BusOp(IntAckStart(7, temp16_.bytes.low)),
BusOp(IntWait(temp16_.bytes.low)),
BusOp(IntAckEnd(temp16_.bytes.low)),
BusOp(IntAckStart(7, temp16_.halves.low)),
BusOp(IntWait(temp16_.halves.low)),
BusOp(IntAckEnd(temp16_.halves.low)),
BusOp(Refresh(4)),
Push(pc_),
{ MicroOp::Move8, &ir_.bytes.high, &temp16_.bytes.high },
{ MicroOp::Move8, &ir_.halves.high, &temp16_.halves.high },
Read16(temp16_, pc_),
{ MicroOp::MoveToNextProgram }
};
@ -253,27 +253,27 @@ void ProcessorStorage::assemble_ed_page(InstructionPage &target) {
NOP_ROW(), /* 0x10 */
NOP_ROW(), /* 0x20 */
NOP_ROW(), /* 0x30 */
/* 0x40 IN B, (C); 0x41 OUT (C), B */ IN_OUT(bc_.bytes.high),
/* 0x40 IN B, (C); 0x41 OUT (C), B */ IN_OUT(bc_.halves.high),
/* 0x42 SBC HL, BC */ SBC16(hl_, bc_), /* 0x43 LD (nn), BC */ StdInstr(Read16Inc(pc_, memptr_), Write16(memptr_, bc_)),
/* 0x44 NEG */ StdInstr({MicroOp::NEG}), /* 0x45 RETN */ StdInstr(Pop(pc_), {MicroOp::RETN}),
/* 0x46 IM 0 */ StdInstr({MicroOp::IM}), /* 0x47 LD I, A */ Instr(6, {MicroOp::Move8, &a_, &ir_.bytes.high}),
/* 0x48 IN C, (C); 0x49 OUT (C), C */ IN_OUT(bc_.bytes.low),
/* 0x46 IM 0 */ StdInstr({MicroOp::IM}), /* 0x47 LD I, A */ Instr(6, {MicroOp::Move8, &a_, &ir_.halves.high}),
/* 0x48 IN C, (C); 0x49 OUT (C), C */ IN_OUT(bc_.halves.low),
/* 0x4a ADC HL, BC */ ADC16(hl_, bc_), /* 0x4b LD BC, (nn) */ StdInstr(Read16Inc(pc_, temp16_), Read16(temp16_, bc_)),
/* 0x4c NEG */ StdInstr({MicroOp::NEG}), /* 0x4d RETI */ StdInstr(Pop(pc_), {MicroOp::RETN}),
/* 0x4e IM 0/1 */ StdInstr({MicroOp::IM}), /* 0x4f LD R, A */ Instr(6, {MicroOp::Move8, &a_, &ir_.bytes.low}),
/* 0x50 IN D, (C); 0x51 OUT (C), D */ IN_OUT(de_.bytes.high),
/* 0x4e IM 0/1 */ StdInstr({MicroOp::IM}), /* 0x4f LD R, A */ Instr(6, {MicroOp::Move8, &a_, &ir_.halves.low}),
/* 0x50 IN D, (C); 0x51 OUT (C), D */ IN_OUT(de_.halves.high),
/* 0x52 SBC HL, DE */ SBC16(hl_, de_), /* 0x53 LD (nn), DE */ StdInstr(Read16Inc(pc_, memptr_), Write16(memptr_, de_)),
/* 0x54 NEG */ StdInstr({MicroOp::NEG}), /* 0x55 RETN */ StdInstr(Pop(pc_), {MicroOp::RETN}),
/* 0x56 IM 1 */ StdInstr({MicroOp::IM}), /* 0x57 LD A, I */ Instr(6, {MicroOp::Move8, &ir_.bytes.high, &a_}, {MicroOp::SetAFlags}),
/* 0x58 IN E, (C); 0x59 OUT (C), E */ IN_OUT(de_.bytes.low),
/* 0x56 IM 1 */ StdInstr({MicroOp::IM}), /* 0x57 LD A, I */ Instr(6, {MicroOp::Move8, &ir_.halves.high, &a_}, {MicroOp::SetAFlags}),
/* 0x58 IN E, (C); 0x59 OUT (C), E */ IN_OUT(de_.halves.low),
/* 0x5a ADC HL, DE */ ADC16(hl_, de_), /* 0x5b LD DE, (nn) */ StdInstr(Read16Inc(pc_, temp16_), Read16(temp16_, de_)),
/* 0x5c NEG */ StdInstr({MicroOp::NEG}), /* 0x5d RETN */ StdInstr(Pop(pc_), {MicroOp::RETN}),
/* 0x5e IM 2 */ StdInstr({MicroOp::IM}), /* 0x5f LD A, R */ Instr(6, {MicroOp::Move8, &ir_.bytes.low, &a_}, {MicroOp::SetAFlags}),
/* 0x60 IN H, (C); 0x61 OUT (C), H */ IN_OUT(hl_.bytes.high),
/* 0x5e IM 2 */ StdInstr({MicroOp::IM}), /* 0x5f LD A, R */ Instr(6, {MicroOp::Move8, &ir_.halves.low, &a_}, {MicroOp::SetAFlags}),
/* 0x60 IN H, (C); 0x61 OUT (C), H */ IN_OUT(hl_.halves.high),
/* 0x62 SBC HL, HL */ SBC16(hl_, hl_), /* 0x63 LD (nn), HL */ StdInstr(Read16Inc(pc_, memptr_), Write16(memptr_, hl_)),
/* 0x64 NEG */ StdInstr({MicroOp::NEG}), /* 0x65 RETN */ StdInstr(Pop(pc_), {MicroOp::RETN}),
/* 0x66 IM 0 */ StdInstr({MicroOp::IM}), /* 0x67 RRD */ StdInstr(Read3(hl_, temp8_), InternalOperation(8), {MicroOp::RRD}, Write3(hl_, temp8_)),
/* 0x68 IN L, (C); 0x69 OUT (C), L */ IN_OUT(hl_.bytes.low),
/* 0x68 IN L, (C); 0x69 OUT (C), L */ IN_OUT(hl_.halves.low),
/* 0x6a ADC HL, HL */ ADC16(hl_, hl_), /* 0x6b LD HL, (nn) */ StdInstr(Read16Inc(pc_, temp16_), Read16(temp16_, hl_)),
/* 0x6c NEG */ StdInstr({MicroOp::NEG}), /* 0x6d RETN */ StdInstr(Pop(pc_), {MicroOp::RETN}),
/* 0x6e IM 0/1 */ StdInstr({MicroOp::IM}), /* 0x6f RLD */ StdInstr(Read3(hl_, temp8_), InternalOperation(8), {MicroOp::RLD}, Write3(hl_, temp8_)),
@ -316,7 +316,7 @@ void ProcessorStorage::assemble_ed_page(InstructionPage &target) {
#undef NOP_ROW
}
void ProcessorStorage::assemble_cb_page(InstructionPage &target, RegisterPair &index, bool add_offsets) {
void ProcessorStorage::assemble_cb_page(InstructionPage &target, RegisterPair16 &index, bool add_offsets) {
#define OCTO_OP_GROUP(m, x) m(x), m(x), m(x), m(x), m(x), m(x), m(x), m(x)
#define CB_PAGE(m, p) m(RLC), m(RRC), m(RL), m(RR), m(SLA), m(SRA), m(SLL), m(SRL), OCTO_OP_GROUP(p, BIT), OCTO_OP_GROUP(m, RES), OCTO_OP_GROUP(m, SET)
@ -343,7 +343,7 @@ void ProcessorStorage::assemble_cb_page(InstructionPage &target, RegisterPair &i
#undef CB_PAGE
}
void ProcessorStorage::assemble_base_page(InstructionPage &target, RegisterPair &index, bool add_offsets, InstructionPage &cb_page) {
void ProcessorStorage::assemble_base_page(InstructionPage &target, RegisterPair16 &index, bool add_offsets, InstructionPage &cb_page) {
#define INC_DEC_LD(r) \
StdInstr({MicroOp::Increment8, &r}), \
StdInstr({MicroOp::Decrement8, &r}), \
@ -360,14 +360,14 @@ void ProcessorStorage::assemble_base_page(InstructionPage &target, RegisterPair
/* 0x02 LD (BC), A */ StdInstr({MicroOp::SetAddrAMemptr, &bc_.full}, Write3(bc_, a_)),
/* 0x03 INC BC; 0x04 INC B; 0x05 DEC B; 0x06 LD B, n */
INC_INC_DEC_LD(bc_, bc_.bytes.high),
INC_INC_DEC_LD(bc_, bc_.halves.high),
/* 0x07 RLCA */ StdInstr({MicroOp::RLCA}),
/* 0x08 EX AF, AF' */ StdInstr({MicroOp::ExAFAFDash}), /* 0x09 ADD HL, BC */ ADD16(index, bc_),
/* 0x0a LD A, (BC) */ StdInstr({MicroOp::Move16, &bc_.full, &memptr_.full}, Read3(memptr_, a_), Inc16(memptr_)),
/* 0x0b DEC BC; 0x0c INC C; 0x0d DEC C; 0x0e LD C, n */
DEC_INC_DEC_LD(bc_, bc_.bytes.low),
DEC_INC_DEC_LD(bc_, bc_.halves.low),
/* 0x0f RRCA */ StdInstr({MicroOp::RRCA}),
/* 0x10 DJNZ */ Instr(6, ReadInc(pc_, temp8_), {MicroOp::DJNZ}, InternalOperation(10), {MicroOp::CalculateIndexAddress, &pc_.full}, {MicroOp::Move16, &memptr_.full, &pc_.full}),
@ -375,7 +375,7 @@ void ProcessorStorage::assemble_base_page(InstructionPage &target, RegisterPair
/* 0x12 LD (DE), A */ StdInstr({MicroOp::SetAddrAMemptr, &de_.full}, Write3(de_, a_)),
/* 0x13 INC DE; 0x14 INC D; 0x15 DEC D; 0x16 LD D, n */
INC_INC_DEC_LD(de_, de_.bytes.high),
INC_INC_DEC_LD(de_, de_.halves.high),
/* 0x17 RLA */ StdInstr({MicroOp::RLA}),
/* 0x18 JR */ StdInstr(ReadInc(pc_, temp8_), InternalOperation(10), {MicroOp::CalculateIndexAddress, &pc_.full}, {MicroOp::Move16, &memptr_.full, &pc_.full}),
@ -383,21 +383,21 @@ void ProcessorStorage::assemble_base_page(InstructionPage &target, RegisterPair
/* 0x1a LD A, (DE) */ StdInstr({MicroOp::Move16, &de_.full, &memptr_.full}, Read3(memptr_, a_), Inc16(memptr_)),
/* 0x1b DEC DE; 0x1c INC E; 0x1d DEC E; 0x1e LD E, n */
DEC_INC_DEC_LD(de_, de_.bytes.low),
DEC_INC_DEC_LD(de_, de_.halves.low),
/* 0x1f RRA */ StdInstr({MicroOp::RRA}),
/* 0x20 JR NZ */ JR(TestNZ), /* 0x21 LD HL, nn */ StdInstr(Read16Inc(pc_, index)),
/* 0x22 LD (nn), HL */ StdInstr(Read16Inc(pc_, memptr_), Write16(memptr_, index)),
/* 0x23 INC HL; 0x24 INC H; 0x25 DEC H; 0x26 LD H, n */
INC_INC_DEC_LD(index, index.bytes.high),
INC_INC_DEC_LD(index, index.halves.high),
/* 0x27 DAA */ StdInstr({MicroOp::DAA}),
/* 0x28 JR Z */ JR(TestZ), /* 0x29 ADD HL, HL */ ADD16(index, index),
/* 0x2a LD HL, (nn) */ StdInstr(Read16Inc(pc_, temp16_), Read16(temp16_, index)),
/* 0x2b DEC HL; 0x2c INC L; 0x2d DEC L; 0x2e LD L, n */
DEC_INC_DEC_LD(index, index.bytes.low),
DEC_INC_DEC_LD(index, index.halves.low),
/* 0x2f CPL */ StdInstr({MicroOp::CPL}),
/* 0x30 JR NC */ JR(TestNC), /* 0x31 LD SP, nn */ StdInstr(Read16Inc(pc_, sp_)),
@ -418,29 +418,29 @@ void ProcessorStorage::assemble_base_page(InstructionPage &target, RegisterPair
/* 0x3f CCF */ StdInstr({MicroOp::CCF}),
/* 0x40 LD B, B; 0x41 LD B, C; 0x42 LD B, D; 0x43 LD B, E; 0x44 LD B, H; 0x45 LD B, L; 0x46 LD B, (HL); 0x47 LD B, A */
LD_GROUP(bc_.bytes.high, bc_.bytes.high),
LD_GROUP(bc_.halves.high, bc_.halves.high),
/* 0x48 LD C, B; 0x49 LD C, C; 0x4a LD C, D; 0x4b LD C, E; 0x4c LD C, H; 0x4d LD C, L; 0x4e LD C, (HL); 0x4f LD C, A */
LD_GROUP(bc_.bytes.low, bc_.bytes.low),
LD_GROUP(bc_.halves.low, bc_.halves.low),
/* 0x50 LD D, B; 0x51 LD D, C; 0x52 LD D, D; 0x53 LD D, E; 0x54 LD D, H; 0x55 LD D, L; 0x56 LD D, (HL); 0x57 LD D, A */
LD_GROUP(de_.bytes.high, de_.bytes.high),
LD_GROUP(de_.halves.high, de_.halves.high),
/* 0x58 LD E, B; 0x59 LD E, C; 0x5a LD E, D; 0x5b LD E, E; 0x5c LD E, H; 0x5d LD E, L; 0x5e LD E, (HL); 0x5f LD E, A */
LD_GROUP(de_.bytes.low, de_.bytes.low),
LD_GROUP(de_.halves.low, de_.halves.low),
/* 0x60 LD H, B; 0x61 LD H, C; 0x62 LD H, D; 0x63 LD H, E; 0x64 LD H, H; 0x65 LD H, L; 0x66 LD H, (HL); 0x67 LD H, A */
LD_GROUP(index.bytes.high, hl_.bytes.high),
LD_GROUP(index.halves.high, hl_.halves.high),
/* 0x68 LD L, B; 0x69 LD L, C; 0x6a LD L, D; 0x6b LD L, E; 0x6c LD L, H; 0x6d LD H, L; 0x6e LD L, (HL); 0x6f LD L, A */
LD_GROUP(index.bytes.low, hl_.bytes.low),
LD_GROUP(index.halves.low, hl_.halves.low),
/* 0x70 LD (HL), B */ StdInstr(INDEX(), Write3(INDEX_ADDR(), bc_.bytes.high)),
/* 0x71 LD (HL), C */ StdInstr(INDEX(), Write3(INDEX_ADDR(), bc_.bytes.low)),
/* 0x72 LD (HL), D */ StdInstr(INDEX(), Write3(INDEX_ADDR(), de_.bytes.high)),
/* 0x73 LD (HL), E */ StdInstr(INDEX(), Write3(INDEX_ADDR(), de_.bytes.low)),
/* 0x74 LD (HL), H */ StdInstr(INDEX(), Write3(INDEX_ADDR(), hl_.bytes.high)), // neither of these stores parts of the index register;
/* 0x75 LD (HL), L */ StdInstr(INDEX(), Write3(INDEX_ADDR(), hl_.bytes.low)), // they always store exactly H and L.
/* 0x70 LD (HL), B */ StdInstr(INDEX(), Write3(INDEX_ADDR(), bc_.halves.high)),
/* 0x71 LD (HL), C */ StdInstr(INDEX(), Write3(INDEX_ADDR(), bc_.halves.low)),
/* 0x72 LD (HL), D */ StdInstr(INDEX(), Write3(INDEX_ADDR(), de_.halves.high)),
/* 0x73 LD (HL), E */ StdInstr(INDEX(), Write3(INDEX_ADDR(), de_.halves.low)),
/* 0x74 LD (HL), H */ StdInstr(INDEX(), Write3(INDEX_ADDR(), hl_.halves.high)), // neither of these stores parts of the index register;
/* 0x75 LD (HL), L */ StdInstr(INDEX(), Write3(INDEX_ADDR(), hl_.halves.low)), // they always store exactly H and L.
/* 0x76 HALT */ StdInstr({MicroOp::HALT}),
/* 0x77 LD (HL), A */ StdInstr(INDEX(), Write3(INDEX_ADDR(), a_)),
@ -478,16 +478,16 @@ void ProcessorStorage::assemble_base_page(InstructionPage &target, RegisterPair
/* 0xc7 RST 00h */ RST(),
/* 0xc8 RET Z */ RET(TestZ), /* 0xc9 RET */ StdInstr(Pop(pc_)),
/* 0xca JP Z */ JP(TestZ), /* 0xcb [CB page] */StdInstr(FINDEX(), {MicroOp::SetInstructionPage, &cb_page}),
/* 0xcc CALL Z */ CALL(TestZ), /* 0xcd CALL */ StdInstr(ReadInc(pc_, temp16_.bytes.low), Read4Inc(pc_, temp16_.bytes.high), Push(pc_), {MicroOp::Move16, &temp16_.full, &pc_.full}),
/* 0xcc CALL Z */ CALL(TestZ), /* 0xcd CALL */ StdInstr(ReadInc(pc_, temp16_.halves.low), Read4Inc(pc_, temp16_.halves.high), Push(pc_), {MicroOp::Move16, &temp16_.full, &pc_.full}),
/* 0xce ADC A, n */ StdInstr(ReadInc(pc_, temp8_), {MicroOp::ADC8, &temp8_}),
/* 0xcf RST 08h */ RST(),
/* 0xd0 RET NC */ RET(TestNC), /* 0xd1 POP DE */ StdInstr(Pop(de_)),
/* 0xd2 JP NC */ JP(TestNC), /* 0xd3 OUT (n), A */StdInstr(ReadInc(pc_, temp16_.bytes.low), {MicroOp::Move8, &a_, &temp16_.bytes.high}, Output(temp16_, a_)),
/* 0xd2 JP NC */ JP(TestNC), /* 0xd3 OUT (n), A */StdInstr(ReadInc(pc_, temp16_.halves.low), {MicroOp::Move8, &a_, &temp16_.halves.high}, Output(temp16_, a_)),
/* 0xd4 CALL NC */ CALL(TestNC), /* 0xd5 PUSH DE */ Instr(6, Push(de_)),
/* 0xd6 SUB n */ StdInstr(ReadInc(pc_, temp8_), {MicroOp::SUB8, &temp8_}),
/* 0xd7 RST 10h */ RST(),
/* 0xd8 RET C */ RET(TestC), /* 0xd9 EXX */ StdInstr({MicroOp::EXX}),
/* 0xda JP C */ JP(TestC), /* 0xdb IN A, (n) */StdInstr(ReadInc(pc_, temp16_.bytes.low), {MicroOp::Move8, &a_, &temp16_.bytes.high}, Input(temp16_, a_)),
/* 0xda JP C */ JP(TestC), /* 0xdb IN A, (n) */StdInstr(ReadInc(pc_, temp16_.halves.low), {MicroOp::Move8, &a_, &temp16_.halves.high}, Input(temp16_, a_)),
/* 0xdc CALL C */ CALL(TestC), /* 0xdd [DD page] */StdInstr({MicroOp::SetInstructionPage, &dd_page_}),
/* 0xde SBC A, n */ StdInstr(ReadInc(pc_, temp8_), {MicroOp::SBC8, &temp8_}),
/* 0xdf RST 18h */ RST(),

View File

@ -118,10 +118,10 @@ class ProcessorStorage {
void install_default_instruction_set();
uint8_t a_;
RegisterPair bc_, de_, hl_;
RegisterPair afDash_, bcDash_, deDash_, hlDash_;
RegisterPair ix_, iy_, pc_, sp_;
RegisterPair ir_, refresh_addr_;
RegisterPair16 bc_, de_, hl_;
RegisterPair16 afDash_, bcDash_, deDash_, hlDash_;
RegisterPair16 ix_, iy_, pc_, sp_;
RegisterPair16 ir_, refresh_addr_;
bool iff1_ = false, iff2_ = false;
int interrupt_mode_ = 0;
uint16_t pc_increment_ = 1;
@ -153,7 +153,7 @@ class ProcessorStorage {
bool wait_line_ = false;
uint8_t operation_;
RegisterPair temp16_, memptr_;
RegisterPair16 temp16_, memptr_;
uint8_t temp8_;
const MicroOp *scheduled_program_counter_ = nullptr;
@ -214,7 +214,7 @@ class ProcessorStorage {
void assemble_fetch_decode_execute(InstructionPage &target, int length);
void assemble_ed_page(InstructionPage &target);
void assemble_cb_page(InstructionPage &target, RegisterPair &index, bool add_offsets);
void assemble_base_page(InstructionPage &target, RegisterPair &index, bool add_offsets, InstructionPage &cb_page);
void assemble_cb_page(InstructionPage &target, RegisterPair16 &index, bool add_offsets);
void assemble_base_page(InstructionPage &target, RegisterPair16 &index, bool add_offsets, InstructionPage &cb_page);
};

View File

@ -0,0 +1,174 @@
Dear Atari Community!
We are happy to announce a new public release of EmuTOS:
EmuTOS 0.9.10 -- December 23, 2018
INTRODUCTION
EmuTOS is a single-user single-tasking operating system for 32-bit Atari
computers, clones and emulators. It can be used as a replacement for the
TOS images typically needed by emulators and can also run on some real
hardware, including the Atari ST(e), TT, and Falcon, and the FireBee. It
can even run on non-Atari hardware such as Amiga and ColdFire Evaluation
Boards. All the source code is open and free, licensed under the GNU
General Public License (GPL).
CHANGES SINCE RELEASE 0.9.4
For a quick summary of changes by release since release 0.9.4, please
refer to doc/changelog.txt.
For a detailed list of all changes since the project started, refer to
the Git repository.
DESCRIPTION
EmuTOS is basically made up of the following:
- The BIOS, which is the basic input output system
- The XBIOS, which provides the interface to the hardware
- The BDOS, which are the high level OS routines, often known as GEMDOS
- The VDI, the virtual device interface, i.e. the screen driver
- The AES, the application environment services or window manager
- The EmuDesk desktop, which is the graphical shell to the user
- EmuCON2, the command-line interpreter
The BIOS and XBIOS code is our own development. It is written from
scratch and implements all relevant TOS 3 BIOS & XBIOS functionality,
and a bit more, e.g. hard disk access. See doc/status.txt for details.
The GEMDOS part is based on Digital Research's GEMDOS sources, which were
made available under GPL license in 1999 by Caldera.
The graphical parts like VDI and AES are now more or less fully
implemented up to TOS v1.04 level. They work in all the graphics modes
of the original Atari ST, with some extensions. For example, systems with
VIDEL support 256 colours and 640x480 screen resolution. Some emulators
can patch EmuTOS to work with much bigger screen resolutions.
The desktop is now almost as nice as the one in TOS 2 or higher (although
there is still work to be done). Of course you are always free to use a
more advanced desktop replacement like TeraDesk.
EmuCON2 is a basic but useful command-line interpreter, written from scratch
by Roger Burrows in 2013 to replace the original CLI.
Since EmuTOS just implements TOS functionality, you might want to use
MiNT on top of it in order to run more modern software. EmuTOS is not
an alternative to MiNT, but it's the only free base OS to boot MiNT.
EMULATION AND FUTURE PLATFORMS
EmuTOS and MiNT cooperate well. Both can utilize the Native Features
(NatFeats) interface for emulators:
https://github.com/aranym/aranym/wiki/natfeats-about
EmuTOS uses this new standard interface for all the relevant native
functions supported by an emulator on which it's running. This interface
proxies the calls to the underlying host OS so that these features don't
need to be emulated. This is both faster and can provide features that
would be infeasible on a real machine. It may allow using modern graphics
cards, provide fast native filesystem access and enable you to use
networking with all bells and whistles - and many other things you might
not have even dreamed of.
The ARAnyM emulator has the most extensive support for NatFeats.
The Hatari emulator supports the basic NatFeats facilities.
HARDWARE
Making EmuTOS run natively on a new hardware platform is more or less just
a question of driver support for EmuTOS. The same for MiNT, if you'd like
to have it running on top of EmuTOS.
This is the currently supported original Atari hardware:
- CPU support for M68000, M68030
- FPU detection
- 68030 MMU and cache
- Memory controller (both ST and Falcon)
- TT-RAM
- Monitor type detection (mono, RGB or VGA)
- DMA controller
- WD 1772 / AJAX Floppy disk controller
- MFP, MFP #2
- PSG
- ST shifter
- STe shifter
- TT shifter
- VIDEL
- ACIAs, IKBD protocol, mouse
- MegaST Real-Time Clock (set clock not tested)
- NVRAM (including RTC)
- Blitter
- Microwire
- SCC
- IDE
- SCSI
- ACSI
EmuTOS also supports the following Atari-compatible hardware:
- CPU support for M68010, M68020, M68040, M68060, ColdFire V4e, and Apollo 68080
- ICD AdSCSI Plus ST Real-Time Clock
- MonSTer expansion card
- Nova/ET4000 graphics card
- SD/MMC
- The Native Features interface to some degree
Currently unsupported hardware features:
- DSP
EmuTOS is also available on some non-Atari hardware:
- Amiga (floppy or ROM for any Amiga, including MapROM support)
- ColdFire Evaluation Boards (M5484LITE, M5485EVB)
AVAILABILITY
The EmuTOS home page is:
http://emutos.sourceforge.net/
The project home is on SourceForge:
http://sourceforge.net/projects/emutos/
The latest releases can be downloaded from:
http://sourceforge.net/projects/emutos/files/emutos/
Development snapshots allow you to test the current development progress:
http://sourceforge.net/projects/emutos/files/snapshots/
The latest sources are always available on GitHub:
https://github.com/emutos/emutos
If you are just curious or would like to help us develop this nice little
OS, you are invited to subscribe to our mailing list for developers at:
https://lists.sourceforge.net/lists/listinfo/emutos-devel
We hope that you like EmuTOS. If you have any suggestions or comments, we
always appreciate hearing both the good and the bad things about it.
The EmuTOS development team.
--
Originally written by Martin Doering
http://emutos.sourceforge.net/

View File

@ -0,0 +1,254 @@
EmuTOS source code consists of several parts, and includes code taken from
other projects - Many thanks to them and to their authors for releasing the
code under GPL.
The 'historical' authors - those who wrote the code before the start of the
EmuTOS project - are mentioned in the individual files they've authored.
Major 'historical' parts are:
- BDOS, VDI - both come from the latest known GEMDOS version from
Digital Research (later versions seem to have been developed by Atari).
- AES, desktop - The C source code for GEM comes from the x86 version.
- Some GEM assembler files come from the AES for the Apple LISA.
All these historical parts were released under the General Public License
by Caldera, Inc. in mid april 2000 (?) (For the record, Caldera bought it
from Novell in 1997 along with DR-DOS; later Caldera disappeared and this
is the copyright notice that refers to Lineo)
Minor borrowed stuff:
- the printf and memcpy stuff is inspired by the Minix kernel and library;
- the processor/FPU detection is taken from the MiNT kernel;
- "Bug" includes parts of the original gettext source code;
- some low-level hardware stuff comes from the Linux kernel;
While the main Amiga support was written from scratch by the EmuTOS team,
some advanced code (FastRAM support for ROM versions) has been borrowed from the
AROS project. Due to license incompatibilities, that code is shipped in the
source archive, but not compiled into the official binaries.
The following is a list of 'recent' contributors - individuals involved in the
EmuTOS project. In this project virtually everybody modifies every file;
nevertheless here is an attempt at identifying who's guilty of what:
Roger Burrows (RFB) <rfburrows at ymail.com>
- Current project admin
- Support for SD/MMC Cards on the FireBee
- SCSI support
- Improvements to IDE, CompactFlash, ACSI, and other mass-storage support
- FAT16 partitions up to 2 GB (inspired by Didier Méquignon's BDOS fork)
- Full support for Falcon video hardware
- Real Falcon 030 support (cache, MMU, SCC, HD floppy)
- Real TT 030 support (video, MFP2)
- Blitter support for horizontal lines/filled rectangles/raster graphics
- Desktop and file selector improvements
- EmuCON2
- Tools: erd (EmuTOS Resource Decompiler) & draft (deletes items from desktop resource)
- Various bugfixes and cleanups
Vincent Rivière (VRI) <vincent.riviere at freesbee.fr>
- Many improvements to the build and configuration process
- Moved project from CVS to Git, and from SourceForge to GitHub
- Implemented automatic builds via Travis CI
- Patches for compiling with GCC 4.x
- ColdFire CPU and FireBee support
- Initial IDE driver
- Big improvements to FastRAM/Alt-RAM handling
- Amiga support
- ColdFire Evaluation Boards support
- Apollo 68080 support
- Various bug fixes and cleanups
Thomas Huth (THH) <huth at tuxfamily.org>
- Lots of bugfixes & cleanups all over the place
- Integration and maintenance of the AES and GEM-Desktop
- XBIOS DMA sound functions
Petr Stehlik (PES) <pstehlik at sophics.cz>
- BIOS disk interface, BDOS filesystem
- Falcon and ARAnyM support
Laurent Vogel (LVL) <lvl at club-internet.fr>
- Original ST hardware (MFP, ACIA, parport, sound, floppy, ACSI)
- Makefile tricks and tools
- NLS support
Martin Doering (MAD) <mdoering at users.sourceforge.net>
- Original project initiator (but retired many years ago)
- Memory setup, VT52 console, Line A, mouse
- Virtually everything not modified later by the others
Thanks to all current and previous translators, who have helped us keep
EmuTOS multi-lingual:
- Czech translation
Bohdan Milar <milarb at volny.cz>
Petr Stehlik <pstehlik at sophics.cz>
Pavel Salač <salac.pavel at gmail.com>
Jan Krupka <krupkaj at centrum.cz>
- Finnish translation
Eero Tamminen
- French translation
Laurent Vogel
Vincent Rivière
- German translation
Thomas Huth
- Greek translation
George Nakos
Christos Tziotzis <ctziotzis at gmail.com>
- Italian translation
Lodovico Zanier <lvc958 at libero.it>
- Spanish translation
Gabriel Huertas
David Gálvez <dgalvez75 at gmail.com>
Jordi Mestres Ruiz <ataristfan at gmail.com>
- Russian translation
Dima Sobolev <avtandil33 at gmail.com>
Thanks also to all mailing list contributors for their help, and
especially:
Stanislav Opichal (SOP) <opichals at seznam.cz>
- FreeMiNT kernel bootstrap via BOOTSTRAP NatFeat
Frank Naumann
- FreeMiNT
Ctirad Fertr <phanatic at volny.cz>,
Milan Jurik <M.Jurik at sh.cvut.cz>
- The ARanyM team
Johan Klockars <rand at cd.chalmers.se>
- fVDI
Henk Robbers <h.robbers at chello.nl>
- XaAES, AHCC
Jacques-Etienne Rahon "Kevin Flynn" <kevin.flynn at wanadoo.fr>
- Extensive demo testing on STeeM
Patrice Mandin and Markus Oberhumer
- Hints and patches for compiling EmuTOS with GCC 3.x
Eero Tamminen
- Many bug reports, extensive testing, testcases supply
- Many documentation updates
- Finnish keyboard mapping
- Hatari debug symbols
- Static source analysis and cleanup
- Line-A implementation
Gerhard Stoll
- Improved our nvmaccess() function
- TOS hypertext
Roger Crettol
- Found and fixed a bug in GEMDOS Pterm() function
- Support for swiss german keyboard
- Some EmuCON improvements
David Savinkoff
- Bug fixes for the BIOS parallel port code
- Improved Isqrt() function
- Other various bugfixes
Olivier Landemarre <olivier.landemarre at free.fr>
- Renamed internal VDI functions to avoid name conflicts
Jean-François Del Nero <jeanfrancoisdelnero at free.fr>
- Improved memory detection on cold boot
- Tested the EmuTOS ROM on real STe hardware
- Various bugfixes
- Invaluable HxC Floppy Emulator for tests on real hardware
David Gálvez <dgalvez75 at gmail.com>
- XHNewCookie() implementation
Fredi Aschwanden <mcs at kingx.com>
and all the ACP team
- Tests on the FireBee hardware
James Boulton <james.boulton at eiconic.com>
- floprw() fix for reading multiple sectors
Stephen Leary <sleary at vavi.co.uk>
- Fixed support for IDE slave device
Miro Kropáček <miro.kropacek at gmail.com>
- Experimental 68040 MMU support
WongCK on Atari-Forum
- Tests on real Falcon 030
Michaël Gibs on English Amiga Board
- Tests on Amiga 1200 with Blizzard 1260 accelerator
Amiman99 on English Amiga Board
- Tests on Amiga 1000
Radoslaw Kujawa
- Compilation fix on 64-bit build systems
Hampa Hug
- Fixed ACSI bugs
Markus Fröschle
- Tests on the FireBee and BaS_gcc support
- Inspired the support for the blitter
- Fix various AES & VDI bugs
Christian Zietz <czietz at gmx.net>
- ROM tests on real ST/STe hardware
- Fix floppy/ACSI bug
- Fix memory detection to support STs as well as STes
- Support for extended MBR partitions
- Add IDE 'twisted cable' support
- IDE performance improvements
- Fix cold boot problems caused by some ST MMUs
- Fix screen corruption after a reset on some Mega(STe) systems
- ET4000/Nova support
- Miscellaneous bug fixes
Jo Even Skarstein <joska at online.no>
- Support for Norwegian & Swedish keyboards
- Support for MonSTer add-on board
Thorsten Otto <admin at tho-otto.de>
- Make EmuTOS more compatible with Atari TOS in several areas
- Add check_read_byte() workaround for ARAnyM-JIT
- Fixes to 68040 PMMU setup
- Inspired the support for window/desktop background configuration
- Found many bugs in desktop shortcut handling
- Help with resource manipulation programs
- Miscellaneous bug fixes
- Lots of source code cleanup
Apollo Team: Gunnar von Boehn, Philippe Flype, Simo Koivukoski,
pisklak, guibrush, TuKo, and all other members...
- Apollo 68080 and Amiga support
Steven Seagal
- Steem SSE support
Stefan Niestegge
- ROM tests on real hardware: STe, Falcon and Amiga 600
Ingo Uhlemann
- ROM tests on real TT hardware
Stefan Haubenthal
- EmuTOS packaging on Aminet: http://aminet.net/package/misc/emu/emutos
Christian Quante
- Various desktop bug fixes & improvements
Keli Hlodversson
- Replaced form_alert() icons with more TOS-like ones

View File

@ -0,0 +1,59 @@
AES/VDI/Line-A bugs:
- The right side of outline characters are clipped e.g. in "LaserChess",
"Diamond miner" & "Minigolf" games, and in vditext tester:
https://sourceforge.net/p/emutos/mailman/message/29276993/
This is due to a bug somewhere in the ugly text_blt() assembler
function in vdi_tblit.S.
- Thick arcs going partly outside of screen have incorrectly
drawn pixels at top of screen in vdiline tester.
- In "MathMaze" and "The Ultimate Minesweeper", game win and score
dialogs leave left/bottom outline on screen when they close.
- Dialog box at the end of Glücksrad game is missing text from
the dialog button (and the button width isn't correct)
- Line-A polygons are one pixel short at both sides. This is
because clc_flit() function does it for VDI (where perimeter
is drawn separately). It is visible e.g. in "Japlish" game.
Video problems:
- Omega's XiTec presentations "swing.prg" throws privilege exception
on exit. Both TOS v3 & EmuTOS survive that OK, but in EmuTOS both
screen and mouse acceleration are messed up: EmuTOS exception restore
is missing videomode & mouse reset.
Atari Falcon / TOS v4 compatibility bugs:
- Escape Paint icons don't show in image operations window and their
place in toolbar window is inverted on mouse click.
- Falcon FalcAMP button icons aren't visible as EmuTOS doesn't support
new style RSC files with CICONs.
Problems that also occur in Atari TOS:
- VDI: when drawing a wide polyline with squared ends and more than one
segment, if the width of the line is greater than twice the length of
an ending segment, the end will have a bump rather than being square.
This is because wideline segments are joined by using filled circles
whose radius is half the width of the line: the bump is a protruding
part of the circle that joins the end segment to the previous one.
Links to programs listed above:
- Diamond Miner:
http://www.atarimania.com/game-atari-st-diamond-miner_31993.html
- Escape Paint:
http://www.pouet.net/prod.php?which=25328
- FalcAMP:
http://deunstg.free.fr/sct1/falcamp/
- Glücksrad:
http://www.atarimania.com/game-atari-st-glucksrad-st_22001.html
- Japlish:
http://www.ntrautanen.fi/marko/arkisto.atari.org/sivut/menu_pelit.htm
- Laserchess:
http://www.atarimania.com/game-atari-st-laserchess_31257.html
- Minigolf (GFA):
http://eerott.mbnet.fi/hatari/sources/minigolf.zip
- Swing:
http://www.pouet.net/prod.php?which=52370
- The Ultimate Minesweeper:
http://www.pouet.net/prod.php?which=28904
- VDI line/text tester:
http://eerott.mbnet.fi/hatari/programs.shtml#vditest
(Links missing to: mathmaze.)

View File

@ -0,0 +1,361 @@
CHANGES BETWEEN RELEASE 0.9.9.1 AND RELEASE 0.9.10
Major changes:
- AES: Avoid unnecessary redraws by AES window manager
- AES: Fix shutdown bug in shel_write()
- BDOS: Improve BDOS write file performance
- BDOS: Improve BDOS sector caching algorithm
- BDOS: Avoid unnecessary directory sector writes in BDOS
- BDOS: Improve Fsnext() performance
- BIOS: Add SCSI support for TT and Falcon
- BIOS: Implement support for ET4000 graphics card
- BIOS: Implement automatic verify for floppy writes
- BIOS: Improve IDE data transfer speed
- BIOS: Improve TT RAM size detection for Storm cards
- BIOS: Fix reboot loop if Ctrl+Alt+Del held down
- EmuCON: Allow resolution change in EmuCON
- EmuDesk: Clean up if EmuDesk terminates abnormally
- EmuDesk: Fix bug in EmuDesk copy function
- EmuDesk: Fix EmuDesk out-of-sequence redraws
- EmuDesk: Make EmuDesk menu for icon/text selection like Atari TOS
- VDI: Improve the appearance of VDI curved lines
Other changes:
- AES: Do not set the scrap directory in appl_init()
- AES: Do not validate the path supplied to scrp_write()
- AES: Fix appl_tplay()
- AES: Fix appl_trecord()
- AES: Fix bug in setting application directory
- AES: Fix file selector bug
- AES: Handle SHADOWED correctly for form_center()
- AES: Make form_center() behave like Atari TOS
- AES: Preserve DTA address across shel_find()
- BDOS: Increase max length of fully-qualified filename
- BIOS: Fix bug in VT52 emulation
- BIOS: Fix bug in rsconf handling for SCC
- BIOS: Fix bugs in keyboard mouse emulation
- BIOS: Fix end-of-partition test in Rwabs()
- BIOS: Fix screen corruption on some (Mega)STe systems
- BIOS: Improve FAT12/FAT16/FAT32 detection
- BIOS: Increase default keyboard auto-repeat speed
- BIOS: Remove IDE delay on Amiga
- BIOS: Remove unneeded delay when accessing the FDC
- EmuDesk: Allow 'Show item' to handle multiple items
- EmuDesk: Fix bug in EmuDesk change resolution handling
- EmuDesk: Fix bug in EmuDesk copy process when disk is full
- EmuDesk: Fix display bug in EmuDesk initialisation
- EmuDesk: Fix EmuDesk mouse cursor initialisation
- EmuDesk: Fix label bug when formatting floppy
- EmuDesk: Fix 'name conflict' bug in copy/move folders
- LineA: Fix bug that affected Aegis Animator
- LineA: Implement early abort for lineA seedfill()
- VDI: Fix bug in v_opnvwk()
- VDI: Fix contourfill() for 8 planes
- VDI: Fix design bug in VDI workstation creation
- XBIOS: Fix crash if Vsetscreen() sets TrueColor mode
- XBIOS: Improve performance of Flopver()
- The usual source code cleanup and minor bug fixes
CHANGES BETWEEN RELEASE 0.9.9 AND RELEASE 0.9.9.1
There was only one change, to fix a major bug in EmuDesk: if a desktop
shortcut for a file/folder was dragged to the trash or a disk icon or
an open window, then all the folders at the same level as the selected
file/folder were included in the operation, causing unwanted deletes/
moves/copies.
CHANGES BETWEEN RELEASE 0.9.8 AND RELEASE 0.9.9
Major changes:
- AES: Allow mouse cursors to be loaded at boot time
- EmuDesk: Add 'Desktop configuration' dialog
- EmuDesk: Allow configuration of window/desktop backgrounds
- EmuDesk: Allow desktop window file mask to be specified
- EmuDesk: Omit unused desktop menu items
- EmuDesk: Open new window with Alt+doubleclick on folder
- General: Automatically build snapshot releases when a commit is pushed
- VDI: Add blitter support for horizontal line drawing
- VDI: Add blitter support for filled rectangle drawing
- VDI: Add blitter support for raster graphics
Other changes:
- AES: Add growbox/shrinkbox effects to form_dial()
- AES: Allow AES USERDEFs to clobber a2/d2 (improve compatibility)
- AES: Call dsptch() on every AES call (improve responsiveness)
- AES: Ensure all DAs see AC_CLOSE before app exits
- AES: Fix problem with mouse clicks being ignored
- AES: Improve mouse cursor images
- AES: Only wait for interrupts when nobody is ready to run
- AES: Replace icons used in alerts
- BIOS: Do not use stack until memory is initialized
- BIOS: Ensure ST MMU register contains valid value
- BIOS: Ensure GetBPB() returns NULL for non-FAT partitions
- BIOS: Fix Mega STe boot problem
- BIOS: Fix XHDrvMap() to return correct value
- BIOS: Fix bug in memset/bzero clearing only 16MB at most
- BIOS: Implement XHDOSLimits (read only)
- BIOS: Amiga/Vampire V2: do not enable Fast IDE by default
- EmuDesk: Add blitter menu item to desktop
- EmuDesk: Add support for desktop drag-and-drop in window
- EmuDesk: Allow any character as date separator
- EmuDesk: Allow copy/move to desktop shortcut for a folder
- EmuDesk: Always issue alert if no windows are available
- EmuDesk: Do not open desktop directory if error occurs
- EmuDesk: Dragging to desktop shortcut for program now launches it
- EmuDesk: Fix 'Install application' bug w/ desktop shortcut
- EmuDesk: Fix alignment of desktop icons on a grid
- EmuDesk: Fix bug: desktop didn't open window for empty drives
- EmuDesk: Fix default dir for programs launched from desktop
- EmuDesk: Fix tail passed by desktop to shel_write()
- EmuDesk: Highlight file shortcut when dropping file on it
- EmuDesk: Improve launching of programs via desktop shortcut
- EmuDesk: Include wildcard spec in desktop window name
- EmuDesk: Make the desktop shel_write() the full pathname
- VDI: Add support for lineA TextBlt write modes 4-19
- VDI: Fix VDI crash when running MiNT + memory protect
- VDI: Fix crash when font scaling in lineA text_blt()
- VDI: Handle bad rotation value in gdp_justified()
- VDI: Translate text_blt() high level code to C
- XBIOS: Fix EsetColor() when color < 0
- The usual source code cleanup and minor bug fixes
CHANGES BETWEEN RELEASE 0.9.7 AND RELEASE 0.9.8
Major changes:
- Amiga: New boot floppy target
- Amiga: Rewrite floppy routines
- Amiga: Support multiple video modes
- BIOS: Autodetect IDE interface with twisted cable at run-time
- EmuDesk: Add support for desktop shortcuts
- EmuDesk: Add support for formatting floppies
- EmuDesk: Add support for user-assignable desktop icons
Other changes:
- AES: Adjust file selector scroll bar width according to resolution
- AES: Allocate Alt-RAM instead of ST-RAM where possible
- AES: Do not use shel_find() to find autorun program
- AES: Fix bug in rsrc_load() that affected PixArt4
- AES: Fix error message if autorun program is not found
- AES: Fix possible data corruption when launching accessories
- AES: Increase min height of slider in file selector
- Amiga: Add support for IKBD keyboard/mouse/joysticks on RS-232
- Amiga: Fix interlaced display with fast CPU
- Amiga: Add target to build ROM optimized for Vampire V2
- Amiga: Add XBIOS joystick support
- Amiga: Improve IDE performance on Vampire V2
- Amiga: Improve IDE support
- Amiga: Add proper floppy media change support
- BDOS: Allow environment to be allocated in Alt-RAM
- BDOS: Fix bug in updating date when month rolls over
- BDOS: Fix Fsfirst(): wrong name format in DTA for label
- BDOS: Speed up Dfree() for 16-bit FATs
- BIOS: Add movep emulation for 68060
- BIOS: Enable data cache on 68040 & 68060
- BIOS: Enable instruction & branch caches on 68060
- BIOS: Fix ACSI bug: non-word-aligned transfers failed
- BIOS: Fix bug in IDE detection of slower devices
- BIOS: Fix crash with unaligned IDE R/W buffer on 68000
- BIOS: Fix floppy bug: non-word-aligned I/Os failed
- BIOS: Improve IDE performance
- BIOS: Improve mediachange detection
- ColdFire: Add RAM TOS target for ColdFire Evaluation Boards
- EmuDesk: Add documentation for new features
- EmuDesk: Add read-only indicator for show-as-text display
- EmuDesk: Allocate Alt-RAM instead of ST-RAM where possible
- EmuDesk: Fix various bugs in desktop copy/move
- EmuDesk: Handle desktop move/copy of folder to itself
- EmuDesk: Holding Control at startup now bypasses all initialisation files
- EmuDesk: Lookup desktop shortcuts directly in menu
- EmuDesk: Make alt-X open the root of X in a window
- EmuDesk: Make desktop keyboard shortcuts use Ctrl modifier
- EmuDesk: Make desktop shortcut keys work for all keyboards
- EmuDesk: Split preferences dialog to allow longer text
- General: Allow EmuTOS static RAM to be allocated in Alt-RAM
- The usual source code cleanup and minor bug fixes
CHANGES BETWEEN RELEASE 0.9.6 AND RELEASE 0.9.7
Major changes:
- BIOS: add support for extended MBR partitions
- BIOS: add support for MonSTer board
- BIOS: configure & size ST-RAM on TT
- BIOS: add support for Eiffel on CAN bus on ColdFire EVB
- BIOS: add _5MS cookie to support FreeMiNT on non-Atari hardware
- BIOS: add support for Apollo Core 68080
- BDOS: set archive flag when file is created/modified
- EmuDesk: allow disk delete via desktop File menu item
- EmuDesk: implement desktop 'Install devices'
- EmuDesk: implement desktop 'Install icon'
- EmuDesk: implement desktop 'Remove desktop icon'
- EmuDesk: rewrite 'Install application'
- EmuCON2: provide a standalone version of EmuCON2
Other changes:
- AES: allow autorun program to start in character mode
- AES: fix bug when File Selector accesses empty drive
- AES: fix loop in file selector if filemask is too long
- AES: fix bug: the file selector modified the DTA pointer
- AES: rewrite wildcmp() to fix bug
- BDOS: fix GEMDOS standard handle numbers
- BDOS: rewrite Fsfirst/Fsnext to fix design problem
- BDOS: use single pool for all internal memory requests
- BDOS: fix I/O status for redirected character devices
- BDOS: fix date stamp in . and .. directory entries
- BDOS: fix return code for Fsfirst()
- BDOS: make EmuTOS respect user-assigned FRB
- BDOS: make ctl-C interrupt Cconin
- BDOS: return EOF indicator on redirected char devices
- BDOS: validate attribute bits passed to Fattrib()
- BDOS: validate handles for Fseek()/Fread()/Fwrite()/Fdatime()
- BIOS: add Norwegian & Swedish keyboard support
- BIOS: add support for byte-swapped IDE cable (disabled by default)
- BIOS: allow configuration of max logical sector size
- BIOS: fix VDI->hardware colour calculation
- BIOS: fix os_conf value and usage in multilanguage ROMs
- BIOS: improve performance of Rwabs() on floppy disks
- BIOS: make Ikbdws()/Midiws() handle 'cnt' like Atari TOS
- BIOS: set density for read/write/format of HD floppies
- BIOS: fix boot on Amiga with 68000 CPU
- BIOS: fix RAM size with BaS_gcc on ColdFire EVB
- BIOS: fix _FPU cookie for 68060 without FPU
- BIOS: fix values returned by VgetRGB()/vq_color()
- EmuDesk: make desktop shift-click behave the same as TOS
- EmuDesk: prompt if folder name conflict during move/copy
- EmuDesk: make many desktop and AES dialogs more concise
- EmuDesk: fix desktop icon drag and drop positioning
- EmuDesk: allow 'Too many windows' alert to be issued
- EmuDesk: always issue extra alert if deleting entire disk
- EmuDesk: always keep part of the mover gadget onscreen
- EmuDesk: avoid unnecessary window refreshes
- EmuDesk: handle name conflict during copy like Atari TOS
- EmuDesk: support additional keys during "Show file"
- EmuDesk: add copyright year in EmuDesk about dialog
- General: display total RAM on welcome screen
- General: fix _drvbits tests for drives > P
- VDI: fix rectangle drawing errors
- VDI: fix bug: v_bar() draws perimeter wrongly
- VDI: fix vq_curaddress(), vs_curaddress()
- Lots of source code cleanup and minor bug fixes
CHANGES BETWEEN RELEASE 0.9.5 AND RELEASE 0.9.6
Major changes:
- AES: fix pattern problem in window title line
- AES: prevent crash when NVDI is installed
- BDOS: fix bug: memory allocated by a TSR could be freed
- BDOS: implement etv_term()
- BIOS: clean up pending IRQ from flopunlk(), fixes some ACSI problems
- BIOS: clear data cache after DMA read, fixes ACSI problem on TT
- BIOS: do not clear the free ST-RAM on startup
- BIOS: enable MIDI input
- BIOS: initialise DMA sound matrix on Falcon
- BIOS: fix Flopxxx XBIOS calls to work with FastRAM
- BIOS: fix floppy motor-on problem during initialisation
- BIOS: fix memory bank detection to work on ST and STe
- BIOS: prevent reset vector being called on cold reset
- EmuCON2: add 'mode' command
- EmuCON2: fix EmuCON crash if system call is intercepted
- EmuDesk: allow TT desktop to select TT medium res
- EmuDesk: fix bug: copy/move could target the wrong folder
- EmuDesk: fix display of numeric values in desktop dialogs
- EmuDesk: fix rubber-banding for text-mode desktop windows
- EmuDesk: hide Shutdown if the machine can't shutdown
- EmuDesk: improve desktop move performance by using rename if possible
- EmuDesk: change menu bar to be more like Atari TOS
- General: fix EmuTOS to run on real TT hardware
- General: merge boot.prg + ramtos.img into emutos.prg
- VDI: fully implement VDI support for TT video
Other changes:
- AES: clean up if program fails to issue appl_exit()
- AES: fix loop when deleting non-existent object
- AES: fix handling of Delete key by objc_edit()
- AES: fix value returned by evnt_button()/evnt_multi()
- AES: reset the default drive on resolution change
- BDOS: fix volume label handling to conform to TOS standards
- BIOS: add new cookie _MCF to the cookiejar
- BIOS: add support for RTC on ICD AdSCSI Plus board
- BIOS: add support for TT MFP (MFP #2)
- BIOS: add support to run "reset-resident" code
- BIOS: allow EmuTOS floppy to boot other floppies
- BIOS: clear system variables if EmuTOS loads into RAM
- BIOS: fix console font height with Hatari extended video modes
- BIOS: fix ide_identify() on Amiga
- BIOS: fix NVRAM year on TT
- BIOS: fix return codes for dmasound functions on ST
- BIOS: fix return codes for TT shifter functions
- BIOS: fix some NVRAM reset problems
- BIOS: fix sound volume on TT
- BIOS: fix _screenpt processing for TT, Falcon
- BIOS: flush the data cache before a warm or cold reset
- BIOS: initialize the IKBD clock on first boot only
- BIOS: rewrite MegaST(e) real time clock handler
- EmuCON2: fix EmuCON welcome message for ST-Low
- EmuDesk: add 'No sort' to desktop sort options
- EmuDesk: add desktop shortcuts for scroll-by-page
- EmuDesk: ensure desktop menu bar fits within screen
- EmuDesk: fix display of volume label in disk info dialog
- EmuDesk: improve EMUDESK.INF error checking
- EmuDesk: show the emulated machine name on Hatari even with --natfeats yes
- General: always use STOP instruction in 'wait for interrupt' loops
- General: create valid filesystem with hidden EmuTOS image on auto-booting floppy
- General: do not wait for a key at EMUTOS.PRG startup
- General: pretend to be TOS 1.04 in 192k ROMs
- General: use country id as part of emutos.prg/emutos.st name
- VDI: fix v_curtext()
- VDI: implement vq_curaddress()
- VDI: improve performance of cursor display routine
- VDI: rewrite vr_trnfm() to fix bugs and save space
- Lots of source code cleanup and minor bug fixes
CHANGES BETWEEN RELEASE 0.9.4 AND RELEASE 0.9.5
Major changes:
- AES/BIOS: implement critical error handler
- BDOS: fix file not found issues with Steem hard disk emulation
- BDOS: implement Pexec mode 7
- BIOS: add alt-arrow support (mouse actions via keyboard)
- BIOS: add dual keyboard support (for Greek/Russian keyboards)
- BIOS: allow user to specify boot partition at startup
- BIOS: allow EmuTOS to recover from program exceptions in user programs
- BIOS: auto-detect multiple IDE interfaces
- BIOS: fix detection of C: drive with Steem
- BIOS: fix early stack initialization on Amiga
- EmuDesk: improve text object alignment for translated strings
- VDI: add line-A flood fill; all line-A opcodes are now supported
Other changes:
- AES: increase button spacing in alerts
- AES: increase AES stack size for ColdFire machines
- BDOS: evaluate TPAsize flags in Pexec processing
- BDOS: fix bug in cross-directory rename
- BIOS: use interrupts for serial console instead of polling
- BIOS: fix FPU detection: 68881/68882 are now differentiated correctly
- BIOS: fix delay_loop() timing for M548X machines
- BIOS: fix key repeat bug when entering values via alt-keypad
- BIOS: implement XHInqDriver() XHDI function
- BIOS: fix some XHDI return codes (EDRIVE and EWRPRO)
- BIOS: add explicit delay for parallel port strobe (fixes printing on fast systems)
- BIOS: fix nationality code in ROM header
- EmuCON2: translate text (note: some country codes use English by choice)
- EmuDesk: allow folders being displayed in an open window to be moved/deleted
- EmuDesk: allow desktop "rubber-banding" in all directions
- EmuDesk: display year as 4 digits where possible
- EmuDesk: use _IDT cookie for EmuDesk date/time formatting
- EmuDesk: fix bug in "sort by date" for directory display
- EmuDesk: allocate screen objects dynamically
- General: convert source code repository to Git
- General: implement error checking for translated alerts
- General: replace "make release-version" with "make version"
- VDI: ignore lineA variable 'multifill' for linea_rect() and linea_polygon()
- VDI: speed up drawing of horizontal lines by v_pline()
- VDI: fix lineA rectangle fill bugs
- VDI: fix gap in circle drawn by v_arc()
- VDI: fix VDI wideline display in 320x480 resolution
- Lots of source code cleanup and minor bug fixes

View File

@ -0,0 +1,295 @@
A brief user's guide to the newer features of EmuDesk, the EmuTOS desktop
=========================================================================
The current version of EmuDesk is based on the TOS 1 desktop, but with
many improvements inspired by the TOS 2/3/4 desktop, including:
1) new menu items
. set file mask
. install icon
. install application
. install devices
. remove desktop icon
. desktop configuration
. blitter
Due to space limitations, the implementation of the above is somewhat
restricted in the 192K ROMs (see the detailed descriptions below). If
you make any changes to the desktop using the above features, you must
save the desktop to preserve the changes.
2) other new features
. user-assignable icons
. user-assignable mouse cursors
. open disk window via keyboard shortcut
. desktop shortcuts
Due to space limitations, desktop shortcuts are not available in the
192K ROMs. Desktop shortcuts are preserved when you save the desktop.
Set file mask
=============
192K ROMs:
This is not available.
Other ROMs:
This is used to change the file mask of the currently-topped window, to
control which files are displayed within the window. Note that folders
are always displayed; the mask affects the display of files only. The
default file mask when a window is opened is "*.*"
Install icon
============
192K ROMs:
This may be used to associate a specific icon with a desktop item (disk
or trash). You may select an existing desktop item and click on "Install
icon...", or you may click on "Install icon..." with no item selected.
If you click on a window icon (file or folder), it will be ignored.
Other ROMs:
This may be used to associate a specific icon with a desktop item (disk
or trash), or a window item (file or folder). You may select an existing
icon and click on "Install icon...", or you may click on "Install icon..."
with no item selected. In the latter case, you'll get a dialog requesting
you to select the type of icon (desktop or window).
. Installing a desktop icon
You may select the type (drive or trash), the label (displayed beneath
it on the desktop), and the icon shape (use the up & down arrows to
scroll through the available shapes). In addition, for drives you can
select the drive letter.
. Installing a window icon
If you pre-selected an icon, you may only change the shape of the icon
for that specific file or folder. If you did not pre-select an item,
you can select the files that the icon will apply to (standard TOS
wildcards may be used), the type of item (file or folder), and the icon
shape. In either case, to change the icon shape, use the up & down
arrows to scroll through the available shapes.
Install application
===================
The basic purpose of "Install application..." is to link an application
to data files with a specified extension. After you have done this, when
you use the desktop to open a file with the specified extension, the
corresponding application is launched. For example, you could associate
all .TXT files with a text editor; then, double-clicking on a .TXT file
would automatically launch the editor.
In addition, you can assign a function key to an application; pressing
the function key at the desktop will then launch the application.
Finally, you can set "autoboot" for one application (only): this will
launch that application during system initialisation, immediately before
the desktop itself runs.
To use "Install application...", highlight one or more applications and
click on "Install application...". In the dialog box, the application
name of the first application selected will be prefilled. The following
fields and buttons specify in detail how the application is run:
. Arguments
If you need to pass information (in addition to the name of the data
file) to the application when it starts, you may specify it here. This
is typically only relevant to utility programs, and the information
needed will be in the application documentation. In most cases, you
should leave this blank.
. Document type
This specifies the extension to associate with this application, for
example TXT or RSC, and is required. Wildcards are allowed.
. Install as F__
This specifies the function key that will launch the application;
values from 1 to 20 are allowed (11-20 are shift-F1 through shift-F10).
Leaving this blank is valid, and means that no function key will launch
the application.
. Boot status
Select "Auto" to autoboot this application (see above). Since only one
autoboot application is allowed, if you set "Auto" for an application,
EmuTOS will automatically disable "Auto" for any existing autoboot
application.
. Application type
Selecting TOS or TTP will launch the program in character mode; GEM or
GTP will launch the application in graphics mode. The appropriate
value will be prefilled according to the type of application selected,
and should not normally be changed.
. Default dir
This specifies the default directory when the application is launched:
either the directory of the application itself, or the top window (i.e.
the directory of the data file). The one to choose depends on the
specific application. If the application has supporting files (such as
resource or help files), it typically will look for them in the default
directory. For such an application, you will need to specify a default
directory of "Application". Otherwise, specify "Window".
. Parameter
When a program is launched due to it being an installed application,
the desktop provides the application with the name of the data file
that caused the launch: this is known as a parameter. In most cases,
the application expects that the full path of the data file will be
provided. Some (usually older) programs may expect the filename only.
Unless the application's documentation indicates otherwise, you should
normally try "Full path" first; if that does not work, you can try
"File name", although that may require you to modify the "Default dir"
specified above.
At the bottom of the dialog box are the following exit buttons:
. Install
Installs the application. You must save the desktop afterwards if you
want the change to be saved across boots.
. Remove
Removes an existing installed application. You must save the desktop
afterwards if you want the change to be saved across boots.
. Skip
Skips installing/removing the current application, and moves on to the
next one you specified. If you only specified one application, this
is the same as Cancel.
. Cancel
Skip installing/removing all remaining applications.
Install devices
===============
This automatically installs icons for all devices that are currently
known to GEMDOS (have an entry in _drvbits) and that do not currently
have an icon. If the device is A: or B:, a floppy icon is installed;
otherwise a hard disk icon is installed.
Remove desktop icon
===================
This is used to remove a disk or trash icon. Highlight the icon you
wish to remove, and click on "Remove desktop icon".
Desktop configuration
=====================
192K ROMs:
This is not available.
Other ROMs:
This is a simplified version of the corresponding Atari TOS menu item.
It allows you to specify the default directory and input parameter for
all applications that are not installed applications. See "Install
application" above, under 'Default dir' and 'Parameter', for further
information about these options.
Blitter
=======
This item allows you to enable or disable the use of the blitter by the
desktop. The item is greyed-out if the system does not have a blitter.
User-assignable icons
=====================
When EmuDesk starts, it looks for a file called EMUICON.RSC in the root
of the boot drive. This file should be a standard Atari resource file,
with at least eight icons. All icons in the file must be 32x32-pixel
monochrome icons. If the file is found, these icons are used for the
desktop and window displays; if not found, a standard set of eight
builtin icons is used instead. The builtin icons (or the first eight
of the loaded icons, if EMUICON.RSC is in use) have the following usage:
0 hard drive
1 floppy drive
2 folder
3 trash
4 printer
5 removable disk
6 generic application icon
7 generic document icon
Icons 8 and above can be used as you wish.
Note that, for historical reasons, these assignments are different from
those used by Atari TOS, so if you have an equivalent RSC file that works
with Atari TOS, you will need to move the icons around to get the same
desktop display.
A default EMUICON.RSC file (currently containing 41 icons) is shipped
with the release; the first 8 icons are the same as the builtin ones.
Also shipped is the corresponding EMUICON.DEF file for use by a resource
editor. You should be aware that each icon consumes about 300 bytes of
RAM, so if you are short of memory, avoid putting too many icons in
EMUICON.RSC.
User-assignable mouse cursors
=============================
When the AES starts, it looks for a file called EMUCURS.RSC in the root
of the boot drive. This file should be a standard Atari resource file,
containing 8 ICONBLKs; each ICONBLK is a container for a mouse cursor.
If the file is found, these cursors are used instead of the builtin
cursors. The usage is as described for the AES graf_mouse() call:
0 arrow
1 text cursor / i-beam
2 busy bee / hourglass
3 pointing hand
4 flat hand
5 thin cross
6 thick cross
7 outline cross
A default EMUCURS.RSC file is shipped with the release; the mouse cursors
in it are the same as the builtin ones. Also shipped is the corresponding
EMUCURS.DEF file for use by a resource editor.
NOTE: Because the mouse cursors are not really ICONBLKs (though they are
stored as such within the resource), editing them with a standard resource
editor is difficult. Thorsten Otto's ORCS resource editor has special
support for mouse cursors and is the recommended tool for modifying them.
Open disk window via keyboard shortcut
======================================
You may now use a keyboard shortcut to display the root directory of a
drive in a new window. To display drive X, hold the Alt key down and
type X, e.g. Alt-C displays drive C, Alt-D displays drive D, and so on.
As in TOS2/3/4, these shortcuts are permanently assigned and cannot be
changed by the user.
NOTE: unlike TOS2/3/4, shortcuts with the Ctrl modifier do NOT update
the drive assigned to the currently topped window; instead, they are
assigned to menu item shortcuts. At the moment, these assignments are
also permanent.
Desktop shortcuts
=================
You may now drag a file or folder to the desktop to create a desktop icon
that is a shortcut to the original file/folder: manipulating the icon
will have the same effect as manipulating the original file or folder.
For example, it may be opened, copied, or moved or deleted; it may have
an "Info/rename" performed on it. Currently, by design, the shortcut is
NOT updated automatically if the original file or folder is moved or
deleted.
The name and shape of the shortcut icon itself may be modified by the
"Install icon" menu item; this does not change the name of the file or
folder that the icon points to. The shortcut icon may be deleted by the
"Remove icon" menu item. To preserve shortcut information across boots,
you must save the desktop.
You may drag a file or folder to a desktop shortcut, with the following
results:
. dragging documents to a desktop shortcut for a folder will copy (or
move, if the control key is held down) them to the folder
. dragging documents to a desktop shortcut for a program will launch the
program, passing the full pathname of the first document
. dragging documents to a desktop shortcut for a non-executable file will
do nothing
If you open a desktop shortcut that points to a file or folder that no
longer exists, an alert will be issued, giving you the choice of removing
the shortcut, locating the desired file or folder, or cancelling the
action. If you choose locate, a file selector will be displayed to
allow you to choose the desired file or folder, and the shortcut will be
updated with the new information.

View File

@ -0,0 +1,227 @@
Programs incompatible with EmuTOS
=================================
This is a list of programs that have program bugs or shortcomings that
prevent them from running properly with EmuTOS, and whose problem has
been definitively identified. It is mainly intended to prevent these
programs from being added to 'bugs.txt' in the future.
Program: STOS programs
----------------------
Error 1: joystick and/or keyboard input doesn't work.
Bug analysis:
STOS Basic compiler routines check for input using undocumented and
TOS-specific locations. Programs using these routines work only with
specific TOS versions, and not with EmuTOS.
Workaround:
Use version of the program that has been fixed to work with modern TOS
versions.
Error 2: STOS error message "Error #046, press any key" during startup.
Bug analysis:
This is caused by a divide-by-zero error in vsm_height() when the
program is run from the AUTO folder. VDI initialisation does not occur
until after AUTO-folder programs have been run, so if a VDI function is
called by an AUTO-folder program, internal variables have not been
initialised. STOS tries to initialise these variables itself, based on
a built-in table of TOS-specific locations. These locations are invalid
for EmuTOS, so the required variables remain zero, causing the error.
Workaround:
Move the program from the AUTO folder to the root of the drive, and run
it from there. While STOS programs will then start, most of them will
remain unusable due to error 1 above.
Program: old game using fixed addresses
---------------------------------------
Error: panic during game startup.
Bug analysis:
Several old, floppy-only games load their data into fixed memory
addresses, which won't work when EmuTOS has loaded something else
there. This can be detected by tracing programs' OS calls with Hatari
(--trace os_all) and checking the used addresses. For example, under
older EmuTOS versions, the Gods game demo (from an ST Format cover disk)
overwrote itself with its game data because of this, and crashed.
Workarounds:
In some games this can be worked around by either using the cartridge
version of EmuTOS (which uses less memory) or by using a custom high-RAM
build of EmuTOS, that uses higher RAM addresses for loading programs and
for memory allocation:
make 512 UNIQUE=uk DEF="-DSTATIC_ALT_RAM_ADDRESS=0x00080000 -DCONF_WITH_FRB=0"
However, this doesn't help with programs which also expect undocumented,
OS internal functions and variables to be at certain locations. The
best workaround is to use a version of the game that has been fixed to
run from HD and with modern TOS versions.
Program: Awele v1.01
--------------------
Error: mono desktop colours are inverted after exiting program.
Bug analysis:
This version of Awele was compiled with PureC and linked with a very
early version of Windform. During WinDOM initialisation, malloc() is
called to allocate an area to save the palette in. However, although
malloc() returns the pointer in A0, the WinDOM code assumes it is in D0.
As a result, an area of low memory is pointed to, which is overwritten
during Awele execution. At program termination, the palette is restored
from the overwritten area, resulting in the error seen.
Workaround:
Use v1.02 of the game.
Program: Cameleon
-----------------
Error 1: program exits immediately when 'Start game' is selected.
Bug analysis:
The program requires an STe. In order to determine whether it is
running on an STe, it checks the contents of location 0x995 (hardcoded).
On Atari TOS, this is where TOS initialisation happens to store the _MCH
cookie but this is *not* how Atari says you should locate it (and it's
not where EmuTOS stores it).
Error 2: program crashes with a Trace exception on any exit.
Bug analysis:
During startup, the program switches to supervisor state via the Super()
system call. Subsequently, the supervisor stack overwrites the program's
user stack. On exit, the user stack pointer is restored and during this
a corrupted value is loaded into the SR, causing a trace excpetion.
Program: (VDI) Invaders and Anduril
-----------------------------------
Error: keys to move an object are ignored (in Invaders, the '-' key; in
Anduril, the 'h' & 'j' keys)
Bug analysis:
Both programs were written by "M.Dheus" who found that the most recent
key input from the keyboard was stored at offset 0x6d from the address
returned by Kbdvbase(), and used that to read the keyboard. This was
never documented by Atari, but was apparently true for all versions of
TOS 1. However it is not true for TOS 2, 3, or 4 (or EmuTOS).
Program: Ramses
---------------
Error: panic
Bug analysis:
Program calls the Line A initialization $A00A and gets the routine
vectors in a2. It gets the address of _v_hide_c, then starts doing
undocumented things with the bytes of the actual routine:
https://sourceforge.net/p/emutos/mailman/message/30605378/
Program: STVidPlay
------------------
Error: "Error in getting file location"
Bug analysis:
Program looks for a specific 2-byte sequence in the hard disk driver
code pointed to by hdv_rw ($476). If it doesn't find that sequence
within bytes 6-48 (or thereabouts) of the start of the driver, it
gives the error message.
Program: Cubase Lite
--------------------
Error: panic
Bug analysis:
On TOS 1.62 etv_timer vector is a delegate to an internal private
function. Cubase Lite tries to guess the address of that private
function in an undocumented way, which crashes on EmuTOS. (Somebody
could write a loader or TSR to change the etv_timer function so that
Cubase will not crash.)
Program: Reservoir Gods games (Bugger, Bunion, SkyFall, Sworm)
--------------------------------------------------------------
Error: panic
Bug analysis:
Games use an undocumented TOS4 vector for keyboard input instead of
accessing kbdvec correctly. This causes EmuTOS to panic.
Workaround:
This can be worked around with the following hack.prg:
https://sourceforge.net/p/emutos/mailman/message/26841274/
Program: OMIKRON.BASIC V3.01 (interpreter)
------------------------------------------
Error: Panic (bus error) during start
Bug analysis:
The program relies on undocumented internal TOS variables at several
points. First, it expects A0 upon return from Mediach (BIOS function)
to point to wplatch (floppy write protect latch variable). On EmuTOS
A0 is 0 and hence a bus error occurs when the program wants to modify
that variable. Second, it parses the bytes of the 0xA00A (hide cursor)
line-A routine to get the address of a variable reflecting the internal
state of the mouse cursor. This is done with the same code used in
"Ramses" (see above). This also fails on EmuTOS, resulting in another
bus error. There may be more accesses to undocumented variables.
Program: STSpeech v2.0
----------------------
Error: panics due to stack corruption
Bug analysis:
The program installs a custom Timer A interrupt handler, and calls the
XBIOS from it. If the Timer A interrupt happens to occur just when an
unrelated BIOS/XBIOS call is manipulating _savptr (saving registers),
then the nested XBIOS call inside the Timer A handler will trash that
BIOS/XBIOS save area, possibly modifying the stack pointer. In the
"Hitchhiker's Guide to the BIOS", Atari documented a workaround for this,
but STSpeech does not use it.
Workaround:
Because this problem is timing-dependent, it does not show up on Atari
TOS, and only shows up in EmuTOS if BigDOS is installed (BigDOS issues
many XBIOS calls). Use program without BigDOS, or anything else doing
a lot of XBIOS calls.
Program: Protracker v2 STE (Equinox version)
--------------------------------------------
Error: crash when "DISK OP." button is selected
Bug analysis:
The program relies on a TOS-specific code sequence, as follows:
1. it searches the ROM (pointed to by location 4) for the first word
equal to 0x47e
2. when found, it uses the longword immediately before as a pointer to
an address; in TOS2, this is a pointer to the mediachange handler
3. it stores the long at offset 0x1c from that address in its data
segment; in TOS2, this is a pointer to (I think) two bytes of
status for the floppy drives
Subsequently, when "DISK OP." is selected, the stored long is used as a
pointer. In TOS2, the value stored is $4216; in EmuTOS, it's zero,
resulting in a crash.
Program: Spectrum 512
---------------------
Error: crash during program initialisation
Bug analysis:
The program relies on a TOS-specific code sequence, as follows:
1. it searches the VBL handler (pointed to by location $70) for the
first word containing a value of 0x6100
2. when found, it uses the word immediately following as an index to
generate an address, and starts searching at that address for a
word containing a value of 0x8606
Under EmuTOS, the address generated is a nonsense address which happens
to be odd, causing an immediate address error.

View File

@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

View File

@ -0,0 +1,583 @@
This file documents the status of the individual parts of EmuTOS.
Here is a quick list of supported emulators/hardware:
This table should be updated regularly. When indicating failure,
if possible add a line telling in which version it did run.
Unless otherwise specified in 'details', systems were tested using
a ROM version of EmuTOS.
system | ok? | who | date | details
--------------------+-------+-----+-------------+----------------
Emulators | | | |
ARAnyM 1.0.2 | yes | VRI | 17 Dec 2018 |
Hatari v2.1.0 | yes | VRI | 16 Dec 2018 |
Pacifist v0.48 | yes | LVL | winter 2001 |
SainT v2.40 | yes | (1) | 10 Dec 2017 | 192k ROM only
STeem SSE 3.9.4 | yes | VRI | 16 Dec 2018 |
STonC v0.8.1 | yes | LVL | 9 Feb 2003 |
STonX 0.6.7.6 | yes | THH | 14 Nov 2008 |
TOSBox 1.10a | no | ? | < Sep 2002 |
WinSTon V0.1r2 | no | ? | < Sep 2002 |
WinUAE 4.1.0 | yes | VRI | 19 Dec 2018 |
--------------------+-------+-----+-------------+----------------
Atari & compatibles | | | |
STfm | yes | (1) | 23 Nov 2017 |
Mega ST | yes | (8) | 7 Dec 2018 | tested with PRG
STe | yes | (1) | 23 Nov 2017 |
Mega STe | yes | (2) | 26 Jun 2004 | only RAMTOS tested
TT030 | yes | RFB | 30 Nov 2018 | tested with PRG
Falcon030 | yes | (7) | 31 Dec 2016 |
Falcon030 | yes | RFB | 29 Nov 2018 | tested with PRG
Falcon030 + CT60 | yes | RFB | 29 Nov 2018 | tested with PRG
Suska III-C (2K15B) | yes | (5) | 23 Apr 2016 |
--------------------+-------+-----+-------------+----------------
Other systems | | | |
Amiga Blizzard 1260 | yes | (3) | Aug 2012 | tested with BlizKick
Amiga 500 + Vampire | yes | VRI | 21 Dec 2018 |
Amiga 600 + Vampire | yes | (6) | Mar 2017 |
Amiga 1000 | yes | (4) | Jul 2012 |
FireBee | yes | VRI | 18 Dec 2018 |
M5484LITE | yes | VRI | 18 Dec 2018 |
(1) reported by Christian Zietz
(2) reported to LVL by Frédéric Pécourt
(3) reported by Michaël Gibs
(4) reported by amiman99
(5) reported by Markus Fröschle
(6) reported by Flype
(7) reported by Stefan Niestegge
(8) reported by Claude Labelle
Now let's talk about the different subsystems, and what is implemented.
NOTE: the information in the following table may be somewhat dated. For
example, most GEMDOS/BIOS/XBIOS functions are known to work without
problems.
This is what the first field of the following table means:
? status unknown
- Not yet implemented
> Partially implemented
X Fully implemented and untested
t Fully implemented and partially tested
T tested and working on an emulator or real hardware
Hardware initialization
----------------------------------------------------------------------------
T CPU setting, tested on: 68000 (real & emu), 68030 (real & emu), 68040 (emu),
68060 (real), 68080 (real), V4e (real)
T FPU
T 68030 MMU and cache initialization
T Memory controller (both ST and Falcon)
T DMA controller
T WD 1772 / AJAX Floppy disk controller
T MFP, MFP#2
T PSG
T ST shifter
T STe shifter
T TT shifter
T VIDEL
T ACIAs, IKBD protocol
t MegaST Real-Time Clock (set clock not tested)
T NVRAM (including RTC)
T Blitter
T Microwire
t DMA sound
- DSP
T SCC
T IDE
T ACSI
T SCSI
T SD/MMC
T NatFeats (a framework for native features on emulators)
BOOT sequence
----------------------------------------------------------------------------
T configure memory
T execute reset routine
T detect monitor type
T detect graphics resolution
T detect processor type, FPU type and hardware features
T setup a cookie jar with system cookies
...
T init floppy drives
T boot floppy
t boot DMA (note it does not work with e.g. AHDI)
T execute reset-resident prgs: undocumented TOS feature, disabled by default
T run AUTO prgs
T run 'command.prg'
T run the default shell, EmuCON
T run the GEM desktop
BIOS devices
----------------------------------------------------------------------------
t 0 PRN: parallel port
t 1 AUX: default serial port
T 2 CON: console (screen)
T 3 MIDI
T 4 IKBD
T 5 raw screen
T 6 ST-compatible serial port
T 7 SCC channel B
T 8 TT-MFP serial port
T 9 SCC channel A
ACIA interrupt handlers
----------------------------------------------------------------------------
- midierr
- ikbderr
t midi input
T ikbd key events
T IKBD clock
T mouse
t joysticks
BIOS Functions
----------------------------------------------------------------------------
T 0x00 Getmpb
T 0x01 Bconstat
T 0x02 Bconin
T 0x03 Bconout
T 0x04 Rwabs
T 0x05 Setexc
T 0x06 Tickcal
T 0x07 Getbpb
T 0x08 Bcostat
T 0x09 Mediach
T 0x0a Drvmap
T 0x0b Kbshift
XBIOS Functions
----------------------------------------------------------------------------
All XBIOS versions:
X 0x00 Initmous
- 0x01 Ssbrk (useless - will not be implemented)
T 0x02 Physbase
T 0x03 Logbase
T 0x04 Getrez
T 0x05 Setscreen
T 0x06 Setpalette
T 0x07 Setcolor
T 0x08 Floprd
T 0x09 Flopwr
T 0x0a Flopfmt
- 0x0b Dbmsg (useless - will not be implemented)
T 0x0c Midiws
X 0x0d Mfpint
X 0x0e Iorec
T 0x0f Rsconf
T 0x10 Keytbl
T 0x11 Random
T 0x12 Protobt
T 0x13 Flopver
- 0x14 Scrdmp
T 0x15 Cursconf
T 0x16 Settime
T 0x17 Gettime
T 0x18 Bioskeys
T 0x19 Ikbdws
T 0x1a Jdisint
T 0x1b Jenabint
T 0x1c Giaccess
T 0x1d Offgibit
T 0x1e Ongibit
T 0x1f Xbtimer
T 0x20 Dosound
- 0x21 Setprt (useless - will not be implemented)
X 0x22 Kbdvbase
T 0x23 Kbrate
- 0x24 Prtblk (useless - will not be implemented)
T 0x25 Vsync
T 0x26 Supexec
- 0x27 Puntaes (useless - will not be implemented)
TOS v1.02:
T 0x29 Floprate
T 0x40 Blitmode
TOS v2.0:
t 0x2a DMAread
t 0x2b DMAwrite
t 0x2c Bconmap
TOS v3.00:
T 0x2e NVMaccess
t 0x50 EsetShift (for TT shifter only)
t 0x51 EgetShift (for TT shifter only)
t 0x52 EsetBank (for TT shifter only)
t 0x53 EsetColor (for TT shifter only)
t 0x54 EsetPalette (for TT shifter only)
t 0x55 EgetPalette (for TT shifter only)
t 0x56 EsetGray (for TT shifter only)
t 0x57 EsetSmear (for TT shifter only)
TOS v4.00:
t 0x58 Vsetmode (for Falcon Videl only)
t 0x59 Vmontype (for Falcon Videl only)
t 0x5a VsetSync (for Falcon Videl only)
t 0x5b VgetSize (for Falcon Videl only)
t 0x5d VsetRGB (for Falcon Videl only)
t 0x5e VgetRGB (for Falcon Videl only)
- 0x96 VsetMask (for Falcon Videl only)
2nd bit in _SND is set:
t 0x80 LockSnd
t 0x81 UnlockSnd
t 0x82 Soundcmd
t 0x83 Setbuffer
t 0x84 Setmode
t 0x85 Settracks
t 0x86 Setmontracks
t 0x87 Setinterrupt
t 0x8c Sndstatus
3rd bit in _SND is set:
t 0x88 Buffoper
t 0x8a Gpio
t 0x8b Devconnect
t 0x8d Buffptr
3&4 bits in _SND are set:
t 0x89 Dsptristate
5th bit in _SND is set:
- 0x60-0x7F, 32 Dsp_* functions
TOS v4 extended XBIOS functionality:
- 16-bit Videl resolution setting
GEMDOS Functions
----------------------------------------------------------------------------
All GEMDOS versions:
T 0x00 Pterm0
T 0x01 Cconin
T 0x02 Cconout
T 0x03 Cauxin
T 0x04 Cauxout
T 0x05 Cprnout
T 0x06 Crawio
T 0x07 Crawin
T 0x08 Cnecin
T 0x09 Cconws
T 0x0a Cconrs
T 0x0b Cconis
T 0x0e Dsetdrv
T 0x10 Cconos
T 0x11 Cprnos
T 0x12 Cauxis
T 0x13 Cauxos
T 0x19 Dgetdrv
T 0x1a Fsetdta
T 0x20 Super
T 0x2a Tgetdate
T 0x2b Tsetdate
T 0x2c Tgettime
T 0x2d Tsettime
T 0x2f Fgetdta
T 0x30 Sversion
T 0x31 Ptermres
T 0x36 Dfree
T 0x39 Dcreate
T 0x3a Ddelete
T 0x3b Dsetpath
T 0x3c Fcreate
T 0x3d Fopen
T 0x3e Fclose
T 0x3f Fread
T 0x40 Fwrite
T 0x41 Fdelete
T 0x42 Fseek
T 0x43 Fattrib
T 0x45 Fdup
T 0x46 Fforce
T 0x47 Dgetpath
T 0x48 Malloc
T 0x49 Mfree
T 0x4a Mshrink
T 0x4b Pexec
T 0x4c Pterm
T 0x4e Fsfirst
T 0x4f Fsnext
T 0x56 Frename
T 0x57 Fdatime
GEMDOS v0.19 (TOS v2):
T 0x14 Maddalt
T 0x44 Mxalloc
(and Pexec mode 6)
Line-A functions
----------------------------------------------------------------------------
T $0 - Initialization
t $1 - Put pixel
t $2 - Get pixel
t $3 - Arbitrary line
t $4 - Horizontal line
t $5 - Filled rectangle
t $6 - Filled polygon (see bugs.txt)
t $7 - Bit block transfer (may miss options not needed by VDI)
t $8 - Text block transfer
T $9 - Show mouse
T $A - Hide mouse
t $B - Transform mouse
t $C - Undraw sprite
t $D - Draw sprite
t $E - Copy raster form
t $F - Seedfill
VDI functions
----------------------------------------------------------------------------
All TOS 1.0 calls are implemented.
T v_opnwk
X v_clswk
T v_opnvwk
T v_clsvwk
T v_clrwk
- v_updwk
> vst_load_fonts (needs GDOS or equivalent)
> vst_unload_fonts (needs GDOS or equivalent)
t vs_clip
T v_pline
T v_pmarker
T v_gtext
T v_fillarea
- v_cellarray (not supported by any current VDI driver)
T v_contourfill
T vr_recfl
T v_bar
T v_arc
T v_pieslice
T v_circle
T v_ellipse
T v_ellarc
T v_ellpie
X v_rbox
T v_rfbox
T v_justified
T vswr_mode
> vs_color
T vsl_type
X vsl_udsty
T vsl_width
> vsl_color
T vsl_ends
T vsm_type
T vsm_height
> vsm_color
T vst_height
T vst_point
T vst_rotation
X vst_font
> vst_color
T vst_effects
T vst_alignment
T vsf_interior
T vsf_style
t vsf_color
T vsf_perimeter
X vsf_udpat
t vro_cpyfm
> vrt_cpyfm
T vr_trnfm
> v_get_pixel
X vsin_mode
X vrq_locator
X vsm_locator
- vrq_valuator
- vsm_valuator
X vrq_choice
X vsm_choice
X vrq_string
X vsm_string
X vsc_form
X vex_timv
T v_show_c
T v_hide_c
X vq_mouse
T vex_butv
T vex_motv
T vex_curv
X vq_key_s
t vq_extnd
> vq_color
> vql_attributes
> vqm_attributes
> vqf_attributes
> vqt_attributes
> vqt_extent
X vqt_width
X vqt_name
- vq_cellarray (not supported by any current VDI driver)
X vqin_mode
X vqt_fontinfo
T vq_chcells
T v_exit_cur
T v_enter_cur
T v_curup
T v_curdown
T v_curright
T v_curleft
T v_curhome
T v_eeos
T v_eeol
T vs_curaddress
T v_curtext
T v_rvon
T v_rvoff
T vq_curaddress
T vq_tabstatus
- v_hardcopy
T v_dspcur (Atari docs are incorrect for this call)
T v_rmcur (Atari docs are incorrect for this call)
- v_form_adv
- v_output_window
- v_clear_disp_list
- v_bit_image
- vs_palette
- vqp_films
- vqp_state
- vsp_state
- vsp_save
- vsp_message
- vqp_error
- v_meta_extents
- v_write_meta
- vm_filename
TOS v4 extended VDI functionality:
- 16-bit support for graphics functions (for now, use fVDI)
AES functions
----------------------------------------------------------------------------
All AES versions:
t appl_init
X appl_read
X appl_write
t appl_find
t appl_tplay
X appl_trecord
X appl_yield (PC-GEM call)
t appl_exit
X evnt_keybd
t evnt_button
X evnt_mouse
t evnt_mesag
X evnt_timer
t evnt_multi
X evnt_dclick
t menu_bar
t menu_icheck
X menu_ienable
X menu_tnormal
X menu_text
t menu_register
X menu_unregister (PC-GEM call)
X menu_click (PC-GEM call)
X objc_add
X objc_delete
t objc_draw
X objc_find
t objc_offset
X objc_order
X objc_edit
t objc_change
t form_do
t form_dial
t form_alert
X form_error
t form_center
X form_keybd
X form_button
t graf_rubberbox
X graf_dragbox
X graf_mbox
T graf_growbox
T graf_shrinkbox
X graf_watchbox
X graf_slidebox
t graf_handle
t graf_mouse
t graf_mkstate
X scrp_read
X scrp_write
X scrp_clear (PC-GEM call)
X fsel_input
t wind_create
t wind_open
t wind_close
t wind_delete
t wind_get
X wind_set
X wind_find
t wind_update
t wind_calc
t rsrc_load
t rsrc_free
t rsrc_gaddr
X rsrc_saddr
t rsrc_obfix
X shel_read
X shel_write
X shel_get
X shel_put
T shel_find
X shel_envrn
X shel_rdef (PC-GEM call)
X shel_wdef (PC-GEM call)
AES v1.40 (TOS >= v1.04):
T fsel_exinput
t wind_new
AES v3.30 (TOS > v3.06):
- menu_attach
- menu_istart
- menu_popup
- menu_settings
AES v3.40 (TOS >= v4):
- objc_sysvar (3D look)
TOS v4 extended AES functionality:
- RSC file color icon support
Misc desktop functions
----------------------------------------------------------------------------
- Cartridge file system support (useless - will not be implemented)

View File

@ -0,0 +1,25 @@
General
BIOS/XBIOS
- implement missing XBIOS calls (e.g. Falcon DSP functions)
- reduce the number of 'extern' in .c files
- check that RAM disks work
- misc. TODOs in floppy.c
- implement full XHDI 1.30
- let EmuTOS set up correct MMU tree on 68040 (?)
BDOS (GEMDOS)
- move mem-only routines out of proc.c into umem.c or iumem.c
VDI
- The linemask for dashed lines is not calculated correct, depending on
the internal calculations of increase in x direction, like in original TOS.
AES
- Implement AES v3.x functions (TOS v3 menu functions and v4 3D look)
DESK
- Add a dialog for configuring the NVRAM (keyboard, languages, ...)
- Support for loading DESKTOP.INF (needs remapping of the icon indexes)
CLI

View File

@ -0,0 +1,22 @@
FUNCTION XHDI VERSION SUPPORTED BY EMUTOS
----------------------------------------------------------------------
XHGETVERSION 0 ALL YES
XHINQTARGET 1 ALL YES
XHRESERVE 2 ALL NO
XHLOCK 3 ALL NO
XHSTOP 4 ALL NO
XHEJECT 5 ALL NO
XHDRVMAP 6 ALL YES
XHINQDEV 7 ALL YES
XHINQDRIVER 8 ALL YES
XHNEWCOOKIE 9 OPTIONAL YES
XHREADWRITE 10 ALL YES
XHINQTARGET2 11 >= 1.01 YES
XHINQDEV2 12 >= 1.10 YES
XHDRIVERSPECIAL 13 OPTIONAL NO
XHGETCAPACITY 14 OPTIONAL YES
XHMEDIUMCHANGED 15 OPTIONAL NO
XHMINTINFO 16 OPTIONAL NO
XHDOSLIMITS 17 OPTIONAL YES
XHLASTACCESS 18 >= 1.25 NO
XHREACCESS 19 >= 1.25 NO

Binary file not shown.

View File

@ -0,0 +1,7 @@
THIS EMULATOR DOES NOT EMULATE THE ATARI ST.
Included here are Atari ST-targeted versions of EmuTOS, being a significant chunk of freely available and redistributable 68000 code that can be used to test the 68000-in-progress.
EmuTOS is distributed under the GPL. See its licence and other information within the doc/ subdirectory.
It was obtained via http://emutos.sourceforge.net/en/

BIN
ROMImages/SinclairQL/js.rom Normal file

Binary file not shown.

View File

@ -0,0 +1,5 @@
Supplied files:
js.rom
This file is copyright Amstrad but non-profit redistribution is permitted.