diff options
| author | B. Watson <urchlay@slackware.uk> | 2025-11-13 05:39:38 -0500 |
|---|---|---|
| committer | B. Watson <urchlay@slackware.uk> | 2025-11-13 05:39:38 -0500 |
| commit | e2da2bffe58a76c091d3496bd3ca2d2f18ea2eb6 (patch) | |
| tree | 5195b221457842d781fadcb94331c93058046744 /f65 | |
| download | unalf-e2da2bffe58a76c091d3496bd3ca2d2f18ea2eb6.tar.gz | |
initial commit
Diffstat (limited to 'f65')
| -rw-r--r-- | f65/Makefile | 10 | ||||
| -rw-r--r-- | f65/README | 32 | ||||
| -rwxr-xr-x | f65/asm2fake65.pl | 72 | ||||
| -rw-r--r-- | f65/f65.c | 68 | ||||
| -rw-r--r-- | f65/f65.h | 129 |
5 files changed, 311 insertions, 0 deletions
diff --git a/f65/Makefile b/f65/Makefile new file mode 100644 index 0000000..91adf06 --- /dev/null +++ b/f65/Makefile @@ -0,0 +1,10 @@ +CFLAGS=-Wall + +all: test + +f65.o: f65.c f65.h + +f65test: f65.o f65test.c + +test: f65test + ./f65test diff --git a/f65/README b/f65/README new file mode 100644 index 0000000..0f7d0a3 --- /dev/null +++ b/f65/README @@ -0,0 +1,32 @@ +f65 - "fake 6502" porting layer + +This is a set of C macros that implements most of the 6502 assembly +language instructions, plus a perl script to convert 6502 assembly +source to C code that calls the macros. You can use it to assist in +porting 6502 assembly routines to C, using either the original asm +source, or a disassembly created with da65. + +What's implemented: 64K of memory. The A/X/Y/S registers. The carry, +zero, and negative flags. Most arithmetic and logic instructions. The +stack. Conditional branches and absolute jumps are implemented as C +goto's. JSR is implemented as a real C function call, and RTS is a +real C return. Labels in the assembly source become C labels (aka goto +targets). Equates in the asm source become C variables. + +What's not implemented: The D flag, and decimal mode in general. The +V flag, and branches based on it. The I and B flags. Interrupts +and the RTI instruction. The Program Counter (though branches, JMP, +JSR, and RTS are implemented without it). ROM routines (including +I/O). Indirect JMP. The "CPU bug" that causes e.g. 'LDA ($FF),y' to +take its high byte from $00 rather than $100. (zeropage, x) addressing +mode. + +I wrote this specifically to port the decompression algorithm from +UnAlf 1.4. Instructions not used by UnAlf probably aren't implemented, +or if they are, they're untested. + +The perl script doesn't magically convert a whole 6502 program to C +source. You'll have to figure out which parts of the 6502 program are +subroutines, and put them in their own C functions. Any data (.byte, +.word, etc) won't be in the C program. Anything that does I/O must be +rewritten in C. diff --git a/f65/asm2fake65.pl b/f65/asm2fake65.pl new file mode 100755 index 0000000..78031a7 --- /dev/null +++ b/f65/asm2fake65.pl @@ -0,0 +1,72 @@ +#!/usr/bin/perl -w + +LINE: while(<>) { + chomp; + + next if /^\s*$/; + next if /^\s+\.byte/; + next if /^\s+\.setcpu/; + next if /^\s+;/; + + # join lone label: with next line + if(/^[a-zA-Z0-9_]+:\s*$/) { + $_ .= <>; + redo LINE; + } + + # comment-only lines: + if(/^;\s+(.*)/) { + print "/* $1 */\n"; + next; + } + + if(s/(^[a-zA-Z0-9_]+):?//) { + my $label = $1; + if(/:=\s*\$([0-9A-F]{4})/) { + print "u16 $label = 0x$1;\n"; + next; + } elsif(/\.byte/) { + /; ([0-9A-F]{4})\s/; + print "u16 $label = 0x$1;\n"; + next; + } else { + print "$label:\n"; + } + } + + s/\s+;.*//; + s/^\s*([a-z]{3})\s*//; + my $mnem = $1; + s/\s*$//; + my $operand = $_; + my $arg = ""; + + print "\t"; + + if(!$operand) { + $mnem .= "_a" if $mnem =~ /asl|lsr|rol|ror/; + print $mnem . "();\n"; + next; + } + + $operand =~ s,\$,0x,; + $operand =~ s,;.*,,; + + #print "mnem '$mnem', operand '$operand'\n"; + if($operand =~ /#(.*)/) { + print $mnem . "_i(" . $1 . ");\n"; + next; + } + + if($operand =~ /\(([^)]+)\),y/) { + print $mnem . "_ind_y(" . $1 . ");\n"; + next; + } + + if($operand =~ /(.+),([xy])/) { + print $mnem . "_abs_" . $2 . "(" . $1 . ");\n"; + next; + } + + print $mnem . "(" . $operand . ");\n"; +} diff --git a/f65/f65.c b/f65/f65.c new file mode 100644 index 0000000..952eac3 --- /dev/null +++ b/f65/f65.c @@ -0,0 +1,68 @@ +/* fake 6502. a perl script converts this: + + ldy #10 +loop: + lda blah,y + sta (p1),y + dey + bpl loop + +to something like: + ldy_i(10) +loop: + lda_absy(blah) + sta_zpy(p1) + dey(); + bpl(loop); + +where e.g. + + #define ldy_i(x) Y=x; ZF=!Y; NF=Y&0x80; + + #define lda_absy(x) A=mem[x+y]; ZF=!A; NF=A&0x80; + + #define sta_zpy(x) mem[(mem[x] | mem[x+1] << 8) + y] = A; + + #define dey() Y--; ZF=!Y; NF=Y&0x80; + + #define bpl(x) if(!NF) goto x + +...and the 'loop:' is a real C line label. + +we don't worry about the V flag since unalf doesn't use it. + +This isn't emulation exactly, and it isn't dynamic recompilation because +it's static. "Static recompilation" maybe? + +*/ + +#include "f65.h" + +u8 A, X, Y, S = 0xff, tmp; /* tmp is result for cmp/cpx/cpy */ +u8 mem[1 << 16]; /* 64K */ +u8 *stack = mem + 0x100; /* page 1 */ + +u8 CF, ZF, NF; /* flags should really be a bitfield, *shrug* */ + +unsigned int wtmp; + +void dump_state(void) { + fprintf(stderr, + "A = $%02x X = $%02x Y = $%02x S = $%02x | " + "CF = %d ZF=%d NF=%d\n", + A, X, Y, S, (CF ? 1 : 0), (ZF ? 1 : 0), (NF ? 1 : 0)); +} + +void dump_mem(int start, int end) { + int count = 0; + while(start <= end) { + if(count % 16 == 0) { + if(count) fputc('\n', stderr); + fprintf(stderr, "%04x:", start); + } + fprintf(stderr, " %02x", mem[start]); + count++; + start++; + } + fputc('\n', stderr); +} diff --git a/f65/f65.h b/f65/f65.h new file mode 100644 index 0000000..d1a3b2d --- /dev/null +++ b/f65/f65.h @@ -0,0 +1,129 @@ +#include <stdio.h> + +#define u8 unsigned char +#define u16 unsigned short + +extern u8 A, X, Y, S, tmp; /* tmp is result for cmp/cpx/cpy */ +extern u8 mem[1 << 16]; /* 64K */ +extern u8 *stack; /* page 1 */ + +extern unsigned int wtmp; /* wide temporary for adc/sbc */ + +extern u8 CF, ZF, NF; /* flags should really be a bitfield, *shrug* */ + +extern void dump_state(void); +extern void dump_mem(int start, int end); + +#define dump_zp() dump_mem(0, 0xff); +#define dump_stack() dump_mem(0x100, 0x1ff); +#define dump_page(x) dump_mem(x * 0x100, x * 0x100 + 0x1ff); + +#define _ind_y(x) mem[Y + (mem[x] | (mem[x+1] << 8))] + +#define setnz(x) ZF=!x; NF=(x&0x80)!=0; + +#define pha() stack[S--] = A; +#define pla() A = stack[++S]; setnz(A); +#define php() stack[S--] = (NF<<7 | ZF<<1 | CF); +#define plp() tmp = stack[++S]; NF=(tmp&0x80)!=0 ; ZF=(tmp&0x02)!=0; CF=tmp&0x01; + +#define lda_i(x) A=x; setnz(A); +#define ldy_i(x) Y=x; setnz(Y); +#define ldx_i(x) X=x; setnz(X); +#define lda(x) A=mem[x]; setnz(A); +#define ldx(x) X=mem[x]; setnz(X); +#define ldy(x) Y=mem[x]; setnz(Y); +#define lda_abs_x(x) A=mem[x+X]; setnz(A); +#define lda_abs_y(x) A=mem[x+Y]; setnz(A); +#define lda_ind_y(x) A=_ind_y(x); setnz(A); + +#define sta(x) mem[x] = A; +#define sta_abs_x(x) mem[x + X] = A; +#define sta_abs_y(x) mem[x + Y] = A; +#define sta_ind_y(x) _ind_y(x) = A; + +#define stx(x) mem[x] = X; +#define sty(x) mem[x] = Y; + +#define sec() CF=1; +#define clc() CF=0; + +#define tax() X=A; setnz(X); +#define tay() Y=A; setnz(Y); +#define txa() A=X; setnz(A); +#define tya() A=Y; setnz(A); +#define txs() S=X; setnz(S); +/* note: tsx doesn't affect flags */ +#define tsx() X=S; + +#define _ror(x) tmp=x&1; x>>=1; x|=(CF<<7); CF=tmp; setnz(x); +#define _rol(x) tmp=(x&0x80)!=0; x<<=1; x|=CF; CF=tmp; setnz(x); + +#define ror_a() _ror(A); +#define rol_a() _rol(A); +#define ror(x) _ror(mem[x]); +#define rol(x) _rol(mem[x]); + +#define _lsr(x) CF=x&1; x>>=1; setnz(x); +#define _asl(x) CF=(x&0x80)!=0; x<<=1; setnz(x); + +#define lsr_a() _lsr(A); +#define asl_a() _asl(A); +#define lsr(x) _lsr(mem[x]); +#define asl(x) _asl(mem[x]); + +#define inx() X++; setnz(X); +#define iny() Y++; setnz(Y); +#define dex() X--; setnz(X); +#define dey() Y--; setnz(Y); + +#define inc(x) mem[x]++; setnz(mem[x]); +#define dec(x) mem[x]--; setnz(mem[x]); + +#define jsr(x) x(); +#define rts() return; + +#define jmp(x) goto x; +#define bne(x) if(!ZF) goto x; +#define beq(x) if(ZF) goto x; +#define bcc(x) if(!CF) goto x; +#define bcs(x) if(CF) goto x; +#define bpl(x) if(!NF) goto x; +#define bmi(x) if(NF) goto x; + +#define ora_i(x) A|=x; setnz(A); +#define ora(x) A|=mem[x]; setnz(A); + +#define and_i(x) A&=x; setnz(A); +#define and(x) A&=mem[x]; setnz(A); +#define and_abx_x(x) A&=mem[x+X]; setnz(A); +#define and_abs_y(x) A&=mem[x+Y]; setnz(A); +#define and_ind_y(x) A&=_ind_y(x); setnz(A); + +#define eor_i(x) A^=x; setnz(A); +#define eor(x) A^=mem[x]; setnz(A); + +#define adc_i(x) wtmp=A+x+CF; A=wtmp&0xff; CF=(wtmp&0x100)!=0; setnz(A); +#define adc(x) adc_i(mem[x]); +#define adc_abs_x(x) adc_i(mem[x+X]); +#define adc_abs_y(x) adc_i(mem[x+Y]); +#define adc_ind_y(x) adc_i(_ind_y(x)); + +/* TODO: actually check this logic! */ +#define _sub(dst,orig,c,operand) wtmp=orig+(operand^0xff)+c; dst=wtmp&0xff; CF=(wtmp&0x100)!=0; setnz(dst); + +#define sbc_i(x) _sub(A,A,CF,x) +#define sbc(x) _sub(A,A,CF,mem[x]) +#define cmp_i(x) _sub(tmp,A,1,x) +#define cpy_i(x) _sub(tmp,Y,1,x) +#define cpx_i(x) _sub(tmp,X,1,x) +#define cmp(x) cmp_i(mem[x]) +#define cmp_abs_x(x) cmp_i(mem[x+X]) +#define cpy(x) cpy_i(mem[x]) +#define cpx(x) cpx_i(mem[x]) + +#define nop() {} + +/* this should produce a compile error instead */ +#define brk() nop() + |
