mirror of
https://github.com/jamessanford/kegs.git
synced 2024-09-12 03:54:38 +00:00
383 lines
7.9 KiB
C
383 lines
7.9 KiB
C
/************************************************************************/
|
|
/* KEGS: Apple //gs Emulator */
|
|
/* Copyright 2002 by Kent Dickey */
|
|
/* */
|
|
/* This code is covered by the GNU GPL */
|
|
/* */
|
|
/* The KEGS web page is kegs.sourceforge.net */
|
|
/* You may contact the author at: kadickey@alumni.princeton.edu */
|
|
/************************************************************************/
|
|
|
|
const char rcsid_clock_c[] = "@(#)$KmKId: clock.c,v 1.31 2004-10-19 17:32:07-04 kentd Exp $";
|
|
|
|
#include "defc.h"
|
|
#include <time.h>
|
|
#ifdef _WIN32
|
|
# include <windows.h>
|
|
# include <mmsystem.h>
|
|
#else
|
|
# include <sys/time.h>
|
|
#endif
|
|
|
|
extern int Verbose;
|
|
extern int g_vbl_count;
|
|
extern int g_rom_version;
|
|
extern int g_config_kegs_update_needed;
|
|
|
|
#define CLK_IDLE 1
|
|
#define CLK_TIME 2
|
|
#define CLK_INTERNAL 3
|
|
#define CLK_BRAM1 4
|
|
#define CLK_BRAM2 5
|
|
|
|
int g_clk_mode = CLK_IDLE;
|
|
int g_clk_read = 0;
|
|
int g_clk_reg1 = 0;
|
|
|
|
extern int g_c033_data;
|
|
extern int g_c034_val;
|
|
|
|
byte g_bram[2][256];
|
|
byte *g_bram_ptr = &(g_bram[0][0]);
|
|
|
|
word32 g_clk_cur_time = 0xa0000000;
|
|
int g_clk_next_vbl_update = 0;
|
|
|
|
double
|
|
get_dtime()
|
|
{
|
|
#ifndef _WIN32
|
|
struct timeval tp1;
|
|
double dsec;
|
|
double dusec;
|
|
#endif
|
|
double dtime;
|
|
|
|
/* Routine used to return actual system time as a double */
|
|
/* No routine cares about the absolute value, only deltas--maybe */
|
|
/* take advantage of that in future to increase usec accuracy */
|
|
|
|
#ifdef _WIN32
|
|
dtime = timeGetTime() / 1000.0;
|
|
#else
|
|
|
|
# ifdef SOLARIS
|
|
gettimeofday(&tp1, (void *)0);
|
|
# else
|
|
gettimeofday(&tp1, (struct timezone *)0);
|
|
# endif
|
|
|
|
dsec = (double)tp1.tv_sec;
|
|
dusec = (double)tp1.tv_usec;
|
|
|
|
dtime = dsec + (dusec / (1000.0 * 1000.0));
|
|
#endif
|
|
|
|
return dtime;
|
|
}
|
|
|
|
int
|
|
micro_sleep(double dtime)
|
|
{
|
|
#ifndef _WIN32
|
|
struct timeval Timer;
|
|
int ret;
|
|
#endif
|
|
|
|
if(dtime <= 0.0) {
|
|
return 0;
|
|
}
|
|
if(dtime >= 1.0) {
|
|
halt_printf("micro_sleep called with %f!!\n", dtime);
|
|
return -1;
|
|
}
|
|
|
|
#if 0
|
|
printf("usleep: %f\n", dtime);
|
|
#endif
|
|
|
|
#ifdef _WIN32
|
|
Sleep(dtime * 1000);
|
|
#else
|
|
Timer.tv_sec = 0;
|
|
Timer.tv_usec = (dtime * 1000000.0);
|
|
if( (ret = select(0, 0, 0, 0, &Timer)) < 0) {
|
|
fprintf(stderr, "micro_sleep (select) ret: %d, errno: %d\n",
|
|
ret, errno);
|
|
return -1;
|
|
}
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
clk_bram_zero()
|
|
{
|
|
int i, j;
|
|
|
|
/* zero out all bram */
|
|
for(i = 0; i < 2; i++) {
|
|
for(j = 0; j < 256; j++) {
|
|
g_bram[i][j] = 0;
|
|
}
|
|
}
|
|
g_bram_ptr = &(g_bram[0][0]);
|
|
}
|
|
|
|
void
|
|
clk_bram_set(int bram_num, int offset, int val)
|
|
{
|
|
g_bram[bram_num][offset] = val;
|
|
}
|
|
|
|
void
|
|
clk_setup_bram_version()
|
|
{
|
|
if(g_rom_version < 3) {
|
|
g_bram_ptr = (&g_bram[0][0]); // ROM 01
|
|
} else {
|
|
g_bram_ptr = (&g_bram[1][0]); // ROM 03
|
|
}
|
|
}
|
|
|
|
void
|
|
clk_write_bram(FILE *fconf)
|
|
{
|
|
int i, j, k;
|
|
|
|
for(i = 0; i < 2; i++) {
|
|
fprintf(fconf, "\n");
|
|
for(j = 0; j < 256; j += 16) {
|
|
fprintf(fconf, "bram%d[%02x] =", 2*i + 1, j);
|
|
for(k = 0; k < 16; k++) {
|
|
fprintf(fconf, " %02x", g_bram[i][j+k]);
|
|
}
|
|
fprintf(fconf, "\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
update_cur_time()
|
|
{
|
|
struct tm *tm_ptr;
|
|
time_t cur_time;
|
|
unsigned int secs, secs2;
|
|
|
|
cur_time = time(0);
|
|
|
|
/* Figure out the timezone (effectively) by diffing two times. */
|
|
/* this is probably not right for a few hours around daylight savings*/
|
|
/* time transition */
|
|
secs2 = mktime(gmtime(&cur_time));
|
|
tm_ptr = localtime(&cur_time);
|
|
secs = mktime(tm_ptr);
|
|
|
|
#ifdef MAC
|
|
/* Mac OS X's mktime function modifies the tm_ptr passed in for */
|
|
/* the CDT timezone and breaks this algorithm. So on a Mac, we */
|
|
/* will use the tm_ptr->gmtoff member to correct the time */
|
|
secs = secs + tm_ptr->tm_gmtoff;
|
|
#else
|
|
secs = (unsigned int)cur_time - (secs2 - secs);
|
|
|
|
if(tm_ptr->tm_isdst) {
|
|
/* adjust for daylight savings time */
|
|
secs += 3600;
|
|
}
|
|
#endif
|
|
|
|
/* add in secs to make date based on Apple Jan 1, 1904 instead of */
|
|
/* Unix's Jan 1, 1970 */
|
|
/* So add in 66 years and 17 leap year days (1904 is a leap year) */
|
|
secs += ((66*365) + 17) * (24*3600);
|
|
|
|
g_clk_cur_time = secs;
|
|
|
|
clk_printf("Update g_clk_cur_time to %08x\n", g_clk_cur_time);
|
|
g_clk_next_vbl_update = g_vbl_count + 5;
|
|
}
|
|
|
|
/* clock_update called by sim65816 every VBL */
|
|
void
|
|
clock_update()
|
|
{
|
|
/* Nothing to do */
|
|
}
|
|
|
|
void
|
|
clock_update_if_needed()
|
|
{
|
|
int diff;
|
|
|
|
diff = g_clk_next_vbl_update - g_vbl_count;
|
|
if(diff < 0 || diff > 60) {
|
|
/* Been a while, re-read the clock */
|
|
update_cur_time();
|
|
}
|
|
}
|
|
|
|
void
|
|
clock_write_c034(word32 val)
|
|
{
|
|
g_c034_val = val & 0x7f;
|
|
if((val & 0x80) != 0) {
|
|
if((val & 0x20) == 0) {
|
|
printf("c034 write not last = 1\n");
|
|
/* set_halt(1); */
|
|
}
|
|
do_clock_data();
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
do_clock_data()
|
|
{
|
|
word32 mask;
|
|
int read;
|
|
int op;
|
|
|
|
clk_printf("In do_clock_data, g_clk_mode: %02x\n", g_clk_mode);
|
|
|
|
read = g_c034_val & 0x40;
|
|
switch(g_clk_mode) {
|
|
case CLK_IDLE:
|
|
g_clk_read = (g_c033_data >> 7) & 1;
|
|
g_clk_reg1 = (g_c033_data >> 2) & 3;
|
|
op = (g_c033_data >> 4) & 7;
|
|
if(!read) {
|
|
/* write */
|
|
switch(op) {
|
|
case 0x0: /* Read/write seconds register */
|
|
g_clk_mode = CLK_TIME;
|
|
clock_update_if_needed();
|
|
break;
|
|
case 0x3: /* internal registers */
|
|
g_clk_mode = CLK_INTERNAL;
|
|
if(g_clk_reg1 & 0x2) {
|
|
/* extend BRAM read */
|
|
g_clk_mode = CLK_BRAM2;
|
|
g_clk_reg1 = (g_c033_data & 7) << 5;
|
|
}
|
|
break;
|
|
case 0x2: /* read/write ram 0x10-0x13 */
|
|
g_clk_mode = CLK_BRAM1;
|
|
g_clk_reg1 += 0x10;
|
|
break;
|
|
case 0x4: /* read/write ram 0x00-0x0f */
|
|
case 0x5: case 0x6: case 0x7:
|
|
g_clk_mode = CLK_BRAM1;
|
|
g_clk_reg1 = (g_c033_data >> 2) & 0xf;
|
|
break;
|
|
default:
|
|
halt_printf("Bad c033_data in CLK_IDLE: %02x\n",
|
|
g_c033_data);
|
|
}
|
|
} else {
|
|
printf("clk read from IDLE mode!\n");
|
|
/* set_halt(1); */
|
|
g_clk_mode = CLK_IDLE;
|
|
}
|
|
break;
|
|
case CLK_BRAM2:
|
|
if(!read) {
|
|
/* get more bits of bram addr */
|
|
if((g_c033_data & 0x83) == 0x00) {
|
|
/* more address bits */
|
|
g_clk_reg1 |= ((g_c033_data >> 2) & 0x1f);
|
|
g_clk_mode = CLK_BRAM1;
|
|
} else {
|
|
halt_printf("CLK_BRAM2: c033_data: %02x!\n",
|
|
g_c033_data);
|
|
g_clk_mode = CLK_IDLE;
|
|
}
|
|
} else {
|
|
halt_printf("CLK_BRAM2: clock read!\n");
|
|
g_clk_mode = CLK_IDLE;
|
|
}
|
|
break;
|
|
case CLK_BRAM1:
|
|
/* access battery ram addr g_clk_reg1 */
|
|
if(read) {
|
|
if(g_clk_read) {
|
|
/* Yup, read */
|
|
g_c033_data = g_bram_ptr[g_clk_reg1];
|
|
clk_printf("Reading BRAM loc %02x: %02x\n",
|
|
g_clk_reg1, g_c033_data);
|
|
} else {
|
|
halt_printf("CLK_BRAM1: said wr, now read\n");
|
|
}
|
|
} else {
|
|
if(g_clk_read) {
|
|
halt_printf("CLK_BRAM1: said rd, now write\n");
|
|
} else {
|
|
/* Yup, write */
|
|
clk_printf("Writing BRAM loc %02x with %02x\n",
|
|
g_clk_reg1, g_c033_data);
|
|
g_bram_ptr[g_clk_reg1] = g_c033_data;
|
|
g_config_kegs_update_needed = 1;
|
|
}
|
|
}
|
|
g_clk_mode = CLK_IDLE;
|
|
break;
|
|
case CLK_TIME:
|
|
if(read) {
|
|
if(g_clk_read == 0) {
|
|
halt_printf("Reading time, but in set mode!\n");
|
|
}
|
|
g_c033_data = (g_clk_cur_time >> (g_clk_reg1 * 8)) &
|
|
0xff;
|
|
clk_printf("Returning time byte %d: %02x\n",
|
|
g_clk_reg1, g_c033_data);
|
|
} else {
|
|
/* Write */
|
|
if(g_clk_read) {
|
|
halt_printf("Write time, but in read mode!\n");
|
|
}
|
|
clk_printf("Writing TIME loc %d with %02x\n",
|
|
g_clk_reg1, g_c033_data);
|
|
mask = 0xff << (8 * g_clk_reg1);
|
|
|
|
g_clk_cur_time = (g_clk_cur_time & (~mask)) |
|
|
((g_c033_data & 0xff) << (8 * g_clk_reg1));
|
|
}
|
|
g_clk_mode = CLK_IDLE;
|
|
break;
|
|
case CLK_INTERNAL:
|
|
if(read) {
|
|
printf("Attempting to read internal reg %02x!\n",
|
|
g_clk_reg1);
|
|
} else {
|
|
switch(g_clk_reg1) {
|
|
case 0x0: /* test register */
|
|
if(g_c033_data & 0xc0) {
|
|
printf("Writing test reg: %02x!\n",
|
|
g_c033_data);
|
|
/* set_halt(1); */
|
|
}
|
|
break;
|
|
case 0x1: /* write protect reg */
|
|
clk_printf("Writing clk wr_protect: %02x\n",
|
|
g_c033_data);
|
|
if(g_c033_data & 0x80) {
|
|
printf("Stop, wr clk wr_prot: %02x\n",
|
|
g_c033_data);
|
|
/* set_halt(1); */
|
|
}
|
|
break;
|
|
default:
|
|
halt_printf("Writing int reg: %02x with %02x\n",
|
|
g_clk_reg1, g_c033_data);
|
|
}
|
|
}
|
|
g_clk_mode = CLK_IDLE;
|
|
break;
|
|
default:
|
|
halt_printf("clk mode: %d unknown!\n", g_clk_mode);
|
|
g_clk_mode = CLK_IDLE;
|
|
break;
|
|
}
|
|
}
|
|
|