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:
parent
4b4ebc82dd
commit
761e720a39
11
Makefile
Normal file
11
Makefile
Normal 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
227
console.c
Normal 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
32
console.h
Normal 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
32
glue.h
Normal 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
|
|
@ -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
287
plugin.c
Normal 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
7
plugin.h
Normal 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
98
readdir.c
Normal 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
27
readdir.h
Normal 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
920
runtime.c
Normal 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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user