1
0
mirror of https://github.com/mist64/perfect6502.git synced 2024-12-27 20:29:43 +00:00
perfect6502/runtime.c
2014-11-28 07:57:38 +01:00

926 lines
23 KiB
C

/*
* Copyright (c) 2009 Michael Steil, James Abbatiello
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
//#define NO_CLRHOME
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#ifdef _WIN32
#include <direct.h> // getcwd, chdir
#include <windows.h> // GetLocalTime, SetLocalTime
#include <conio.h> // _kbhit, _getch
#else
#include <sys/time.h>
#include <unistd.h>
#endif
#include "stat.h"
#include "readdir.h"
#include "plugin.h"
#include "glue.h"
#include "console.h"
unsigned char RAM[65536];
int
stack4(unsigned short a, unsigned short b, unsigned short c, unsigned short d) {
// printf("stack4: %x,%x,%x,%x\n", a, b, c, d);
if (STACK16(S+1) + 1 != a) return 0;
if (STACK16(S+3) + 1 != b) return 0;
if (STACK16(S+5) + 1 != c) return 0;
if (STACK16(S+7) + 1 != d) return 0;
return 1;
}
/*
* CHRGET/CHRGOT
* CBMBASIC implements CHRGET/CHRGOT as self-modifying
* code in the zero page. This cannot be done with
* static recompilation, so here is a reimplementation
* of these functions in C.
0073 E6 7A INC $7A
0075 D0 02 BNE $0079
0077 E6 7B INC $7B
0079 AD XX XX LDA $XXXX
007C C9 3A CMP #$3A ; colon
007E B0 0A BCS $008A
0080 C9 20 CMP #$20 ; space
0082 F0 EF BEQ $0073
0084 38 SEC
0085 E9 30 SBC #$30 ; 0
0087 38 SEC
0088 E9 D0 SBC #$D0
008A 60 RTS
*/
static void
CHRGET_common(int inc) {
unsigned short temp16;
if (!inc) goto CHRGOT_start;
CHRGET_start:
RAM[0x7A]++; SETSZ(RAM[0x7A]);
if (!Z) goto CHRGOT_start;
RAM[0x7B]++; SETSZ(RAM[0x7B]);
CHRGOT_start:
A = RAM[RAM[0x7A] | RAM[0x7B]<<8]; SETSZ(A);
temp16 = ((unsigned short)A) - ((unsigned short)0x3A); SETNC(temp16); SETSZ(temp16&0xFF);
if (C) return;
temp16 = ((unsigned short)A) - ((unsigned short)0x20); SETNC(temp16); SETSZ(temp16&0xFF);
if (Z) goto CHRGET_start;
C = 1;
temp16 = (unsigned short)A-(unsigned short)0x30-(unsigned short)(1-C); SETV(((A ^ temp16) & 0x80) && ((A ^ 0x30) & 0x80)); A = (unsigned char)temp16; SETSZ(A); SETNC(temp16);
C = 1;
temp16 = (unsigned short)A-(unsigned short)0xD0-(unsigned short)(1-C); SETV(((A ^ temp16) & 0x80) && ((A ^ 0xD0) & 0x80)); A = (unsigned char)temp16; SETSZ(A); SETNC(temp16);
}
void
CHRGET() {
CHRGET_common(1);
}
void
CHRGOT() {
CHRGET_common(0);
}
/************************************************************/
/* KERNAL interface implementation */
/* http://members.tripod.com/~Frank_Kontros/kernal/addr.htm */
/************************************************************/
/* KERNAL constants */
#if 0
#define RAM_BOT 0x0400 /* we could just as well start at 0x0400, as there is no screen RAM */
#else
#define RAM_BOT 0x0800
#endif
#define RAM_TOP 0xA000
#define KERN_ERR_NONE 0
#define KERN_ERR_FILE_OPEN 2
#define KERN_ERR_FILE_NOT_OPEN 3
#define KERN_ERR_FILE_NOT_FOUND 4
#define KERN_ERR_DEVICE_NOT_PRESENT 5
#define KERN_ERR_NOT_INPUT_FILE 6
#define KERN_ERR_NOT_OUTPUT_FILE 7
#define KERN_ERR_MISSING_FILE_NAME 8
#define KERN_ERR_ILLEGAL_DEVICE_NUMBER 9
#define KERN_ST_TIME_OUT_READ 0x02
#define KERN_ST_EOF 0x40
/* KERNAL internal state */
unsigned char kernal_msgflag, kernal_status = 0;
unsigned short kernal_filename;
unsigned char kernal_filename_len;
unsigned char kernal_lfn, kernal_dev, kernal_sec;
int kernal_quote = 0;
unsigned char kernal_output = 0, kernal_input = 0;
FILE* kernal_files[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
int kernal_files_next[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
/* shell script hack */
int readycount = 0;
int interactive;
FILE *input_file;
int
init_os(int argc, char **argv) {
// printf("init_os %d\n", argc);
if (!argc) /* continuation */
return PC;
if (argc>1) {
interactive = 0;
input_file = fopen(argv[1], "r");
if (!input_file) {
printf("Error opening: %s\n", argv[1]);
exit(1);
}
if (fgetc(input_file)=='#') {
char c;
do {
c = fgetc(input_file);
} while((c!=13)&&(c!=10));
} else {
fseek(input_file, 0, SEEK_SET);
}
} else {
interactive = 1;
input_file = NULL;
}
srand((unsigned int)time(NULL));
return 0xE394; /* main entry point of BASIC */
}
unsigned short orig_error, orig_main, orig_crnch, orig_qplop, orig_gone, orig_eval;
int plugin = 0;
void
replace_vector(unsigned short address, unsigned short new, unsigned short *old) {
*old = RAM[address] | (RAM[address+1]<<8);
RAM[address] = (new)&0xFF;
RAM[address+1] = (new)>>8;
}
void
plugin_on() {
if (plugin)
return;
replace_vector(VEC_ERROR, MAGIC_ERROR, &orig_error);
replace_vector(VEC_MAIN, MAGIC_MAIN, &orig_main);
replace_vector(VEC_CRNCH, MAGIC_CRNCH, &orig_crnch);
replace_vector(VEC_QPLOP, MAGIC_QPLOP, &orig_qplop);
replace_vector(VEC_GONE, MAGIC_GONE, &orig_gone);
replace_vector(VEC_EVAL, MAGIC_EVAL, &orig_eval);
plugin = 1;
}
static void
plugin_off() {
unsigned short dummy;
if (!plugin)
return;
replace_vector(VEC_ERROR, orig_error, &dummy);
replace_vector(VEC_MAIN, orig_main, &dummy);
replace_vector(VEC_CRNCH, orig_crnch, &dummy);
replace_vector(VEC_QPLOP, orig_qplop, &dummy);
replace_vector(VEC_GONE, orig_gone, &dummy);
replace_vector(VEC_EVAL, orig_eval, &dummy);
plugin = 0;
}
static void
SETMSG() {
kernal_msgflag = A;
A = kernal_status;
}
static void
MEMTOP() {
#if DEBUG /* CBMBASIC doesn't do this */
if (!C) {
printf("UNIMPL: set top of RAM");
exit(1);
}
#endif
X = RAM_TOP&0xFF;
Y = RAM_TOP>>8;
/*
* if we want to turn on the plugin
* automatically at start, we can do it here.
*/
//plugin_on();
}
/* MEMBOT */
static void
MEMBOT() {
#if DEBUG /* CBMBASIC doesn't do this */
if (!C) {
printf("UNIMPL: set bot of RAM");
exit(1);
}
#endif
X = RAM_BOT&0xFF;
Y = RAM_BOT>>8;
}
/* READST */
static void
READST() {
A = kernal_status;
}
/* SETLFS */
static void
SETLFS() {
kernal_lfn = A;
kernal_dev = X;
kernal_sec = Y;
}
/* SETNAM */
static void
SETNAM() {
kernal_filename = X | Y<<8;
kernal_filename_len = A;
}
/* OPEN */
static void
OPEN() {
kernal_status = 0;
if (kernal_files[kernal_lfn]) {
C = 1;
A = KERN_ERR_FILE_OPEN;
} else if (kernal_filename_len == 0) {
C = 1;
A = KERN_ERR_MISSING_FILE_NAME;
} else {
unsigned char savedbyte = RAM[kernal_filename+kernal_filename_len];
const char* mode = kernal_sec == 0 ? "r" : "w";
RAM[kernal_filename+kernal_filename_len] = 0;
kernal_files[kernal_lfn] = fopen((char*)(RAM+kernal_filename), mode);
RAM[kernal_filename+kernal_filename_len] = savedbyte;
if (kernal_files[kernal_lfn]) {
kernal_files_next[kernal_lfn] = EOF;
C = 0;
} else {
C = 1;
A = KERN_ERR_FILE_NOT_FOUND;
}
}
}
/* CLOSE */
static void
CLOSE() {
if (!kernal_files[kernal_lfn]) {
C = 1;
A = KERN_ERR_FILE_NOT_OPEN;
} else {
fclose(kernal_files[kernal_lfn]);
kernal_files[kernal_lfn] = 0;
C = 0;
}
}
/* CHKIN */
static void
CHKIN() {
kernal_status = 0;
if (!kernal_files[X]) {
C = 1;
A = KERN_ERR_FILE_NOT_OPEN;
} else {
// TODO Check read/write mode
kernal_input = X;
C = 0;
}
}
/* CHKOUT */
static void
CHKOUT() {
kernal_status = 0;
if (!kernal_files[X]) {
C = 1;
A = KERN_ERR_FILE_NOT_OPEN;
} else {
// TODO Check read/write mode
kernal_output = X;
C = 0;
}
}
/* CLRCHN */
static void
CLRCHN() {
kernal_input = 0;
kernal_output = 0;
}
static const char run[] = { 'R', 'U', 'N', 13 };
int fakerun = 0;
int fakerun_index = 0;
/* CHRIN */
static void
CHRIN() {
if ((!interactive) && (readycount==2)) {
exit(0);
}
if (kernal_input != 0) {
if (feof(kernal_files[kernal_input])) {
kernal_status |= KERN_ST_EOF;
kernal_status |= KERN_ST_TIME_OUT_READ;
A = 13;
} else {
if (kernal_files_next[kernal_input] == EOF)
kernal_files_next[kernal_input] = fgetc(kernal_files[kernal_input]);
A = kernal_files_next[kernal_input];
kernal_files_next[kernal_input] = fgetc(kernal_files[kernal_input]);
if (kernal_files_next[kernal_input] == EOF)
kernal_status |= KERN_ST_EOF;
}
} else if (!input_file) {
A = getchar(); /* stdin */
if (A=='\n') A = '\r';
} else {
if (fakerun) {
A = run[fakerun_index++];
if (fakerun_index==sizeof(run))
input_file = 0; /* switch to stdin */
} else {
A = fgetc(input_file);
if ((A==255)&&(readycount==1)) {
fakerun = 1;
fakerun_index = 0;
A = run[fakerun_index++];
}
if (A=='\n') A = '\r';
}
}
C = 0;
}
/* CHROUT */
static void
CHROUT() {
//return;
//exit(1);
#if 0
int a = *(unsigned short*)(&RAM[0x0100+S+1]) + 1;
int b = *(unsigned short*)(&RAM[0x0100+S+3]) + 1;
int c = *(unsigned short*)(&RAM[0x0100+S+5]) + 1;
int d = *(unsigned short*)(&RAM[0x0100+S+7]) + 1;
printf("CHROUT: %d @ %x,%x,%x,%x\n", A, a, b, c, d);
#endif
if (!interactive) {
if (stack4(0xe10f,0xab4a,0xab30,0xe430)) {
/* COMMODORE 64 BASIC V2 */
C = 0;
return;
}
if (stack4(0xe10f,0xab4a,0xab30,0xe43d)) {
/* 38911 */
C = 0;
return;
}
if (stack4(0xe10f,0xab4a,0xab30,0xe444)) {
/* BASIC BYTES FREE */
C = 0;
return;
}
}
if (stack4(0xe10f,0xab4a,0xab30,0xa47b)) {
/* READY */
if (A=='R') readycount++;
if (!interactive) {
C = 0;
return;
}
}
if (stack4(0xe10f,0xab4a,0xaadc,0xa486)) {
/*
* CR after each entered numbered program line:
* The CBM screen editor returns CR when the user
* hits return, but does not print the character,
* therefore CBMBASIC does. On UNIX, the terminal
* prints all input characters, so we have to avoid
* printing it again
*/
C = 0;
return;
}
#if 0
printf("CHROUT: %c (%d)\n", A, A);
#else
if (kernal_output) {
if (fputc(A, kernal_files[kernal_output]) == EOF) {
C = 1;
A = KERN_ERR_NOT_OUTPUT_FILE;
} else
C = 0;
} else {
if (kernal_quote) {
if (A == '"' || A == '\n' || A == '\r') kernal_quote = 0;
putchar(A);
} else {
switch (A) {
case 5:
set_color(COLOR_WHITE);
break;
case 10:
break;
case 13:
putchar(13);
putchar(10);
break;
case 17: /* CSR DOWN */
down_cursor();
break;
case 19: /* CSR HOME */
move_cursor(0, 0);
break;
case 28:
set_color(COLOR_RED);
break;
case 29: /* CSR RIGHT */
right_cursor();
break;
case 30:
set_color(COLOR_GREEN);
break;
case 31:
set_color(COLOR_BLUE);
break;
case 129:
set_color(COLOR_ORANGE);
break;
case 144:
set_color(COLOR_BLACK);
break;
case 145: /* CSR UP */
up_cursor();
break;
case 147: /* clear screen */
#ifndef NO_CLRHOME
clear_screen();
#endif
break;
case 149:
set_color(COLOR_BROWN);
break;
case 150:
set_color(COLOR_LTRED);
break;
case 151:
set_color(COLOR_GREY1);
break;
case 152:
set_color(COLOR_GREY2);
break;
case 153:
set_color(COLOR_LTGREEN);
break;
case 154:
set_color(COLOR_LTBLUE);
break;
case 155:
set_color(COLOR_GREY3);
break;
case 156:
set_color(COLOR_PURPLE);
break;
case 158:
set_color(COLOR_YELLOW);
break;
case 159:
set_color(COLOR_CYAN);
break;
case 157: /* CSR LEFT */
left_cursor();
break;
case '"':
kernal_quote = 1;
// fallthrough
default:
putchar(A);
}
}
#endif
fflush(stdout);
C = 0;
}
}
/* LOAD */
static void
LOAD() {
FILE *f;
struct stat st;
unsigned short start;
unsigned short end;
unsigned char savedbyte;
if (A) {
printf("UNIMPL: VERIFY\n");
exit(1);
}
if (!kernal_filename_len)
goto missing_file_name;
/* on special filename $ read directory entries and load they in the basic area memory */
if( RAM[kernal_filename]=='$' ) {
DIR *dirp;
struct dirent *dp;
int i, file_size;
unsigned short old_memp, memp = 0x0801; // TODO hack!
old_memp = memp;
memp += 2;
RAM[memp++] = 0;
RAM[memp++] = 0;
RAM[memp++] = 0x12; /* REVERS ON */
RAM[memp++] = '"';
for(i=0; i<16; i++)
RAM[memp+i] = ' ';
if( (getcwd((char*)&RAM[memp], 256)) == NULL )
goto device_not_present;
memp += strlen((char*)&RAM[memp]); /* only 16 on COMMODORE DOS */
RAM[memp++] = '"';
RAM[memp++] = ' ';
RAM[memp++] = '0';
RAM[memp++] = '0';
RAM[memp++] = ' ';
RAM[memp++] = '2';
RAM[memp++] = 'A';
RAM[memp++] = 0;
RAM[old_memp] = (memp) & 0xFF;
RAM[old_memp+1] = (memp) >> 8;
if ( !(dirp = opendir(".")) )
goto device_not_present;
while ((dp = readdir(dirp))) {
size_t namlen = strlen(dp->d_name);
stat(dp->d_name, &st);
file_size = (st.st_size + 253)/254; /* convert file size from num of bytes to num of blocks(254 bytes) */
if (file_size>0xFFFF)
file_size = 0xFFFF;
old_memp = memp;
memp += 2;
RAM[memp++] = file_size & 0xFF;
RAM[memp++] = file_size >> 8;
if (file_size<1000) {
RAM[memp++] = ' ';
if (file_size<100) {
RAM[memp++] = ' ';
if (file_size<10) {
RAM[memp++] = ' ';
}
}
}
RAM[memp++] = '"';
if (namlen>16)
namlen=16; /* TODO hack */
memcpy(&RAM[memp], dp->d_name, namlen);
memp += namlen;
RAM[memp++] = '"';
for (i=namlen; i<16; i++)
RAM[memp++] = ' ';
RAM[memp++] = ' ';
RAM[memp++] = 'P';
RAM[memp++] = 'R';
RAM[memp++] = 'G';
RAM[memp++] = ' ';
RAM[memp++] = ' ';
RAM[memp++] = 0;
RAM[old_memp] = (memp) & 0xFF;
RAM[old_memp+1] = (memp) >> 8;
}
RAM[memp] = 0;
RAM[memp+1] = 0;
(void)closedir(dirp);
end = memp + 2;
/*
for (i=0; i<255; i++) {
if (!(i&15))
printf("\n %04X ", 0x0800+i);
printf("%02X ", RAM[0x0800+i]);
}
*/
goto load_noerr;
} /* end if( RAM[kernal_filename]=='$' ) */
savedbyte = RAM[kernal_filename+kernal_filename_len]; /* TODO possible overflow */
RAM[kernal_filename+kernal_filename_len] = 0;
/* on directory filename chdir on it */
if( (stat((char*)&RAM[kernal_filename], &st)) == -1 )
goto file_not_found;
if(S_ISDIR(st.st_mode)) {
if( (chdir((char*)&RAM[kernal_filename])) == -1 )
goto device_not_present;
RAM[0x0801] = RAM[0x0802] = 0;
end = 0x0803;
goto load_noerr;
}
/* on file load it read it and load in the basic area memory */
f = fopen((char*)&RAM[kernal_filename], "rb");
if (!f)
goto file_not_found;
start = ((unsigned char)fgetc(f)) | ((unsigned char)fgetc(f))<<8;
if (!kernal_sec)
start = X | Y<<8;
end = start + fread(&RAM[start], 1, 65536-start, f); /* TODO may overwrite ROM */
printf("LOADING FROM $%04X to $%04X\n", start, end);
fclose(f);
load_noerr:
X = end & 0xFF;
Y = end >> 8;
C = 0;
A = KERN_ERR_NONE;
return;
file_not_found:
C = 1;
A = KERN_ERR_FILE_NOT_FOUND;
return;
device_not_present:
C = 1;
A = KERN_ERR_DEVICE_NOT_PRESENT;
return;
missing_file_name:
C = 1;
A = KERN_ERR_MISSING_FILE_NAME;
return;
}
/* SAVE */
static void
SAVE() {
FILE *f;
unsigned char savedbyte;
unsigned short start;
unsigned short end;
start = RAM[A] | RAM[A+1]<<8;
end = X | Y << 8;
if (end<start) {
C = 1;
A = KERN_ERR_NONE;
return;
}
if (!kernal_filename_len) {
C = 1;
A = KERN_ERR_MISSING_FILE_NAME;
return;
}
savedbyte = RAM[kernal_filename+kernal_filename_len]; /* TODO possible overflow */
RAM[kernal_filename+kernal_filename_len] = 0;
f = fopen((char*)&RAM[kernal_filename], "wb"); /* overwrite - these are not the COMMODORE DOS semantics! */
if (!f) {
C = 1;
A = KERN_ERR_FILE_NOT_FOUND;
return;
}
fputc(start & 0xFF, f);
fputc(start >> 8, f);
fwrite(&RAM[start], end-start, 1, f);
fclose(f);
C = 0;
A = KERN_ERR_NONE;
}
/* SETTIM */
static void
SETTIM() {
unsigned long jiffies = Y*65536 + X*256 + A;
unsigned long seconds = jiffies/60;
#ifdef _WIN32
SYSTEMTIME st;
GetLocalTime(&st);
st.wHour = (WORD)(seconds/3600);
st.wMinute = (WORD)(seconds/60);
st.wSecond = (WORD)(seconds%60);
st.wMilliseconds = (WORD)((jiffies % 60) * 1000 / 60);
SetLocalTime(&st);
#else
time_t now = time(0);
struct tm bd;
struct timeval tv;
localtime_r(&now, &bd);
bd.tm_sec = seconds%60;
bd.tm_min = seconds/60;
bd.tm_hour = seconds/3600;
tv.tv_sec = mktime(&bd);
tv.tv_usec = (jiffies % 60) * (1000000/60);
settimeofday(&tv, 0);
#endif
}
/* RDTIM */
static void
RDTIM() {
unsigned long jiffies;
#ifdef _WIN32
SYSTEMTIME st;
GetLocalTime(&st);
jiffies = ((st.wHour*60 + st.wMinute)*60 + st.wSecond)*60 + st.wMilliseconds * 60 / 1000;
#else
time_t now = time(0);
struct tm bd;
struct timeval tv;
localtime_r(&now, &bd);
gettimeofday(&tv, 0);
jiffies = ((bd.tm_hour*60 + bd.tm_min)*60 + bd.tm_sec)*60 + tv.tv_usec / (1000000/60);
#endif
Y = (unsigned char)(jiffies/65536);
X = (unsigned char)((jiffies%65536)/256);
A = (unsigned char)(jiffies%256);
}
/* STOP */
static void
STOP() {
SETZ(0); /* TODO we don't support the STOP key */
}
/* GETIN */
static void
GETIN() {
if (kernal_input != 0) {
if (feof(kernal_files[kernal_input])) {
kernal_status |= KERN_ST_EOF;
kernal_status |= KERN_ST_TIME_OUT_READ;
A = 199;
} else {
if (kernal_files_next[kernal_input] == EOF)
kernal_files_next[kernal_input] = fgetc(kernal_files[kernal_input]);
A = kernal_files_next[kernal_input];
kernal_files_next[kernal_input] = fgetc(kernal_files[kernal_input]);
if (kernal_files_next[kernal_input] == EOF)
kernal_status |= KERN_ST_EOF;
}
C = 0;
} else {
#ifdef _WIN32
if (_kbhit())
A = _getch();
else
A = 0;
#else
A = getchar();
#endif
if (A=='\n') A = '\r';
C = 0;
}
}
/* CLALL */
static void
CLALL() {
int i;
for (i = 0; i < sizeof(kernal_files)/sizeof(kernal_files[0]); ++i) {
if (kernal_files[i]) {
fclose(kernal_files[i]);
kernal_files[i] = 0;
}
}
}
/* PLOT */
static void
PLOT() {
if (C) {
int CX, CY;
get_cursor(&CX, &CY);
Y = CX;
X = CY;
} else {
printf("UNIMPL: set cursor %d %d\n", Y, X);
exit(1);
}
}
/* IOBASE */
static void
IOBASE() {
#define CIA 0xDC00 /* we could put this anywhere... */
/*
* IOBASE is just used inside RND to get a timer value.
* So, let's fake this here, too.
* Commodore BASIC reads offsets 4/5 and 6/7 to get the
* two timers of the CIA.
*/
int pseudo_timer;
pseudo_timer = rand();
RAM[CIA+4] = pseudo_timer&0xff;
RAM[CIA+5] = pseudo_timer>>8;
pseudo_timer = rand(); /* more entropy! */
RAM[CIA+8] = pseudo_timer&0xff;
RAM[CIA+9] = pseudo_timer>>8;
X = CIA & 0xFF;
Y = CIA >> 8;
}
int
kernal_dispatch() {
//{ printf("kernal_dispatch $%04X; ", PC); int i; printf("stack (%02X): ", S); for (i=S+1; i<0x100; i++) { printf("%02X ", RAM[0x0100+i]); } printf("\n"); }
unsigned int new_pc;
switch(PC) {
case 0x0073: CHRGET(); break;
case 0x0079: CHRGOT(); break;
case 0xFF90: SETMSG(); break;
case 0xFF99: MEMTOP(); break;
case 0xFF9C: MEMBOT(); break;
case 0xFFB7: READST(); break;
case 0xFFBA: SETLFS(); break;
case 0xFFBD: SETNAM(); break;
case 0xFFC0: OPEN(); break;
case 0xFFC3: CLOSE(); break;
case 0xFFC6: CHKIN(); break;
case 0xFFC9: CHKOUT(); break;
case 0xFFCC: CLRCHN(); break;
case 0xFFCF: CHRIN(); break;
case 0xFFD2: CHROUT(); break;
case 0xFFD5: LOAD(); break;
case 0xFFD8: SAVE(); break;
case 0xFFDB: SETTIM(); break;
case 0xFFDE: RDTIM(); break;
case 0xFFE1: STOP(); break;
case 0xFFE4: GETIN(); break;
case 0xFFE7: CLALL(); break;
case 0xFFF0: PLOT(); break;
case 0xFFF3: IOBASE(); break;
case 0: plugin_off(); S+=2; break;
case 1: plugin_on(); S+=2; break;
case MAGIC_ERROR: new_pc = plugin_error(); PUSH_WORD(new_pc? new_pc-1:orig_error-1); break;
case MAGIC_MAIN: new_pc = plugin_main(); PUSH_WORD(new_pc? new_pc-1:orig_main-1); break;
case MAGIC_CRNCH: new_pc = plugin_crnch(); PUSH_WORD(new_pc? new_pc-1:orig_crnch-1); break;
case MAGIC_QPLOP: new_pc = plugin_qplop(); PUSH_WORD(new_pc? new_pc-1:orig_qplop-1); break;
case MAGIC_GONE: new_pc = plugin_gone(); PUSH_WORD(new_pc? new_pc-1:orig_gone-1); break;
case MAGIC_EVAL: new_pc = plugin_eval(); PUSH_WORD(new_pc? new_pc-1:orig_eval-1); break;
case MAGIC_CONTINUATION: /*printf("--CONTINUATION--\n");*/ return 0;
#if 0
default: printf("unknown PC=$%04X S=$%02X\n", PC, S); exit(1);
#else
default: return 1;
#endif
}
return 1;
}