mirror of
https://github.com/Russell-S-Harper/COMMON.git
synced 2024-11-28 05:51:09 +00:00
commit
e887a83539
10
common-post-process/Makefile
Normal file
10
common-post-process/Makefile
Normal file
@ -0,0 +1,10 @@
|
||||
TGT=common-post-process
|
||||
|
||||
$(TGT): main.c $(TGT).h $(TGT).re $(TGT).l $(TGT).y
|
||||
re2c -is $(TGT).re -o $(TGT).c
|
||||
flex --header-file=$(TGT).yy.h -o $(TGT).yy.c $(TGT).l
|
||||
bison -d $(TGT).y
|
||||
gcc main.c $(TGT).c $(TGT).tab.c $(TGT).yy.c -lfl -lm -o $(TGT)
|
||||
|
||||
clean:
|
||||
rm -f $(TGT).c $(TGT).yy.h $(TGT).yy.c $(TGT).tab.h $(TGT).tab.c
|
71
common-post-process/common-post-process.h
Normal file
71
common-post-process/common-post-process.h
Normal file
@ -0,0 +1,71 @@
|
||||
#ifndef __COMMON_POST_PROCESS_H
|
||||
#define __COMMON_POST_PROCESS_H
|
||||
|
||||
/* How many bits for ... */
|
||||
|
||||
/* ... internal significands */
|
||||
#define INT_SIGN 48
|
||||
|
||||
/* ... internal fractions */
|
||||
#define INT_FRAC 16
|
||||
|
||||
/* ... internal full numbers */
|
||||
#define INT_FULL (INT_SIGN + INT_FRAC)
|
||||
|
||||
/* ... exported significands */
|
||||
#define EXP_SIGN 20
|
||||
|
||||
/* ... exported fractions */
|
||||
#define EXP_FRAC 10
|
||||
|
||||
/* ... exported full numbers */
|
||||
#define EXP_FULL (EXP_SIGN + EXP_FRAC)
|
||||
|
||||
/*
|
||||
Internal calculations may require left shifting of
|
||||
numbers by INT_FRAC bits, so be aware that the higher
|
||||
bits of numbers may be lost if multiplying or dividing
|
||||
large numbers.
|
||||
|
||||
These are the safe upper limits for various internal
|
||||
significand and fraction combinations in bits.
|
||||
|
||||
+----------+----------+-------------+
|
||||
| INT_SIGN | INT_FRAC | Upper Limit |
|
||||
+----------+----------+-------------+
|
||||
| 64 | 0 | 2^63-1 |
|
||||
| 56 | 8 | 2^47-1 |
|
||||
| 48 | 16 | 2^31-1 |
|
||||
| 40 | 24 | 2^15-1 |
|
||||
+----------+----------+-------------+
|
||||
*/
|
||||
|
||||
/* Definitions for flex and bison */
|
||||
|
||||
#ifndef TOKEN_LEN
|
||||
#define TOKEN_LEN 256
|
||||
#endif /* TOKEN_LEN */
|
||||
#define TOKENS 128
|
||||
#define IO_BUFFER (TOKENS * TOKEN_LEN)
|
||||
|
||||
typedef enum
|
||||
{
|
||||
TT_COMMAND, TT_DEFAULT, TT_WHITE_SPACE, TT_END_OF_LINE
|
||||
} TOKEN_TYPE;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
TOKEN_TYPE type;
|
||||
char text[TOKEN_LEN];
|
||||
} TOKEN;
|
||||
|
||||
extern long long result;
|
||||
|
||||
int yyparse (void);
|
||||
void yyerror(char const *s);
|
||||
|
||||
int tokenizeInput(const char *cursor, TOKEN *tokens);
|
||||
long long parseCommon(const char *input);
|
||||
long long shiftCommon(long long base, long long amount);
|
||||
|
||||
#endif /* __COMMON_POST_PROCESS_H */
|
34
common-post-process/common-post-process.l
Normal file
34
common-post-process/common-post-process.l
Normal file
@ -0,0 +1,34 @@
|
||||
%{
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "common-post-process.tab.h"
|
||||
|
||||
%}
|
||||
|
||||
DEC [0-9]+(\.[0-9]+)?
|
||||
HEX \$[0-9A-Fa-f]+(\.[0-9A-Fa-f]+)?
|
||||
OCT &[0-7]+(\.[0-7]+)?
|
||||
BIN %[01]+(\.[01]+)?
|
||||
|
||||
%%
|
||||
|
||||
[A-Za-z_][A-Za-z0-9_]* { strncpy(yylval.sval, yytext, sizeof(yylval.sval)); return IDENTIFIER; }
|
||||
{DEC}|{HEX}|{OCT}|{BIN} { strncpy(yylval.sval, yytext, sizeof(yylval.sval)); return CONSTANT; }
|
||||
"<<" return LEFT_OP;
|
||||
">>" return RIGHT_OP;
|
||||
(<=)|(=<) return LE_OP;
|
||||
(>=)|(=>) return GE_OP;
|
||||
(<>)|(><) return NE_OP;
|
||||
"&&" return AND_OP;
|
||||
"||" return OR_OP;
|
||||
[ \t\n]+ ; /* Ignore whitespace */
|
||||
[-()<>+~!*/%=&^|] return yytext[0];
|
||||
. { fprintf(stderr, "SET (invalid character '%s')\n", yytext); exit(1); }
|
||||
|
||||
%%
|
||||
|
||||
int yywrap(void) {
|
||||
return 1;
|
||||
}
|
112
common-post-process/common-post-process.re
Normal file
112
common-post-process/common-post-process.re
Normal file
@ -0,0 +1,112 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "common-post-process.h"
|
||||
|
||||
static int copyToken(TOKEN *tokens, int index, TOKEN_TYPE type, const char *text, int length);
|
||||
|
||||
int tokenizeInput(const char *cursor, TOKEN *tokens)
|
||||
{
|
||||
const char *marker, *token;
|
||||
int i;
|
||||
|
||||
for (i = 0; *cursor && i < TOKENS; )
|
||||
{
|
||||
token = cursor;
|
||||
/*!re2c
|
||||
re2c:define:YYCTYPE = "char";
|
||||
re2c:define:YYCURSOR = cursor;
|
||||
re2c:define:YYMARKER = marker;
|
||||
re2c:variable:yych = c;
|
||||
re2c:indent:top = 2;
|
||||
re2c:yyfill:enable = 0;
|
||||
re2c:yych:conversion = 1;
|
||||
|
||||
BELL = "\x07" ;
|
||||
BACKSPACE = "\x08" ;
|
||||
HORIZONTAL_TAB = "\x09" ;
|
||||
LINE_FEED = "\x0a" ;
|
||||
VERTICAL_TAB = "\x0b" ;
|
||||
FORM_FEED = "\x0c" ;
|
||||
CARRIAGE_RETURN = "\x0d" ;
|
||||
ESCAPE = "\x1b" ;
|
||||
DELETE = "\x7f" ;
|
||||
|
||||
SPACE = "\x20" ;
|
||||
EXCLAMATION_MARK = "\x21" ;
|
||||
QUOTATION_MARK = "\x22" ;
|
||||
NUMBER_SIGN = "\x23" ;
|
||||
DOLLAR_SIGN = "\x24" ;
|
||||
PERCENT_SIGN = "\x25" ;
|
||||
AMPERSAND = "\x26" ;
|
||||
APOSTROPHE = "\x27" ;
|
||||
LEFT_PARENTHESIS = "\x28" ;
|
||||
RIGHT_PARENTHESIS = "\x29" ;
|
||||
ASTERISK = "\x2a" ;
|
||||
PLUS_SIGN = "\x2b" ;
|
||||
COMMA = "\x2c" ;
|
||||
HYPHEN_MINUS = "\x2d" ;
|
||||
FULL_STOP = "\x2e" ;
|
||||
SOLIDUS = "\x2f" ;
|
||||
COLON = "\x3a" ;
|
||||
SEMICOLON = "\x3b" ;
|
||||
LESS_THAN_SIGN = "\x3c" ;
|
||||
EQUALS_SIGN = "\x3d" ;
|
||||
GREATER_THAN_SIGN = "\x3e" ;
|
||||
QUESTION_MARK = "\x3f" ;
|
||||
COMMERCIAL_AT = "\x40" ;
|
||||
LEFT_SQUARE_BRACKET = "\x5b" ;
|
||||
REVERSE_SOLIDUS = "\x5c" ;
|
||||
RIGHT_SQUARE_BRACKET = "\x5d" ;
|
||||
CIRCUMFLEX_ACCENT = "\x5e" ;
|
||||
LOW_LINE = "\x5f" ;
|
||||
GRAVE_ACCENT = "\x60" ;
|
||||
LEFT_CURLY_BRACKET = "\x7b" ;
|
||||
VERTICAL_LINE = "\x7c" ;
|
||||
RIGHT_CURLY_BRACKET = "\x7d" ;
|
||||
TILDE = "\x7e" ;
|
||||
|
||||
DIGIT = [0-9] ;
|
||||
|
||||
LETTER = [A-Za-z] ;
|
||||
|
||||
WHITE_SPACE = BELL | BACKSPACE | HORIZONTAL_TAB | ESCAPE | DELETE | SPACE ;
|
||||
|
||||
VALID = DIGIT | LETTER | EXCLAMATION_MARK | DOLLAR_SIGN | PERCENT_SIGN | AMPERSAND
|
||||
| LEFT_PARENTHESIS | RIGHT_PARENTHESIS | ASTERISK | PLUS_SIGN | HYPHEN_MINUS
|
||||
| FULL_STOP | SOLIDUS | LESS_THAN_SIGN | EQUALS_SIGN | GREATER_THAN_SIGN
|
||||
| LOW_LINE | VERTICAL_LINE | TILDE | WHITE_SPACE ;
|
||||
|
||||
COMMAND = LOW_LINE [Ss][Ee][Tt] LOW_LINE [Vv] WHITE_SPACE* LEFT_PARENTHESIS WHITE_SPACE* QUOTATION_MARK VALID+ QUOTATION_MARK WHITE_SPACE* RIGHT_PARENTHESIS ;
|
||||
|
||||
DEFAULT = . ;
|
||||
|
||||
END_OF_LINE = ( LINE_FEED | VERTICAL_TAB | FORM_FEED | CARRIAGE_RETURN )+ ;
|
||||
|
||||
COMMAND { i = copyToken(tokens, i, TT_COMMAND, token, (int)(cursor - token)); continue; }
|
||||
DEFAULT { i = copyToken(tokens, i, TT_DEFAULT, token, (int)(cursor - token)); continue; }
|
||||
WHITE_SPACE { i = copyToken(tokens, i, TT_WHITE_SPACE, token, (int)(cursor - token)); continue; }
|
||||
END_OF_LINE { i = copyToken(tokens, i, TT_END_OF_LINE, token, (int)(cursor - token)); continue; }
|
||||
*/
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
int copyToken(TOKEN *tokens, int index, TOKEN_TYPE type, const char *text, int length)
|
||||
{
|
||||
if (index < TOKENS)
|
||||
{
|
||||
int i;
|
||||
|
||||
tokens[index].type = type;
|
||||
|
||||
for (i = 0; i < length && i + 1 < TOKEN_LEN; ++i)
|
||||
tokens[index].text[i] = text[i];
|
||||
|
||||
tokens[index].text[i] = '\0';
|
||||
|
||||
++index;
|
||||
}
|
||||
return index;
|
||||
}
|
172
common-post-process/common-post-process.y
Normal file
172
common-post-process/common-post-process.y
Normal file
@ -0,0 +1,172 @@
|
||||
%error-verbose
|
||||
|
||||
%code requires {
|
||||
#include "common-post-process.h"
|
||||
}
|
||||
|
||||
%{
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "common-post-process.h"
|
||||
#include "common-post-process.yy.h"
|
||||
%}
|
||||
|
||||
%union {
|
||||
long long ival;
|
||||
char sval[TOKEN_LEN];
|
||||
}
|
||||
|
||||
%token <sval> CONSTANT IDENTIFIER
|
||||
%token LEFT_OP RIGHT_OP LE_OP GE_OP NE_OP AND_OP OR_OP
|
||||
|
||||
%type <ival> expr primary_expr unary_expr mult_expr add_expr shift_expr rel_expr eq_expr and_expr xor_expr or_expr logical_and_expr logical_or_expr
|
||||
|
||||
%start expr
|
||||
%%
|
||||
|
||||
primary_expr
|
||||
: '*' { fprintf(stderr, "SET (program counter * is not allowed)\n"); exit(1); }
|
||||
| IDENTIFIER { fprintf(stderr, "SET (unsubstituted constant '%s', use #define)\n", $1); exit(1); }
|
||||
| CONSTANT { $$ = parseCommon($1); }
|
||||
| '(' expr ')' { $$ = $2; }
|
||||
;
|
||||
|
||||
unary_expr
|
||||
: primary_expr
|
||||
| '<' unary_expr { $$ = $2 & (0xff << INT_FRAC); }
|
||||
| '>' unary_expr { $$ = $2 >> CHAR_BIT & (0xff << INT_FRAC); }
|
||||
| '+' unary_expr { $$ = +$2; }
|
||||
| '-' unary_expr { $$ = -$2; }
|
||||
| '~' unary_expr { $$ = ~$2; }
|
||||
| '!' unary_expr { $$ = !$2; }
|
||||
;
|
||||
|
||||
mult_expr
|
||||
: unary_expr
|
||||
| mult_expr '*' unary_expr { $$ = ($1 * $3) >> INT_FRAC; }
|
||||
| mult_expr '/' unary_expr { $$ = ($1 << INT_FRAC) / $3; }
|
||||
| mult_expr '%' unary_expr { $$ = $1 % $3; }
|
||||
;
|
||||
|
||||
add_expr
|
||||
: mult_expr
|
||||
| add_expr '+' mult_expr { $$ = $1 + $3; }
|
||||
| add_expr '-' mult_expr { $$ = $1 - $3; }
|
||||
;
|
||||
|
||||
shift_expr
|
||||
: add_expr
|
||||
| shift_expr LEFT_OP add_expr { $$ = shiftCommon($1, +$3); }
|
||||
| shift_expr RIGHT_OP add_expr { $$ = shiftCommon($1, -$3); }
|
||||
;
|
||||
|
||||
rel_expr
|
||||
: shift_expr
|
||||
| rel_expr '<' shift_expr { $$ = ($1 < $3)? 1: 0; }
|
||||
| rel_expr '>' shift_expr { $$ = ($1 > $3)? 1: 0; }
|
||||
| rel_expr LE_OP shift_expr { $$ = ($1 <= $3)? 1: 0; }
|
||||
| rel_expr GE_OP shift_expr { $$ = ($1 >= $3)? 1: 0; }
|
||||
;
|
||||
|
||||
eq_expr
|
||||
: rel_expr
|
||||
| eq_expr '=' rel_expr { $$ = ($1 == $3)? 1: 0; }
|
||||
| eq_expr NE_OP rel_expr { $$ = ($1 == $3)? 0: 1; }
|
||||
;
|
||||
|
||||
and_expr
|
||||
: eq_expr
|
||||
| and_expr '&' eq_expr { $$ = $1 & $3; }
|
||||
;
|
||||
|
||||
xor_expr
|
||||
: and_expr
|
||||
| xor_expr '^' and_expr { $$ = $1 ^ $3; }
|
||||
;
|
||||
|
||||
or_expr
|
||||
: xor_expr
|
||||
| or_expr '|' xor_expr { $$ = $1 | $3; }
|
||||
;
|
||||
|
||||
logical_and_expr
|
||||
: or_expr
|
||||
| logical_and_expr AND_OP or_expr { $$ = ($1 && $3)? 1: 0; }
|
||||
;
|
||||
|
||||
logical_or_expr
|
||||
: logical_and_expr
|
||||
| logical_or_expr OR_OP logical_and_expr { $$ = ($1 || $3)? 1: 0; }
|
||||
;
|
||||
|
||||
expr
|
||||
: logical_or_expr { result = $1; }
|
||||
;
|
||||
|
||||
%%
|
||||
|
||||
void yyerror(char const *s)
|
||||
{
|
||||
fprintf (stderr, "SET (%s)\n", s);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
long long parseCommon(const char *input)
|
||||
{
|
||||
char digit[2] = {'\0', '\0'};
|
||||
double base = 10.0, working = 0.0;
|
||||
int sign;
|
||||
long long output;
|
||||
/* Check the first character for a base specification */
|
||||
switch (*input) {
|
||||
case '$':
|
||||
base = 16.0;
|
||||
++input;
|
||||
break;
|
||||
case '&':
|
||||
base = 8.0;
|
||||
++input;
|
||||
break;
|
||||
case '%':
|
||||
base = 2.0;
|
||||
++input;
|
||||
break;
|
||||
}
|
||||
/* Get all the digits before the point */
|
||||
while (*input && *input != '.') {
|
||||
digit[0] = *input;
|
||||
working = working * base + (double)strtol(digit, NULL, base);
|
||||
++input;
|
||||
}
|
||||
/* Has a fraction? */
|
||||
if (*input == '.') {
|
||||
double fraction = 0.0, divisor = 1.0;
|
||||
++input;
|
||||
/* Get all the digits after the point */
|
||||
while (*input) {
|
||||
digit[0] = *input;
|
||||
fraction = fraction * base + (double)strtol(digit, NULL, base);
|
||||
divisor *= base;
|
||||
++input;
|
||||
}
|
||||
/* Convert to fraction and add it */
|
||||
working += fraction / divisor;
|
||||
}
|
||||
/* Convert to integer */
|
||||
sign = (working < 0.0)? -1: +1;
|
||||
output = sign * (long long)floor(fabs(working) * (double)(1 << INT_FRAC));
|
||||
/* Done */
|
||||
return output;
|
||||
}
|
||||
|
||||
long long shiftCommon(long long base, long long amount)
|
||||
{
|
||||
double scaling = (double)(1 << INT_FRAC);
|
||||
double x = (double) base / scaling, y = (double) amount / scaling;
|
||||
double working = x * pow(2.0, y);
|
||||
int sign = (working < 0.0)? -1: +1;
|
||||
return sign * (long long)floor(fabs(working) * scaling);
|
||||
}
|
58
common-post-process/main.c
Normal file
58
common-post-process/main.c
Normal file
@ -0,0 +1,58 @@
|
||||
#include <stdio.h>
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "common-post-process.h"
|
||||
#include "common-post-process.yy.h"
|
||||
|
||||
long long result;
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
TOKEN tokens[TOKENS];
|
||||
char buffer[IO_BUFFER] = {'\0'};
|
||||
|
||||
/* Process input */
|
||||
while (fgets(buffer, IO_BUFFER, stdin))
|
||||
{
|
||||
/* Tokenize the line */
|
||||
int count;
|
||||
if (count = tokenizeInput(buffer, tokens))
|
||||
{
|
||||
/* Iterate through the tokens */
|
||||
int i;
|
||||
for (i = 0; i < count; ++i)
|
||||
{
|
||||
int j;
|
||||
unsigned long working;
|
||||
const char *s = "", *p, *q;
|
||||
switch (tokens[i].type)
|
||||
{
|
||||
/* Process each _SET_V("<expr>") command */
|
||||
case TT_COMMAND:
|
||||
/* Extract the start and ending indices */
|
||||
p = strchr(tokens[i].text, '"') + 1;
|
||||
q = strrchr(tokens[i].text, '"');
|
||||
/* Parse the input */
|
||||
yyin = fmemopen((void *)p, q - p, "r");
|
||||
yyparse();
|
||||
fclose(yyin);
|
||||
/* Output */
|
||||
working = (unsigned long)((result >> (INT_FRAC - EXP_FRAC)) % (1 << EXP_FULL));
|
||||
for (j = 0; j < EXP_FULL; j += CHAR_BIT)
|
||||
{
|
||||
printf("%s$%02lX", s, working & 0xff);
|
||||
working >>= CHAR_BIT;
|
||||
s = ", ";
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* Default, just print it */
|
||||
printf("%s", tokens[i].text);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
12
common/Makefile
Normal file
12
common/Makefile
Normal file
@ -0,0 +1,12 @@
|
||||
system.obj: common.obj page6.obj
|
||||
cat common.obj page6.obj > system.obj
|
||||
|
||||
common.obj: rom.h common.h common.asm
|
||||
xa -M common.asm -l common.lbl -o common.obj
|
||||
|
||||
page6.obj: rom.h macros.h page6.src
|
||||
cpp -P page6.src | ./common-post-process > page6.asm
|
||||
xa -M page6.asm -l page6.lbl -o page6.obj
|
||||
|
||||
clean:
|
||||
rm -f page6.asm common.obj page6.obj common.lbl page6.lbl
|
390
common/common.asm
Normal file
390
common/common.asm
Normal file
@ -0,0 +1,390 @@
|
||||
#include "rom.h"
|
||||
#include "common.h"
|
||||
|
||||
; ROM code header
|
||||
.WORD CMN_CD, _END_CMN_CD - CMN_CD
|
||||
|
||||
; beginning of ROM code
|
||||
* = CMN_CD
|
||||
|
||||
_CMN .( ; common function interpreter
|
||||
JSR _SAV ; save registers
|
||||
PLA
|
||||
STA _PCL ; set program counter from return address
|
||||
PLA
|
||||
STA _PCH
|
||||
INC _PCL ; advance the program counter
|
||||
BNE _1
|
||||
INC _PCH
|
||||
_1 JSR _2 ; interpret and execute one common instruction
|
||||
JMP _1
|
||||
_2 LDY #0
|
||||
LDA (_PC),Y ; get operand
|
||||
INC _PCL ; advance the program counter
|
||||
BNE _3
|
||||
INC _PCH
|
||||
_3 TAX ; save operand for later
|
||||
AND #$F0
|
||||
BEQ _4 ; go to 0X instructions
|
||||
CMP #$F0 ; check for FX functions
|
||||
BEQ _5 ; go to FX instructions
|
||||
LSR ; get offset to XR instructions
|
||||
LSR
|
||||
LSR
|
||||
TAY
|
||||
DEY
|
||||
DEY
|
||||
LDA FN_XR+1,Y ; push high address
|
||||
PHA
|
||||
LDA FN_XR,Y ; push low address
|
||||
PHA
|
||||
TXA ; restore operand
|
||||
AND #$F ; mask to get register
|
||||
ASL ; shift to get offset to register
|
||||
ASL
|
||||
TAX ; back to index
|
||||
RTS ; "return" to routine
|
||||
_4 TXA ; get operand
|
||||
ASL ; shift to get offset to 0X instructions
|
||||
TAY
|
||||
LDA FN_0X+1,Y ; push high address
|
||||
PHA
|
||||
LDA FN_0X,Y ; push low address
|
||||
PHA
|
||||
TXA ; restore operand
|
||||
RTS ; "return" to routine
|
||||
_5 TXA ; get operand
|
||||
AND #$F ; mask to get index
|
||||
ASL ; shift to get offset to FX instructions
|
||||
TAY
|
||||
LDA FN_FX+1,Y ; push high address
|
||||
PHA
|
||||
LDA FN_FX,Y ; push low address
|
||||
PHA
|
||||
TXA ; restore operand
|
||||
RTS ; "return" to routine
|
||||
.)
|
||||
|
||||
_INI .( ; initialize common
|
||||
; copy system functions (TODO)
|
||||
; load program (TODO)
|
||||
LDA #0 ; clear RSI
|
||||
STA _RSI
|
||||
JMP (_PC) ; go to last loaded block
|
||||
.)
|
||||
|
||||
_SAV .( ; save the registers prior to entering common
|
||||
STA _ACC
|
||||
STX _IDX
|
||||
STY _IDY
|
||||
PHP
|
||||
PLA
|
||||
STA _PS
|
||||
CLD
|
||||
RTS
|
||||
.)
|
||||
|
||||
_RES .( ; restore the registers prior to leaving common
|
||||
LDA _PS
|
||||
PHA
|
||||
LDA _ACC
|
||||
LDX _IDX
|
||||
LDY _IDY
|
||||
PLP
|
||||
RTS
|
||||
.)
|
||||
|
||||
_SET .( ; SET r aabbcc.dd 1r dd cc bb aa Rr <- aabbcc.dd - set register
|
||||
LDY #0
|
||||
LDA (_PC),Y ; transfer four bytes over
|
||||
STA _R0,X
|
||||
INY
|
||||
LDA (_PC),Y
|
||||
STA _R0+1,X
|
||||
INY
|
||||
LDA (_PC),Y
|
||||
STA _R0+2,X
|
||||
INY
|
||||
LDA (_PC),Y
|
||||
STA _R0+3,X
|
||||
LDA #4 ; update program counter
|
||||
CLC
|
||||
ADC _PCL
|
||||
STA _PCL
|
||||
BCC _1
|
||||
INC _PCH
|
||||
_1 RTS ; done
|
||||
.)
|
||||
|
||||
_POP .( ; POP r 2r Rr <- RS - pop from stack
|
||||
LDY _RSI ; get register stack index
|
||||
DEY ; transfer four bytes over
|
||||
LDA _RS,Y
|
||||
STA _R0+3,X
|
||||
DEY
|
||||
LDA _RS,Y
|
||||
STA _R0+2,X
|
||||
DEY
|
||||
LDA _RS,Y
|
||||
STA _R0+1,X
|
||||
DEY
|
||||
LDA _RS,Y
|
||||
STA _R0,X
|
||||
STY _RSI ; update register stack index
|
||||
RTS
|
||||
.)
|
||||
|
||||
_PSH .( ; PSH r 3r RS <- Rr - push onto stack
|
||||
LDY _RSI ; get register stack index
|
||||
LDA _R0,X ; transfer four bytes over
|
||||
STA _RS,Y
|
||||
INY
|
||||
LDA _R0+1,X
|
||||
STA _RS,Y
|
||||
INY
|
||||
LDA _R0+2,X
|
||||
STA _RS,Y
|
||||
INY
|
||||
LDA _R0+3,X
|
||||
STA _RS,Y
|
||||
INY
|
||||
STY _RSI ; update register stack index
|
||||
RTS
|
||||
.)
|
||||
|
||||
_EXC .( ; EXC r 4r Rr <-> RS - exchange Rr with stack
|
||||
LDY _RSI ; RS to RD
|
||||
LDA _RS-1,Y
|
||||
STA _RD+3
|
||||
LDA _RS-2,Y
|
||||
STA _RD+2
|
||||
LDA _RS-3,Y
|
||||
STA _RD+1
|
||||
LDA _RS-4,Y
|
||||
STA _RD
|
||||
LDA _R0,X ; copy Rr to RS
|
||||
STA _RS-4,Y
|
||||
LDA _R0+1,X
|
||||
STA _RS-3,Y
|
||||
LDA _R0+2,X
|
||||
STA _RS-2,Y
|
||||
LDA _R0+3,X
|
||||
STA _RS-1,Y
|
||||
LDA _RD ; copy RD to Rr
|
||||
STA _R0,X
|
||||
LDA _RD+1
|
||||
STA _R0+1,X
|
||||
LDA _RD+2
|
||||
STA _R0+2,X
|
||||
LDA _RD+3
|
||||
STA _R0+3,X
|
||||
RTS
|
||||
.)
|
||||
|
||||
_INR .( ; INR r 5r Rr <- Rr + 1.0 - increment register
|
||||
LDA _R0+3,X
|
||||
AND #_MSK_O ; check for existing overflow condition
|
||||
BEQ _4
|
||||
EOR #_MSK_O
|
||||
BEQ _4
|
||||
BNE _3 ; existing overflow, skip increment operation
|
||||
_4 LDA #_PLS_1 ; adding plus one
|
||||
CLC
|
||||
ADC _R0+1,X
|
||||
STA _R0+1,X
|
||||
BCC _1
|
||||
LDA #0
|
||||
ADC _R0+2,X
|
||||
STA _R0+2,X
|
||||
BCC _1
|
||||
LDA #0
|
||||
ADC _R0+3,X
|
||||
STA _R0+3,X
|
||||
_1 LDA _R0+3,X
|
||||
AND #_MSK_O ; check for overflow
|
||||
BEQ _2
|
||||
EOR #_MSK_O
|
||||
BEQ _2
|
||||
_3 LDA _F ; set overflow
|
||||
ORA #_F_O
|
||||
STA _F
|
||||
BNE _5
|
||||
_2 LDA _F ; clear overflow
|
||||
AND #_F_O^$FF
|
||||
STA _F
|
||||
_5 RTS
|
||||
.)
|
||||
|
||||
_DCR .( ; DCR r 6r Rr <- Rr - 1.0 - decrement register
|
||||
LDA _R0+3,X
|
||||
AND #_MSK_U ; check for existing underflow condition
|
||||
BEQ _4
|
||||
EOR #_MSK_U
|
||||
BEQ _4
|
||||
BNE _3 ; existing underflow, skip decrement operation
|
||||
_4 LDA #_MNS_1 ; adding minus one
|
||||
CLC
|
||||
ADC _R0+1,X
|
||||
STA _R0+1,X
|
||||
LDA #$FF
|
||||
ADC _R0+2,X
|
||||
STA _R0+2,X
|
||||
LDA #$FF
|
||||
ADC _R0+3,X
|
||||
STA _R0+3,X
|
||||
_1 LDA _R0+3,X
|
||||
AND #_MSK_U ; check for underflow
|
||||
BEQ _2
|
||||
EOR #_MSK_U
|
||||
BEQ _2
|
||||
_3 LDA _F ; set underflow
|
||||
ORA #_F_U
|
||||
STA _F
|
||||
BNE _5
|
||||
_2 LDA _F ; clear underflow
|
||||
AND #_F_U^$FF
|
||||
STA _F
|
||||
_5 RTS
|
||||
.)
|
||||
|
||||
_TST .( ; TST r 7r F <- Rr <=> 0.0 - test register
|
||||
RTS
|
||||
.)
|
||||
|
||||
_DEC .( ; DEC r 8r Rr <- dec(Rr) - convert Rr from hex aabbcc.dd to decimal ######.##
|
||||
RTS
|
||||
.)
|
||||
|
||||
_HEX .( ; HEX r 9r Rr <- hex(Rr) - convert Rr from decimal ######.## to hex aabbcc.dd
|
||||
RTS
|
||||
.)
|
||||
|
||||
_ADD .( ; ADD r pq ar pq Rr <- Rp + Rq - addition
|
||||
RTS
|
||||
.)
|
||||
|
||||
_SUB .( ; SUB r pq br pq Rr <- Rp - Rq - subtraction
|
||||
RTS
|
||||
.)
|
||||
|
||||
_MUL .( ; MUL r pq cr pq Rr <- Rp * Rq - multiplication
|
||||
RTS
|
||||
.)
|
||||
|
||||
_DIV .( ; DIV r pq dr pq Rr <- Rp / Rq - division
|
||||
RTS
|
||||
.)
|
||||
|
||||
_MOD .( ; MOD r pq er pq Rr <- Rp % Rq - modulus
|
||||
RTS
|
||||
.)
|
||||
|
||||
_ESC .( ; ESC 00 - escape back into regular assembler
|
||||
PLA ; discard the COMMON _1 return address
|
||||
PLA
|
||||
JSR _RES ; restore the registers
|
||||
JMP (_PC) ; get back in the code
|
||||
.)
|
||||
|
||||
_RTN .( ; RTN 01 - return from subroutine
|
||||
RTS
|
||||
.)
|
||||
|
||||
_BRS .( ; BRS xxyy 02 yy xx PC <- PC + xxyy - branch to subroutine
|
||||
RTS
|
||||
.)
|
||||
|
||||
_BRA .( ; BRA xxyy 03 yy xx PC <- PC + xxyy - branch always
|
||||
RTS
|
||||
.)
|
||||
|
||||
_BRE .( ; BRE xxyy 04 yy xx PC <- PC + xxyy - branch if Rp = Rq (after CMP)
|
||||
LDA #_F_E
|
||||
BNE _BRX
|
||||
.)
|
||||
|
||||
_BRG .( ; BRG xxyy 05 yy xx PC <- PC + xxyy - branch if Rp > Rq (after CMP)
|
||||
LDA #_F_G
|
||||
BNE _BRX
|
||||
.)
|
||||
|
||||
_BRL .( ; BRL xxyy 06 yy xx PC <- PC + xxyy - branch if Rp < Rq (after CMP)
|
||||
LDA #_F_L
|
||||
BNE _BRX
|
||||
.)
|
||||
|
||||
_BRZ .( ; BRZ xxyy 07 yy xx PC <- PC + xxyy - branch if Rr = 0.0 (after TST)
|
||||
LDA #_F_Z
|
||||
BNE _BRX
|
||||
.)
|
||||
|
||||
_BRP .( ; BRP xxyy 08 yy xx PC <- PC + xxyy - branch if Rr > 0.0 (after TST)
|
||||
LDA #_F_P
|
||||
BNE _BRX
|
||||
.)
|
||||
|
||||
_BRN .( ; BRN xxyy 09 yy xx PC <- PC + xxyy - branch if Rr < 0.0 (after TST)
|
||||
LDA #_F_N
|
||||
BNE _BRX
|
||||
.)
|
||||
|
||||
_BRO .( ; BRO xxyy 0a yy xx PC <- PC + xxyy - branch if overflow (after arithmetic operations)
|
||||
LDA #_F_O
|
||||
BNE _BRX
|
||||
.)
|
||||
|
||||
_BRU .( ; BRU xxyy 0b yy xx PC <- PC + xxyy - branch if underflow (after arithmetic operations)
|
||||
LDA #_F_U
|
||||
BNE _BRX ; (TODO) this line can probably be removed
|
||||
.)
|
||||
|
||||
_BRX .( ; generic branch testing
|
||||
AND _F ; check the bit
|
||||
BNE _BRA ; if set, branch
|
||||
INC _PCL ; not set, advance the program counter
|
||||
BNE _1
|
||||
INC _PCH
|
||||
_1 RTS
|
||||
.)
|
||||
|
||||
_CPR .( ; CPR pq 0c pq Rp <- Rq - copy register
|
||||
RTS
|
||||
.)
|
||||
|
||||
_LDI .( ; LDI pq 0d pq Rp <- (Rq:bbcc) - load indirect from memory
|
||||
RTS
|
||||
.)
|
||||
|
||||
_SVI .( ; SVI pq 0e pq (Rp:bbcc) <- Rq - save indirect to memory
|
||||
RTS
|
||||
.)
|
||||
|
||||
_CMR .( ; CMR pq 0f pq F <- Rp <=> Rq - compare registers
|
||||
RTS
|
||||
.)
|
||||
|
||||
_END_CMN_CD
|
||||
|
||||
; ROM data header
|
||||
.WORD CMN_DT, _END_CMN_DT - CMN_DT
|
||||
|
||||
; beginning of ROM data
|
||||
* = CMN_DT
|
||||
|
||||
FN_0X .WORD _ESC-1, _RTN-1, _BRS-1, _BRA-1, _BRE-1, _BRG-1, _BRL-1, _BRZ-1,
|
||||
.WORD _BRP-1, _BRN-1, _BRO-1, _BRU-1, _CPR-1, _LDI-1, _SVI-1, _CMR-1
|
||||
FN_XR .WORD _SET-1, _POP-1, _PSH-1, _EXC-1, _INR-1, _DCR-1, _TST-1,
|
||||
.WORD _DEC-1, _HEX-1, _ADD-1, _SUB-1, _MUL-1, _DIV-1, _MOD-1
|
||||
|
||||
_END_CMN_DT
|
||||
|
||||
; 6502 addresses
|
||||
.WORD ADDR, 6
|
||||
|
||||
; 6502 NMI, Reset and IRQ
|
||||
* = $FFFA
|
||||
ADDR .WORD 0, _INI, 0
|
||||
|
||||
|
||||
|
||||
|
139
common/common.h
Normal file
139
common/common.h
Normal file
@ -0,0 +1,139 @@
|
||||
#ifndef __COMMON_H
|
||||
#define __COMMON_H
|
||||
|
||||
; Using four byte quantities aabbccdd with one bit overflow/underflow, 21 bits significand, 10 bits fraction
|
||||
|
||||
; Largest value: $3fffffff or 1048575.999(9)
|
||||
; Smallest value: $c0000001 or -1048575.999(0)
|
||||
; Largest value for DEC: $3d08ffff or 999999.999(9)
|
||||
; Smallest value for DEC: $c2f70001 or -999999.999(0)
|
||||
|
||||
; Instructions
|
||||
|
||||
; SET r aabbcc.dd 1r dd cc bb aa Rr <- aabbccdd - set register
|
||||
; POP r 2r Rr <- RS - pop from stack
|
||||
; PSH r 3r RS <- Rr - push onto stack
|
||||
; EXC r 4r Rr <-> RS - exchange Rr with stack
|
||||
; INR r 5r Rr <- Rr + 1.0 - increment register
|
||||
; DCR r 6r Rr <- Rr - 1.0 - decrement register
|
||||
; TST r 7r F <- Rr <=> 0.0 - test register
|
||||
; DEC r 8r Rr <- dec(Rr) - convert Rr from hex aabbccdd to decimal #########
|
||||
; HEX r 9r Rr <- hex(Rr) - convert Rr from decimal ######### to hex aabbccdd
|
||||
; ADD r pq ar pq Rr <- Rp + Rq - addition
|
||||
; SUB r pq br pq Rr <- Rp - Rq - subtraction
|
||||
; MUL r pq cr pq Rr <- Rp * Rq - multiplication
|
||||
; DIV r pq dr pq Rr <- Rp / Rq - division
|
||||
; MOD r pq er pq Rr <- Rp % Rq - modulus
|
||||
; EXT z ... fz ... - system and user defined functions
|
||||
|
||||
; ESC 00 - escape back into regular assembler
|
||||
; RTN 01 - return from subroutine
|
||||
; BRS xxyy 02 yy xx PC <- PC + xxyy - branch to subroutine
|
||||
; BRA xxyy 03 yy xx PC <- PC + xxyy - branch always
|
||||
; BRE xxyy 04 yy xx PC <- PC + xxyy - branch if Rp = Rq (after CMR)
|
||||
; BRG xxyy 05 yy xx PC <- PC + xxyy - branch if Rp > Rq (after CMR)
|
||||
; BRL xxyy 06 yy xx PC <- PC + xxyy - branch if Rp < Rq (after CMR)
|
||||
; BRZ xxyy 07 yy xx PC <- PC + xxyy - branch if Rr = 0.0 (after TST)
|
||||
; BRP xxyy 08 yy xx PC <- PC + xxyy - branch if Rr > 0.0 (after TST)
|
||||
; BRN xxyy 09 yy xx PC <- PC + xxyy - branch if Rr < 0.0 (after TST)
|
||||
; BRO xxyy 0a yy xx PC <- PC + xxyy - branch if overflow (after arithmetic operations)
|
||||
; BRU xxyy 0b yy xx PC <- PC + xxyy - branch if underflow (after arithmetic operations)
|
||||
; CPR pq 0c pq Rp <- Rq - copy register
|
||||
; LDI pq 0d pq Rp <- (int(Rq)) - load indirect from memory
|
||||
; SVI pq 0e pq (int(Rp)) <- Rq - save indirect to memory
|
||||
; CMR pq 0f pq F <- Rp <=> Rq - compare registers
|
||||
|
||||
; 64 bytes in page zero for common registers
|
||||
_R0 = $C0
|
||||
_R1 = _R0 + 4
|
||||
_R2 = _R1 + 4
|
||||
_R3 = _R2 + 4
|
||||
_R4 = _R3 + 4
|
||||
_R5 = _R4 + 4
|
||||
_R6 = _R5 + 4
|
||||
_R7 = _R6 + 4
|
||||
_R8 = _R7 + 4
|
||||
_R9 = _R8 + 4
|
||||
_RA = _R9 + 4
|
||||
_RB = _RA + 4
|
||||
_RC = _RB + 4 ; workspace for arithmetic operations
|
||||
_RD = _RC + 4 ; as above and for EXC
|
||||
_RE = _RD + 4 ; register E maintains common status
|
||||
_RF = _RE + 4 ; register F saves/restores processor status
|
||||
|
||||
; register E maintains common status
|
||||
; (dd cc bb aa) aa: index for register stack RS / ccbb: program counter PC / dd: flags F UONPZLGE
|
||||
_RSI = _RE ; register stack index
|
||||
_PCL = _RSI + 1 ; program counter low
|
||||
_PCH = _PCL + 1 ; program counter high
|
||||
_F = _PCH + 1 ; flags
|
||||
_PC = _PCL ; program counter
|
||||
|
||||
; bits for flags
|
||||
_F_E = 1 ; if Rp = Rq (after CMP)
|
||||
_F_G = 2 ; if Rp > Rq (after CMP)
|
||||
_F_L = 4 ; if Rp < Rq (after CMP)
|
||||
_F_Z = 8 ; if Rr = 0.0 (after TST)
|
||||
_F_P = 16 ; if Rr > 0.0 (after TST)
|
||||
_F_N = 32 ; if Rr < 0.0 (after TST)
|
||||
_F_O = 64 ; if overflow (after arithmetic operations)
|
||||
_F_U = 128 ; if underflow (after arithmetic operations)
|
||||
|
||||
; register F saves/restores processor status
|
||||
; (dd cc bb aa) aa: accumulator, bb: index X, cc: index Y, dd: processor status
|
||||
_ACC = _RF ; saved accumulator to restore
|
||||
_IDX = _ACC + 1 ; saved index X to restore
|
||||
_IDY = _IDX + 1 ; saved index Y to restore
|
||||
_PS = _IDY + 1 ; saved processor status to restore
|
||||
|
||||
; 256 bytes of page two
|
||||
_RS = $200 ; register stack
|
||||
|
||||
; 64 bytes of page three
|
||||
FN_FX = $300 ; list of system and user functions
|
||||
|
||||
; function constants
|
||||
_ESC_C = $00
|
||||
_RTN_C = $01
|
||||
_BRS_C = $02
|
||||
_BRA_C = $03
|
||||
_BRE_C = $04
|
||||
_BRG_C = $05
|
||||
_BRL_C = $06
|
||||
_BRZ_C = $07
|
||||
_BRP_C = $08
|
||||
_BRN_C = $09
|
||||
_BRO_C = $0a
|
||||
_BRU_C = $0b
|
||||
_CPR_C = $0c
|
||||
_LDI_C = $0d
|
||||
_SVI_C = $0e
|
||||
_CMR_C = $0f
|
||||
|
||||
_SET_C = $10
|
||||
_POP_C = $20
|
||||
_PSH_C = $30
|
||||
_EXC_C = $40
|
||||
_INR_C = $50
|
||||
_DCR_C = $60
|
||||
_TST_C = $70
|
||||
_DEC_C = $80
|
||||
_HEX_C = $90
|
||||
_ADD_C = $a0
|
||||
_SUB_C = $b0
|
||||
_MUL_C = $c0
|
||||
_DIV_C = $d0
|
||||
_MOD_C = $e0
|
||||
_EXT_C = $f0
|
||||
|
||||
; common constants
|
||||
|
||||
; plus and minus 1 for increment and decrement
|
||||
_PLS_1 = %00000100 ; i.e. the $04 part of $00000400
|
||||
_MNS_1 = %11111100 ; i.e. the $FC part of $FFFFFC00
|
||||
|
||||
; masks for overflow and unerflow
|
||||
_MSK_O = %11000000 ; mask for overflow
|
||||
_MSK_U = %11000000 ; mask for underflow
|
||||
|
||||
#endif /* __COMMON_H */
|
98
common/macros.h
Normal file
98
common/macros.h
Normal file
@ -0,0 +1,98 @@
|
||||
#include "common.h"
|
||||
|
||||
#ifndef MACROS_H
|
||||
#define MACROS_H
|
||||
|
||||
; registers
|
||||
#define R0 0
|
||||
#define R1 1
|
||||
#define R2 2
|
||||
#define R3 3
|
||||
#define R4 4
|
||||
#define R5 5
|
||||
#define R6 6
|
||||
#define R7 7
|
||||
#define R8 8
|
||||
#define R9 9
|
||||
#define RA 10
|
||||
#define RB 11
|
||||
#define RC 12
|
||||
#define RD 13
|
||||
#define RE 14
|
||||
#define RF 15
|
||||
|
||||
; system functions
|
||||
#define S0 0
|
||||
#define S1 1
|
||||
#define S2 2
|
||||
#define S3 3
|
||||
#define S4 4
|
||||
#define S5 5
|
||||
#define S6 6
|
||||
#define S7 7
|
||||
#define S8 8
|
||||
#define S9 9
|
||||
#define SA 10
|
||||
#define SB 11
|
||||
#define SC 12
|
||||
#define SD 13
|
||||
#define SE 14
|
||||
#define SF 15
|
||||
|
||||
; user functions
|
||||
#define U0 15
|
||||
#define U1 14
|
||||
#define U2 13
|
||||
#define U3 12
|
||||
#define U4 11
|
||||
#define U5 10
|
||||
#define U6 9
|
||||
#define U7 8
|
||||
#define U8 7
|
||||
#define U9 6
|
||||
#define UA 5
|
||||
#define UB 4
|
||||
#define UC 3
|
||||
#define UD 2
|
||||
#define UE 1
|
||||
#define UF 0
|
||||
|
||||
; macros
|
||||
#define ESC .BYTE _ESC_C
|
||||
#define RTN .BYTE _RTN_C
|
||||
#define BRS(o) .BYTE _BRS_C, <(o - * - 3), >(o - * - 3)
|
||||
#define BRA(o) .BYTE _BRA_C, <(o - * - 3), >(o - * - 3)
|
||||
#define BRE(o) .BYTE _BRE_C, <(o - * - 3), >(o - * - 3)
|
||||
#define BRG(o) .BYTE _BRG_C, <(o - * - 3), >(o - * - 3)
|
||||
#define BRL(o) .BYTE _BRL_C, <(o - * - 3), >(o - * - 3)
|
||||
#define BRZ(o) .BYTE _BRZ_C, <(o - * - 3), >(o - * - 3)
|
||||
#define BRP(o) .BYTE _BRP_C, <(o - * - 3), >(o - * - 3)
|
||||
#define BRN(o) .BYTE _BRN_C, <(o - * - 3), >(o - * - 3)
|
||||
#define BRO(o) .BYTE _BRO_C, <(o - * - 3), >(o - * - 3)
|
||||
#define BRU(o) .BYTE _BRU_C, <(o - * - 3), >(o - * - 3)
|
||||
#define CPR(p, q) .BYTE _CPR_C, p * 16 + q
|
||||
#define LDI(p, q) .BYTE _LDI_C, p * 16 + q
|
||||
#define SVI(p, q) .BYTE _SVI_C, p * 16 + q
|
||||
#define CMR(p, q) .BYTE _CMR_C, p * 16 + q
|
||||
#define SET(r, v) .BYTE _SET_C + r, _SET_V(#v)
|
||||
#define POP(r) .BYTE _POP_C + r
|
||||
#define PSH(r) .BYTE _PSH_C + r
|
||||
#define EXC(r) .BYTE _EXC_C + r
|
||||
#define INR(r) .BYTE _INR_C + r
|
||||
#define DCR(r) .BYTE _DCR_C + r
|
||||
#define TST(r) .BYTE _TST_C + r
|
||||
#define DEC(r) .BYTE _DEC_C + r
|
||||
#define HEX(r) .BYTE _HEX_C + r
|
||||
#define ADD(r) .BYTE _ADD_C + r
|
||||
#define SUB(r, p, q) .BYTE _SUB_C + r, p * 16 + q
|
||||
#define MUL(r, p, q) .BYTE _MUL_C + r, p * 16 + q
|
||||
#define DIV(r, p, q) .BYTE _DIV_C + r, p * 16 + q
|
||||
#define MOD(r, p, q) .BYTE _MOD_C + r, p * 16 + q
|
||||
#define EXT(f) .BYTE _EXT_C + f
|
||||
|
||||
; header, begin and end of blocks
|
||||
#define HDR(a) .WORD a, _END_##a - a:* = * - 4:a .(
|
||||
#define BGN(a) a .(
|
||||
#define END(a) .):_END_##a
|
||||
|
||||
#endif // MACROS_H
|
36
common/page6.src
Normal file
36
common/page6.src
Normal file
@ -0,0 +1,36 @@
|
||||
#include "rom.h"
|
||||
#include "macros.h"
|
||||
|
||||
* = $600
|
||||
|
||||
HDR(DEMO)
|
||||
CMN
|
||||
SET(R0, +1048575.999)
|
||||
SET(R1, -1048575.999)
|
||||
SET(R2, 0.0)
|
||||
SET(R3, 0.0)
|
||||
INR(R0)
|
||||
DCR(R1)
|
||||
INR(R2)
|
||||
DCR(R3)
|
||||
INR(R0)
|
||||
DCR(R1)
|
||||
ESC
|
||||
BRK
|
||||
|
||||
BGN(FACTORIAL)
|
||||
SET(R1, $10.4456)
|
||||
SET(R2, 1)
|
||||
HEX(R1)
|
||||
MOD(R3, R1, R2)
|
||||
SUB(R1, R1, R3)
|
||||
_1 TST(R1)
|
||||
BRZ(_2)
|
||||
MUL(R2, R2, R1)
|
||||
DEC(R1)
|
||||
BRA(_1)
|
||||
_2 EXT(S0)
|
||||
RTN
|
||||
END(FACTORIAL)
|
||||
|
||||
END(DEMO)
|
11
common/rom.h
Normal file
11
common/rom.h
Normal file
@ -0,0 +1,11 @@
|
||||
#ifndef __ROM_H
|
||||
#define __ROM_H
|
||||
|
||||
; ROM addresses
|
||||
CMN_CD = $F800
|
||||
CMN_DT = $FF00
|
||||
|
||||
; macros
|
||||
#define CMN JSR CMN_CD
|
||||
|
||||
#endif /* __ROM_H */
|
3
emulator/Makefile
Normal file
3
emulator/Makefile
Normal file
@ -0,0 +1,3 @@
|
||||
emulator: emulator.h emulator.c main.c
|
||||
gcc -o emulator emulator.c main.c
|
||||
|
964
emulator/emulator.c
Normal file
964
emulator/emulator.c
Normal file
@ -0,0 +1,964 @@
|
||||
/* Fake6502 CPU emulator core v1.1 *******************
|
||||
* (c)2011 Mike Chambers (miker00lz@gmail.com) *
|
||||
*****************************************************
|
||||
* v1.1 - Small bugfix in BIT opcode, but it was the *
|
||||
* difference between a few games in my NES *
|
||||
* emulator working and being broken! *
|
||||
* I went through the rest carefully again *
|
||||
* after fixing it just to make sure I didn't *
|
||||
* have any other typos! (Dec. 17, 2011) *
|
||||
* *
|
||||
* v1.0 - First release (Nov. 24, 2011) *
|
||||
*****************************************************
|
||||
* LICENSE: This source code is released into the *
|
||||
* public domain, but if you use it please do give *
|
||||
* credit. I put a lot of effort into writing this! *
|
||||
* *
|
||||
*****************************************************
|
||||
* Fake6502 is a MOS Technology 6502 CPU emulation *
|
||||
* engine in C. It was written as part of a Nintendo *
|
||||
* Entertainment System emulator I've been writing. *
|
||||
* *
|
||||
* A couple important things to know about are two *
|
||||
* defines in the code. One is "UNDOCUMENTED" which, *
|
||||
* when defined, allows Fake6502 to compile with *
|
||||
* full support for the more predictable *
|
||||
* undocumented instructions of the 6502. If it is *
|
||||
* undefined, undocumented opcodes just act as NOPs. *
|
||||
* *
|
||||
* The other define is "NES_CPU", which causes the *
|
||||
* code to compile without support for binary-coded *
|
||||
* decimal (BCD) support for the ADC and SBC *
|
||||
* opcodes. The Ricoh 2A03 CPU in the NES does not *
|
||||
* support BCD, but is otherwise identical to the *
|
||||
* standard MOS 6502. (Note that this define is *
|
||||
* enabled in this file if you haven't changed it *
|
||||
* yourself. If you're not emulating a NES, you *
|
||||
* should comment it out.) *
|
||||
* *
|
||||
* If you do discover an error in timing accuracy, *
|
||||
* or operation in general please e-mail me at the *
|
||||
* address above so that I can fix it. Thank you! *
|
||||
* *
|
||||
*****************************************************
|
||||
* Usage: *
|
||||
* *
|
||||
* Fake6502 requires you to provide two external *
|
||||
* functions: *
|
||||
* *
|
||||
* uint8_t read6502(uint16_t address) *
|
||||
* void write6502(uint16_t address, uint8_t value) *
|
||||
* *
|
||||
* You may optionally pass Fake6502 the pointer to a *
|
||||
* function which you want to be called after every *
|
||||
* emulated instruction. This function should be a *
|
||||
* void with no parameters expected to be passed to *
|
||||
* it. *
|
||||
* *
|
||||
* This can be very useful. For example, in a NES *
|
||||
* emulator, you check the number of clock ticks *
|
||||
* that have passed so you can know when to handle *
|
||||
* APU events. *
|
||||
* *
|
||||
* To pass Fake6502 this pointer, use the *
|
||||
* hookexternal(void *funcptr) function provided. *
|
||||
* *
|
||||
* To disable the hook later, pass NULL to it. *
|
||||
*****************************************************
|
||||
* Useful functions in this emulator: *
|
||||
* *
|
||||
* void reset6502() *
|
||||
* - Call this once before you begin execution. *
|
||||
* *
|
||||
* void exec6502(uint32_t tickcount) *
|
||||
* - Execute 6502 code up to the next specified *
|
||||
* count of clock ticks. *
|
||||
* *
|
||||
* void step6502() *
|
||||
* - Execute a single instrution. *
|
||||
* *
|
||||
* void irq6502() *
|
||||
* - Trigger a hardware IRQ in the 6502 core. *
|
||||
* *
|
||||
* void nmi6502() *
|
||||
* - Trigger an NMI in the 6502 core. *
|
||||
* *
|
||||
* void hookexternal(void *funcptr) *
|
||||
* - Pass a pointer to a void function taking no *
|
||||
* parameters. This will cause Fake6502 to call *
|
||||
* that function once after each emulated *
|
||||
* instruction. *
|
||||
* *
|
||||
*****************************************************
|
||||
* Useful variables in this emulator: *
|
||||
* *
|
||||
* uint32_t clockticks6502 *
|
||||
* - A running total of the emulated cycle count. *
|
||||
* *
|
||||
* uint32_t instructions *
|
||||
* - A running total of the total emulated *
|
||||
* instruction count. This is not related to *
|
||||
* clock cycle timing. *
|
||||
* *
|
||||
*****************************************************/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "emulator.h"
|
||||
|
||||
#define FLAG_CARRY 0x01
|
||||
#define FLAG_ZERO 0x02
|
||||
#define FLAG_INTERRUPT 0x04
|
||||
#define FLAG_DECIMAL 0x08
|
||||
#define FLAG_BREAK 0x10
|
||||
#define FLAG_CONSTANT 0x20
|
||||
#define FLAG_OVERFLOW 0x40
|
||||
#define FLAG_SIGN 0x80
|
||||
|
||||
#define BASE_STACK 0x100
|
||||
|
||||
#define saveaccum(n) a = (uint8_t)((n) & 0x00FF)
|
||||
|
||||
|
||||
//flag modifier macros
|
||||
#define setcarry() status |= FLAG_CARRY
|
||||
#define clearcarry() status &= (~FLAG_CARRY)
|
||||
#define setzero() status |= FLAG_ZERO
|
||||
#define clearzero() status &= (~FLAG_ZERO)
|
||||
#define setinterrupt() status |= FLAG_INTERRUPT
|
||||
#define clearinterrupt() status &= (~FLAG_INTERRUPT)
|
||||
#define setdecimal() status |= FLAG_DECIMAL
|
||||
#define cleardecimal() status &= (~FLAG_DECIMAL)
|
||||
#define setoverflow() status |= FLAG_OVERFLOW
|
||||
#define clearoverflow() status &= (~FLAG_OVERFLOW)
|
||||
#define setsign() status |= FLAG_SIGN
|
||||
#define clearsign() status &= (~FLAG_SIGN)
|
||||
|
||||
|
||||
//flag calculation macros
|
||||
#define zerocalc(n) {\
|
||||
if ((n) & 0x00FF) clearzero();\
|
||||
else setzero();\
|
||||
}
|
||||
|
||||
#define signcalc(n) {\
|
||||
if ((n) & 0x0080) setsign();\
|
||||
else clearsign();\
|
||||
}
|
||||
|
||||
#define carrycalc(n) {\
|
||||
if ((n) & 0xFF00) setcarry();\
|
||||
else clearcarry();\
|
||||
}
|
||||
|
||||
#define overflowcalc(n, m, o) { /* n = result, m = accumulator, o = memory */ \
|
||||
if (((n) ^ (uint16_t)(m)) & ((n) ^ (o)) & 0x0080) setoverflow();\
|
||||
else clearoverflow();\
|
||||
}
|
||||
|
||||
|
||||
//6502 CPU registers
|
||||
uint16_t pc;
|
||||
uint8_t sp, a, x, y, status;
|
||||
|
||||
|
||||
//helper variables
|
||||
uint32_t instructions = 0; //keep track of total instructions executed
|
||||
uint32_t clockticks6502 = 0, clockgoal6502 = 0;
|
||||
uint16_t oldpc, ea, reladdr, value, result;
|
||||
uint8_t opcode, oldstatus;
|
||||
|
||||
//externally supplied functions
|
||||
extern uint8_t read6502(uint16_t address);
|
||||
extern void write6502(uint16_t address, uint8_t value);
|
||||
|
||||
//a few general functions used by various other functions
|
||||
void push16(uint16_t pushval) {
|
||||
write6502(BASE_STACK + sp, (pushval >> 8) & 0xFF);
|
||||
write6502(BASE_STACK + ((sp - 1) & 0xFF), pushval & 0xFF);
|
||||
sp -= 2;
|
||||
}
|
||||
|
||||
void push8(uint8_t pushval) {
|
||||
write6502(BASE_STACK + sp--, pushval);
|
||||
}
|
||||
|
||||
uint16_t pull16() {
|
||||
uint16_t temp16;
|
||||
temp16 = read6502(BASE_STACK + ((sp + 1) & 0xFF)) | ((uint16_t)read6502(BASE_STACK + ((sp + 2) & 0xFF)) << 8);
|
||||
sp += 2;
|
||||
return(temp16);
|
||||
}
|
||||
|
||||
uint8_t pull8() {
|
||||
return (read6502(BASE_STACK + ++sp));
|
||||
}
|
||||
|
||||
void reset6502() {
|
||||
pc = (uint16_t)read6502(0xFFFC) | ((uint16_t)read6502(0xFFFD) << 8);
|
||||
a = 0;
|
||||
x = 0;
|
||||
y = 0;
|
||||
sp = 0xFD;
|
||||
status |= FLAG_CONSTANT;
|
||||
}
|
||||
|
||||
|
||||
static void (*addrtable[256])();
|
||||
static void (*optable[256])();
|
||||
uint8_t penaltyop, penaltyaddr;
|
||||
|
||||
//addressing mode functions, calculates effective addresses
|
||||
static void imp() { //implied
|
||||
}
|
||||
|
||||
static void acc() { //accumulator
|
||||
}
|
||||
|
||||
static void imm() { //immediate
|
||||
ea = pc++;
|
||||
}
|
||||
|
||||
static void zp() { //zero-page
|
||||
ea = (uint16_t)read6502((uint16_t)pc++);
|
||||
}
|
||||
|
||||
static void zpx() { //zero-page,X
|
||||
ea = ((uint16_t)read6502((uint16_t)pc++) + (uint16_t)x) & 0xFF; //zero-page wraparound
|
||||
}
|
||||
|
||||
static void zpy() { //zero-page,Y
|
||||
ea = ((uint16_t)read6502((uint16_t)pc++) + (uint16_t)y) & 0xFF; //zero-page wraparound
|
||||
}
|
||||
|
||||
static void rel() { //relative for branch ops (8-bit immediate value, sign-extended)
|
||||
reladdr = (uint16_t)read6502(pc++);
|
||||
if (reladdr & 0x80) reladdr |= 0xFF00;
|
||||
}
|
||||
|
||||
static void abso() { //absolute
|
||||
ea = (uint16_t)read6502(pc) | ((uint16_t)read6502(pc+1) << 8);
|
||||
pc += 2;
|
||||
}
|
||||
|
||||
static void absx() { //absolute,X
|
||||
uint16_t startpage;
|
||||
ea = ((uint16_t)read6502(pc) | ((uint16_t)read6502(pc+1) << 8));
|
||||
startpage = ea & 0xFF00;
|
||||
ea += (uint16_t)x;
|
||||
|
||||
if (startpage != (ea & 0xFF00)) { //one cycle penlty for page-crossing on some opcodes
|
||||
penaltyaddr = 1;
|
||||
}
|
||||
|
||||
pc += 2;
|
||||
}
|
||||
|
||||
static void absy() { //absolute,Y
|
||||
uint16_t startpage;
|
||||
ea = ((uint16_t)read6502(pc) | ((uint16_t)read6502(pc+1) << 8));
|
||||
startpage = ea & 0xFF00;
|
||||
ea += (uint16_t)y;
|
||||
|
||||
if (startpage != (ea & 0xFF00)) { //one cycle penlty for page-crossing on some opcodes
|
||||
penaltyaddr = 1;
|
||||
}
|
||||
|
||||
pc += 2;
|
||||
}
|
||||
|
||||
static void ind() { //indirect
|
||||
uint16_t eahelp, eahelp2;
|
||||
eahelp = (uint16_t)read6502(pc) | (uint16_t)((uint16_t)read6502(pc+1) << 8);
|
||||
eahelp2 = (eahelp & 0xFF00) | ((eahelp + 1) & 0x00FF); //replicate 6502 page-boundary wraparound bug
|
||||
ea = (uint16_t)read6502(eahelp) | ((uint16_t)read6502(eahelp2) << 8);
|
||||
pc += 2;
|
||||
}
|
||||
|
||||
static void indx() { // (indirect,X)
|
||||
uint16_t eahelp;
|
||||
eahelp = (uint16_t)(((uint16_t)read6502(pc++) + (uint16_t)x) & 0xFF); //zero-page wraparound for table pointer
|
||||
ea = (uint16_t)read6502(eahelp & 0x00FF) | ((uint16_t)read6502((eahelp+1) & 0x00FF) << 8);
|
||||
}
|
||||
|
||||
static void indy() { // (indirect),Y
|
||||
uint16_t eahelp, eahelp2, startpage;
|
||||
eahelp = (uint16_t)read6502(pc++);
|
||||
eahelp2 = (eahelp & 0xFF00) | ((eahelp + 1) & 0x00FF); //zero-page wraparound
|
||||
ea = (uint16_t)read6502(eahelp) | ((uint16_t)read6502(eahelp2) << 8);
|
||||
startpage = ea & 0xFF00;
|
||||
ea += (uint16_t)y;
|
||||
|
||||
if (startpage != (ea & 0xFF00)) { //one cycle penlty for page-crossing on some opcodes
|
||||
penaltyaddr = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static uint16_t getvalue() {
|
||||
if (addrtable[opcode] == acc) return((uint16_t)a);
|
||||
else return((uint16_t)read6502(ea));
|
||||
}
|
||||
|
||||
static uint16_t getvalue16() {
|
||||
return((uint16_t)read6502(ea) | ((uint16_t)read6502(ea+1) << 8));
|
||||
}
|
||||
|
||||
static void putvalue(uint16_t saveval) {
|
||||
if (addrtable[opcode] == acc) a = (uint8_t)(saveval & 0x00FF);
|
||||
else write6502(ea, (saveval & 0x00FF));
|
||||
}
|
||||
|
||||
|
||||
//instruction handler functions
|
||||
static void adc() {
|
||||
penaltyop = 1;
|
||||
value = getvalue();
|
||||
result = (uint16_t)a + value + (uint16_t)(status & FLAG_CARRY);
|
||||
|
||||
carrycalc(result);
|
||||
zerocalc(result);
|
||||
overflowcalc(result, a, value);
|
||||
signcalc(result);
|
||||
|
||||
#ifndef NES_CPU
|
||||
if (status & FLAG_DECIMAL) {
|
||||
clearcarry();
|
||||
|
||||
if ((a & 0x0F) > 0x09) {
|
||||
a += 0x06;
|
||||
}
|
||||
if ((a & 0xF0) > 0x90) {
|
||||
a += 0x60;
|
||||
setcarry();
|
||||
}
|
||||
|
||||
clockticks6502++;
|
||||
}
|
||||
#endif
|
||||
|
||||
saveaccum(result);
|
||||
}
|
||||
|
||||
static void and() {
|
||||
penaltyop = 1;
|
||||
value = getvalue();
|
||||
result = (uint16_t)a & value;
|
||||
|
||||
zerocalc(result);
|
||||
signcalc(result);
|
||||
|
||||
saveaccum(result);
|
||||
}
|
||||
|
||||
static void asl() {
|
||||
value = getvalue();
|
||||
result = value << 1;
|
||||
|
||||
carrycalc(result);
|
||||
zerocalc(result);
|
||||
signcalc(result);
|
||||
|
||||
putvalue(result);
|
||||
}
|
||||
|
||||
static void bcc() {
|
||||
if ((status & FLAG_CARRY) == 0) {
|
||||
oldpc = pc;
|
||||
pc += reladdr;
|
||||
if ((oldpc & 0xFF00) != (pc & 0xFF00)) clockticks6502 += 2; //check if jump crossed a page boundary
|
||||
else clockticks6502++;
|
||||
}
|
||||
}
|
||||
|
||||
static void bcs() {
|
||||
if ((status & FLAG_CARRY) == FLAG_CARRY) {
|
||||
oldpc = pc;
|
||||
pc += reladdr;
|
||||
if ((oldpc & 0xFF00) != (pc & 0xFF00)) clockticks6502 += 2; //check if jump crossed a page boundary
|
||||
else clockticks6502++;
|
||||
}
|
||||
}
|
||||
|
||||
static void beq() {
|
||||
if ((status & FLAG_ZERO) == FLAG_ZERO) {
|
||||
oldpc = pc;
|
||||
pc += reladdr;
|
||||
if ((oldpc & 0xFF00) != (pc & 0xFF00)) clockticks6502 += 2; //check if jump crossed a page boundary
|
||||
else clockticks6502++;
|
||||
}
|
||||
}
|
||||
|
||||
static void bit() {
|
||||
value = getvalue();
|
||||
result = (uint16_t)a & value;
|
||||
|
||||
zerocalc(result);
|
||||
status = (status & 0x3F) | (uint8_t)(value & 0xC0);
|
||||
}
|
||||
|
||||
static void bmi() {
|
||||
if ((status & FLAG_SIGN) == FLAG_SIGN) {
|
||||
oldpc = pc;
|
||||
pc += reladdr;
|
||||
if ((oldpc & 0xFF00) != (pc & 0xFF00)) clockticks6502 += 2; //check if jump crossed a page boundary
|
||||
else clockticks6502++;
|
||||
}
|
||||
}
|
||||
|
||||
static void bne() {
|
||||
if ((status & FLAG_ZERO) == 0) {
|
||||
oldpc = pc;
|
||||
pc += reladdr;
|
||||
if ((oldpc & 0xFF00) != (pc & 0xFF00)) clockticks6502 += 2; //check if jump crossed a page boundary
|
||||
else clockticks6502++;
|
||||
}
|
||||
}
|
||||
|
||||
static void bpl() {
|
||||
if ((status & FLAG_SIGN) == 0) {
|
||||
oldpc = pc;
|
||||
pc += reladdr;
|
||||
if ((oldpc & 0xFF00) != (pc & 0xFF00)) clockticks6502 += 2; //check if jump crossed a page boundary
|
||||
else clockticks6502++;
|
||||
}
|
||||
}
|
||||
|
||||
static void brk() {
|
||||
pc++;
|
||||
push16(pc); //push next instruction address onto stack
|
||||
push8(status | FLAG_BREAK); //push CPU status to stack
|
||||
setinterrupt(); //set interrupt flag
|
||||
pc = (uint16_t)read6502(0xFFFE) | ((uint16_t)read6502(0xFFFF) << 8);
|
||||
}
|
||||
|
||||
static void bvc() {
|
||||
if ((status & FLAG_OVERFLOW) == 0) {
|
||||
oldpc = pc;
|
||||
pc += reladdr;
|
||||
if ((oldpc & 0xFF00) != (pc & 0xFF00)) clockticks6502 += 2; //check if jump crossed a page boundary
|
||||
else clockticks6502++;
|
||||
}
|
||||
}
|
||||
|
||||
static void bvs() {
|
||||
if ((status & FLAG_OVERFLOW) == FLAG_OVERFLOW) {
|
||||
oldpc = pc;
|
||||
pc += reladdr;
|
||||
if ((oldpc & 0xFF00) != (pc & 0xFF00)) clockticks6502 += 2; //check if jump crossed a page boundary
|
||||
else clockticks6502++;
|
||||
}
|
||||
}
|
||||
|
||||
static void clc() {
|
||||
clearcarry();
|
||||
}
|
||||
|
||||
static void cld() {
|
||||
cleardecimal();
|
||||
}
|
||||
|
||||
static void cli() {
|
||||
clearinterrupt();
|
||||
}
|
||||
|
||||
static void clv() {
|
||||
clearoverflow();
|
||||
}
|
||||
|
||||
static void cmp() {
|
||||
penaltyop = 1;
|
||||
value = getvalue();
|
||||
result = (uint16_t)a - value;
|
||||
|
||||
if (a >= (uint8_t)(value & 0x00FF)) setcarry();
|
||||
else clearcarry();
|
||||
if (a == (uint8_t)(value & 0x00FF)) setzero();
|
||||
else clearzero();
|
||||
signcalc(result);
|
||||
}
|
||||
|
||||
static void cpx() {
|
||||
value = getvalue();
|
||||
result = (uint16_t)x - value;
|
||||
|
||||
if (x >= (uint8_t)(value & 0x00FF)) setcarry();
|
||||
else clearcarry();
|
||||
if (x == (uint8_t)(value & 0x00FF)) setzero();
|
||||
else clearzero();
|
||||
signcalc(result);
|
||||
}
|
||||
|
||||
static void cpy() {
|
||||
value = getvalue();
|
||||
result = (uint16_t)y - value;
|
||||
|
||||
if (y >= (uint8_t)(value & 0x00FF)) setcarry();
|
||||
else clearcarry();
|
||||
if (y == (uint8_t)(value & 0x00FF)) setzero();
|
||||
else clearzero();
|
||||
signcalc(result);
|
||||
}
|
||||
|
||||
static void dec() {
|
||||
value = getvalue();
|
||||
result = value - 1;
|
||||
|
||||
zerocalc(result);
|
||||
signcalc(result);
|
||||
|
||||
putvalue(result);
|
||||
}
|
||||
|
||||
static void dex() {
|
||||
x--;
|
||||
|
||||
zerocalc(x);
|
||||
signcalc(x);
|
||||
}
|
||||
|
||||
static void dey() {
|
||||
y--;
|
||||
|
||||
zerocalc(y);
|
||||
signcalc(y);
|
||||
}
|
||||
|
||||
static void eor() {
|
||||
penaltyop = 1;
|
||||
value = getvalue();
|
||||
result = (uint16_t)a ^ value;
|
||||
|
||||
zerocalc(result);
|
||||
signcalc(result);
|
||||
|
||||
saveaccum(result);
|
||||
}
|
||||
|
||||
static void inc() {
|
||||
value = getvalue();
|
||||
result = value + 1;
|
||||
|
||||
zerocalc(result);
|
||||
signcalc(result);
|
||||
|
||||
putvalue(result);
|
||||
}
|
||||
|
||||
static void inx() {
|
||||
x++;
|
||||
|
||||
zerocalc(x);
|
||||
signcalc(x);
|
||||
}
|
||||
|
||||
static void iny() {
|
||||
y++;
|
||||
|
||||
zerocalc(y);
|
||||
signcalc(y);
|
||||
}
|
||||
|
||||
static void jmp() {
|
||||
pc = ea;
|
||||
}
|
||||
|
||||
static void jsr() {
|
||||
push16(pc - 1);
|
||||
pc = ea;
|
||||
}
|
||||
|
||||
static void lda() {
|
||||
penaltyop = 1;
|
||||
value = getvalue();
|
||||
a = (uint8_t)(value & 0x00FF);
|
||||
|
||||
zerocalc(a);
|
||||
signcalc(a);
|
||||
}
|
||||
|
||||
static void ldx() {
|
||||
penaltyop = 1;
|
||||
value = getvalue();
|
||||
x = (uint8_t)(value & 0x00FF);
|
||||
|
||||
zerocalc(x);
|
||||
signcalc(x);
|
||||
}
|
||||
|
||||
static void ldy() {
|
||||
penaltyop = 1;
|
||||
value = getvalue();
|
||||
y = (uint8_t)(value & 0x00FF);
|
||||
|
||||
zerocalc(y);
|
||||
signcalc(y);
|
||||
}
|
||||
|
||||
static void lsr() {
|
||||
value = getvalue();
|
||||
result = value >> 1;
|
||||
|
||||
if (value & 1) setcarry();
|
||||
else clearcarry();
|
||||
zerocalc(result);
|
||||
signcalc(result);
|
||||
|
||||
putvalue(result);
|
||||
}
|
||||
|
||||
static void nop() {
|
||||
switch (opcode) {
|
||||
case 0x1C:
|
||||
case 0x3C:
|
||||
case 0x5C:
|
||||
case 0x7C:
|
||||
case 0xDC:
|
||||
case 0xFC:
|
||||
penaltyop = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void ora() {
|
||||
penaltyop = 1;
|
||||
value = getvalue();
|
||||
result = (uint16_t)a | value;
|
||||
|
||||
zerocalc(result);
|
||||
signcalc(result);
|
||||
|
||||
saveaccum(result);
|
||||
}
|
||||
|
||||
static void pha() {
|
||||
push8(a);
|
||||
}
|
||||
|
||||
static void php() {
|
||||
push8(status | FLAG_BREAK);
|
||||
}
|
||||
|
||||
static void pla() {
|
||||
a = pull8();
|
||||
|
||||
zerocalc(a);
|
||||
signcalc(a);
|
||||
}
|
||||
|
||||
static void plp() {
|
||||
status = pull8() | FLAG_CONSTANT;
|
||||
}
|
||||
|
||||
static void rol() {
|
||||
value = getvalue();
|
||||
result = (value << 1) | (status & FLAG_CARRY);
|
||||
|
||||
carrycalc(result);
|
||||
zerocalc(result);
|
||||
signcalc(result);
|
||||
|
||||
putvalue(result);
|
||||
}
|
||||
|
||||
static void ror() {
|
||||
value = getvalue();
|
||||
result = (value >> 1) | ((status & FLAG_CARRY) << 7);
|
||||
|
||||
if (value & 1) setcarry();
|
||||
else clearcarry();
|
||||
zerocalc(result);
|
||||
signcalc(result);
|
||||
|
||||
putvalue(result);
|
||||
}
|
||||
|
||||
static void rti() {
|
||||
status = pull8();
|
||||
value = pull16();
|
||||
pc = value;
|
||||
}
|
||||
|
||||
static void rts() {
|
||||
value = pull16();
|
||||
pc = value + 1;
|
||||
}
|
||||
|
||||
static void sbc() {
|
||||
penaltyop = 1;
|
||||
value = getvalue() ^ 0x00FF;
|
||||
result = (uint16_t)a + value + (uint16_t)(status & FLAG_CARRY);
|
||||
|
||||
carrycalc(result);
|
||||
zerocalc(result);
|
||||
overflowcalc(result, a, value);
|
||||
signcalc(result);
|
||||
|
||||
#ifndef NES_CPU
|
||||
if (status & FLAG_DECIMAL) {
|
||||
clearcarry();
|
||||
|
||||
a -= 0x66;
|
||||
if ((a & 0x0F) > 0x09) {
|
||||
a += 0x06;
|
||||
}
|
||||
if ((a & 0xF0) > 0x90) {
|
||||
a += 0x60;
|
||||
setcarry();
|
||||
}
|
||||
|
||||
clockticks6502++;
|
||||
}
|
||||
#endif
|
||||
|
||||
saveaccum(result);
|
||||
}
|
||||
|
||||
static void sec() {
|
||||
setcarry();
|
||||
}
|
||||
|
||||
static void sed() {
|
||||
setdecimal();
|
||||
}
|
||||
|
||||
static void sei() {
|
||||
setinterrupt();
|
||||
}
|
||||
|
||||
static void sta() {
|
||||
putvalue(a);
|
||||
}
|
||||
|
||||
static void stx() {
|
||||
putvalue(x);
|
||||
}
|
||||
|
||||
static void sty() {
|
||||
putvalue(y);
|
||||
}
|
||||
|
||||
static void tax() {
|
||||
x = a;
|
||||
|
||||
zerocalc(x);
|
||||
signcalc(x);
|
||||
}
|
||||
|
||||
static void tay() {
|
||||
y = a;
|
||||
|
||||
zerocalc(y);
|
||||
signcalc(y);
|
||||
}
|
||||
|
||||
static void tsx() {
|
||||
x = sp;
|
||||
|
||||
zerocalc(x);
|
||||
signcalc(x);
|
||||
}
|
||||
|
||||
static void txa() {
|
||||
a = x;
|
||||
|
||||
zerocalc(a);
|
||||
signcalc(a);
|
||||
}
|
||||
|
||||
static void txs() {
|
||||
sp = x;
|
||||
}
|
||||
|
||||
static void tya() {
|
||||
a = y;
|
||||
|
||||
zerocalc(a);
|
||||
signcalc(a);
|
||||
}
|
||||
|
||||
//undocumented instructions
|
||||
#ifdef UNDOCUMENTED
|
||||
static void lax() {
|
||||
lda();
|
||||
ldx();
|
||||
}
|
||||
|
||||
static void sax() {
|
||||
sta();
|
||||
stx();
|
||||
putvalue(a & x);
|
||||
if (penaltyop && penaltyaddr) clockticks6502--;
|
||||
}
|
||||
|
||||
static void dcp() {
|
||||
dec();
|
||||
cmp();
|
||||
if (penaltyop && penaltyaddr) clockticks6502--;
|
||||
}
|
||||
|
||||
static void isb() {
|
||||
inc();
|
||||
sbc();
|
||||
if (penaltyop && penaltyaddr) clockticks6502--;
|
||||
}
|
||||
|
||||
static void slo() {
|
||||
asl();
|
||||
ora();
|
||||
if (penaltyop && penaltyaddr) clockticks6502--;
|
||||
}
|
||||
|
||||
static void rla() {
|
||||
rol();
|
||||
and();
|
||||
if (penaltyop && penaltyaddr) clockticks6502--;
|
||||
}
|
||||
|
||||
static void sre() {
|
||||
lsr();
|
||||
eor();
|
||||
if (penaltyop && penaltyaddr) clockticks6502--;
|
||||
}
|
||||
|
||||
static void rra() {
|
||||
ror();
|
||||
adc();
|
||||
if (penaltyop && penaltyaddr) clockticks6502--;
|
||||
}
|
||||
#else
|
||||
#define lax nop
|
||||
#define sax nop
|
||||
#define dcp nop
|
||||
#define isb nop
|
||||
#define slo nop
|
||||
#define rla nop
|
||||
#define sre nop
|
||||
#define rra nop
|
||||
#endif
|
||||
|
||||
|
||||
static void (*addrtable[256])() = {
|
||||
/* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | */
|
||||
/* 0 */ imp, indx, imp, indx, zp, zp, zp, zp, imp, imm, acc, imm, abso, abso, abso, abso, /* 0 */
|
||||
/* 1 */ rel, indy, imp, indy, zpx, zpx, zpx, zpx, imp, absy, imp, absy, absx, absx, absx, absx, /* 1 */
|
||||
/* 2 */ abso, indx, imp, indx, zp, zp, zp, zp, imp, imm, acc, imm, abso, abso, abso, abso, /* 2 */
|
||||
/* 3 */ rel, indy, imp, indy, zpx, zpx, zpx, zpx, imp, absy, imp, absy, absx, absx, absx, absx, /* 3 */
|
||||
/* 4 */ imp, indx, imp, indx, zp, zp, zp, zp, imp, imm, acc, imm, abso, abso, abso, abso, /* 4 */
|
||||
/* 5 */ rel, indy, imp, indy, zpx, zpx, zpx, zpx, imp, absy, imp, absy, absx, absx, absx, absx, /* 5 */
|
||||
/* 6 */ imp, indx, imp, indx, zp, zp, zp, zp, imp, imm, acc, imm, ind, abso, abso, abso, /* 6 */
|
||||
/* 7 */ rel, indy, imp, indy, zpx, zpx, zpx, zpx, imp, absy, imp, absy, absx, absx, absx, absx, /* 7 */
|
||||
/* 8 */ imm, indx, imm, indx, zp, zp, zp, zp, imp, imm, imp, imm, abso, abso, abso, abso, /* 8 */
|
||||
/* 9 */ rel, indy, imp, indy, zpx, zpx, zpy, zpy, imp, absy, imp, absy, absx, absx, absy, absy, /* 9 */
|
||||
/* A */ imm, indx, imm, indx, zp, zp, zp, zp, imp, imm, imp, imm, abso, abso, abso, abso, /* A */
|
||||
/* B */ rel, indy, imp, indy, zpx, zpx, zpy, zpy, imp, absy, imp, absy, absx, absx, absy, absy, /* B */
|
||||
/* C */ imm, indx, imm, indx, zp, zp, zp, zp, imp, imm, imp, imm, abso, abso, abso, abso, /* C */
|
||||
/* D */ rel, indy, imp, indy, zpx, zpx, zpx, zpx, imp, absy, imp, absy, absx, absx, absx, absx, /* D */
|
||||
/* E */ imm, indx, imm, indx, zp, zp, zp, zp, imp, imm, imp, imm, abso, abso, abso, abso, /* E */
|
||||
/* F */ rel, indy, imp, indy, zpx, zpx, zpx, zpx, imp, absy, imp, absy, absx, absx, absx, absx /* F */
|
||||
};
|
||||
|
||||
static void (*optable[256])() = {
|
||||
/* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | */
|
||||
/* 0 */ brk, ora, nop, slo, nop, ora, asl, slo, php, ora, asl, nop, nop, ora, asl, slo, /* 0 */
|
||||
/* 1 */ bpl, ora, nop, slo, nop, ora, asl, slo, clc, ora, nop, slo, nop, ora, asl, slo, /* 1 */
|
||||
/* 2 */ jsr, and, nop, rla, bit, and, rol, rla, plp, and, rol, nop, bit, and, rol, rla, /* 2 */
|
||||
/* 3 */ bmi, and, nop, rla, nop, and, rol, rla, sec, and, nop, rla, nop, and, rol, rla, /* 3 */
|
||||
/* 4 */ rti, eor, nop, sre, nop, eor, lsr, sre, pha, eor, lsr, nop, jmp, eor, lsr, sre, /* 4 */
|
||||
/* 5 */ bvc, eor, nop, sre, nop, eor, lsr, sre, cli, eor, nop, sre, nop, eor, lsr, sre, /* 5 */
|
||||
/* 6 */ rts, adc, nop, rra, nop, adc, ror, rra, pla, adc, ror, nop, jmp, adc, ror, rra, /* 6 */
|
||||
/* 7 */ bvs, adc, nop, rra, nop, adc, ror, rra, sei, adc, nop, rra, nop, adc, ror, rra, /* 7 */
|
||||
/* 8 */ nop, sta, nop, sax, sty, sta, stx, sax, dey, nop, txa, nop, sty, sta, stx, sax, /* 8 */
|
||||
/* 9 */ bcc, sta, nop, nop, sty, sta, stx, sax, tya, sta, txs, nop, nop, sta, nop, nop, /* 9 */
|
||||
/* A */ ldy, lda, ldx, lax, ldy, lda, ldx, lax, tay, lda, tax, nop, ldy, lda, ldx, lax, /* A */
|
||||
/* B */ bcs, lda, nop, lax, ldy, lda, ldx, lax, clv, lda, tsx, lax, ldy, lda, ldx, lax, /* B */
|
||||
/* C */ cpy, cmp, nop, dcp, cpy, cmp, dec, dcp, iny, cmp, dex, nop, cpy, cmp, dec, dcp, /* C */
|
||||
/* D */ bne, cmp, nop, dcp, nop, cmp, dec, dcp, cld, cmp, nop, dcp, nop, cmp, dec, dcp, /* D */
|
||||
/* E */ cpx, sbc, nop, isb, cpx, sbc, inc, isb, inx, sbc, nop, sbc, cpx, sbc, inc, isb, /* E */
|
||||
/* F */ beq, sbc, nop, isb, nop, sbc, inc, isb, sed, sbc, nop, isb, nop, sbc, inc, isb /* F */
|
||||
};
|
||||
|
||||
static const uint32_t ticktable[256] = {
|
||||
/* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | */
|
||||
/* 0 */ 7, 6, 2, 8, 3, 3, 5, 5, 3, 2, 2, 2, 4, 4, 6, 6, /* 0 */
|
||||
/* 1 */ 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, /* 1 */
|
||||
/* 2 */ 6, 6, 2, 8, 3, 3, 5, 5, 4, 2, 2, 2, 4, 4, 6, 6, /* 2 */
|
||||
/* 3 */ 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, /* 3 */
|
||||
/* 4 */ 6, 6, 2, 8, 3, 3, 5, 5, 3, 2, 2, 2, 3, 4, 6, 6, /* 4 */
|
||||
/* 5 */ 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, /* 5 */
|
||||
/* 6 */ 6, 6, 2, 8, 3, 3, 5, 5, 4, 2, 2, 2, 5, 4, 6, 6, /* 6 */
|
||||
/* 7 */ 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, /* 7 */
|
||||
/* 8 */ 2, 6, 2, 6, 3, 3, 3, 3, 2, 2, 2, 2, 4, 4, 4, 4, /* 8 */
|
||||
/* 9 */ 2, 6, 2, 6, 4, 4, 4, 4, 2, 5, 2, 5, 5, 5, 5, 5, /* 9 */
|
||||
/* A */ 2, 6, 2, 6, 3, 3, 3, 3, 2, 2, 2, 2, 4, 4, 4, 4, /* A */
|
||||
/* B */ 2, 5, 2, 5, 4, 4, 4, 4, 2, 4, 2, 4, 4, 4, 4, 4, /* B */
|
||||
/* C */ 2, 6, 2, 8, 3, 3, 5, 5, 2, 2, 2, 2, 4, 4, 6, 6, /* C */
|
||||
/* D */ 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, /* D */
|
||||
/* E */ 2, 6, 2, 8, 3, 3, 5, 5, 2, 2, 2, 2, 4, 4, 6, 6, /* E */
|
||||
/* F */ 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7 /* F */
|
||||
};
|
||||
|
||||
|
||||
void nmi6502() {
|
||||
push16(pc);
|
||||
push8(status);
|
||||
status |= FLAG_INTERRUPT;
|
||||
pc = (uint16_t)read6502(0xFFFA) | ((uint16_t)read6502(0xFFFB) << 8);
|
||||
}
|
||||
|
||||
void irq6502() {
|
||||
push16(pc);
|
||||
push8(status);
|
||||
status |= FLAG_INTERRUPT;
|
||||
pc = (uint16_t)read6502(0xFFFE) | ((uint16_t)read6502(0xFFFF) << 8);
|
||||
}
|
||||
|
||||
uint8_t callexternal = 0;
|
||||
void (*loopexternal)();
|
||||
|
||||
void exec6502(uint32_t tickcount) {
|
||||
clockgoal6502 += tickcount;
|
||||
|
||||
while (clockticks6502 < clockgoal6502) {
|
||||
opcode = read6502(pc++);
|
||||
status |= FLAG_CONSTANT;
|
||||
|
||||
penaltyop = 0;
|
||||
penaltyaddr = 0;
|
||||
|
||||
(*addrtable[opcode])();
|
||||
(*optable[opcode])();
|
||||
clockticks6502 += ticktable[opcode];
|
||||
if (penaltyop && penaltyaddr) clockticks6502++;
|
||||
|
||||
instructions++;
|
||||
|
||||
if (callexternal) (*loopexternal)();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void step6502() {
|
||||
opcode = read6502(pc++);
|
||||
status |= FLAG_CONSTANT;
|
||||
|
||||
penaltyop = 0;
|
||||
penaltyaddr = 0;
|
||||
|
||||
(*addrtable[opcode])();
|
||||
(*optable[opcode])();
|
||||
clockticks6502 += ticktable[opcode];
|
||||
if (penaltyop && penaltyaddr) clockticks6502++;
|
||||
clockgoal6502 = clockticks6502;
|
||||
|
||||
instructions++;
|
||||
|
||||
if (callexternal) (*loopexternal)();
|
||||
}
|
||||
|
||||
void hookexternal(void *funcptr) {
|
||||
if (funcptr != (void *)NULL) {
|
||||
loopexternal = funcptr;
|
||||
callexternal = 1;
|
||||
} else callexternal = 0;
|
||||
}
|
52
emulator/emulator.h
Normal file
52
emulator/emulator.h
Normal file
@ -0,0 +1,52 @@
|
||||
#ifndef __EMULATOR_H
|
||||
#define __EMULATOR_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* Accessing memory */
|
||||
extern uint8_t read6502(uint16_t address);
|
||||
extern void write6502(uint16_t address, uint8_t value);
|
||||
|
||||
/* Call this once before you begin execution. */
|
||||
void reset6502();
|
||||
|
||||
/* Execute 6502 code up to the next specified count of clock ticks. */
|
||||
void exec6502(uint32_t tickcount);
|
||||
|
||||
/* Execute a single instrution. */
|
||||
void step6502();
|
||||
|
||||
/* Trigger a hardware IRQ in the 6502 core. */
|
||||
void irq6502();
|
||||
|
||||
/* Trigger an NMI in the 6502 core. */
|
||||
void nmi6502();
|
||||
|
||||
/* Pass a pointer to a void function taking no parameters. This will
|
||||
cause Fake6502 to call that function once after each emulated
|
||||
instruction. */
|
||||
void hookexternal(void *funcptr);
|
||||
|
||||
/* Useful variables in this emulator */
|
||||
|
||||
/* Running total of the emulated cycle count. */
|
||||
extern uint32_t clockticks6502;
|
||||
|
||||
/* Running total of the total emulated instruction count. This is not
|
||||
related to clock cycle timing. */
|
||||
extern uint32_t instructions;
|
||||
|
||||
/* When this is defined, undocumented opcodes are handled otherwise,
|
||||
they're simply treated as NOPs. */
|
||||
#define UNDOCUMENTED
|
||||
|
||||
/* When this is defined, the binary-coded decimal (BCD) status flag
|
||||
is not honored by ADC and SBC. The 2A03 CPU in the Nintendo
|
||||
Entertainment System does not support BCD operation. */
|
||||
#undef NES_CPU
|
||||
|
||||
/* 6502 CPU registers */
|
||||
extern uint16_t pc;
|
||||
extern uint8_t sp, a, x, y, status;
|
||||
|
||||
#endif /* __EMULATOR_H */
|
63
emulator/main.c
Normal file
63
emulator/main.c
Normal file
@ -0,0 +1,63 @@
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include "emulator.h"
|
||||
|
||||
/* Register E maintains common status */
|
||||
#define _RE 0xf8
|
||||
|
||||
/* (dd cc bb aa) aa: index for register stack RS / ccbb: program counter PC / dd: flags F UONPZLGE */
|
||||
#define _RSI _RE /* register stack index */
|
||||
#define _PCL _RSI + 1 /* program counter low */
|
||||
#define _PCH _PCL + 1 /* program counter high */
|
||||
#define _F _PCH + 1 /* flags */
|
||||
#define _PC _PCL /* program counter */
|
||||
|
||||
uint8_t memory[65536];
|
||||
|
||||
uint8_t read6502(uint16_t address) {
|
||||
return memory[address];
|
||||
}
|
||||
|
||||
void write6502(uint16_t address, uint8_t value) {
|
||||
memory[address] = value;
|
||||
}
|
||||
|
||||
void hook() {
|
||||
int i, j;
|
||||
|
||||
printf("\n%04x %u %u\n", pc, instructions, clockticks6502);
|
||||
for (i = 0x00c0; i < 0x0100; i += 16) {
|
||||
printf("%04x ", i);
|
||||
for (j = 0; j < 16; ++j)
|
||||
printf("%02x%s", memory[i + j], (i + j + 1) % 16? " ": "\n");
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
|
||||
uint8_t header[4];
|
||||
|
||||
while (fread(header, sizeof(header), 1, stdin))
|
||||
{
|
||||
uint16_t index = header[0] + (header[1] << 8);
|
||||
uint16_t length = header[2] + (header[3] << 8);
|
||||
|
||||
printf("\n%04x %u\n", index, length);
|
||||
|
||||
if (fread(memory + index, length, 1, stdin))
|
||||
{
|
||||
memory[_PCL] = header[0];
|
||||
memory[_PCH] = header[1];
|
||||
}
|
||||
}
|
||||
|
||||
hookexternal(hook);
|
||||
|
||||
reset6502();
|
||||
|
||||
do
|
||||
step6502();
|
||||
while (memory[pc]);
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue
Block a user