1
0
mirror of https://github.com/mist64/perfect6502.git synced 2024-06-02 17:41:32 +00:00

added KERNAL runtime

This commit is contained in:
Michael Steil 2010-09-22 10:05:49 +00:00
parent 4b4ebc82dd
commit 761e720a39
11 changed files with 1673 additions and 0 deletions

11
Makefile Normal file
View File

@ -0,0 +1,11 @@
OBJS=runtime.o plugin.o perfect6502.o console.o
CFLAGS=-Wall -O3
all: cbmbasic
cbmbasic: $(OBJS)
$(CC) -o cbmbasic $(OBJS)
clean:
rm -f $(OBJS) cbmbasic

227
console.c Normal file
View File

@ -0,0 +1,227 @@
#include "console.h"
#include <stdlib.h>
#include <stdio.h>
#ifdef _WIN32
#include <windows.h>
static int get_console_info(HANDLE *h, COORD *pos, DWORD *size) {
CONSOLE_SCREEN_BUFFER_INFO info;
*h = GetStdHandle(STD_OUTPUT_HANDLE);
if( *h == INVALID_HANDLE_VALUE || GetConsoleScreenBufferInfo(*h, &info) == FALSE )
return -1;
*pos = info.dwCursorPosition;
*size = (DWORD)info.dwSize.X * info.dwSize.Y;
return 0;
}
#endif /* _WIN32 */
void clear_screen() {
#ifdef _WIN32
static COORD upper_left = {0, 0};
COORD dummy1;
DWORD dummy2, size;
HANDLE hstd_out;
if( (get_console_info(&hstd_out, &dummy1, &size)) == -1 )
return;
FillConsoleOutputCharacter(hstd_out, ' ', size, upper_left, &dummy2);
SetConsoleCursorPosition(hstd_out, upper_left);
#else /* ANSI */
fputs("\033[2J\033[;H", stdout);
#endif /* _WIN32 */
}
void up_cursor() {
#ifdef _WIN32
COORD pos;
DWORD dummy;
HANDLE hstd_out;
if( (get_console_info(&hstd_out, &pos, &dummy)) == -1 )
return;
--pos.Y;
SetConsoleCursorPosition(hstd_out, pos);
#else /* ANSI */
fputs("\033[A", stdout);
#endif /* _WIN32 */
}
void down_cursor() {
#ifdef _WIN32
COORD pos;
DWORD dummy;
HANDLE hstd_out;
if( (get_console_info(&hstd_out, &pos, &dummy)) == -1 )
return;
++pos.Y;
SetConsoleCursorPosition(hstd_out, pos);
#else /* ANSI */
fputs("\033[B", stdout);
#endif /* _WIN32 */
}
void left_cursor() {
#ifdef _WIN32
COORD pos;
DWORD dummy;
HANDLE hstd_out;
if( (get_console_info(&hstd_out, &pos, &dummy)) == -1 )
return;
--pos.X;
SetConsoleCursorPosition(hstd_out, pos);
#else /* ANSI */
fputs("\033[D", stdout);
#endif /* _WIN32 */
}
void right_cursor() {
#ifdef _WIN32
COORD pos;
DWORD dummy;
HANDLE hstd_out;
if( (get_console_info(&hstd_out, &pos, &dummy)) == -1 )
return;
++pos.X;
SetConsoleCursorPosition(hstd_out, pos);
#else /* ANSI */
fputs("\033[C", stdout);
#endif /* _WIN32 */
}
void move_cursor(int x, int y) {
#ifdef _WIN32
COORD dummy1;
DWORD dummy2;
HANDLE hstd_out;
const COORD pos = {x-1, y-1};
if( (get_console_info(&hstd_out, &dummy1, &dummy2)) == -1 )
return;
SetConsoleCursorPosition(hstd_out, pos);
#else /* ANSI */
printf("\033[%d;%df", y, x);
#endif /* _WIN32 */
}
void get_cursor(int* x, int* y)
{
#ifdef _WIN32
COORD pos;
DWORD dummy;
HANDLE hstd_out;
if( (get_console_info(&hstd_out, &pos, &dummy)) == -1 )
{
*x = 0;
*y = 0;
}
else
{
*x = pos.X;
*y = pos.Y;
}
#else /* ANSI */
/* TODO we always return 0/0 as the cursor position */
*x = 0;
*y = 0;
#endif /* _WIN32 */
}
void set_color(int c)
{
#ifdef _WIN32
HANDLE h;
CONSOLE_SCREEN_BUFFER_INFO info;
WORD bg;
h = GetStdHandle(STD_OUTPUT_HANDLE);
if( h == INVALID_HANDLE_VALUE || GetConsoleScreenBufferInfo(h, &info) == FALSE )
return;
bg = info.wAttributes & (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_INTENSITY);
switch (c)
{
case COLOR_WHITE:
info.wAttributes = bg | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY;
break;
case COLOR_RED:
info.wAttributes = bg | FOREGROUND_RED;
break;
case COLOR_GREEN:
info.wAttributes = bg | FOREGROUND_GREEN;
break;
case COLOR_BLUE:
info.wAttributes = bg | FOREGROUND_BLUE;
break;
case COLOR_BLACK:
info.wAttributes = bg;
break;
case COLOR_PURPLE:
info.wAttributes = bg | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY;
break;
case COLOR_YELLOW:
info.wAttributes = bg | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY;
break;
case COLOR_CYAN:
info.wAttributes = bg | FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY;
break;
case COLOR_ORANGE:
info.wAttributes = bg | FOREGROUND_GREEN | FOREGROUND_BLUE;
break;
case COLOR_BROWN:
info.wAttributes = bg | FOREGROUND_RED | FOREGROUND_GREEN;
break;
case COLOR_LTRED:
info.wAttributes = bg | FOREGROUND_RED | FOREGROUND_INTENSITY;
break;
case COLOR_GREY1:
info.wAttributes = bg | FOREGROUND_RED | FOREGROUND_BLUE;
break;
case COLOR_GREY2:
info.wAttributes = bg | FOREGROUND_INTENSITY;
break;
case COLOR_LTGREEN:
info.wAttributes = bg | FOREGROUND_GREEN | FOREGROUND_INTENSITY;
break;
case COLOR_LTBLUE:
info.wAttributes = bg | FOREGROUND_BLUE | FOREGROUND_INTENSITY;
break;
case COLOR_GREY3:
info.wAttributes = bg | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN;
break;
}
SetConsoleTextAttribute(h, info.wAttributes);
#else /* ANSI */
/* TODO */
#endif /* _WIN32 */
}

32
console.h Normal file
View File

@ -0,0 +1,32 @@
#ifndef CONSOLE_H_INCLUDED
#define CONSOLE_H_INCLUDED
enum {
COLOR_WHITE,
COLOR_RED,
COLOR_GREEN,
COLOR_BLUE,
COLOR_BLACK,
COLOR_PURPLE,
COLOR_YELLOW,
COLOR_CYAN,
COLOR_ORANGE,
COLOR_BROWN,
COLOR_LTRED,
COLOR_GREY1,
COLOR_GREY2,
COLOR_LTGREEN,
COLOR_LTBLUE,
COLOR_GREY3,
};
void clear_screen();
void up_cursor();
void down_cursor();
void left_cursor();
void right_cursor();
void move_cursor(int x, int y);
void get_cursor(int* x, int* y);
void set_color(int c);
#endif /* CONSOLE_H_INCLUDED */

32
glue.h Normal file
View File

@ -0,0 +1,32 @@
extern unsigned char RAM[65536];
extern unsigned char A, X, Y, S;
extern unsigned short PC;
extern unsigned char V, B, D, I, C, N, Z;
#define SETZ(a) Z=a;
#define SETSZ(a) Z = (a)? 0:1; N = ((signed char)(a))<0?1:0
#define SETNC(a) C = (a)&0x100? 0:1
#define SETV(a) /* not needed */
#define STACK16(i) (RAM[0x0100+i]|(RAM[0x0100+i+1]<<8))
#define PUSH(b) RAM[0x0100+S--] = (b)
#define PUSH_WORD(b) PUSH((b)>>8); PUSH((b)&0xFF)
void CHRGET(void);
void CHRGOT(void);
int main(int, char **);
#define VEC_ERROR 0x0300
#define VEC_MAIN 0x0302
#define VEC_CRNCH 0x0304
#define VEC_QPLOP 0x0306
#define VEC_GONE 0x0308
#define VEC_EVAL 0x030A
#define MAGIC_ERROR 0xFF00
#define MAGIC_MAIN 0xFF01
#define MAGIC_CRNCH 0xFF02
#define MAGIC_QPLOP 0xFF03
#define MAGIC_GONE 0xFF04
#define MAGIC_EVAL 0xFF05
#define MAGIC_CONTINUATION 0xFFFF

View File

@ -62,6 +62,10 @@ transistor_t transistors[TRANSISTORS];
uint8_t memory[65536];
int cycle;
uint8_t A, X, Y, S, P;
uint16_t PC;
BOOL N, Z, C;
void
setupNodes()
{
@ -605,6 +609,20 @@ readPC()
void
chipStatus()
{
//exit(1);
PC = readPC();
printf("PC = %x\n", PC);
if (PC >= 0xFF90) {
A = readA();
X = readX();
Y = readY();
S = readSP();
P = readP();
N = P >> 7;
Z = (P >> 1) & 1;
C = P & 1;
kernal_dispatch();
}
printf("halfcyc:%d phi0:%d AB:%04X D:%02X RnW:%d PC:%04X A:%02X X:%02X Y:%02X SP:%02X P:%02X\n",
cycle,
isNodeHigh(clk0),

287
plugin.c Normal file
View File

@ -0,0 +1,287 @@
/*
* Copyright (c) 2009 Michael Steil
* 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.
*/
/*
* This plugin interface makes use of the standard plugin facility built into
* Commodore BASIC that is used by BASIC extensions such as Simons' BASIC.
* There are several vectors at 0x0300 in RAM for functions like error printing,
* tokenizing, de-tokenizing and the interpreter loop. We hook this from C.
* Since this adds code to the interpreter loop, it is disabled by default,
* and can be enabled like this:
*
* SYS 1
*
* It can be disabled with:
*
* SYS 0
*
* Please note that the current implementation does not tokenize new keywords,
* but stores them verbatim and compares strings when during execution, which
* is very bad for performance. Also, there is currently no demo code for
* added functions.
*/
#include <stdio.h>
#include <stdlib.h>
#ifdef _WIN32
#include <windows.h>
#undef ERROR_FILE_NOT_FOUND /* avoid conflict with CBM value below */
#endif
#include "plugin.h"
#include "glue.h"
#include "console.h"
static unsigned short
get_chrptr() {
return RAM[0x7A] | RAM[0x7B]<<8;
}
static void
set_chrptr(unsigned short a) {
RAM[0x7A] = a & 0xFF;
RAM[0x7B] = a >> 8;
}
int
compare(const char *s1) {
const unsigned char *s = (const unsigned char *)s1;
unsigned short chrptr = get_chrptr();
while (*s) {
CHRGET();
if (A != *s++) {
set_chrptr(chrptr);
return 0;
}
}
CHRGET();
return 1;
}
/*
* Continuation
*
* This will put a magic value onto the stack and run the main
* function again with another PC value as a start address.
* When the code returns, it will find the magic value, and
* the main function will quit, so we end up here again.
*/
static void
call(unsigned short pc) {
PC = pc;
PUSH_WORD(MAGIC_CONTINUATION-1);
main(0,0);
}
static void
check_comma() {
call(0xAEFD);
}
static unsigned short
get_word() {
call(0xAD8A);
call(0xB7F7);
return RAM[0x14] | (RAM[0x15]<<8);
}
static unsigned char
get_byte() {
call(0xB79E);
return X;
}
static void
get_string(char *s) {
int i;
call(0xAD9E);
call(0xB6A3);
for (i = 0; i < A; i++)
s[i] = RAM[(X|(Y<<8))+i];
s[A] = 0;
}
#define ERROR_TOO_MANY_FILES 0x01
#define ERROR_FILE_OPEN 0x02
#define ERROR_FILE_NOT_OPEN 0x03
#define ERROR_FILE_NOT_FOUND 0x04
#define ERROR_DEVICE_NOT_PRESENT 0x05
#define ERROR_NOT_INPUT_FILE 0x06
#define ERROR_NOT_OUTPUT_FILE 0x07
#define ERROR_MISSING_FILE_NAME 0x08
#define ERROR_ILLEGAL_DEVICE_NUMBER 0x09
#define ERROR_NEXT_WITHOUT_FOR 0x0A
#define ERROR_SYNTAX 0x0B
#define ERROR_RETURN_WITHOUT_GOSUB 0x0C
#define ERROR_OUT_OF_DATA 0x0D
#define ERROR_ILLEGAL_QUANTITY 0x0E
#define ERROR_OVERFLOW 0x0F
#define ERROR_OUT_OF_MEMORY 0x10
#define ERROR_UNDEFD_STATMENT 0x11
#define ERROR_BAD_SUBSCRIPT 0x12
#define ERROR_REDIMD_ARRAY 0x13
#define ERROR_DEVISION_BY_ZERO 0x14
#define ERROR_ILLEGAL_DIRECT 0x15
#define ERROR_TYPE_MISMATCH 0x16
#define ERROR_STRING_TOO_LONG 0x17
#define ERROR_FILE_DATA 0x18
#define ERROR_FORMULA_TOO_COMPLEX 0x19
#define ERROR_CANT_CONTINUE 0x1A
#define ERROR_UNDEFD_FUNCTION 0x1B
#define ERROR_VERIFY 0x1C
#define ERROR_LOAD 0x1D
#define ERROR_BREAK 0x1E
static unsigned short
error(unsigned char index) {
X = index;
return 0xA437; /* error handler */
}
/*
* Print BASIC Error Message
*
* We could add handling of extra error codes here, or
* print friendlier strings, or implement "ON ERROR GOTO".
*/
unsigned short
plugin_error() {
return 0;
}
/*
* BASIC Warm Start
*
* This gets called whenever we are in direct mode.
*/
unsigned short
plugin_main() {
return 0;
}
/*
* Tokenize BASIC Text
*/
unsigned short
plugin_crnch() {
return 0;
}
/*
* BASIC Text LIST
*/
unsigned short
plugin_qplop() {
return 0;
}
/*
* BASIC Char. Dispatch
*
* This is used for interpreting statements.
*/
unsigned short
plugin_gone() {
set_chrptr(get_chrptr()+1);
for (;;) {
unsigned short chrptr;
set_chrptr(get_chrptr()-1);
chrptr = get_chrptr();
/*
* this example shows:
* - how to get a 16 bit integer
* - how to get an 8 bit integer
* - how to check for a comma delimiter
* - how to do error handling
*/
if (compare("LOCATE")) {
unsigned char x,y;
y = get_byte(); /* 'line' first */
check_comma();
x = get_byte(); /* then 'column' */
/* XXX ignores terminal size */
if (x>80 || y>25 || x==0 || y==0)
return error(ERROR_ILLEGAL_QUANTITY);
move_cursor(x, y);
continue;
}
/*
* this example shows:
* - how to override existing keywords
* - how to hand the instruction to the
* original interpreter if we don't want
* to handle it
*/
if (compare("\222")) { /* 0x92 - WAIT */
unsigned short a;
unsigned char b;
a = get_word();
check_comma();
b = get_byte();
if (a==6502) {
printf("MICROSOFT!");
continue;
} else {
set_chrptr(chrptr);
return 0;
}
}
/*
* this example shows:
* - how to deal with new keywords that contain
* existing keywords
* - how to parse a string
*/
if (compare("\236TEM")) {
char s[256];
get_string(s);
system(s);
continue;
}
if (compare("QUIT")) {
exit(0);
}
break;
}
return 0;
}
/*
* BASIC Token Evaluation
*
* This is used for expression evaluation.
* New functions and operators go here.
*/
unsigned short
plugin_eval() {
return 0;
}

7
plugin.h Normal file
View File

@ -0,0 +1,7 @@
unsigned short plugin_error(void);
unsigned short plugin_main(void);
unsigned short plugin_crnch(void);
unsigned short plugin_qplop(void);
unsigned short plugin_gone(void);
unsigned short plugin_eval(void);

98
readdir.c Normal file
View File

@ -0,0 +1,98 @@
#define WIN32_LEAN_AND_MEAN
#include <errno.h>
#include <stdlib.h>
#include <windows.h> // FindFirstFile, FindNextFile
#include "readdir.h"
typedef struct dir_private
{
HANDLE h;
int first_read;
struct dirent buf;
} DIR;
DIR *opendir(const char *name)
{
DIR *ret = NULL;
char name2[MAX_PATH];
WIN32_FIND_DATAA findData;
if (strlen(name) >= MAX_PATH - 2 - 1)
{
errno = ENAMETOOLONG;
return ret;
}
strcpy(name2, name);
strcat(name2, "/*");
if (!(ret = (DIR *)malloc(sizeof(DIR))))
{
errno = ENOMEM;
return ret;
}
ret->h = FindFirstFileA(name2, &findData);
if (ret->h == INVALID_HANDLE_VALUE)
{
DWORD err = GetLastError();
free(ret);
ret = NULL;
switch (err)
{
case ERROR_FILE_NOT_FOUND: errno = ENOENT; break;
case ERROR_PATH_NOT_FOUND: errno = ENOENT; break;
case ERROR_ACCESS_DENIED: errno = EACCES; break;
case ERROR_DIRECTORY: errno = ENOTDIR; break;
default: errno = EINVAL;
}
return ret;
}
strcpy(ret->buf.d_name, findData.cFileName);
ret->first_read = 1;
return ret;
}
struct dirent *readdir(DIR *dir)
{
WIN32_FIND_DATAA findData;
if (!dir)
{
errno = EBADF;
return NULL;
}
if (dir->first_read)
{
dir->first_read = 0;
return &dir->buf;
}
if (!FindNextFileA(dir->h, &findData))
{
switch (GetLastError())
{
case ERROR_NO_MORE_FILES: errno = ENOENT; break;
case ERROR_INVALID_HANDLE: errno = EBADF; break;
default: errno = EINVAL;
}
return NULL;
}
strcpy(dir->buf.d_name, findData.cFileName);
return &dir->buf;
}
int closedir(DIR *dir)
{
if (!dir)
{
errno = EBADF;
return -1;
}
FindClose(dir->h);
free(dir);
return 0;
}

27
readdir.h Normal file
View File

@ -0,0 +1,27 @@
#ifndef READDIR_H_INCLUDED
#define READDIR_H_INCLUDED
#include <sys/types.h>
#ifdef _WIN32
#ifndef MAX_PATH
#define MAX_PATH 260
#endif
struct dirent
{
char d_name[MAX_PATH];
};
typedef struct dir_private DIR;
DIR *opendir(const char *name);
struct dirent *readdir(DIR *dir);
int closedir(DIR *dir);
#else
#include <dirent.h>
#endif
#endif /* READDIR_H_INCLUDED */

920
runtime.c Normal file
View File

@ -0,0 +1,920 @@
/*
* 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(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() {
#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;
default: printf("unknown PC=$%04X S=$%02X\n", PC, S); exit(1);
}
return 1;
}

14
stat.h Normal file
View File

@ -0,0 +1,14 @@
#ifndef STAT_H_INCLUDED
#define STAT_H_INCLUDED
#include <sys/types.h>
#include <sys/stat.h>
#ifdef _WIN32
#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
#define stat _stat
#else
#include <unistd.h>
#endif
#endif /* STAT_H_INCLUDED */