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.
This commit is contained in:
joevt 2024-04-19 23:59:01 -07:00 committed by dingusdev
parent 9a70c3bdb0
commit d06d80619e
2 changed files with 68 additions and 16 deletions

View File

@ -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<std::chrono::seconds>(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]);

View File

@ -48,6 +48,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <devices/common/nvram.h>
#include <memory>
#include <chrono>
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<std::chrono::system_clock> 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);