Merge pull request #2 from Russell-S-Harper/master

Synchronizing.
This commit is contained in:
Russell-S-Harper 2018-08-06 22:17:00 -04:00 committed by GitHub
commit e887a83539
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 2225 additions and 0 deletions

View 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

View 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 */

View 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;
}

View 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;
}

View 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);
}

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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;
}