From 9a8e2c396f1c76b501adcc6c24d2dfe34737f61d Mon Sep 17 00:00:00 2001 From: Michael Steil Date: Fri, 28 Nov 2014 07:57:38 +0100 Subject: [PATCH] fixed line endings --- runtime.c | 1850 ++++++++++++++++++++++++++--------------------------- 1 file changed, 925 insertions(+), 925 deletions(-) diff --git a/runtime.c b/runtime.c index 1d1ee47..9c2f280 100644 --- a/runtime.c +++ b/runtime.c @@ -1,925 +1,925 @@ -/* - * 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 -#include -#include -#include -#ifdef _WIN32 -#include // getcwd, chdir -#include // GetLocalTime, SetLocalTime -#include // _kbhit, _getch -#else -#include -#include -#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> 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; -} - +/* + * 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 +#include +#include +#include +#ifdef _WIN32 +#include // getcwd, chdir +#include // GetLocalTime, SetLocalTime +#include // _kbhit, _getch +#else +#include +#include +#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> 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; +} +