From d06d80619e543d710c2621b23c15131e90049af2 Mon Sep 17 00:00:00 2001 From: joevt Date: Fri, 19 Apr 2024 23:59:01 -0700 Subject: [PATCH] viacuda: Add 1 Sec Timer, fix Get/Set Real Time. GET_REAL_TIME was using the wrong offset and wasn't setting out_count. May need to add time zone offset which could be different between host and guest. get-time and set-time can be tested in Open Firmware. SET_REAL_TIME is used by the Date/Time Control Panel when you change the date. It is unknown what method Mac OS uses to get the time at boot. Mac OS 8.6 does not use GET_REAL_TIME during boot, so the time is left as 12:00 AM Jan 1, 1904. --- devices/common/viacuda.cpp | 77 ++++++++++++++++++++++++++++++-------- devices/common/viacuda.h | 7 +++- 2 files changed, 68 insertions(+), 16 deletions(-) diff --git a/devices/common/viacuda.cpp b/devices/common/viacuda.cpp index 796705a..6a62b5a 100644 --- a/devices/common/viacuda.cpp +++ b/devices/common/viacuda.cpp @@ -87,6 +87,17 @@ ViaCuda::ViaCuda() { this->cuda_init(); this->int_ctrl = nullptr; + + std::tm tm = { + .tm_sec = 0, + .tm_min = 0, + .tm_hour = 0, + .tm_mday = 1, + .tm_mon = 1 - 1, + .tm_year = 1904 - 1900, + .tm_isdst = -1 // Use DST value from local time zone + }; + mac_epoch = std::chrono::system_clock::from_time_t(std::mktime(&tm)); } ViaCuda::~ViaCuda() @@ -495,10 +506,7 @@ void ViaCuda::process_adb_command() { } void ViaCuda::autopoll_handler() { - if (!this->autopoll_enabled) - return; - - uint8_t poll_command = this->adb_bus_obj->poll(); + uint8_t poll_command = this->autopoll_enabled ? this->adb_bus_obj->poll() : 0; if (poll_command) { if (!this->old_tip || !this->treq) { @@ -520,6 +528,36 @@ void ViaCuda::autopoll_handler() { // draw guest system's attention schedule_sr_int(USECS_TO_NSECS(30)); + } else if (this->one_sec_mode == 3) { + uint32_t this_time = calc_real_time(); + if (this_time != this->last_time) { + + if ((this->last_time & 15) == 0) { + /* + FIXME: This doesn't do anything in Mac OS 8.6. + A real Mac probably doesn't do this. So why do I? + supermario checks for packets that are not PKT_TICK + to set the time but I don't know which Mac OS versions + have that code and if that code is triggered by this. + */ + response_header(9, 0); + this->out_buf[3] = CUDA_GET_REAL_TIME; + uint32_t real_time = this_time + time_offset; + WRITE_DWORD_BE_U(&this->out_buf[3], real_time); + this->out_count = 7; + } else { + response_header(CUDA_PKT_TICK, 0); + this->out_count = 1; + } + this->last_time = this_time; + + // assert TREQ + this->via_regs[VIA_B] &= ~CUDA_TREQ; + this->treq = 0; + + // draw guest system's attention + schedule_sr_int(USECS_TO_NSECS(30)); + } } } @@ -556,13 +594,14 @@ void ViaCuda::pseudo_command(int cmd, int data_count) { } this->is_open_ended = true; break; - case CUDA_GET_REAL_TIME: + case CUDA_GET_REAL_TIME: { response_header(CUDA_PKT_PSEUDO, 0); - this->out_buf[2] = (uint8_t)((this->real_time >> 24) & 0xFF); - this->out_buf[3] = (uint8_t)((this->real_time >> 16) & 0xFF); - this->out_buf[4] = (uint8_t)((this->real_time >> 8) & 0xFF); - this->out_buf[5] = (uint8_t)((this->real_time) & 0xFF); + uint32_t this_time = this->calc_real_time(); + uint32_t real_time = this_time + time_offset; + WRITE_DWORD_BE_U(&this->out_buf[3], real_time); + this->out_count = 7; break; + } case CUDA_WRITE_MCU_MEM: addr = READ_WORD_BE_A(&this->in_buf[2]); // if addr is inside PRAM, update PRAM with data from in_buf @@ -587,13 +626,13 @@ void ViaCuda::pseudo_command(int cmd, int data_count) { error_response(CUDA_ERR_BAD_PAR); } break; - case CUDA_SET_REAL_TIME: + case CUDA_SET_REAL_TIME: { response_header(CUDA_PKT_PSEUDO, 0); - this->real_time = ((uint32_t)in_buf[2]) >> 24; - this->real_time += ((uint32_t)in_buf[3]) >> 16; - this->real_time += ((uint32_t)in_buf[4]) >> 8; - this->real_time += ((uint32_t)in_buf[5]); + uint32_t real_time = this->calc_real_time(); + uint32_t new_time = READ_DWORD_BE_U(&in_buf[2]); + this->time_offset = new_time - real_time; break; + } case CUDA_WRITE_PRAM: addr = READ_WORD_BE_A(&this->in_buf[2]); if (addr <= 0xFF) { @@ -637,7 +676,8 @@ void ViaCuda::pseudo_command(int cmd, int data_count) { this->out_buf[3] = (uint8_t)((this->device_mask) & 0xFF); break; case CUDA_ONE_SECOND_MODE: - LOG_F(INFO, "Cuda: One Second Interrupt - Byte Sent: %d", this->in_buf[2]); + LOG_F(INFO, "Cuda: One Second Interrupt Mode: %d", this->in_buf[2]); + this->one_sec_mode = this->in_buf[2]; response_header(CUDA_PKT_PSEUDO, 0); break; case CUDA_READ_WRITE_I2C: @@ -673,6 +713,13 @@ void ViaCuda::pseudo_command(int cmd, int data_count) { } } +uint32_t ViaCuda::calc_real_time() { + auto end = std::chrono::system_clock::now(); + auto elapsed_systemclock = end - this->mac_epoch; + auto elapsed_seconds = std::chrono::duration_cast(elapsed_systemclock); + return uint32_t(elapsed_seconds.count()); +} + /* sends data from the current I2C to host ad infinitum */ void ViaCuda::i2c_handler() { this->receive_byte(this->curr_i2c_addr, &this->via_regs[VIA_SR]); diff --git a/devices/common/viacuda.h b/devices/common/viacuda.h index d097570..d0c9f73 100644 --- a/devices/common/viacuda.h +++ b/devices/common/viacuda.h @@ -48,6 +48,7 @@ along with this program. If not, see . #include #include +#include class AdbBus; class InterruptCtrl; @@ -213,7 +214,10 @@ private: int32_t out_count; int32_t out_pos; uint8_t poll_rate; - int32_t real_time = 0; + uint32_t last_time = 0; + uint32_t time_offset = 0; + std::chrono::time_point mac_epoch; + uint8_t one_sec_mode = 0; bool file_server; uint16_t device_mask = 0; @@ -247,6 +251,7 @@ private: void process_packet(); void process_adb_command(); void pseudo_command(int cmd, int data_count); + uint32_t calc_real_time(); void null_out_handler(void); void pram_out_handler(void);