mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-26 23:52:26 +00:00
Merge pull request #143 from TomHarte/MotorControl
Introduces both manual and automatic tape control for the ZX80 and 81
This commit is contained in:
commit
37696532c2
@ -23,7 +23,9 @@ Machine::Machine() :
|
||||
nmi_is_enabled_(false),
|
||||
tape_player_(ZX8081ClockRate),
|
||||
use_fast_tape_hack_(false),
|
||||
tape_advance_delay_(0) {
|
||||
tape_advance_delay_(0),
|
||||
tape_is_automatically_playing_(false),
|
||||
tape_is_playing_(false) {
|
||||
set_clock_rate(ZX8081ClockRate);
|
||||
tape_player_.set_motor_control(true);
|
||||
clear_all_keys();
|
||||
@ -55,7 +57,7 @@ int Machine::perform_machine_cycle(const CPU::Z80::PartialMachineCycle &cycle) {
|
||||
|
||||
if(is_zx81_) horizontal_counter_ %= 207;
|
||||
if(!tape_advance_delay_) {
|
||||
tape_player_.run_for_cycles(cycle.length);
|
||||
if(tape_is_automatically_playing_ || tape_is_playing_) tape_player_.run_for_cycles(cycle.length);
|
||||
} else {
|
||||
tape_advance_delay_ = std::max(tape_advance_delay_ - cycle.length, 0);
|
||||
}
|
||||
@ -147,6 +149,9 @@ int Machine::perform_machine_cycle(const CPU::Z80::PartialMachineCycle &cycle) {
|
||||
tape_player_.get_tape()->seek(time);
|
||||
}
|
||||
}
|
||||
|
||||
// Check for automatic tape control.
|
||||
tape_is_automatically_playing_ = use_automatic_tape_motor_control_ && (address >= automatic_tape_motor_start_address_) && (address < automatic_tape_motor_end_address_);
|
||||
is_opcode_read = true;
|
||||
|
||||
case CPU::Z80::PartialMachineCycle::Read:
|
||||
@ -209,12 +214,16 @@ void Machine::configure_as_target(const StaticAnalyser::Target &target) {
|
||||
tape_return_address_ = 0x380;
|
||||
vsync_start_cycle_ = 16;
|
||||
vsync_end_cycle_ = 32;
|
||||
automatic_tape_motor_start_address_ = 0x0340;
|
||||
automatic_tape_motor_end_address_ = 0x03c3;
|
||||
} else {
|
||||
rom_ = zx80_rom_;
|
||||
tape_trap_address_ = 0x220;
|
||||
tape_return_address_ = 0x248;
|
||||
vsync_start_cycle_ = 13;
|
||||
vsync_end_cycle_ = 33;
|
||||
automatic_tape_motor_start_address_ = 0x0206;
|
||||
automatic_tape_motor_end_address_ = 0x024d;
|
||||
}
|
||||
rom_mask_ = (uint16_t)(rom_.size() - 1);
|
||||
|
||||
|
@ -63,11 +63,18 @@ class Machine:
|
||||
void clear_all_keys();
|
||||
|
||||
inline void set_use_fast_tape_hack(bool activate) { use_fast_tape_hack_ = activate; }
|
||||
inline void set_use_automatic_tape_motor_control(bool enabled) {
|
||||
use_automatic_tape_motor_control_ = enabled;
|
||||
if(!enabled) tape_is_automatically_playing_ = false;
|
||||
}
|
||||
inline void set_tape_is_playing(bool is_playing) { tape_is_playing_ = is_playing; }
|
||||
|
||||
private:
|
||||
std::shared_ptr<Video> video_;
|
||||
std::vector<uint8_t> zx81_rom_, zx80_rom_;
|
||||
|
||||
uint16_t tape_trap_address_, tape_return_address_;
|
||||
uint16_t automatic_tape_motor_start_address_, automatic_tape_motor_end_address_;
|
||||
|
||||
std::vector<uint8_t> ram_;
|
||||
uint16_t ram_mask_, ram_base_;
|
||||
@ -94,6 +101,8 @@ class Machine:
|
||||
uint8_t latched_video_byte_;
|
||||
|
||||
bool use_fast_tape_hack_;
|
||||
bool use_automatic_tape_motor_control_;
|
||||
bool tape_is_playing_, tape_is_automatically_playing_;
|
||||
int tape_advance_delay_;
|
||||
};
|
||||
|
||||
|
@ -96,6 +96,7 @@
|
||||
4B924E991E74D22700B76AF1 /* AtariStaticAnalyserTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B924E981E74D22700B76AF1 /* AtariStaticAnalyserTests.mm */; };
|
||||
4B9252CE1E74D28200B76AF1 /* Atari ROMs in Resources */ = {isa = PBXBuildFile; fileRef = 4B9252CD1E74D28200B76AF1 /* Atari ROMs */; };
|
||||
4B92EACA1B7C112B00246143 /* 6502TimingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B92EAC91B7C112B00246143 /* 6502TimingTests.swift */; };
|
||||
4B95FA9D1F11893B0008E395 /* ZX8081OptionsPanel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B95FA9C1F11893B0008E395 /* ZX8081OptionsPanel.swift */; };
|
||||
4B96F7221D75119A0058BB2D /* Tape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B96F7201D75119A0058BB2D /* Tape.cpp */; };
|
||||
4B9CCDA11DA279CA0098B625 /* Vic20OptionsPanel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B9CCDA01DA279CA0098B625 /* Vic20OptionsPanel.swift */; };
|
||||
4BA0F68E1EEA0E8400E9489E /* ZX8081.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BA0F68C1EEA0E8400E9489E /* ZX8081.cpp */; };
|
||||
@ -609,6 +610,7 @@
|
||||
4B924E981E74D22700B76AF1 /* AtariStaticAnalyserTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AtariStaticAnalyserTests.mm; sourceTree = "<group>"; };
|
||||
4B9252CD1E74D28200B76AF1 /* Atari ROMs */ = {isa = PBXFileReference; lastKnownFileType = folder; path = "Atari ROMs"; sourceTree = "<group>"; };
|
||||
4B92EAC91B7C112B00246143 /* 6502TimingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = 6502TimingTests.swift; sourceTree = "<group>"; };
|
||||
4B95FA9C1F11893B0008E395 /* ZX8081OptionsPanel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ZX8081OptionsPanel.swift; sourceTree = "<group>"; };
|
||||
4B96F7201D75119A0058BB2D /* Tape.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Tape.cpp; path = ../../StaticAnalyser/Acorn/Tape.cpp; sourceTree = "<group>"; };
|
||||
4B96F7211D75119A0058BB2D /* Tape.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Tape.hpp; path = ../../StaticAnalyser/Acorn/Tape.hpp; sourceTree = "<group>"; };
|
||||
4B9CCDA01DA279CA0098B625 /* Vic20OptionsPanel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Vic20OptionsPanel.swift; sourceTree = "<group>"; };
|
||||
@ -1312,6 +1314,7 @@
|
||||
4B8FE2211DA19FB20090D3CE /* MachinePanel.swift */,
|
||||
4B2A332E1DB86869002876E3 /* OricOptionsPanel.swift */,
|
||||
4B9CCDA01DA279CA0098B625 /* Vic20OptionsPanel.swift */,
|
||||
4B95FA9C1F11893B0008E395 /* ZX8081OptionsPanel.swift */,
|
||||
4B8FE2131DA19D5F0090D3CE /* Atari2600Options.xib */,
|
||||
4B8FE2171DA19D5F0090D3CE /* ElectronOptions.xib */,
|
||||
4B8FE2151DA19D5F0090D3CE /* MachineDocument.xib */,
|
||||
@ -2566,6 +2569,7 @@
|
||||
4BD4A8CD1E077E8A0020D856 /* PCMSegment.cpp in Sources */,
|
||||
4BD14B111D74627C0088EAD6 /* StaticAnalyser.cpp in Sources */,
|
||||
4BBF99151C8FBA6F0075DAFB /* CRTOpenGL.cpp in Sources */,
|
||||
4B95FA9D1F11893B0008E395 /* ZX8081OptionsPanel.swift in Sources */,
|
||||
4B0CCC451C62D0B3001CAC5F /* CRT.cpp in Sources */,
|
||||
4BCF1FA41DADC3DD0039D2E7 /* Oric.cpp in Sources */,
|
||||
4BEA525E1DF33323007E74F2 /* Tape.cpp in Sources */,
|
||||
|
@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="12120" systemVersion="16F73" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="12121" systemVersion="16F73" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="12120"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="12121"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
@ -12,17 +12,17 @@
|
||||
</customObject>
|
||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||
<window title="Options" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" hidesOnDeactivate="YES" oneShot="NO" releasedWhenClosed="NO" showsToolbarButton="NO" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" id="ota-g7-hOL" customClass="MachinePanel" customModule="Clock_Signal" customModuleProvider="target">
|
||||
<window title="Options" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" hidesOnDeactivate="YES" oneShot="NO" releasedWhenClosed="NO" showsToolbarButton="NO" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" id="ota-g7-hOL" customClass="ZX8081OptionsPanel" customModule="Clock_Signal" customModuleProvider="target">
|
||||
<windowStyleMask key="styleMask" titled="YES" closable="YES" utility="YES" nonactivatingPanel="YES" HUD="YES"/>
|
||||
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
||||
<rect key="contentRect" x="83" y="102" width="200" height="54"/>
|
||||
<rect key="contentRect" x="83" y="102" width="261" height="100"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="1366" height="768"/>
|
||||
<view key="contentView" id="7Pv-WL-2Rq">
|
||||
<rect key="frame" x="0.0" y="0.0" width="200" height="54"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="261" height="100"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<button translatesAutoresizingMaskIntoConstraints="NO" id="sBT-cU-h7s">
|
||||
<rect key="frame" x="18" y="18" width="164" height="18"/>
|
||||
<rect key="frame" x="18" y="64" width="225" height="18"/>
|
||||
<buttonCell key="cell" type="check" title="Load Tapes Quickly" bezelStyle="regularSquare" imagePosition="left" alignment="left" state="on" inset="2" id="w0l-ha-esm">
|
||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
@ -31,18 +31,46 @@
|
||||
<action selector="setFastLoading:" target="ota-g7-hOL" id="me0-h2-Ga5"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button translatesAutoresizingMaskIntoConstraints="NO" id="qSb-72-6Os">
|
||||
<rect key="frame" x="18" y="44" width="225" height="18"/>
|
||||
<buttonCell key="cell" type="check" title="Control Tape Motor Automatically" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="CzC-YT-lgA">
|
||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="setAutomaticTapeMotorConrol:" target="ota-g7-hOL" id="bpF-1P-tga"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="tkN-gI-RmT">
|
||||
<rect key="frame" x="20" y="19" width="221" height="19"/>
|
||||
<buttonCell key="cell" type="roundRect" title="Play Tape" bezelStyle="roundedRect" alignment="center" enabled="NO" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="cTq-f9-Gzx">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="cellTitle"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="playOrPauseTape:" target="ota-g7-hOL" id="O0K-pL-nOr"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="qSb-72-6Os" firstAttribute="leading" secondItem="7Pv-WL-2Rq" secondAttribute="leading" constant="20" id="05p-Jn-ueX"/>
|
||||
<constraint firstAttribute="trailing" secondItem="sBT-cU-h7s" secondAttribute="trailing" constant="20" id="79b-2A-2c7"/>
|
||||
<constraint firstItem="sBT-cU-h7s" firstAttribute="top" secondItem="7Pv-WL-2Rq" secondAttribute="top" constant="20" id="E5m-wo-X92"/>
|
||||
<constraint firstAttribute="bottom" secondItem="sBT-cU-h7s" secondAttribute="bottom" constant="20" id="eZe-Eu-INg"/>
|
||||
<constraint firstItem="qSb-72-6Os" firstAttribute="top" secondItem="sBT-cU-h7s" secondAttribute="bottom" constant="6" id="WxD-kP-vwf"/>
|
||||
<constraint firstAttribute="bottom" secondItem="tkN-gI-RmT" secondAttribute="bottom" constant="20" id="Xnu-On-nOA"/>
|
||||
<constraint firstItem="tkN-gI-RmT" firstAttribute="leading" secondItem="7Pv-WL-2Rq" secondAttribute="leading" constant="20" id="fHf-K0-PsU"/>
|
||||
<constraint firstItem="tkN-gI-RmT" firstAttribute="top" secondItem="qSb-72-6Os" secondAttribute="bottom" constant="8" id="gLh-vE-Cqk"/>
|
||||
<constraint firstAttribute="trailing" secondItem="qSb-72-6Os" secondAttribute="trailing" constant="20" id="mQz-p8-aYf"/>
|
||||
<constraint firstItem="sBT-cU-h7s" firstAttribute="leading" secondItem="7Pv-WL-2Rq" secondAttribute="leading" constant="20" id="nDy-Xc-Ug9"/>
|
||||
<constraint firstAttribute="trailing" secondItem="tkN-gI-RmT" secondAttribute="trailing" constant="20" id="vgD-A3-m6T"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="automaticTapeMotorControlButton" destination="qSb-72-6Os" id="bB6-FP-TKM"/>
|
||||
<outlet property="fastLoadingButton" destination="sBT-cU-h7s" id="uWa-EB-mbd"/>
|
||||
<outlet property="playOrPauseTapeButton" destination="tkN-gI-RmT" id="UnJ-nb-3mv"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="-2" y="-8"/>
|
||||
<point key="canvasLocation" x="28.5" y="15"/>
|
||||
</window>
|
||||
</objects>
|
||||
</document>
|
||||
|
@ -10,6 +10,7 @@
|
||||
#import "CSElectron.h"
|
||||
#import "CSOric.h"
|
||||
#import "CSVic20.h"
|
||||
#import "CSZX8081.h"
|
||||
|
||||
#import "CSStaticAnalyser.h"
|
||||
|
||||
|
@ -0,0 +1,49 @@
|
||||
//
|
||||
// ZX8081OptionsPanel.swift
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 08/07/2017.
|
||||
// Copyright © 2017 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
class ZX8081OptionsPanel: MachinePanel {
|
||||
var zx8081: CSZX8081! {
|
||||
get {
|
||||
return self.machine as! CSZX8081
|
||||
}
|
||||
}
|
||||
|
||||
@IBOutlet var automaticTapeMotorControlButton: NSButton!
|
||||
var automaticTapeMotorControlDefaultsKey: String {
|
||||
get { return prefixedUserDefaultsKey("automaticTapeMotorControl") }
|
||||
}
|
||||
@IBAction func setAutomaticTapeMotorConrol(_ sender: NSButton!) {
|
||||
let isEnabled = sender.state == NSOnState
|
||||
UserDefaults.standard.set(isEnabled, forKey: self.automaticTapeMotorControlDefaultsKey)
|
||||
self.playOrPauseTapeButton.isEnabled = !isEnabled
|
||||
self.zx8081.useAutomaticTapeMotorControl = isEnabled
|
||||
}
|
||||
|
||||
@IBOutlet var playOrPauseTapeButton: NSButton!
|
||||
@IBAction func playOrPauseTape(_ sender: NSButton!) {
|
||||
self.zx8081.tapeIsPlaying = !self.zx8081.tapeIsPlaying
|
||||
self.playOrPauseTapeButton.title = self.zx8081.tapeIsPlaying
|
||||
? NSLocalizedString("Stop Tape", comment: "Text for a button that will stop tape playback")
|
||||
: NSLocalizedString("Play Tape", comment: "Text for a button that will start tape playback")
|
||||
}
|
||||
|
||||
// MARK: option restoration
|
||||
override func establishStoredOptions() {
|
||||
super.establishStoredOptions()
|
||||
|
||||
let standardUserDefaults = UserDefaults.standard
|
||||
standardUserDefaults.register(defaults: [
|
||||
self.automaticTapeMotorControlDefaultsKey: true
|
||||
])
|
||||
|
||||
let automaticTapeMotorControlIsEnabled = standardUserDefaults.bool(forKey: self.automaticTapeMotorControlDefaultsKey)
|
||||
self.automaticTapeMotorControlButton.state = automaticTapeMotorControlIsEnabled ? NSOnState : NSOffState
|
||||
self.playOrPauseTapeButton.isEnabled = !automaticTapeMotorControlIsEnabled
|
||||
self.zx8081.useAutomaticTapeMotorControl = automaticTapeMotorControlIsEnabled
|
||||
}
|
||||
}
|
@ -14,4 +14,7 @@
|
||||
|
||||
@property (nonatomic, assign) BOOL useFastLoadingHack;
|
||||
|
||||
@property (nonatomic, assign) BOOL useAutomaticTapeMotorControl;
|
||||
@property (nonatomic, assign) BOOL tapeIsPlaying;
|
||||
|
||||
@end
|
||||
|
@ -110,4 +110,18 @@
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setTapeIsPlaying:(BOOL)tapeIsPlaying {
|
||||
@synchronized(self) {
|
||||
_tapeIsPlaying = tapeIsPlaying;
|
||||
_zx8081.set_tape_is_playing(tapeIsPlaying ? true : false);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setUseAutomaticTapeMotorControl:(BOOL)useAutomaticTapeMotorControl {
|
||||
@synchronized(self) {
|
||||
_useAutomaticTapeMotorControl = useAutomaticTapeMotorControl;
|
||||
_zx8081.set_use_automatic_tape_motor_control(useAutomaticTapeMotorControl ? true : false);
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
@ -54,7 +54,7 @@ Tape::Pulse ZX80O81P::virtual_get_next_pulse() {
|
||||
// Start with 1 second of silence.
|
||||
if(!is_past_silence_ || has_finished_data()) {
|
||||
pulse.type = Pulse::Type::Low;
|
||||
pulse.length.length = 10;
|
||||
pulse.length.length = 1;
|
||||
pulse.length.clock_rate = 1;
|
||||
is_past_silence_ = true;
|
||||
has_ended_final_byte_ = has_finished_data();
|
||||
|
Loading…
Reference in New Issue
Block a user