mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-18 17:06:15 +00:00
Merge pull request #475 from TomHarte/VisibleActivity
Shows activity indicators on the Mac
This commit is contained in:
commit
7a5d16ccf8
@ -7,7 +7,9 @@
|
||||
//
|
||||
|
||||
#include "1770.hpp"
|
||||
|
||||
#include "../../Storage/Disk/Encodings/MFM/Constants.hpp"
|
||||
#include "../../Outputs/Log.hpp"
|
||||
|
||||
using namespace WD;
|
||||
|
||||
@ -25,10 +27,10 @@ void WD1770::set_register(int address, uint8_t value) {
|
||||
if((value&0xf0) == 0xd0) {
|
||||
if(value == 0xd0) {
|
||||
// Force interrupt **immediately**.
|
||||
printf("Force interrupt immediately\n");
|
||||
LOG("Force interrupt immediately");
|
||||
posit_event(static_cast<int>(Event1770::ForceInterrupt));
|
||||
} else {
|
||||
printf("!!!TODO: force interrupt!!!\n");
|
||||
ERROR("!!!TODO: force interrupt!!!");
|
||||
update_status([] (Status &status) {
|
||||
status.type = Status::One;
|
||||
});
|
||||
@ -193,7 +195,7 @@ void WD1770::posit_event(int new_event_type) {
|
||||
// Wait for a new command, branch to the appropriate handler.
|
||||
case 0:
|
||||
wait_for_command:
|
||||
printf("Idle...\n");
|
||||
LOG("Idle...");
|
||||
set_data_mode(DataMode::Scanning);
|
||||
index_hole_count_ = 0;
|
||||
|
||||
@ -209,7 +211,7 @@ void WD1770::posit_event(int new_event_type) {
|
||||
status.interrupt_request = false;
|
||||
});
|
||||
|
||||
printf("Starting %02x\n", command_);
|
||||
LOG("Starting " << std::hex << command_ << std::endl);
|
||||
|
||||
if(!(command_ & 0x80)) goto begin_type_1;
|
||||
if(!(command_ & 0x40)) goto begin_type_2;
|
||||
@ -327,7 +329,7 @@ void WD1770::posit_event(int new_event_type) {
|
||||
}
|
||||
|
||||
if(header_[0] == track_) {
|
||||
printf("Reached track %d\n", track_);
|
||||
LOG("Reached track " << std::dec << track_);
|
||||
update_status([] (Status &status) {
|
||||
status.crc_error = false;
|
||||
});
|
||||
@ -396,20 +398,20 @@ void WD1770::posit_event(int new_event_type) {
|
||||
READ_ID();
|
||||
|
||||
if(index_hole_count_ == 5) {
|
||||
printf("Failed to find sector %d\n", sector_);
|
||||
LOG("Failed to find sector " << std::dec << sector_);
|
||||
update_status([] (Status &status) {
|
||||
status.record_not_found = true;
|
||||
});
|
||||
goto wait_for_command;
|
||||
}
|
||||
if(distance_into_section_ == 7) {
|
||||
printf("Considering %d/%d\n", header_[0], header_[2]);
|
||||
LOG("Considering " << std::dec << header_[0] << "/" << header_[2]);
|
||||
set_data_mode(DataMode::Scanning);
|
||||
if( header_[0] == track_ && header_[2] == sector_ &&
|
||||
(has_motor_on_line() || !(command_&0x02) || ((command_&0x08) >> 3) == header_[1])) {
|
||||
printf("Found %d/%d\n", header_[0], header_[2]);
|
||||
LOG("Found " << std::dec << header_[0] << "/" << header_[2]);
|
||||
if(get_crc_generator().get_value()) {
|
||||
printf("CRC error; back to searching\n");
|
||||
LOG("CRC error; back to searching");
|
||||
update_status([] (Status &status) {
|
||||
status.crc_error = true;
|
||||
});
|
||||
@ -465,7 +467,7 @@ void WD1770::posit_event(int new_event_type) {
|
||||
distance_into_section_++;
|
||||
if(distance_into_section_ == 2) {
|
||||
if(get_crc_generator().get_value()) {
|
||||
printf("CRC error; terminating\n");
|
||||
LOG("CRC error; terminating");
|
||||
update_status([this] (Status &status) {
|
||||
status.crc_error = true;
|
||||
});
|
||||
@ -476,7 +478,7 @@ void WD1770::posit_event(int new_event_type) {
|
||||
sector_++;
|
||||
goto test_type2_write_protection;
|
||||
}
|
||||
printf("Finished reading sector %d\n", sector_);
|
||||
LOG("Finished reading sector " << std::dec << sector_);
|
||||
goto wait_for_command;
|
||||
}
|
||||
goto type2_check_crc;
|
||||
@ -558,7 +560,7 @@ void WD1770::posit_event(int new_event_type) {
|
||||
sector_++;
|
||||
goto test_type2_write_protection;
|
||||
}
|
||||
printf("Wrote sector %d\n", sector_);
|
||||
LOG("Wrote sector " << std::dec << sector_);
|
||||
goto wait_for_command;
|
||||
|
||||
|
||||
|
@ -7,9 +7,8 @@
|
||||
//
|
||||
|
||||
#include "i8272.hpp"
|
||||
//#include "../../Storage/Disk/Encodings/MFM/Encoder.hpp"
|
||||
|
||||
#include <cstdio>
|
||||
#include "../../Outputs/Log.hpp"
|
||||
|
||||
using namespace Intel::i8272;
|
||||
|
||||
@ -115,7 +114,7 @@ void i8272::run_for(Cycles cycles) {
|
||||
while(steps--) {
|
||||
// Perform a step.
|
||||
int direction = (drives_[c].target_head_position < drives_[c].head_position) ? -1 : 1;
|
||||
printf("Target %d versus believed %d\n", drives_[c].target_head_position, drives_[c].head_position);
|
||||
LOG("Target " << std::dec << drives_[c].target_head_position << " versus believed " << drives_[c].head_position);
|
||||
select_drive(c);
|
||||
get_drive().step(Storage::Disk::HeadPosition(direction));
|
||||
if(drives_[c].target_head_position >= 0) drives_[c].head_position += direction;
|
||||
@ -386,17 +385,17 @@ void i8272::posit_event(int event_type) {
|
||||
// the index hole limit is breached or a sector is found with a cylinder, head, sector and size equal to the
|
||||
// values in the internal registers.
|
||||
index_hole_limit_ = 2;
|
||||
// printf("Seeking %02x %02x %02x %02x\n", cylinder_, head_, sector_, size_);
|
||||
// LOG("Seeking " << std::dec << cylinder_ << " " << head_ " " << sector_ << " " << size_);
|
||||
find_next_sector:
|
||||
FIND_HEADER();
|
||||
if(!index_hole_limit_) {
|
||||
// Two index holes have passed wihout finding the header sought.
|
||||
// printf("Not found\n");
|
||||
// LOG("Not found");
|
||||
SetNoData();
|
||||
goto abort;
|
||||
}
|
||||
index_hole_count_ = 0;
|
||||
// printf("Header\n");
|
||||
// LOG("Header");
|
||||
READ_HEADER();
|
||||
if(index_hole_count_) {
|
||||
// This implies an index hole was sighted within the header. Error out.
|
||||
@ -407,11 +406,11 @@ void i8272::posit_event(int event_type) {
|
||||
// This implies a CRC error in the header; mark as such but continue.
|
||||
SetDataError();
|
||||
}
|
||||
// printf("Considering %02x %02x %02x %02x [%04x]\n", header_[0], header_[1], header_[2], header_[3], get_crc_generator().get_value());
|
||||
// LOG("Considering << std::hex << header_[0] << " " << header_[1] << " " << header_[2] << " " << header_[3] << " [" << get_crc_generator().get_value() << "]");
|
||||
if(header_[0] != cylinder_ || header_[1] != head_ || header_[2] != sector_ || header_[3] != size_) goto find_next_sector;
|
||||
|
||||
// Branch to whatever is supposed to happen next
|
||||
// printf("Proceeding\n");
|
||||
// LOG("Proceeding");
|
||||
switch(command_[0] & 0x1f) {
|
||||
case CommandReadData:
|
||||
case CommandReadDeletedData:
|
||||
@ -425,7 +424,7 @@ void i8272::posit_event(int event_type) {
|
||||
|
||||
// Performs the read data or read deleted data command.
|
||||
read_data:
|
||||
printf("Read [deleted] data [%02x %02x %02x %02x ... %02x %02x]\n", command_[2], command_[3], command_[4], command_[5], command_[6], command_[8]);
|
||||
LOG("Read [deleted] data [" << std::hex << command_[2] << " " << command_[3] << " " << command_[4] << " " << command_[5] << " ... " << command_[6] << " " << command_[8] << "]");
|
||||
read_next_data:
|
||||
goto read_write_find_header;
|
||||
|
||||
@ -509,7 +508,7 @@ void i8272::posit_event(int event_type) {
|
||||
goto post_st012chrn;
|
||||
|
||||
write_data:
|
||||
printf("Write [deleted] data [%02x %02x %02x %02x ... %02x %02x]\n", command_[2], command_[3], command_[4], command_[5], command_[6], command_[8]);
|
||||
LOG("Write [deleted] data [" << std::hex << command_[2] << " " << command_[3] << " " << command_[4] << " " << command_[5] << " ... " << command_[6] << " " << command_[8] << "]");
|
||||
|
||||
if(get_drive().get_is_read_only()) {
|
||||
SetNotWriteable();
|
||||
@ -544,7 +543,7 @@ void i8272::posit_event(int event_type) {
|
||||
goto write_loop;
|
||||
}
|
||||
|
||||
printf("Wrote %d bytes\n", distance_into_section_);
|
||||
LOG("Wrote " << std::dec << distance_into_section_ << " bytes");
|
||||
write_crc();
|
||||
expects_input_ = false;
|
||||
WAIT_FOR_EVENT(Event::DataWritten);
|
||||
@ -560,7 +559,7 @@ void i8272::posit_event(int event_type) {
|
||||
// Performs the read ID command.
|
||||
read_id:
|
||||
// Establishes the drive and head being addressed, and whether in double density mode.
|
||||
printf("Read ID [%02x %02x]\n", command_[0], command_[1]);
|
||||
LOG("Read ID [" << std::hex << command_[0] << " " << command_[1] << "]");
|
||||
|
||||
// Sets a maximum index hole limit of 2 then waits either until it finds a header mark or sees too many index holes.
|
||||
// If a header mark is found, reads in the following bytes that produce a header. Otherwise branches to data not found.
|
||||
@ -582,7 +581,7 @@ void i8272::posit_event(int event_type) {
|
||||
|
||||
// Performs read track.
|
||||
read_track:
|
||||
printf("Read track [%02x %02x %02x %02x]\n", command_[2], command_[3], command_[4], command_[5]);
|
||||
LOG("Read track [" << std::hex << command_[2] << " " << command_[3] << " " << command_[4] << " " << command_[5] << "]");
|
||||
|
||||
// Wait for the index hole.
|
||||
WAIT_FOR_EVENT(Event::IndexHole);
|
||||
@ -623,7 +622,7 @@ void i8272::posit_event(int event_type) {
|
||||
|
||||
// Performs format [/write] track.
|
||||
format_track:
|
||||
printf("Format track\n");
|
||||
LOG("Format track");
|
||||
if(get_drive().get_is_read_only()) {
|
||||
SetNotWriteable();
|
||||
goto abort;
|
||||
@ -667,7 +666,7 @@ void i8272::posit_event(int event_type) {
|
||||
break;
|
||||
}
|
||||
|
||||
printf("W: %02x %02x %02x %02x, %04x\n", header_[0], header_[1], header_[2], header_[3], get_crc_generator().get_value());
|
||||
LOG("W: " << std::hex << header_[0] << " " << header_[1] << " " << header_[2] << " " << header_[3] << ", " << get_crc_generator().get_value());
|
||||
write_crc();
|
||||
|
||||
// Write the sector body.
|
||||
@ -699,15 +698,15 @@ void i8272::posit_event(int event_type) {
|
||||
goto post_st012chrn;
|
||||
|
||||
scan_low:
|
||||
printf("Scan low unimplemented!!\n");
|
||||
ERROR("Scan low unimplemented!!");
|
||||
goto wait_for_command;
|
||||
|
||||
scan_low_or_equal:
|
||||
printf("Scan low or equal unimplemented!!\n");
|
||||
ERROR("Scan low or equal unimplemented!!");
|
||||
goto wait_for_command;
|
||||
|
||||
scan_high_or_equal:
|
||||
printf("Scan high or equal unimplemented!!\n");
|
||||
ERROR("Scan high or equal unimplemented!!");
|
||||
goto wait_for_command;
|
||||
|
||||
// Performs both recalibrate and seek commands. These commands occur asynchronously, so the actual work
|
||||
@ -738,11 +737,11 @@ void i8272::posit_event(int event_type) {
|
||||
// up in run_for understands to mean 'keep going until track 0 is active').
|
||||
if(command_.size() > 2) {
|
||||
drives_[drive].target_head_position = command_[2];
|
||||
printf("Seek to %02x\n", command_[2]);
|
||||
LOG("Seek to " << std::hex << command_[2]);
|
||||
} else {
|
||||
drives_[drive].target_head_position = -1;
|
||||
drives_[drive].head_position = 0;
|
||||
printf("Recalibrate\n");
|
||||
LOG("Recalibrate");
|
||||
}
|
||||
|
||||
// Check whether any steps are even needed; if not then mark as completed already.
|
||||
@ -755,7 +754,7 @@ void i8272::posit_event(int event_type) {
|
||||
|
||||
// Performs sense interrupt status.
|
||||
sense_interrupt_status:
|
||||
printf("Sense interrupt status\n");
|
||||
LOG("Sense interrupt status");
|
||||
{
|
||||
// Find the first drive that is in the CompletedSeeking state.
|
||||
int found_drive = -1;
|
||||
@ -783,7 +782,7 @@ void i8272::posit_event(int event_type) {
|
||||
// Performs specify.
|
||||
specify:
|
||||
// Just store the values, and terminate the command.
|
||||
printf("Specify\n");
|
||||
LOG("Specify");
|
||||
step_rate_time_ = 16 - (command_[1] >> 4); // i.e. 1 to 16ms
|
||||
head_unload_time_ = (command_[1] & 0x0f) << 4; // i.e. 16 to 240ms
|
||||
head_load_time_ = command_[2] & ~1; // i.e. 2 to 254 ms in increments of 2ms
|
||||
@ -794,7 +793,7 @@ void i8272::posit_event(int event_type) {
|
||||
goto wait_for_command;
|
||||
|
||||
sense_drive_status:
|
||||
printf("Sense drive status\n");
|
||||
LOG("Sense drive status");
|
||||
{
|
||||
int drive = command_[1] & 3;
|
||||
select_drive(drive);
|
||||
@ -833,11 +832,11 @@ void i8272::posit_event(int event_type) {
|
||||
// Posts whatever is in result_stack_ as a result phase. Be aware that it is a stack, so the
|
||||
// last thing in it will be returned first.
|
||||
post_result:
|
||||
printf("Result to %02x, main %02x: ", command_[0] & 0x1f, main_status_);
|
||||
LOGNBR("Result to " << std::hex << (command_[0] & 0x1f) << ", main " << main_status_);
|
||||
for(std::size_t c = 0; c < result_stack_.size(); c++) {
|
||||
printf("%02x ", result_stack_[result_stack_.size() - 1 - c]);
|
||||
LOGNBR(result_stack_[result_stack_.size() - 1 - c]);
|
||||
}
|
||||
printf("\n");
|
||||
LOGNBR(std::endl);
|
||||
|
||||
// Set ready to send data to the processor, no longer in non-DMA execution phase.
|
||||
is_executing_ = false;
|
||||
|
@ -485,6 +485,10 @@ class ConcreteMachine:
|
||||
if(activity_observer_) {
|
||||
activity_observer_->register_led(caps_led);
|
||||
activity_observer_->set_led_status(caps_led, caps_led_state_);
|
||||
|
||||
if(plus3_) {
|
||||
plus3_->set_activity_observer(observer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,14 +11,12 @@
|
||||
using namespace Electron;
|
||||
|
||||
Plus3::Plus3() : WD1770(P1770) {
|
||||
drives_.emplace_back(new Storage::Disk::Drive(8000000, 300, 2));
|
||||
drives_.emplace_back(new Storage::Disk::Drive(8000000, 300, 2));
|
||||
set_control_register(last_control_, 0xff);
|
||||
}
|
||||
|
||||
void Plus3::set_disk(std::shared_ptr<Storage::Disk::Disk> disk, int drive) {
|
||||
if(!drives_[drive]) {
|
||||
drives_[drive].reset(new Storage::Disk::Drive(8000000, 300, 2));
|
||||
if(drive == selected_drive_) set_drive(drives_[drive]);
|
||||
}
|
||||
void Plus3::set_disk(std::shared_ptr<Storage::Disk::Disk> disk, size_t drive) {
|
||||
drives_[drive]->set_disk(disk);
|
||||
}
|
||||
|
||||
@ -42,8 +40,8 @@ void Plus3::set_control_register(uint8_t control, uint8_t changes) {
|
||||
}
|
||||
}
|
||||
if(changes & 0x04) {
|
||||
if(drives_[0]) drives_[0]->set_head((control & 0x04) ? 1 : 0);
|
||||
if(drives_[1]) drives_[1]->set_head((control & 0x04) ? 1 : 0);
|
||||
drives_[0]->set_head((control & 0x04) ? 1 : 0);
|
||||
drives_[1]->set_head((control & 0x04) ? 1 : 0);
|
||||
}
|
||||
if(changes & 0x08) set_is_double_density(!(control & 0x08));
|
||||
}
|
||||
@ -55,6 +53,9 @@ void Plus3::set_motor_on(bool on) {
|
||||
}
|
||||
|
||||
void Plus3::set_activity_observer(Activity::Observer *observer) {
|
||||
drives_[0]->set_activity_observer(observer, "Drive 1", true);
|
||||
drives_[1]->set_activity_observer(observer, "Drive 2", true);
|
||||
size_t index = 0;
|
||||
for(const auto &drive: drives_) {
|
||||
drive->set_activity_observer(observer, "Drive " + std::to_string(index+1), true);
|
||||
++index;
|
||||
}
|
||||
}
|
||||
|
@ -18,17 +18,18 @@ class Plus3 : public WD::WD1770 {
|
||||
public:
|
||||
Plus3();
|
||||
|
||||
void set_disk(std::shared_ptr<Storage::Disk::Disk> disk, int drive);
|
||||
void set_disk(std::shared_ptr<Storage::Disk::Disk> disk, size_t drive);
|
||||
void set_control_register(uint8_t control);
|
||||
void set_activity_observer(Activity::Observer *observer);
|
||||
|
||||
private:
|
||||
void set_control_register(uint8_t control, uint8_t changes);
|
||||
std::shared_ptr<Storage::Disk::Drive> drives_[2];
|
||||
std::vector<std::shared_ptr<Storage::Disk::Drive>> drives_;
|
||||
int selected_drive_ = 0;
|
||||
uint8_t last_control_ = 0;
|
||||
|
||||
void set_motor_on(bool on);
|
||||
std::string drive_name(size_t drive);
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -13,6 +13,8 @@ using namespace MSX;
|
||||
DiskROM::DiskROM(const std::vector<uint8_t> &rom) :
|
||||
WD1770(P1793),
|
||||
rom_(rom) {
|
||||
drives_[0].reset(new Storage::Disk::Drive(8000000, 300, 2));
|
||||
drives_[1].reset(new Storage::Disk::Drive(8000000, 300, 2));
|
||||
set_is_double_density(true);
|
||||
}
|
||||
|
||||
@ -23,16 +25,16 @@ void DiskROM::write(uint16_t address, uint8_t value, bool pc_is_outside_bios) {
|
||||
break;
|
||||
case 0x7ffc:
|
||||
selected_head_ = value & 1;
|
||||
if(drives_[0]) drives_[0]->set_head(selected_head_);
|
||||
if(drives_[1]) drives_[1]->set_head(selected_head_);
|
||||
drives_[0]->set_head(selected_head_);
|
||||
drives_[1]->set_head(selected_head_);
|
||||
break;
|
||||
case 0x7ffd: {
|
||||
selected_drive_ = value & 1;
|
||||
set_drive(drives_[selected_drive_]);
|
||||
|
||||
bool drive_motor = !!(value & 0x80);
|
||||
if(drives_[0]) drives_[0]->set_motor_on(drive_motor);
|
||||
if(drives_[1]) drives_[1]->set_motor_on(drive_motor);
|
||||
drives_[0]->set_motor_on(drive_motor);
|
||||
drives_[1]->set_motor_on(drive_motor);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
@ -57,12 +59,6 @@ void DiskROM::run_for(HalfCycles half_cycles) {
|
||||
}
|
||||
|
||||
void DiskROM::set_disk(std::shared_ptr<Storage::Disk::Disk> disk, size_t drive) {
|
||||
if(!drives_[drive]) {
|
||||
drives_[drive].reset(new Storage::Disk::Drive(8000000, 300, 2));
|
||||
drives_[drive]->set_head(selected_head_);
|
||||
if(drive == selected_drive_) set_drive(drives_[drive]);
|
||||
drives_[drive]->set_activity_observer(observer_, drive_name(drive), true);
|
||||
}
|
||||
drives_[drive]->set_disk(disk);
|
||||
}
|
||||
|
||||
@ -72,14 +68,9 @@ void DiskROM::set_head_load_request(bool head_load) {
|
||||
}
|
||||
|
||||
void DiskROM::set_activity_observer(Activity::Observer *observer) {
|
||||
size_t c = 0;
|
||||
observer_ = observer;
|
||||
size_t c = 1;
|
||||
for(auto &drive: drives_) {
|
||||
if(drive) drive->set_activity_observer(observer, drive_name(c), true);
|
||||
drive->set_activity_observer(observer, "Drive " + std::to_string(c), true);
|
||||
++c;
|
||||
}
|
||||
}
|
||||
|
||||
std::string DiskROM::drive_name(size_t index) {
|
||||
return "Drive " + std::to_string(index);
|
||||
}
|
||||
|
@ -41,8 +41,6 @@ class DiskROM: public ROMSlotHandler, public WD::WD1770 {
|
||||
std::array<std::shared_ptr<Storage::Disk::Drive>, 2> drives_;
|
||||
|
||||
void set_head_load_request(bool head_load) override;
|
||||
std::string drive_name(size_t index);
|
||||
Activity::Observer *observer_ = nullptr;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -115,6 +115,7 @@
|
||||
4B07835B1FC11D42001D12BB /* Configurable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0783591FC11D10001D12BB /* Configurable.cpp */; };
|
||||
4B08A2751EE35D56008B7065 /* Z80InterruptTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B08A2741EE35D56008B7065 /* Z80InterruptTests.swift */; };
|
||||
4B08A2781EE39306008B7065 /* TestMachine.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B08A2771EE39306008B7065 /* TestMachine.mm */; };
|
||||
4B08A56920D72BEF0016CE5A /* Activity.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4B08A56720D72BEF0016CE5A /* Activity.xib */; };
|
||||
4B0CCC451C62D0B3001CAC5F /* CRT.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0CCC421C62D0B3001CAC5F /* CRT.cpp */; };
|
||||
4B0E04EA1FC9E5DA00F43484 /* CAS.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0E04E81FC9E5DA00F43484 /* CAS.cpp */; };
|
||||
4B0E04EB1FC9E78800F43484 /* CAS.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0E04E81FC9E5DA00F43484 /* CAS.cpp */; };
|
||||
@ -705,6 +706,7 @@
|
||||
4B08A2761EE39306008B7065 /* TestMachine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TestMachine.h; sourceTree = "<group>"; };
|
||||
4B08A2771EE39306008B7065 /* TestMachine.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = TestMachine.mm; sourceTree = "<group>"; };
|
||||
4B08A2791EE3957B008B7065 /* TestMachine+ForSubclassEyesOnly.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "TestMachine+ForSubclassEyesOnly.h"; sourceTree = "<group>"; };
|
||||
4B08A56820D72BEF0016CE5A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = "Clock Signal/Base.lproj/Activity.xib"; sourceTree = SOURCE_ROOT; };
|
||||
4B0B6E121C9DBD5D00FFB60D /* CRTConstants.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = CRTConstants.hpp; sourceTree = "<group>"; };
|
||||
4B0CCC421C62D0B3001CAC5F /* CRT.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CRT.cpp; sourceTree = "<group>"; };
|
||||
4B0CCC431C62D0B3001CAC5F /* CRT.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CRT.hpp; sourceTree = "<group>"; };
|
||||
@ -1379,6 +1381,7 @@
|
||||
4BD4A8CF1E077FD20020D856 /* PCMTrackTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = PCMTrackTests.mm; sourceTree = "<group>"; };
|
||||
4BD5F1931D13528900631CD1 /* CSBestEffortUpdater.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSBestEffortUpdater.h; path = Updater/CSBestEffortUpdater.h; sourceTree = "<group>"; };
|
||||
4BD5F1941D13528900631CD1 /* CSBestEffortUpdater.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = CSBestEffortUpdater.mm; path = Updater/CSBestEffortUpdater.mm; sourceTree = "<group>"; };
|
||||
4BD601A920D89F2A00CBCE57 /* Log.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Log.hpp; path = ../../Outputs/Log.hpp; sourceTree = "<group>"; };
|
||||
4BD61663206B2AC700236112 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = "Clock Signal/Base.lproj/QuickLoadOptions.xib"; sourceTree = SOURCE_ROOT; };
|
||||
4BD67DC9209BE4D600AB2146 /* StaticAnalyser.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = StaticAnalyser.hpp; sourceTree = "<group>"; };
|
||||
4BD67DCA209BE4D600AB2146 /* StaticAnalyser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StaticAnalyser.cpp; sourceTree = "<group>"; };
|
||||
@ -1772,6 +1775,7 @@
|
||||
children = (
|
||||
4B0CCC411C62D0B3001CAC5F /* CRT */,
|
||||
4BD060A41FE49D3C006E14BE /* Speaker */,
|
||||
4BD601A920D89F2A00CBCE57 /* Log.hpp */,
|
||||
);
|
||||
name = Outputs;
|
||||
sourceTree = "<group>";
|
||||
@ -1988,6 +1992,7 @@
|
||||
4B55CE5E1C3B7D960093A61B /* MachineDocument.swift */,
|
||||
4B8FE2211DA19FB20090D3CE /* MachinePanel.swift */,
|
||||
4B95FA9C1F11893B0008E395 /* ZX8081OptionsPanel.swift */,
|
||||
4B08A56720D72BEF0016CE5A /* Activity.xib */,
|
||||
4BC5FC2E20CDDDEE00410AA0 /* AppleIIOptions.xib */,
|
||||
4B8FE2131DA19D5F0090D3CE /* Atari2600Options.xib */,
|
||||
4B8FE2151DA19D5F0090D3CE /* MachineDocument.xib */,
|
||||
@ -3284,6 +3289,7 @@
|
||||
4BA141BD2072E8A500A31EC9 /* MachinePicker.xib in Resources */,
|
||||
4B1EDB451E39A0AC009D6819 /* chip.png in Resources */,
|
||||
4B2A332D1DB86821002876E3 /* OricOptions.xib in Resources */,
|
||||
4B08A56920D72BEF0016CE5A /* Activity.xib in Resources */,
|
||||
4BD61664206B2AC800236112 /* QuickLoadOptions.xib in Resources */,
|
||||
4B8FE21B1DA19D5F0090D3CE /* Atari2600Options.xib in Resources */,
|
||||
4B8FE21C1DA19D5F0090D3CE /* MachineDocument.xib in Resources */,
|
||||
@ -3984,6 +3990,14 @@
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
4B08A56720D72BEF0016CE5A /* Activity.xib */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
4B08A56820D72BEF0016CE5A /* Base */,
|
||||
);
|
||||
name = Activity.xib;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4B1497961EE4B97F00CE2596 /* ZX8081Options.xib */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
|
117
OSBindings/Mac/Clock Signal/Base.lproj/Activity.xib
Normal file
117
OSBindings/Mac/Clock Signal/Base.lproj/Activity.xib
Normal file
@ -0,0 +1,117 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14113" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14113"/>
|
||||
<capability name="Aspect ratio constraints" minToolsVersion="5.1"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<customObject id="-2" userLabel="File's Owner" customClass="MachineDocument" customModule="Clock_Signal" customModuleProvider="target">
|
||||
<connections>
|
||||
<outlet property="activityPanel" destination="ZW7-Bw-4RP" id="GRG-Q6-RQU"/>
|
||||
</connections>
|
||||
</customObject>
|
||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||
<window title="Activity" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" hidesOnDeactivate="YES" oneShot="NO" releasedWhenClosed="NO" showsToolbarButton="NO" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" id="ZW7-Bw-4RP" customClass="NSPanel">
|
||||
<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="131"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="1440" height="900"/>
|
||||
<view key="contentView" id="tpZ-0B-QQu">
|
||||
<rect key="frame" x="0.0" y="0.0" width="200" height="131"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<levelIndicator verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ySY-ir-hzb" userLabel="First indicator">
|
||||
<rect key="frame" x="20" y="95" width="17" height="16"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" secondItem="ySY-ir-hzb" secondAttribute="height" multiplier="1:1" id="UX0-hT-7Td"/>
|
||||
</constraints>
|
||||
<levelIndicatorCell key="cell" alignment="left" maxValue="1" warningValue="2" criticalValue="2" levelIndicatorStyle="continuousCapacity" id="DhQ-Di-tRT"/>
|
||||
</levelIndicator>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Tah-UQ-vdf">
|
||||
<rect key="frame" x="44" y="94" width="59" height="17"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Activity 1" id="a5P-Ci-RzC">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<levelIndicator verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ncQ-wN-C61" userLabel="Second indicator">
|
||||
<rect key="frame" x="20" y="70" width="17" height="16"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" secondItem="ncQ-wN-C61" secondAttribute="height" multiplier="1:1" id="176-v3-mVW"/>
|
||||
</constraints>
|
||||
<levelIndicatorCell key="cell" alignment="left" maxValue="1" warningValue="2" criticalValue="2" levelIndicatorStyle="continuousCapacity" id="jlb-bk-FPd"/>
|
||||
</levelIndicator>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="14O-Lq-Npx">
|
||||
<rect key="frame" x="44" y="69" width="61" height="17"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Activity 2" id="NE1-CO-pGI">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<levelIndicator verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="0rV-Th-Zwt" userLabel="Third indicator">
|
||||
<rect key="frame" x="20" y="45" width="17" height="16"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" secondItem="0rV-Th-Zwt" secondAttribute="height" multiplier="1:1" id="Ai8-b3-Nn5"/>
|
||||
</constraints>
|
||||
<levelIndicatorCell key="cell" alignment="left" maxValue="1" warningValue="2" criticalValue="2" levelIndicatorStyle="continuousCapacity" id="CJy-Jn-eCL"/>
|
||||
</levelIndicator>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Acy-tT-OFH">
|
||||
<rect key="frame" x="44" y="44" width="61" height="17"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Activity 3" id="FSR-y6-7WE">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<levelIndicator verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="bvH-EJ-TYb" userLabel="Fourth indicator">
|
||||
<rect key="frame" x="20" y="20" width="17" height="16"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" secondItem="bvH-EJ-TYb" secondAttribute="height" multiplier="1:1" id="cKc-q1-2Q4"/>
|
||||
</constraints>
|
||||
<levelIndicatorCell key="cell" alignment="left" maxValue="1" warningValue="2" criticalValue="2" levelIndicatorStyle="continuousCapacity" id="eoN-hl-30l"/>
|
||||
</levelIndicator>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="R0g-Oa-VB5">
|
||||
<rect key="frame" x="44" y="19" width="62" height="17"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Activity 4" id="aGr-cd-jC0">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="14O-Lq-Npx" firstAttribute="centerY" secondItem="ncQ-wN-C61" secondAttribute="centerY" id="0Ht-U2-sPg"/>
|
||||
<constraint firstItem="bvH-EJ-TYb" firstAttribute="top" secondItem="0rV-Th-Zwt" secondAttribute="bottom" constant="9" id="0xw-qA-6vP"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="14O-Lq-Npx" secondAttribute="trailing" constant="20" id="5eo-XI-a3W"/>
|
||||
<constraint firstItem="Tah-UQ-vdf" firstAttribute="centerY" secondItem="ySY-ir-hzb" secondAttribute="centerY" id="6Hn-ts-mTi"/>
|
||||
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="0rV-Th-Zwt" secondAttribute="bottom" constant="20" id="CNH-SV-KNi"/>
|
||||
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="bvH-EJ-TYb" secondAttribute="bottom" constant="20" id="DYI-qH-t52"/>
|
||||
<constraint firstItem="R0g-Oa-VB5" firstAttribute="leading" secondItem="bvH-EJ-TYb" secondAttribute="trailing" constant="10" id="Dgy-JI-nA1"/>
|
||||
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="ncQ-wN-C61" secondAttribute="bottom" constant="20" id="ENa-Xb-FBv"/>
|
||||
<constraint firstItem="R0g-Oa-VB5" firstAttribute="centerY" secondItem="bvH-EJ-TYb" secondAttribute="centerY" id="Gfq-mB-Y1z"/>
|
||||
<constraint firstItem="Acy-tT-OFH" firstAttribute="centerY" secondItem="0rV-Th-Zwt" secondAttribute="centerY" id="ImF-rK-oOr"/>
|
||||
<constraint firstItem="Acy-tT-OFH" firstAttribute="leading" secondItem="0rV-Th-Zwt" secondAttribute="trailing" constant="10" id="JSU-pZ-l9Q"/>
|
||||
<constraint firstItem="ySY-ir-hzb" firstAttribute="leading" secondItem="tpZ-0B-QQu" secondAttribute="leading" constant="20" id="KMh-EO-rxE"/>
|
||||
<constraint firstItem="0rV-Th-Zwt" firstAttribute="top" secondItem="ncQ-wN-C61" secondAttribute="bottom" constant="9" id="Q2g-yM-nlJ"/>
|
||||
<constraint firstItem="ncQ-wN-C61" firstAttribute="leading" secondItem="tpZ-0B-QQu" secondAttribute="leading" constant="20" id="QUI-Hc-Bcl"/>
|
||||
<constraint firstItem="0rV-Th-Zwt" firstAttribute="leading" secondItem="tpZ-0B-QQu" secondAttribute="leading" constant="20" id="bKh-4L-mqj"/>
|
||||
<constraint firstItem="bvH-EJ-TYb" firstAttribute="leading" secondItem="tpZ-0B-QQu" secondAttribute="leading" constant="20" id="cPA-Ls-fLj"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="Tah-UQ-vdf" secondAttribute="trailing" constant="20" id="igX-7U-TeE"/>
|
||||
<constraint firstItem="14O-Lq-Npx" firstAttribute="leading" secondItem="ncQ-wN-C61" secondAttribute="trailing" constant="10" id="jjP-qH-Pqg"/>
|
||||
<constraint firstItem="Tah-UQ-vdf" firstAttribute="leading" secondItem="ySY-ir-hzb" secondAttribute="trailing" constant="10" id="lux-Nz-K7E"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="Acy-tT-OFH" secondAttribute="trailing" constant="20" id="mEe-VT-dNr"/>
|
||||
<constraint firstItem="ncQ-wN-C61" firstAttribute="top" secondItem="ySY-ir-hzb" secondAttribute="bottom" constant="9" id="mSc-jj-amw"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="R0g-Oa-VB5" secondAttribute="trailing" constant="20" id="sR8-Ph-suC"/>
|
||||
<constraint firstItem="ySY-ir-hzb" firstAttribute="top" secondItem="tpZ-0B-QQu" secondAttribute="top" constant="20" id="wbj-48-DYq"/>
|
||||
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="ySY-ir-hzb" secondAttribute="bottom" constant="20" id="yxz-Hb-Pol"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<point key="canvasLocation" x="84" y="115"/>
|
||||
</window>
|
||||
</objects>
|
||||
</document>
|
@ -146,10 +146,16 @@
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="View" id="HyV-fh-RgO">
|
||||
<items>
|
||||
<menuItem title="Show Options" keyEquivalent="o" id="WCd-6R-baV">
|
||||
<menuItem title="Show Activity" keyEquivalent="a" id="WCd-6R-baV">
|
||||
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
|
||||
<connections>
|
||||
<action selector="showOptions:" target="-1" id="ge3-Qg-kb5"/>
|
||||
<action selector="showActivity:" target="-1" id="oeF-uJ-cOS"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Show Options" keyEquivalent="o" id="GtG-CV-Uro">
|
||||
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
|
||||
<connections>
|
||||
<action selector="showOptions:" target="-1" id="M6T-DE-Duo"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
|
@ -40,6 +40,11 @@ class MachineDocument:
|
||||
optionsPanel?.setIsVisible(true)
|
||||
}
|
||||
|
||||
@IBOutlet var activityPanel: NSPanel!
|
||||
@IBAction func showActivity(_ sender: AnyObject!) {
|
||||
activityPanel.setIsVisible(true)
|
||||
}
|
||||
|
||||
fileprivate var audioQueue: CSAudioQueue! = nil
|
||||
fileprivate var bestEffortUpdater: CSBestEffortUpdater?
|
||||
|
||||
@ -102,7 +107,7 @@ class MachineDocument:
|
||||
}
|
||||
}
|
||||
|
||||
func machineSpeakerDidChangeInputClock(_ machine: CSMachine!) {
|
||||
func machineSpeakerDidChangeInputClock(_ machine: CSMachine) {
|
||||
setupAudioQueueClockRate()
|
||||
}
|
||||
|
||||
@ -119,6 +124,9 @@ class MachineDocument:
|
||||
}
|
||||
|
||||
override func close() {
|
||||
activityPanel?.setIsVisible(false)
|
||||
activityPanel = nil
|
||||
|
||||
optionsPanel?.setIsVisible(false)
|
||||
optionsPanel = nil
|
||||
|
||||
@ -147,6 +155,7 @@ class MachineDocument:
|
||||
self.machine = machine
|
||||
self.optionsPanelNibName = analysis.optionsPanelNibName
|
||||
setupMachineOutput()
|
||||
setupActivityDisplay()
|
||||
}
|
||||
}
|
||||
|
||||
@ -285,9 +294,103 @@ class MachineDocument:
|
||||
menuItem.state = machine.inputMode == .joystick ? .on : .off
|
||||
return true
|
||||
|
||||
case #selector(self.showActivity(_:)):
|
||||
return self.activityPanel != nil
|
||||
|
||||
default: break
|
||||
}
|
||||
}
|
||||
return super.validateUserInterfaceItem(item)
|
||||
}
|
||||
|
||||
// MARK: Activity display.
|
||||
class LED {
|
||||
let levelIndicator: NSLevelIndicator
|
||||
init(levelIndicator: NSLevelIndicator) {
|
||||
self.levelIndicator = levelIndicator
|
||||
}
|
||||
var isLit = false
|
||||
var isBlinking = false
|
||||
}
|
||||
fileprivate var leds: [String: LED] = [:]
|
||||
func setupActivityDisplay() {
|
||||
var leds = machine.leds
|
||||
if leds.count > 0 {
|
||||
Bundle.main.loadNibNamed(NSNib.Name(rawValue: "Activity"), owner: self, topLevelObjects: nil)
|
||||
showActivity(nil)
|
||||
|
||||
// Add a constraints to minimise window height.
|
||||
// let heightConstraint = NSLayoutConstraint(
|
||||
// item: self.activityPanel.contentView!,
|
||||
// attribute: .height,
|
||||
// relatedBy: .equal,
|
||||
// toItem: nil,
|
||||
// attribute: .notAnAttribute,
|
||||
// multiplier: 0.0,
|
||||
// constant: 20.0)
|
||||
// heightConstraint.priority = .defaultLow
|
||||
// self.activityPanel.contentView?.addConstraint(heightConstraint)
|
||||
|
||||
// Inspect the activity panel for indicators.
|
||||
var activityIndicators: [NSLevelIndicator] = []
|
||||
var textFields: [NSTextField] = []
|
||||
if let contentView = self.activityPanel.contentView {
|
||||
for view in contentView.subviews {
|
||||
if let levelIndicator = view as? NSLevelIndicator {
|
||||
activityIndicators.append(levelIndicator)
|
||||
}
|
||||
|
||||
if let textField = view as? NSTextField {
|
||||
textFields.append(textField)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If there are fewer level indicators than LEDs, trim that list.
|
||||
if activityIndicators.count < leds.count {
|
||||
leds.removeSubrange(activityIndicators.count ..< leds.count)
|
||||
}
|
||||
|
||||
// Remove unused views.
|
||||
for c in leds.count ..< activityIndicators.count {
|
||||
textFields[c].removeFromSuperview()
|
||||
activityIndicators[c].removeFromSuperview()
|
||||
}
|
||||
|
||||
// Apply labels and create leds entries.
|
||||
for c in 0 ..< leds.count {
|
||||
textFields[c].stringValue = leds[c]
|
||||
self.leds[leds[c]] = LED(levelIndicator: activityIndicators[c])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func machine(_ machine: CSMachine, ledShouldBlink ledName: String) {
|
||||
// If there is such an LED, switch it off for 0.03 of a second; if it's meant
|
||||
// to be off at the end of that, leave it off. Don't allow the blinks to
|
||||
// pile up — allow there to be only one in flight at a time.
|
||||
if let led = leds[ledName] {
|
||||
DispatchQueue.main.async {
|
||||
if !led.isBlinking {
|
||||
led.levelIndicator.floatValue = 0.0
|
||||
led.isBlinking = true
|
||||
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.03) {
|
||||
led.levelIndicator.floatValue = led.isLit ? 1.0 : 0.0
|
||||
led.isBlinking = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func machine(_ machine: CSMachine, led ledName: String, didChangeToLit isLit: Bool) {
|
||||
// If there is such an LED, switch it appropriately.
|
||||
if let led = leds[ledName] {
|
||||
DispatchQueue.main.async {
|
||||
led.levelIndicator.floatValue = isLit ? 1.0 : 0.0
|
||||
led.isLit = isLit
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,9 @@
|
||||
|
||||
@class CSMachine;
|
||||
@protocol CSMachineDelegate
|
||||
- (void)machineSpeakerDidChangeInputClock:(CSMachine *)machine;
|
||||
- (void)machineSpeakerDidChangeInputClock:(nonnull CSMachine *)machine;
|
||||
- (void)machine:(nonnull CSMachine *)machine led:(nonnull NSString *)led didChangeToLit:(BOOL)isLit;
|
||||
- (void)machine:(nonnull CSMachine *)machine ledShouldBlink:(nonnull NSString *)led;
|
||||
@end
|
||||
|
||||
typedef NS_ENUM(NSInteger, CSMachineVideoSignal) {
|
||||
@ -35,32 +37,32 @@ typedef NS_ENUM(NSInteger, CSMachineKeyboardInputMode) {
|
||||
|
||||
@interface CSMachine : NSObject
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
- (nonnull instancetype)init NS_UNAVAILABLE;
|
||||
/*!
|
||||
Initialises an instance of CSMachine.
|
||||
|
||||
@param result The CSStaticAnalyser result that describes the machine needed.
|
||||
*/
|
||||
- (instancetype)initWithAnalyser:(CSStaticAnalyser *)result NS_DESIGNATED_INITIALIZER;
|
||||
- (nullable instancetype)initWithAnalyser:(nonnull CSStaticAnalyser *)result NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
- (void)runForInterval:(NSTimeInterval)interval;
|
||||
|
||||
- (float)idealSamplingRateFromRange:(NSRange)range;
|
||||
- (void)setAudioSamplingRate:(float)samplingRate bufferSize:(NSUInteger)bufferSize;
|
||||
|
||||
- (void)setView:(CSOpenGLView *)view aspectRatio:(float)aspectRatio;
|
||||
- (void)setView:(nullable CSOpenGLView *)view aspectRatio:(float)aspectRatio;
|
||||
- (void)drawViewForPixelSize:(CGSize)pixelSize onlyIfDirty:(BOOL)onlyIfDirty;
|
||||
|
||||
- (void)setKey:(uint16_t)key characters:(NSString *)characters isPressed:(BOOL)isPressed;
|
||||
- (void)setKey:(uint16_t)key characters:(nullable NSString *)characters isPressed:(BOOL)isPressed;
|
||||
- (void)clearAllKeys;
|
||||
|
||||
@property (nonatomic, strong) CSAudioQueue *audioQueue;
|
||||
@property (nonatomic, readonly) CSOpenGLView *view;
|
||||
@property (nonatomic, weak) id<CSMachineDelegate> delegate;
|
||||
@property (nonatomic, strong, nullable) CSAudioQueue *audioQueue;
|
||||
@property (nonatomic, readonly, nonnull) CSOpenGLView *view;
|
||||
@property (nonatomic, weak, nullable) id<CSMachineDelegate> delegate;
|
||||
|
||||
@property (nonatomic, readonly) NSString *userDefaultsPrefix;
|
||||
@property (nonatomic, readonly, nonnull) NSString *userDefaultsPrefix;
|
||||
|
||||
- (void)paste:(NSString *)string;
|
||||
- (void)paste:(nonnull NSString *)string;
|
||||
|
||||
@property (nonatomic, assign) BOOL useFastLoadingHack;
|
||||
@property (nonatomic, assign) CSMachineVideoSignal videoSignal;
|
||||
@ -73,8 +75,11 @@ typedef NS_ENUM(NSInteger, CSMachineKeyboardInputMode) {
|
||||
@property (nonatomic, readonly) BOOL hasJoystick;
|
||||
@property (nonatomic, assign) CSMachineKeyboardInputMode inputMode;
|
||||
|
||||
// LED list.
|
||||
@property (nonatomic, readonly, nonnull) NSArray<NSString *> *leds;
|
||||
|
||||
// Special-case accessors; undefined behaviour if accessed for a machine not of the corresponding type.
|
||||
@property (nonatomic, readonly) CSAtari2600 *atari2600;
|
||||
@property (nonatomic, readonly) CSZX8081 *zx8081;
|
||||
@property (nonatomic, readonly, nullable) CSAtari2600 *atari2600;
|
||||
@property (nonatomic, readonly, nullable) CSZX8081 *zx8081;
|
||||
|
||||
@end
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "MachineForTarget.hpp"
|
||||
#include "StandardOptions.hpp"
|
||||
#include "Typer.hpp"
|
||||
#include "../../../../Activity/Observer.hpp"
|
||||
|
||||
#import "CSStaticAnalyser+TargetVector.h"
|
||||
#import "NSBundle+DataResource.h"
|
||||
@ -28,6 +29,7 @@
|
||||
@interface CSMachine() <CSFastLoading>
|
||||
- (void)speaker:(Outputs::Speaker::Speaker *)speaker didCompleteSamples:(const int16_t *)samples length:(int)length;
|
||||
- (void)speakerDidChangeInputClock:(Outputs::Speaker::Speaker *)speaker;
|
||||
- (void)addLED:(NSString *)led;
|
||||
@end
|
||||
|
||||
struct LockProtectedDelegate {
|
||||
@ -50,14 +52,38 @@ struct SpeakerDelegate: public Outputs::Speaker::Speaker::Delegate, public LockP
|
||||
}
|
||||
};
|
||||
|
||||
struct ActivityObserver: public Activity::Observer {
|
||||
void register_led(const std::string &name) override {
|
||||
[machine addLED:[NSString stringWithUTF8String:name.c_str()]];
|
||||
}
|
||||
|
||||
void register_drive(const std::string &name) override {
|
||||
}
|
||||
|
||||
void set_led_status(const std::string &name, bool lit) override {
|
||||
[machine.delegate machine:machine led:[NSString stringWithUTF8String:name.c_str()] didChangeToLit:lit];
|
||||
}
|
||||
|
||||
void announce_drive_event(const std::string &name, DriveEvent event) override {
|
||||
[machine.delegate machine:machine ledShouldBlink:[NSString stringWithUTF8String:name.c_str()]];
|
||||
}
|
||||
|
||||
void set_drive_motor_status(const std::string &name, bool is_on) override {
|
||||
}
|
||||
|
||||
__unsafe_unretained CSMachine *machine;
|
||||
};
|
||||
|
||||
@implementation CSMachine {
|
||||
SpeakerDelegate _speakerDelegate;
|
||||
ActivityObserver _activityObserver;
|
||||
NSLock *_delegateMachineAccessLock;
|
||||
|
||||
CSStaticAnalyser *_analyser;
|
||||
std::unique_ptr<Machine::DynamicMachine> _machine;
|
||||
|
||||
std::bitset<65536> _depressedKeys;
|
||||
NSMutableArray<NSString *> *_leds;
|
||||
}
|
||||
|
||||
- (instancetype)initWithAnalyser:(CSStaticAnalyser *)result {
|
||||
@ -71,6 +97,13 @@ struct SpeakerDelegate: public Outputs::Speaker::Speaker::Delegate, public LockP
|
||||
|
||||
_inputMode = _machine->keyboard_machine() ? CSMachineKeyboardInputModeKeyboard : CSMachineKeyboardInputModeJoystick;
|
||||
|
||||
_leds = [[NSMutableArray alloc] init];
|
||||
Activity::Source *const activity_source = _machine->activity_source();
|
||||
if(activity_source) {
|
||||
_activityObserver.machine = self;
|
||||
activity_source->set_activity_observer(&_activityObserver);
|
||||
}
|
||||
|
||||
_delegateMachineAccessLock = [[NSLock alloc] init];
|
||||
|
||||
_speakerDelegate.machine = self;
|
||||
@ -408,4 +441,14 @@ struct SpeakerDelegate: public Outputs::Speaker::Speaker::Delegate, public LockP
|
||||
return !!_machine->keyboard_machine();
|
||||
}
|
||||
|
||||
#pragma mark - Activity observation
|
||||
|
||||
- (void)addLED:(NSString *)led {
|
||||
[_leds addObject:led];
|
||||
}
|
||||
|
||||
- (NSArray<NSString *> *)leds {
|
||||
return _leds;
|
||||
}
|
||||
|
||||
@end
|
||||
|
31
Outputs/Log.hpp
Normal file
31
Outputs/Log.hpp
Normal file
@ -0,0 +1,31 @@
|
||||
//
|
||||
// Log.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 18/06/2018.
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef Log_h
|
||||
#define Log_h
|
||||
|
||||
#ifdef NDEBUG
|
||||
|
||||
#define LOG(x)
|
||||
#define LOGNBR(x)
|
||||
#define ERROR(x)
|
||||
#define ERRORNBR(x)
|
||||
|
||||
#else
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#define LOG(x) std::cout << x << std::endl
|
||||
#define LOGNBR(x) std::cout << x
|
||||
#define ERROR(x) std::cerr << x << std::endl
|
||||
#define ERRORNBR(x) std::cerr << x
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* Log_h */
|
@ -113,7 +113,6 @@ bool Drive::get_is_read_only() {
|
||||
}
|
||||
|
||||
bool Drive::get_is_ready() {
|
||||
return true;
|
||||
return ready_index_count_ == 2;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user