1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-11-17 10:06:21 +00:00

Add full cursor automation.

This commit is contained in:
Thomas Harte 2024-05-12 22:16:29 -04:00
parent c82517c9fd
commit 18ffb9294f
5 changed files with 141 additions and 292 deletions

View File

@ -1,226 +0,0 @@
//
// SWIIndex.cpp
// Clock Signal
//
// Created by Thomas Harte on 05/05/2024.
// Copyright © 2024 Thomas Harte. All rights reserved.
//
#include "SWIIndex.hpp"
#include <cstdio>
#include <set>
using namespace Analyser::Static::Acorn;
SWIDescription::SWIDescription(uint32_t comment) {
chunk_offset = comment & 0b111111;
chunk_number = (comment >> 6) & 0b11111111111;
error_flag = comment & (1 << 17);
swi_group = SWIGroup((comment >> 18) & 0b11);
os_flag = (comment >> 20) & 0b1111;
static std::set<uint32_t> encountered;
const uint32_t number = comment & uint32_t(~(1 << 17));
switch(number) {
case 0x00:
name = "OS_WriteC";
registers[0].type = Register::Type::Character;
break;
case 0x01:
name = "OS_WriteS";
registers[0].type = Register::Type::FollowingString;
break;
case 0x02:
name = "OS_Write0";
registers[0].type = Register::Type::PointerToString;
break;
case 0x03:
name = "OS_NewLine";
break;
case 0x04:
name = "OS_ReadC";
break;
case 0x05:
name = "OS_CLI";
registers[0].type = Register::Type::PointerToString;
break;
case 0x06:
name = "OS_Byte";
registers[0].type = Register::Type::ReasonCode;
registers[1].type = Register::Type::ReasonCodeDependent;
registers[2].type = Register::Type::ReasonCodeDependent;
break;
case 0x07:
name = "OS_Word";
registers[0].type = Register::Type::ReasonCode;
registers[1].type = Register::Type::Pointer;
break;
case 0x08:
name = "OS_File";
registers[0].type = Register::Type::ReasonCode;
break;
case 0x09:
name = "OS_Args";
registers[0].type = Register::Type::ReasonCode;
registers[1].type = Register::Type::Pointer;
registers[2].type = Register::Type::ReasonCodeDependent;
break;
case 0x0c:
name = "OS_GBPB";
registers[0].type = Register::Type::ReasonCode;
break;
case 0x0d:
name = "OS_Find";
registers[0].type = Register::Type::ReasonCode;
break;
case 0x0f:
name = "OS_Control";
registers[0].type = Register::Type::Pointer;
registers[1].type = Register::Type::Pointer;
registers[2].type = Register::Type::Pointer;
registers[3].type = Register::Type::Pointer;
break;
case 0x1d:
name = "OS_Heap";
registers[0].type = Register::Type::ReasonCode;
registers[1].type = Register::Type::Pointer;
registers[2].type = Register::Type::Pointer;
registers[3].type = Register::Type::ReasonCodeDependent;
break;
case 0x3a:
name = "OS_ValidateAddress";
registers[0].type = Register::Type::Pointer;
registers[1].type = Register::Type::Pointer;
break;
case 0x400e2:
name = "Wimp_PlotIcon";
registers[1].type = Register::Type::Pointer;
break;
default:
if(encountered.find(number) == encountered.end()) {
encountered.insert(number);
printf("SWI: %08x\n", number);
}
break;
}
}
// The following has the effect of logging all taken SWIs and their return codes.
/* if(
(instruction & 0x0f00'0000) == 0x0f00'0000 &&
executor.registers().test(InstructionSet::ARM::Condition(instruction >> 28))
) {
if(instruction & 0x2'0000) {
swis.emplace_back();
swis.back().count = swi_count++;
swis.back().opcode = instruction;
swis.back().address = executor.pc();
swis.back().return_address = executor.registers().pc(4);
for(int c = 0; c < 10; c++) swis.back().regs[c] = executor.registers()[uint32_t(c)];
// Possibly capture more detail.
//
// Cf. http://productsdb.riscos.com/support/developers/prm_index/numswilist.html
uint32_t pointer = 0;
switch(instruction & 0xfd'ffff) {
case 0x41501:
swis.back().swi_name = "MessageTrans_OpenFile";
// R0: pointer to file descriptor; R1: pointer to filename; R2: pointer to hold file data.
// (R0 and R1 are in the RMA if R2 = 0)
pointer = executor.registers()[1];
break;
case 0x41502:
swis.back().swi_name = "MessageTrans_Lookup";
break;
case 0x41506:
swis.back().swi_name = "MessageTrans_ErrorLookup";
break;
case 0x4028a:
swis.back().swi_name = "Podule_EnumerateChunksWithInfo";
break;
case 0x4000a:
swis.back().swi_name = "Econet_ReadLocalStationAndNet";
break;
case 0x4000e:
swis.back().swi_name = "Econet_SetProtection";
break;
case 0x40015:
swis.back().swi_name = "Econet_ClaimPort";
break;
case 0x40541:
swis.back().swi_name = "FileCore_Create";
break;
case 0x80156:
case 0x8015b:
swis.back().swi_name = "PDriver_MiscOpForDriver";
break;
case 0x1e:
swis.back().swi_name = "OS_Module";
break;
case 0x20:
swis.back().swi_name = "OS_Release";
break;
case 0x21:
swis.back().swi_name = "OS_ReadUnsigned";
break;
case 0x23:
swis.back().swi_name = "OS_ReadVarVal";
// R0: pointer to variable name.
pointer = executor.registers()[0];
break;
case 0x24:
swis.back().swi_name = "OS_SetVarVal";
// R0: pointer to variable name.
pointer = executor.registers()[0];
break;
case 0x26:
swis.back().swi_name = "OS_GSRead";
break;
case 0x27:
swis.back().swi_name = "OS_GSTrans";
pointer = executor.registers()[0];
break;
case 0x29:
swis.back().swi_name = "OS_FSControl";
break;
case 0x2a:
swis.back().swi_name = "OS_ChangeDynamicArea";
break;
case 0x4c:
swis.back().swi_name = "OS_ReleaseDeviceVector";
break;
case 0x43057:
swis.back().swi_name = "Territory_LowerCaseTable";
break;
case 0x43058:
swis.back().swi_name = "Territory_UpperCaseTable";
break;
case 0x42fc0:
swis.back().swi_name = "Portable_Speed";
break;
case 0x42fc1:
swis.back().swi_name = "Portable_Control";
break;
}
}*/

View File

@ -1,53 +0,0 @@
//
// SWIIndex.hpp
// Clock Signal
//
// Created by Thomas Harte on 05/05/2024.
// Copyright © 2024 Thomas Harte. All rights reserved.
//
#ifndef SWIIndex_hpp
#define SWIIndex_hpp
#include <array>
#include <cstdint>
#include <string>
namespace Analyser::Static::Acorn {
enum class SWIGroup: uint8_t {
OperatingSystem = 0b00,
OperatingSystemModules = 0b01,
ThirdPartyApplications = 0b10,
UserApplications = 0b11,
};
struct SWIDescription {
SWIDescription(uint32_t comment);
uint8_t chunk_offset;
SWIGroup swi_group;
uint16_t chunk_number;
uint8_t os_flag;
bool error_flag;
std::string name;
struct Register {
enum class Type {
Unused,
ReasonCode,
Pointer,
PointerToString,
ReasonCodeDependent,
Character,
/// A string that appears immediately after the SWI in memory.
FollowingString,
} type = Type::Unused;
};
std::array<Register, 14> registers;
};
}
#endif /* SWIIndex_hpp */

View File

@ -124,6 +124,10 @@ class ConcreteMachine:
executor_.bus.set_rom(roms.find(risc_os)->second);
insert_media(target.media);
if(!target.media.disks.empty()) {
autoload_phase_ = AutoloadPhase::WaitingForStartup;
}
fill_pipeline(0);
}
@ -197,7 +201,31 @@ class ConcreteMachine:
}
} break;
// case 0x400c2:
case 0x400c5: {
const uint32_t address = executor_.registers()[1];
uint32_t x1, y1, x2, y2;
executor_.bus.read(address + 4, x1, false);
executor_.bus.read(address + 8, y1, false);
executor_.bus.read(address + 12, x2, false);
executor_.bus.read(address + 16, y2, false);
printf("Wimp_OpenWindow: %d, %d -> %d, %d\n", x1, y1, x2, y2);
} break;
case 0x400c2:
if(autoload_phase_ == AutoloadPhase::WaitingForStartup) {
// Wait a further second, mouse down to (32, 240), left click.
// That'll trigger disk access.
cursor_actions_.push_back(CursorAction::wait(24'000'000));
cursor_actions_.push_back(CursorAction::move_to(32, 240));
cursor_actions_.push_back(CursorAction::button(0, true));
cursor_actions_.push_back(CursorAction::wait(12'000'000));
cursor_actions_.push_back(CursorAction::button(0, false));
autoload_phase_ = AutoloadPhase::OpeningDisk;
}
printf("!!");
[[fallthrough]];
case 0x400e2: {
// Wimp_PlotIcon; try to determine what's on-screen next.
const uint32_t address = executor_.registers()[1];
@ -265,6 +293,64 @@ class ConcreteMachine:
const bool use_original_speed = executor_.bus.video().frame_rate_overages() > 10;
#endif
//
// Mouse scripting.
//
if(!cursor_actions_.empty()) {
const auto move_to_next = [&]() {
cursor_action_waited_ = 0;
cursor_actions_.erase(cursor_actions_.begin());
};
const auto &action = cursor_actions_.front();
switch(action.type) {
case CursorAction::Type::MoveTo: {
// A measure of where within the tip lies within
// the default RISC OS cursor.
constexpr int ActionPointOffset = 20;
constexpr int MaxStep = 8;
const auto position = executor_.bus.video().cursor_location();
if(!position) break;
const auto [x, y] = *position;
auto x_diff = action.value.move_to.x - (x + ActionPointOffset);
auto y_diff = action.value.move_to.y - y;
if(abs(x_diff) < 2 && abs(y_diff) < 2) {
move_to_next();
break;
}
if(abs(y_diff) > MaxStep || abs(x_diff) > MaxStep) {
if(abs(y_diff) > abs(x_diff)) {
x_diff = (x_diff * MaxStep + abs(y_diff) - 1) / abs(y_diff);
y_diff = std::clamp(y_diff, -MaxStep, MaxStep);
} else {
y_diff = (y_diff * MaxStep + abs(x_diff) - 1) / abs(x_diff);
x_diff = std::clamp(x_diff, -MaxStep, MaxStep);
}
}
get_mouse().move(x_diff, y_diff);
} break;
case CursorAction::Type::Wait:
cursor_action_waited_ += cycles.as<int>();
if(cursor_action_waited_ >= action.value.wait.duration) {
move_to_next();
}
break;
case CursorAction::Type::Button:
get_mouse().set_button_pressed(action.value.button.button, action.value.button.down);
move_to_next();
break;
}
}
//
// Execution proper.
//
// TODO: divide up the following if necessary to put scripted mouse actions
// at predictably-regular steps.
if(use_original_speed) run_for<true>(cycles);
else run_for<false>(cycles);
}
@ -280,7 +366,6 @@ class ConcreteMachine:
case 4: macro_tick<4, original_speed>(); break;
case 6: macro_tick<6, original_speed>(); break;
}
}
}
@ -403,6 +488,57 @@ class ConcreteMachine:
SWISubversion latched_subversion_;
} pipeline_;
struct CursorAction {
enum class Type {
MoveTo,
Button,
Wait,
} type;
union {
struct {
int x, y;
} move_to;
struct {
int duration;
} wait;
struct {
int button;
bool down;
} button;
} value;
static CursorAction move_to(int x, int y) {
CursorAction action;
action.type = Type::MoveTo;
action.value.move_to.x = x;
action.value.move_to.y = y;
return action;
}
static CursorAction wait(int duration) {
CursorAction action;
action.type = Type::Wait;
action.value.wait.duration = duration;
return action;
}
static CursorAction button(int button, bool down) {
CursorAction action;
action.type = Type::Button;
action.value.button.button = button;
action.value.button.down = down;
return action;
}
};
std::vector<CursorAction> cursor_actions_;
int cursor_action_waited_ = 0;
enum class AutoloadPhase {
WaitingForStartup,
OpeningDisk,
Ended,
};
AutoloadPhase autoload_phase_ = AutoloadPhase::Ended;
};
}

View File

@ -252,7 +252,7 @@ struct Video {
// The following is provided for input automation;
// it does not correlate with real hardware functionality.
//
std::optional<std::pair<uint32_t, uint32_t>> cursor_location() {
std::optional<std::pair<int, int>> cursor_location() {
if(
!dma_enabled_ ||
vertical_timing_.cursor_end <= vertical_timing_.cursor_start ||
@ -264,8 +264,8 @@ struct Video {
const auto horizontal_start = horizontal_timing_.display_start + horizontal_state_.output_latency(colour_depth_);
return std::make_pair(
horizontal_timing_.cursor_start + 6 - (horizontal_start * 2),
vertical_timing_.cursor_start - vertical_timing_.display_start);
int(horizontal_timing_.cursor_start) + 6 - int(horizontal_start * 2),
int(vertical_timing_.cursor_start) - int(vertical_timing_.display_start));
}
private:

View File

@ -971,8 +971,6 @@
4BB505862B9634F30031C43C /* Archimedes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BB505842B9634F30031C43C /* Archimedes.cpp */; };
4BB505872B9634F30031C43C /* Archimedes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BB505842B9634F30031C43C /* Archimedes.cpp */; };
4BB505892B9C0E6F0031C43C /* Messy ARM in Resources */ = {isa = PBXBuildFile; fileRef = 4BB505882B9C0E6F0031C43C /* Messy ARM */; };
4BB508672BE816E8000ACC9F /* SWIIndex.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BB508652BE816E8000ACC9F /* SWIIndex.cpp */; };
4BB508682BE816E8000ACC9F /* SWIIndex.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BB508652BE816E8000ACC9F /* SWIIndex.cpp */; };
4BB697CB1D4B6D3E00248BDF /* TimedEventLoop.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BB697C91D4B6D3E00248BDF /* TimedEventLoop.cpp */; };
4BB697CE1D4BA44400248BDF /* CommodoreGCR.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BB697CC1D4BA44400248BDF /* CommodoreGCR.cpp */; };
4BB73EA21B587A5100552FC2 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BB73EA11B587A5100552FC2 /* AppDelegate.swift */; };
@ -2124,8 +2122,6 @@
4BB505842B9634F30031C43C /* Archimedes.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Archimedes.cpp; sourceTree = "<group>"; };
4BB505852B9634F30031C43C /* Archimedes.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Archimedes.hpp; sourceTree = "<group>"; };
4BB505882B9C0E6F0031C43C /* Messy ARM */ = {isa = PBXFileReference; lastKnownFileType = folder; path = "Messy ARM"; sourceTree = "<group>"; };
4BB508652BE816E8000ACC9F /* SWIIndex.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = SWIIndex.cpp; sourceTree = "<group>"; };
4BB508662BE816E8000ACC9F /* SWIIndex.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = SWIIndex.hpp; sourceTree = "<group>"; };
4BB5B995281B1D3E00522DA9 /* RegisterSizes.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = RegisterSizes.hpp; sourceTree = "<group>"; };
4BB5B996281B1E3F00522DA9 /* Perform.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Perform.hpp; sourceTree = "<group>"; };
4BB5B997281B1F7B00522DA9 /* Status.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Status.hpp; sourceTree = "<group>"; };
@ -3689,8 +3685,6 @@
4B8944ED201967B4007DE474 /* StaticAnalyser.hpp */,
4B8944EF201967B4007DE474 /* Tape.hpp */,
4BE32313205327D7006EF799 /* Target.hpp */,
4BB508652BE816E8000ACC9F /* SWIIndex.cpp */,
4BB508662BE816E8000ACC9F /* SWIIndex.hpp */,
);
path = Acorn;
sourceTree = "<group>";
@ -5933,7 +5927,6 @@
4B055AED1FAE9BA20060FFFF /* Z80Storage.cpp in Sources */,
4B1B88BC202E2EC100B67DFF /* MultiKeyboardMachine.cpp in Sources */,
4BC890D4230F86020025A55A /* DirectAccessDevice.cpp in Sources */,
4BB508682BE816E8000ACC9F /* SWIIndex.cpp in Sources */,
4B2E86C925D892EF0024F1E9 /* DAT.cpp in Sources */,
4B6AAEAE230E40250078E864 /* Target.cpp in Sources */,
4BF437EF209D0F7E008CBD6B /* SegmentParser.cpp in Sources */,
@ -6300,7 +6293,6 @@
4BDB61EB2032806E0048AF91 /* CSAtari2600.mm in Sources */,
4BFDD78C1F7F2DB4008579B9 /* ImplicitSectors.cpp in Sources */,
4B894526201967B4007DE474 /* StaticAnalyser.cpp in Sources */,
4BB508672BE816E8000ACC9F /* SWIIndex.cpp in Sources */,
4BEE0A6F1D72496600532C7B /* Cartridge.cpp in Sources */,
4B051CB62680158600CA44E8 /* EXDos.cpp in Sources */,
4BB505782B962DDF0031C43C /* SoundGenerator.cpp in Sources */,