From 6d1c9546232645af9a3708aad433cc453c0caf36 Mon Sep 17 00:00:00 2001
From: Thomas Harte <thomas.harte@gmail.com>
Date: Wed, 10 Aug 2022 12:00:06 -0400
Subject: [PATCH 1/7] Make ST RAM size selectable, default to 1MB.

---
 Analyser/Static/AtariST/Target.hpp | 13 ++++++++++++-
 Machines/Atari/ST/AtariST.cpp      | 27 +++++++++++++++++++++++----
 2 files changed, 35 insertions(+), 5 deletions(-)

diff --git a/Analyser/Static/AtariST/Target.hpp b/Analyser/Static/AtariST/Target.hpp
index fd025baf8..b4d01279f 100644
--- a/Analyser/Static/AtariST/Target.hpp
+++ b/Analyser/Static/AtariST/Target.hpp
@@ -17,7 +17,18 @@ namespace Static {
 namespace AtariST {
 
 struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Target> {
-	Target() : Analyser::Static::Target(Machine::AtariST) {}
+	ReflectableEnum(MemorySize,
+		FiveHundredAndTwelveKilobytes,
+		OneMegabyte,
+		FourMegabytes);
+	MemorySize memory_size = MemorySize::OneMegabyte;
+
+	Target() : Analyser::Static::Target(Machine::AtariST) {
+		if(needs_declare()) {
+			DeclareField(memory_size);
+			AnnounceEnum(MemorySize);
+		}
+	}
 };
 
 }
diff --git a/Machines/Atari/ST/AtariST.cpp b/Machines/Atari/ST/AtariST.cpp
index f72647ef4..5a350f03d 100644
--- a/Machines/Atari/ST/AtariST.cpp
+++ b/Machines/Atari/ST/AtariST.cpp
@@ -35,12 +35,14 @@
 #include "../../Utility/MemoryPacker.hpp"
 #include "../../Utility/MemoryFuzzer.hpp"
 
+#include "../../../Analyser/Static/AtariST/Target.hpp"
+
 namespace Atari {
 namespace ST {
 
 constexpr int CLOCK_RATE = 8021247;
 
-using Target = Analyser::Static::Target;
+using Target = Analyser::Static::AtariST::Target;
 class ConcreteMachine:
 	public Atari::ST::Machine,
 	public CPU::MC68000Mk2::BusHandler,
@@ -70,10 +72,22 @@ class ConcreteMachine:
 			set_clock_rate(CLOCK_RATE);
 			speaker_.set_input_rate(float(CLOCK_RATE) / 4.0f);
 
-			ram_.resize(512 * 1024);	// i.e. 512kb
-			video_->set_ram(reinterpret_cast<uint16_t *>(ram_.data()), ram_.size());
+			switch(target.memory_size) {
+				default:
+				case Target::MemorySize::FiveHundredAndTwelveKilobytes:
+					ram_.resize(512 * 1024);
+				break;
+				case Target::MemorySize::OneMegabyte:
+					ram_.resize(1024 * 1024);
+				break;
+				case Target::MemorySize::FourMegabytes:
+					ram_.resize(4 * 1024 * 1024);
+				break;
+			}
 			Memory::Fuzz(ram_);
 
+			video_->set_ram(reinterpret_cast<uint16_t *>(ram_.data()), ram_.size());
+
 			constexpr ROM::Name rom_name = ROM::Name::AtariSTTOS100;
 			ROM::Request request(rom_name);
 			auto roms = rom_fetcher(request);
@@ -685,7 +699,12 @@ class ConcreteMachine:
 using namespace Atari::ST;
 
 Machine *Machine::AtariST(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher) {
-	return new ConcreteMachine(*target, rom_fetcher);
+	auto *const atari_target = dynamic_cast<const Analyser::Static::AtariST::Target *>(target);
+	if(!atari_target) {
+		return nullptr;
+	}
+
+	return new ConcreteMachine(*atari_target, rom_fetcher);
 }
 
 Machine::~Machine() {}

From 6b001e310694519b27b20b2a88158b652e663d50 Mon Sep 17 00:00:00 2001
From: Thomas Harte <thomas.harte@gmail.com>
Date: Wed, 10 Aug 2022 14:58:19 -0400
Subject: [PATCH 2/7] Add ST RAM size selection to the macOS UI.

---
 .../Machine/StaticAnalyser/CSStaticAnalyser.h |  2 +-
 .../StaticAnalyser/CSStaticAnalyser.mm        |  7 +++-
 .../Base.lproj/MachinePicker.xib              | 40 ++++++++++++++-----
 .../MachinePicker/MachinePicker.swift         | 12 +++++-
 4 files changed, 47 insertions(+), 14 deletions(-)

diff --git a/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.h b/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.h
index 970dbf641..4f96141f6 100644
--- a/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.h	
+++ b/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.h	
@@ -128,7 +128,7 @@ typedef int Kilobytes;
 - (instancetype)initWithAmstradCPCModel:(CSMachineCPCModel)model;
 - (instancetype)initWithAppleIIModel:(CSMachineAppleIIModel)model diskController:(CSMachineAppleIIDiskController)diskController;
 - (instancetype)initWithAppleIIgsModel:(CSMachineAppleIIgsModel)model memorySize:(Kilobytes)memorySize;
-- (instancetype)initWithAtariSTModel:(CSMachineAtariSTModel)model;
+- (instancetype)initWithAtariSTModel:(CSMachineAtariSTModel)model memorySize:(Kilobytes)memorySize;
 - (instancetype)initWithElectronDFS:(BOOL)dfs adfs:(BOOL)adfs ap6:(BOOL)ap6 sidewaysRAM:(BOOL)sidewaysRAM;
 - (instancetype)initWithEnterpriseModel:(CSMachineEnterpriseModel)model speed:(CSMachineEnterpriseSpeed)speed exosVersion:(CSMachineEnterpriseEXOS)exosVersion basicVersion:(CSMachineEnterpriseBASIC)basicVersion dos:(CSMachineEnterpriseDOS)dos;
 - (instancetype)initWithMacintoshModel:(CSMachineMacintoshModel)model;
diff --git a/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm b/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm
index 5da15a508..964dd8612 100644
--- a/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm	
+++ b/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm	
@@ -135,11 +135,16 @@
 }
 
 
-- (instancetype)initWithAtariSTModel:(CSMachineAtariSTModel)model {
+- (instancetype)initWithAtariSTModel:(CSMachineAtariSTModel)model memorySize:(Kilobytes)memorySize {
 	self = [super init];
 	if(self) {
 		using Target = Analyser::Static::AtariST::Target;
 		auto target = std::make_unique<Target>();
+		switch(memorySize) {
+			default:			target->memory_size = Target::MemorySize::FiveHundredAndTwelveKilobytes;	break;
+			case 1024:			target->memory_size = Target::MemorySize::OneMegabyte;						break;
+			case 4096:			target->memory_size = Target::MemorySize::FourMegabytes;					break;
+		}
 		_targets.push_back(std::move(target));
 	}
 	return self;
diff --git a/OSBindings/Mac/Clock Signal/MachinePicker/Base.lproj/MachinePicker.xib b/OSBindings/Mac/Clock Signal/MachinePicker/Base.lproj/MachinePicker.xib
index 51d26aa1c..a1a5ebc4e 100644
--- a/OSBindings/Mac/Clock Signal/MachinePicker/Base.lproj/MachinePicker.xib	
+++ b/OSBindings/Mac/Clock Signal/MachinePicker/Base.lproj/MachinePicker.xib	
@@ -1,8 +1,8 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="19529" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
+<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="20037" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
     <dependencies>
         <deployment identifier="macosx"/>
-        <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="19529"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="20037"/>
         <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
     </dependencies>
     <objects>
@@ -300,22 +300,39 @@ Gw
                             </tabViewItem>
                             <tabViewItem label="Atari ST" identifier="atarist" id="a6Y-mx-yFn">
                                 <view key="view" id="nnv-Wi-7hc">
-                                    <rect key="frame" x="10" y="7" width="400" height="223"/>
+                                    <rect key="frame" x="10" y="7" width="400" height="222"/>
                                     <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                                     <subviews>
-                                        <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="nLf-LI-nWO">
-                                            <rect key="frame" x="18" y="187" width="364" height="16"/>
-                                            <textFieldCell key="cell" lineBreakMode="clipping" title="At present only a 512k Atari ST is supported." id="gBA-ke-mur">
-                                                <font key="font" usesAppearanceFont="YES"/>
+                                        <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="dKg-qC-BBF">
+                                            <rect key="frame" x="18" y="184" width="58" height="16"/>
+                                            <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Memory:" id="ZBF-0r-RNK">
+                                                <font key="font" metaFont="system"/>
                                                 <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
-                                                <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
+                                                <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
                                             </textFieldCell>
                                         </textField>
+                                        <popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="QD0-qk-qCa">
+                                            <rect key="frame" x="79" y="178" width="80" height="25"/>
+                                            <popUpButtonCell key="cell" type="push" title="512 kb" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" borderStyle="borderAndBezel" tag="512" imageScaling="axesIndependently" inset="2" selectedItem="LVX-CI-lo9" id="dSS-yv-CDV">
+                                                <behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
+                                                <font key="font" metaFont="menu"/>
+                                                <menu key="menu" id="jsD-9U-bwN">
+                                                    <items>
+                                                        <menuItem title="512 kb" tag="512" id="LVX-CI-lo9"/>
+                                                        <menuItem title="1 mb" state="on" tag="1024" id="jMF-5n-E33"/>
+                                                        <menuItem title="4 mb" tag="4096" id="Z77-x8-Hzh"/>
+                                                    </items>
+                                                </menu>
+                                            </popUpButtonCell>
+                                        </popUpButton>
                                     </subviews>
                                     <constraints>
-                                        <constraint firstItem="nLf-LI-nWO" firstAttribute="leading" secondItem="nnv-Wi-7hc" secondAttribute="leading" constant="20" symbolic="YES" id="EIm-9Q-Rcj"/>
-                                        <constraint firstItem="nLf-LI-nWO" firstAttribute="top" secondItem="nnv-Wi-7hc" secondAttribute="top" constant="20" symbolic="YES" id="f9w-Lj-5dn"/>
-                                        <constraint firstAttribute="trailing" secondItem="nLf-LI-nWO" secondAttribute="trailing" constant="20" symbolic="YES" id="m3L-Nx-Aqz"/>
+                                        <constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="QD0-qk-qCa" secondAttribute="trailing" constant="20" symbolic="YES" id="2Ui-SB-1wY"/>
+                                        <constraint firstItem="dKg-qC-BBF" firstAttribute="centerY" secondItem="QD0-qk-qCa" secondAttribute="centerY" id="8tu-sO-8Ob"/>
+                                        <constraint firstItem="QD0-qk-qCa" firstAttribute="top" secondItem="nnv-Wi-7hc" secondAttribute="top" constant="20" symbolic="YES" id="AIX-7g-VAr"/>
+                                        <constraint firstItem="QD0-qk-qCa" firstAttribute="leading" secondItem="dKg-qC-BBF" secondAttribute="trailing" constant="8" symbolic="YES" id="EW6-qd-vjy"/>
+                                        <constraint firstItem="QD0-qk-qCa" firstAttribute="leading" secondItem="dKg-qC-BBF" secondAttribute="trailing" constant="8" symbolic="YES" id="bCB-i4-OhD"/>
+                                        <constraint firstItem="dKg-qC-BBF" firstAttribute="leading" secondItem="nnv-Wi-7hc" secondAttribute="leading" constant="20" symbolic="YES" id="pDt-6K-6iz"/>
                                     </constraints>
                                 </view>
                             </tabViewItem>
@@ -959,6 +976,7 @@ Gw
                 <outlet property="appleIIModelButton" destination="jli-ac-Sij" id="Jm3-f7-C17"/>
                 <outlet property="appleIIgsMemorySizeButton" destination="nQa-YS-utT" id="pTV-XL-zX3"/>
                 <outlet property="appleIIgsModelButton" destination="gcS-uy-mzl" id="Jcc-jC-cV1"/>
+                <outlet property="atariSTMemorySizeButton" destination="QD0-qk-qCa" id="aKa-L6-2Te"/>
                 <outlet property="cpcModelTypeButton" destination="2zv-Zo-rmO" id="F4C-b2-eS0"/>
                 <outlet property="electronADFSButton" destination="945-wU-JOH" id="Fjm-W8-kvh"/>
                 <outlet property="electronAP6Button" destination="cG2-Ph-S3Z" id="vkq-1J-KBG"/>
diff --git a/OSBindings/Mac/Clock Signal/MachinePicker/MachinePicker.swift b/OSBindings/Mac/Clock Signal/MachinePicker/MachinePicker.swift
index d6749071a..b70d4c3df 100644
--- a/OSBindings/Mac/Clock Signal/MachinePicker/MachinePicker.swift	
+++ b/OSBindings/Mac/Clock Signal/MachinePicker/MachinePicker.swift	
@@ -31,6 +31,9 @@ class MachinePicker: NSObject, NSTableViewDataSource, NSTableViewDelegate {
 	@IBOutlet var appleIIgsModelButton: NSPopUpButton!
 	@IBOutlet var appleIIgsMemorySizeButton: NSPopUpButton!
 
+	// MARK: - Atari ST properties
+	@IBOutlet var atariSTMemorySizeButton: NSPopUpButton!
+
 	// MARK: - CPC properties
 	@IBOutlet var cpcModelTypeButton: NSPopUpButton!
 
@@ -108,6 +111,9 @@ class MachinePicker: NSObject, NSTableViewDataSource, NSTableViewDelegate {
 		appleIIgsModelButton.selectItem(withTag: standardUserDefaults.integer(forKey: "new.appleIIgsModel"))
 		appleIIgsMemorySizeButton.selectItem(withTag: standardUserDefaults.integer(forKey: "new.appleIIgsMemorySize"))
 
+		// Atari ST settings
+		atariSTMemorySizeButton.selectItem(withTag: standardUserDefaults.integer(forKey: "new.atariSTMemorySize"))
+
 		// CPC settings
 		cpcModelTypeButton.selectItem(withTag: standardUserDefaults.integer(forKey: "new.cpcModel"))
 
@@ -169,6 +175,9 @@ class MachinePicker: NSObject, NSTableViewDataSource, NSTableViewDelegate {
 		standardUserDefaults.set(appleIIgsModelButton.selectedTag(), forKey: "new.appleIIgsModel")
 		standardUserDefaults.set(appleIIgsMemorySizeButton.selectedTag(), forKey: "new.appleIIgsMemorySize")
 
+		// Atari ST settings
+		standardUserDefaults.set(atariSTMemorySizeButton.selectedTag(), forKey: "new.atariSTMemorySize")
+
 		// CPC settings
 		standardUserDefaults.set(cpcModelTypeButton.selectedTag(), forKey: "new.cpcModel")
 
@@ -275,7 +284,8 @@ class MachinePicker: NSObject, NSTableViewDataSource, NSTableViewDelegate {
 				return CSStaticAnalyser(appleIIgsModel: model, memorySize: memorySize)
 
 			case "atarist":
-				return CSStaticAnalyser(atariSTModel: .model512k)
+				let memorySize = Kilobytes(atariSTMemorySizeButton.selectedTag())
+				return CSStaticAnalyser(atariSTModel: .model512k, memorySize: memorySize)
 
 			case "cpc":
 				switch cpcModelTypeButton.selectedTag() {

From 69f92963f96783f4f97bb772c5b683e714c66500 Mon Sep 17 00:00:00 2001
From: Thomas Harte <thomas.harte@gmail.com>
Date: Wed, 10 Aug 2022 15:39:55 -0400
Subject: [PATCH 3/7] Add Atari ST RAM size to Qt UI.

---
 OSBindings/Qt/mainwindow.cpp |  9 +++++++--
 OSBindings/Qt/mainwindow.ui  | 39 ++++++++++++++++++++++++++++++------
 2 files changed, 40 insertions(+), 8 deletions(-)

diff --git a/OSBindings/Qt/mainwindow.cpp b/OSBindings/Qt/mainwindow.cpp
index 39a1b646a..6eae7d315 100644
--- a/OSBindings/Qt/mainwindow.cpp
+++ b/OSBindings/Qt/mainwindow.cpp
@@ -1069,7 +1069,11 @@ void MainWindow::start_atariST() {
 	using Target = Analyser::Static::AtariST::Target;
 	auto target = std::make_unique<Target>();
 
-	/* There are no options yet for an Atari ST. */
+	switch(ui->atariSTRAMComboBox->currentIndex()) {
+		default:	target->memory_size = Target::MemorySize::FiveHundredAndTwelveKilobytes;	break;
+		case 1:		target->memory_size = Target::MemorySize::OneMegabyte;						break;
+		case 2:		target->memory_size = Target::MemorySize::FourMegabytes;					break;
+	}
 
 	launchTarget(std::move(target));
 }
@@ -1274,7 +1278,8 @@ void MainWindow::launchTarget(std::unique_ptr<Analyser::Static::Target> &&target
 	/* Amstrad CPC. */													\
 	ComboBox(amstradCPCModelComboBox, "amstradcpc.model");				\
 																		\
-	/* Atari ST: nothing */												\
+	/* Atari ST. */														\
+	ComboBox(atariSTRAMComboBox, "atarist.memorySize");					\
 																		\
 	/* Electron. */														\
 	CheckBox(electronDFSCheckBox, "electron.hasDFS");					\
diff --git a/OSBindings/Qt/mainwindow.ui b/OSBindings/Qt/mainwindow.ui
index 3d05cc783..511df37ea 100644
--- a/OSBindings/Qt/mainwindow.ui
+++ b/OSBindings/Qt/mainwindow.ui
@@ -343,12 +343,39 @@
         <string>Atari ST</string>
        </attribute>
        <layout class="QVBoxLayout">
-        <item alignment="Qt::AlignTop">
-         <widget class="QLabel">
-          <property name="text">
-           <string>At present only a 512k Atari ST is supported.</string>
-          </property>
-         </widget>
+        <item>
+         <layout class="QHBoxLayout">
+          <item>
+           <layout class="QFormLayout">
+            <item row="0" column="0">
+             <widget class="QLabel">
+              <property name="text">
+               <string>RAM:</string>
+              </property>
+             </widget>
+            </item>
+            <item row="0" column="1">
+             <widget class="QComboBox" name="atariSTRAMComboBox">
+              <item>
+               <property name="text">
+                <string>512 kb</string>
+               </property>
+              </item>
+              <item>
+               <property name="text">
+                <string>1 mb</string>
+               </property>
+              </item>
+              <item>
+               <property name="text">
+                <string>4 mb</string>
+               </property>
+              </item>
+             </widget>
+            </item>
+           </layout>
+          </item>
+         </layout>
         </item>
        </layout>
       </widget>

From b6f45d9a90b5bb05f54520fa57d522db3e23f263 Mon Sep 17 00:00:00 2001
From: Thomas Harte <thomas.harte@gmail.com>
Date: Wed, 10 Aug 2022 15:40:46 -0400
Subject: [PATCH 4/7] Fix struct/class confusion.

---
 InstructionSets/PowerPC/Decoder.cpp | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/InstructionSets/PowerPC/Decoder.cpp b/InstructionSets/PowerPC/Decoder.cpp
index 2c740a54a..bcb619866 100644
--- a/InstructionSets/PowerPC/Decoder.cpp
+++ b/InstructionSets/PowerPC/Decoder.cpp
@@ -595,10 +595,10 @@ Instruction Decoder<model, validate_reserved_bits>::decode(uint32_t opcode) {
 	return Instruction(opcode);
 }
 
-template class InstructionSet::PowerPC::Decoder<InstructionSet::PowerPC::Model::MPC601, true>;
-template class InstructionSet::PowerPC::Decoder<InstructionSet::PowerPC::Model::MPC603, true>;
-template class InstructionSet::PowerPC::Decoder<InstructionSet::PowerPC::Model::MPC620, true>;
+template struct InstructionSet::PowerPC::Decoder<InstructionSet::PowerPC::Model::MPC601, true>;
+template struct InstructionSet::PowerPC::Decoder<InstructionSet::PowerPC::Model::MPC603, true>;
+template struct InstructionSet::PowerPC::Decoder<InstructionSet::PowerPC::Model::MPC620, true>;
 
-template class InstructionSet::PowerPC::Decoder<InstructionSet::PowerPC::Model::MPC601, false>;
-template class InstructionSet::PowerPC::Decoder<InstructionSet::PowerPC::Model::MPC603, false>;
-template class InstructionSet::PowerPC::Decoder<InstructionSet::PowerPC::Model::MPC620, false>;
+template struct InstructionSet::PowerPC::Decoder<InstructionSet::PowerPC::Model::MPC601, false>;
+template struct InstructionSet::PowerPC::Decoder<InstructionSet::PowerPC::Model::MPC603, false>;
+template struct InstructionSet::PowerPC::Decoder<InstructionSet::PowerPC::Model::MPC620, false>;

From e2a8b26b5757e23691cdaec5d63c7f9c748125a3 Mon Sep 17 00:00:00 2001
From: Thomas Harte <thomas.harte@gmail.com>
Date: Wed, 10 Aug 2022 16:36:11 -0400
Subject: [PATCH 5/7] Display properly from greater RAM sizes.

---
 Machines/Atari/ST/Video.cpp | 5 +++--
 Machines/Atari/ST/Video.hpp | 1 +
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/Machines/Atari/ST/Video.cpp b/Machines/Atari/ST/Video.cpp
index a0a841e82..da9412a5a 100644
--- a/Machines/Atari/ST/Video.cpp
+++ b/Machines/Atari/ST/Video.cpp
@@ -130,8 +130,9 @@ Video::Video() :
 	crt_.set_visible_area(crt_.get_rect_for_area(33, 260, 440, 1700, 4.0f / 3.0f));
 }
 
-void Video::set_ram(uint16_t *ram, size_t) {
+void Video::set_ram(uint16_t *ram, size_t size) {
 	ram_ = ram;
+	ram_mask_ = int((size >> 1) - 1);
 }
 
 void Video::set_scan_target(Outputs::Display::ScanTarget *scan_target) {
@@ -202,7 +203,7 @@ void Video::run_for(HalfCycles duration) {
 			const int end_column = (since_load + run_length - 1) >> 3;
 
 			while(start_column != end_column) {
-				data_latch_[data_latch_position_] = ram_[current_address_ & 262143];
+				data_latch_[data_latch_position_] = ram_[current_address_ & ram_mask_];
 				data_latch_position_ = (data_latch_position_ + 1) & 127;
 				++current_address_;
 				++start_column;
diff --git a/Machines/Atari/ST/Video.hpp b/Machines/Atari/ST/Video.hpp
index 867d6c632..90fc72e19 100644
--- a/Machines/Atari/ST/Video.hpp
+++ b/Machines/Atari/ST/Video.hpp
@@ -138,6 +138,7 @@ class Video {
 		int current_address_ = 0;
 
 		uint16_t *ram_ = nullptr;
+		int ram_mask_ = 0;
 
 		int x_ = 0, y_ = 0, next_y_ = 0;
 		bool load_ = false;

From 94231ca3e3bae0a4a46eea27d42f0f5c7fba5921 Mon Sep 17 00:00:00 2001
From: Thomas Harte <thomas.harte@gmail.com>
Date: Wed, 10 Aug 2022 16:41:45 -0400
Subject: [PATCH 6/7] Put word-sizing responsibility on the caller.

---
 Machines/Atari/ST/AtariST.cpp | 5 ++++-
 Machines/Atari/ST/Video.cpp   | 2 +-
 Machines/Atari/ST/Video.hpp   | 2 +-
 3 files changed, 6 insertions(+), 3 deletions(-)

diff --git a/Machines/Atari/ST/AtariST.cpp b/Machines/Atari/ST/AtariST.cpp
index 5a350f03d..6925ad85a 100644
--- a/Machines/Atari/ST/AtariST.cpp
+++ b/Machines/Atari/ST/AtariST.cpp
@@ -86,7 +86,10 @@ class ConcreteMachine:
 			}
 			Memory::Fuzz(ram_);
 
-			video_->set_ram(reinterpret_cast<uint16_t *>(ram_.data()), ram_.size());
+			video_->set_ram(
+				reinterpret_cast<uint16_t *>(ram_.data()),
+				ram_.size() >> 1
+			);
 
 			constexpr ROM::Name rom_name = ROM::Name::AtariSTTOS100;
 			ROM::Request request(rom_name);
diff --git a/Machines/Atari/ST/Video.cpp b/Machines/Atari/ST/Video.cpp
index da9412a5a..7549c857c 100644
--- a/Machines/Atari/ST/Video.cpp
+++ b/Machines/Atari/ST/Video.cpp
@@ -132,7 +132,7 @@ Video::Video() :
 
 void Video::set_ram(uint16_t *ram, size_t size) {
 	ram_ = ram;
-	ram_mask_ = int((size >> 1) - 1);
+	ram_mask_ = int(size - 1);
 }
 
 void Video::set_scan_target(Outputs::Display::ScanTarget *scan_target) {
diff --git a/Machines/Atari/ST/Video.hpp b/Machines/Atari/ST/Video.hpp
index 90fc72e19..a467c33cc 100644
--- a/Machines/Atari/ST/Video.hpp
+++ b/Machines/Atari/ST/Video.hpp
@@ -37,7 +37,7 @@ class Video {
 		Video();
 
 		/*!
-			Sets the memory pool that provides video, and its size.
+			Sets the memory pool that provides video, and its size in words.
 		*/
 		void set_ram(uint16_t *, size_t size);
 

From c7373a5d3e5a7831c04831edb39c5367646d4d10 Mon Sep 17 00:00:00 2001
From: Thomas Harte <thomas.harte@gmail.com>
Date: Sat, 13 Aug 2022 10:09:34 -0400
Subject: [PATCH 7/7] Overtly treat .ST images as FAT12.

---
 Analyser/Static/StaticAnalyser.cpp            |  3 +-
 .../Clock Signal.xcodeproj/project.pbxproj    | 10 -----
 Storage/Disk/DiskImage/Formats/FAT12.cpp      |  2 +-
 Storage/Disk/DiskImage/Formats/ST.cpp         | 42 ------------------
 Storage/Disk/DiskImage/Formats/ST.hpp         | 43 -------------------
 5 files changed, 2 insertions(+), 98 deletions(-)
 delete mode 100644 Storage/Disk/DiskImage/Formats/ST.cpp
 delete mode 100644 Storage/Disk/DiskImage/Formats/ST.hpp

diff --git a/Analyser/Static/StaticAnalyser.cpp b/Analyser/Static/StaticAnalyser.cpp
index 99668d6e1..dc59652d8 100644
--- a/Analyser/Static/StaticAnalyser.cpp
+++ b/Analyser/Static/StaticAnalyser.cpp
@@ -53,7 +53,6 @@
 #include "../../Storage/Disk/DiskImage/Formats/NIB.hpp"
 #include "../../Storage/Disk/DiskImage/Formats/OricMFMDSK.hpp"
 #include "../../Storage/Disk/DiskImage/Formats/SSD.hpp"
-#include "../../Storage/Disk/DiskImage/Formats/ST.hpp"
 #include "../../Storage/Disk/DiskImage/Formats/STX.hpp"
 #include "../../Storage/Disk/DiskImage/Formats/WOZ.hpp"
 
@@ -199,7 +198,7 @@ static Media GetMediaAndPlatforms(const std::string &file_name, TargetPlatform::
 	Format("sg", result.cartridges, Cartridge::BinaryDump, TargetPlatform::Sega)								// SG
 	Format("sms", result.cartridges, Cartridge::BinaryDump, TargetPlatform::Sega)								// SMS
 	Format("ssd", result.disks, Disk::DiskImageHolder<Storage::Disk::SSD>, TargetPlatform::Acorn)				// SSD
-	Format("st", result.disks, Disk::DiskImageHolder<Storage::Disk::ST>, TargetPlatform::AtariST)				// ST
+	Format("st", result.disks, Disk::DiskImageHolder<Storage::Disk::FAT12>, TargetPlatform::AtariST)				// ST
 	Format("stx", result.disks, Disk::DiskImageHolder<Storage::Disk::STX>, TargetPlatform::AtariST)				// STX
 	Format("tap", result.tapes, Tape::CommodoreTAP, TargetPlatform::Commodore)									// TAP (Commodore)
 	Format("tap", result.tapes, Tape::OricTAP, TargetPlatform::Oric)											// TAP (Oric)
diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj
index 4d3770d04..bc29cfaa2 100644
--- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj	
+++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj	
@@ -366,7 +366,6 @@
 		4B778F0223A5EBA40000D260 /* MFMSectorDump.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B58601C1F806AB200AEE2E3 /* MFMSectorDump.cpp */; };
 		4B778F0323A5EBB00000D260 /* FAT12.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEBFB4B2002C4BF000708CC /* FAT12.cpp */; };
 		4B778F0423A5EBB00000D260 /* OricMFMDSK.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4518971F75FD1B00926311 /* OricMFMDSK.cpp */; };
-		4B778F0523A5EBB00000D260 /* ST.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BE0A3EC237BB170002AB46F /* ST.cpp */; };
 		4B778F0623A5EC150000D260 /* CAS.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0E04E81FC9E5DA00F43484 /* CAS.cpp */; };
 		4B778F0723A5EC150000D260 /* CommodoreTAP.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC91B811D1F160E00884B76 /* CommodoreTAP.cpp */; };
 		4B778F0823A5EC150000D260 /* CSW.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B3BF5AE1F146264005B6C36 /* CSW.cpp */; };
@@ -1018,8 +1017,6 @@
 		4BDB61EB2032806E0048AF91 /* CSAtari2600.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B2A539A1D117D36003C6002 /* CSAtari2600.mm */; };
 		4BDB61EC203285AE0048AF91 /* Atari2600OptionsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B8FE21F1DA19D7C0090D3CE /* Atari2600OptionsController.swift */; };
 		4BDDBA991EF3451200347E61 /* Z80MachineCycleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BDDBA981EF3451200347E61 /* Z80MachineCycleTests.swift */; };
-		4BE0A3EE237BB170002AB46F /* ST.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BE0A3EC237BB170002AB46F /* ST.cpp */; };
-		4BE0A3EF237BB170002AB46F /* ST.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BE0A3EC237BB170002AB46F /* ST.cpp */; };
 		4BE211DE253E4E4800435408 /* 65C02_no_Rockwell_test.bin in Resources */ = {isa = PBXBuildFile; fileRef = 4BE211DD253E4E4800435408 /* 65C02_no_Rockwell_test.bin */; };
 		4BE211FF253FC80900435408 /* StaticAnalyser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BE211FE253FC80900435408 /* StaticAnalyser.cpp */; };
 		4BE21200253FC80900435408 /* StaticAnalyser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BE211FE253FC80900435408 /* StaticAnalyser.cpp */; };
@@ -2134,8 +2131,6 @@
 		4BDCC5F81FB27A5E001220C5 /* ROMMachine.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ROMMachine.hpp; sourceTree = "<group>"; };
 		4BDDBA981EF3451200347E61 /* Z80MachineCycleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Z80MachineCycleTests.swift; sourceTree = "<group>"; };
 		4BE0151C286A8C8E00EA42E9 /* MemorySwitches.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = MemorySwitches.hpp; sourceTree = "<group>"; };
-		4BE0A3EC237BB170002AB46F /* ST.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ST.cpp; sourceTree = "<group>"; };
-		4BE0A3ED237BB170002AB46F /* ST.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ST.hpp; sourceTree = "<group>"; };
 		4BE211DD253E4E4800435408 /* 65C02_no_Rockwell_test.bin */ = {isa = PBXFileReference; lastKnownFileType = archive.macbinary; name = 65C02_no_Rockwell_test.bin; path = "Klaus Dormann/65C02_no_Rockwell_test.bin"; sourceTree = "<group>"; };
 		4BE211FC253FC80800435408 /* StaticAnalyser.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = StaticAnalyser.hpp; sourceTree = "<group>"; };
 		4BE211FD253FC80900435408 /* Target.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Target.hpp; sourceTree = "<group>"; };
@@ -2874,7 +2869,6 @@
 				4B0F94FC208C1A1600FE41D9 /* NIB.cpp */,
 				4B4518971F75FD1B00926311 /* OricMFMDSK.cpp */,
 				4B4518991F75FD1B00926311 /* SSD.cpp */,
-				4BE0A3EC237BB170002AB46F /* ST.cpp */,
 				4B7BA03323C58B1E00B98D9E /* STX.cpp */,
 				4B6ED2EE208E2F8A0047B343 /* WOZ.cpp */,
 				4B80CD75256CA15E00176FCC /* 2MG.hpp */,
@@ -2894,7 +2888,6 @@
 				4B0F94FD208C1A1600FE41D9 /* NIB.hpp */,
 				4B4518981F75FD1B00926311 /* OricMFMDSK.hpp */,
 				4B45189A1F75FD1B00926311 /* SSD.hpp */,
-				4BE0A3ED237BB170002AB46F /* ST.hpp */,
 				4B7BA03223C58B1E00B98D9E /* STX.hpp */,
 				4B6ED2EF208E2F8A0047B343 /* WOZ.hpp */,
 				4BFDD7891F7F2DB4008579B9 /* Utility */,
@@ -5660,7 +5653,6 @@
 				4B8318BC22D3E588006DB630 /* DisplayMetrics.cpp in Sources */,
 				4BEDA40E25B2844B000C2DBD /* Decoder.cpp in Sources */,
 				4B1B88BD202E3D3D00B67DFF /* MultiMachine.cpp in Sources */,
-				4BE0A3EF237BB170002AB46F /* ST.cpp in Sources */,
 				4B055A971FAE85BB0060FFFF /* ZX8081.cpp in Sources */,
 				4B055AAD1FAE85FD0060FFFF /* PCMTrack.cpp in Sources */,
 				4B2130E3273A7A0A008A77B4 /* Audio.cpp in Sources */,
@@ -5862,7 +5854,6 @@
 				4BD67DD0209BF27B00AB2146 /* Encoder.cpp in Sources */,
 				4BAE495920328897004BE78E /* ZX8081Controller.swift in Sources */,
 				4B89451A201967B4007DE474 /* ConfidenceSummary.cpp in Sources */,
-				4BE0A3EE237BB170002AB46F /* ST.cpp in Sources */,
 				4B54C0C51F8D91D90050900F /* Keyboard.cpp in Sources */,
 				4BEE149A227FC0EA00133682 /* IWM.cpp in Sources */,
 				4B69FB441C4D941400B5F0AA /* TapeUEF.cpp in Sources */,
@@ -6183,7 +6174,6 @@
 				4BE90FFD22D5864800FB464D /* MacintoshVideoTests.mm in Sources */,
 				4B4F478A25367EDC004245B8 /* 65816AddressingTests.swift in Sources */,
 				4B778F0B23A5EC150000D260 /* TapeUEF.cpp in Sources */,
-				4B778F0523A5EBB00000D260 /* ST.cpp in Sources */,
 				4B778F0C23A5EC150000D260 /* TZX.cpp in Sources */,
 				4B778F1B23A5ED380000D260 /* Video.cpp in Sources */,
 				4B778F4723A5F1DD0000D260 /* StaticAnalyser.cpp in Sources */,
diff --git a/Storage/Disk/DiskImage/Formats/FAT12.cpp b/Storage/Disk/DiskImage/Formats/FAT12.cpp
index 76c04bf10..574e61c96 100644
--- a/Storage/Disk/DiskImage/Formats/FAT12.cpp
+++ b/Storage/Disk/DiskImage/Formats/FAT12.cpp
@@ -53,5 +53,5 @@ int FAT12::get_head_count() {
 }
 
 long FAT12::get_file_offset_for_position(Track::Address address) {
-	return (address.position.as_int()*head_count_ + address.head) * sector_size_ * sector_count_;
+	return (address.position.as_int() * head_count_ + address.head) * sector_size_ * sector_count_;
 }
diff --git a/Storage/Disk/DiskImage/Formats/ST.cpp b/Storage/Disk/DiskImage/Formats/ST.cpp
deleted file mode 100644
index 7c4a05194..000000000
--- a/Storage/Disk/DiskImage/Formats/ST.cpp
+++ /dev/null
@@ -1,42 +0,0 @@
-//
-//  ST.cpp
-//  Clock Signal
-//
-//  Created by Thomas Harte on 12/11/2019.
-//  Copyright © 2019 Thomas Harte. All rights reserved.
-//
-
-#include "ST.hpp"
-
-namespace {
-	constexpr int sectors_per_track = 10;
-	constexpr int sector_size = 2;
-}
-
-using namespace Storage::Disk;
-
-ST::ST(const std::string &file_name) : MFMSectorDump(file_name) {
-	// Very loose validation: the file needs to be a whole number of tracks,
-	// and not more than 160 of them.
-	const auto stats = file_.stats();
-	if(stats.st_size % (512*10)) throw Error::InvalidFormat;
-	if(stats.st_size > 512*10*160) throw Error::InvalidFormat;
-
-	// Head count: 2 if there are more than 80 tracks. Otherwise 1.
-	head_count_ = (stats.st_size >= 512 * 10 * 80) ? 2 : 1;
-	track_count_ = std::max(80, int(stats.st_size / (512 * 10 * head_count_)));
-
-	set_geometry(sectors_per_track, sector_size, 1, true);
-}
-
-HeadPosition ST::get_maximum_head_position() {
-	return HeadPosition(track_count_);
-}
-
-int ST::get_head_count() {
-	return head_count_;
-}
-
-long ST::get_file_offset_for_position(Track::Address address) {
-	return (address.position.as_int() * head_count_ + address.head) * 512 * sectors_per_track;
-}
diff --git a/Storage/Disk/DiskImage/Formats/ST.hpp b/Storage/Disk/DiskImage/Formats/ST.hpp
deleted file mode 100644
index de1218264..000000000
--- a/Storage/Disk/DiskImage/Formats/ST.hpp
+++ /dev/null
@@ -1,43 +0,0 @@
-//
-//  ST.hpp
-//  Clock Signal
-//
-//  Created by Thomas Harte on 12/11/2019.
-//  Copyright © 2019 Thomas Harte. All rights reserved.
-//
-
-#ifndef ST_hpp
-#define ST_hpp
-
-#include "MFMSectorDump.hpp"
-
-namespace Storage {
-namespace Disk {
-
-/*!
-	Provides a @c Disk containing an ST disk image: a decoded sector dump of an Atari ST disk.
-*/
-class ST: public MFMSectorDump {
-	public:
-		/*!
-			Construct an @c ST containing content from the file with name @c file_name.
-
-			@throws Storage::FileHolder::Error::CantOpen if this file can't be opened.
-			@throws Error::InvalidFormat if the file doesn't appear to contain a .ST format image.
-		*/
-		ST(const std::string &file_name);
-
-		HeadPosition get_maximum_head_position() final;
-		int get_head_count() final;
-
-	private:
-		long get_file_offset_for_position(Track::Address address) final;
-
-		int head_count_;
-		int track_count_;
-};
-
-}
-}
-
-#endif /* ST_hpp */