diff --git a/distella.c b/distella.c new file mode 100644 index 0000000..0e14834 --- /dev/null +++ b/distella.c @@ -0,0 +1,786 @@ +#include +#include +#include +#include "table.c" +#include "vcs.c" +#include +#include "queue.c" + +#define IMPLIED 0 +#define ACCUMULATOR 1 +#define IMMEDIATE 2 + +#define ZERO_PAGE 3 +#define ZERO_PAGE_X 4 +#define ZERO_PAGE_Y 5 + +#define ABSOLUTE 6 +#define ABSOLUTE_X 7 +#define ABSOLUTE_Y 8 + +#define ABS_INDIRECT 9 +#define INDIRECT_X 10 +#define INDIRECT_Y 11 + +#define RELATIVE 12 + +#define ASS_CODE 13 + +/* Marked bits */ + +#define REFERENCED 1 +#define VALID_ENTRY 2 +#define DATA 4 +#define GFX 8 +#define REACHABLE 16 + +#define BYTE unsigned char +#define ADDRESS unsigned int + +extern int clength[]; + +struct resource { + ADDRESS start; + ADDRESS load; + unsigned int length; + ADDRESS end; + int disp_data; +} app_data; + +BYTE *mem = NULL; /* Memory */ +BYTE labels[4096]; +BYTE reserved[64]; +BYTE ioresrvd[24]; +char orgmnc[16],linebuff[80],nextline[80]; +FILE *cfg; + +unsigned int pc,pcbeg,pcend,offset,rti_adr,start_adr,k; +int aflag,cflag,dflag,fflag,pflag,rflag,sflag,intflag,lineno,charcnt; + +struct qnode *addressq; + +/* Prototypes */ + +void disasm(unsigned int,int); +int check_bit(BYTE, int); +unsigned int read_adr(void); +int load_config(char *); +void showgfx(unsigned char); +void check_range(unsigned int, unsigned int); +int mark(unsigned int,int); +unsigned int filesize(FILE *stream); +int file_load(char[]); + +void main(int argc,char *argv[]) +{ + int c,i,j; + char file[50],config[50], parms[132]; + char oflag; + time_t currtime; + + mem=(BYTE *)malloc(4096); + memset(mem,0,4096); + app_data.start=0x0; + app_data.load=0x0000; + app_data.length=0; + app_data.end=0x0FFF; + app_data.disp_data=0; + addressq = NULL; + intflag = 0; + + strcpy(file,""); + aflag = 1; + cflag = 0; + fflag = 0; + pflag = 0; + sflag = 0; + rflag = 0; + dflag = 1; + strcpy(orgmnc," ORG "); + strcpy(parms,""); + for (i=0;i 1 && (*++argv)[0] == '-') + while (c = *++argv[0]) + switch(c) { + case 'a': + aflag = 0; + break; + case 'c': + cflag = 1; + i=0; + while (*++argv[0] != '\0') + config[i++] = *argv[0]; + config[i]=*argv[0]--; + fprintf(stderr,"Using %s config file\n",config); + break; + case 'd': + dflag = 0; + break; + case 'o': + oflag = *++argv[0]; + switch (oflag) { + case '1': + strcpy(orgmnc," ORG "); + break; + case '2': + strcpy(orgmnc," *="); + break; + case '3': + strcpy(orgmnc," .OR "); + break; + default: + fprintf(stderr,"Illegal org type %c\n",oflag); + break; + } + break; + case 'p': + pflag = 1; + break; + case 's': + sflag = 1; + break; + case 'i': + intflag = 1; + break; + case 'r': + rflag = 1; + break; + case 'f': + fflag = 1; + break; + default: + fprintf(stderr,"DiStella: illegal option %c\n",c); + exit(1); + } + strcpy(file,*++argv); + + if (argc != 1) { + fprintf(stderr,"DiStella v2.10 - February 25, 1997\n"); + fprintf(stderr,"\nUse: DiStella [options] file\n"); + fprintf(stderr," options:\n"); + fprintf(stderr," -a Turns 'A' off in accumulator instructions\n"); + fprintf(stderr," -c Defines optional config file to use. (e.g. -cpacman.cfg)\n"); + fprintf(stderr," -d Disables automatic code determination\n"); + fprintf(stderr," -f Forces correct address length\n"); + fprintf(stderr," -i Process Interrupt Handler\n"); + fprintf(stderr," -o# ORG variation: # = 1- ORG $XXXX 2- *=$XXXX 3- .OR $XXXX\n"); + fprintf(stderr," -p Insert psuedo-mnemonic 'processor 6502'\n"); + fprintf(stderr," -r Relocate calls out of address range\n"); + fprintf(stderr," -s Cycle count\n"); + fprintf(stderr,"\n Config file:\n"); + fprintf(stderr," ORG XXXX - Start of disassembly\n"); + fprintf(stderr," DATA XXXX XXXX - Defines data range\n"); + fprintf(stderr," GFX XXXX XXXX - Defines graphics range - overrides DATA definition\n"); + fprintf(stderr," CODE XXXX XXXX - Defines code range - overrides DATA and GFX\n"); + fprintf(stderr,"\n Example: DiStella -pafs pacman.bin > pacman.s\n"); + fprintf(stderr,"\n Email: rcolbert@oasis.novia.net or dan.boris@coat.com\n"); + exit(0); + } + + if (!file_load(file)) { + fprintf(stderr,"Unable to load %s\n",file); + exit(0); + } + + pc=app_data.end-3; + start_adr=read_adr(); + if (app_data.end == 0x7ff) + offset=(start_adr & 0xf800); + else + offset=(start_adr - (start_adr % 0x1000)); + + if (cflag && !load_config(config)) { + fprintf(stderr,"Unable to load config file %s\n",config); + exit(0); + } + + fprintf(stderr,"PASS 1\n"); + + addressq=addq(addressq,start_adr); + rti_adr=read_adr(); + if (intflag) { + addressq=addq(addressq,rti_adr); + mark(rti_adr,REFERENCED); + } + if (dflag) { + while(addressq != NULL) { + pc=addressq->address; + pcbeg=pc; + addressq=delq(addressq); + disasm(pc,1); + for (k=pcbeg;k<=pcend;k++) + mark(k,REACHABLE); + } + + for (k=0;k<=app_data.end;k=k+1) { + if (!check_bit(labels[k],REACHABLE)) + mark(k+offset,DATA); + } + } + + fprintf(stderr,"PASS 2\n"); + disasm(offset,2); + + time(&currtime); + printf("; Disassembly of %s\n",file); + printf("; Disassembled %s",ctime(&currtime)); + printf("; Using DiStella v2.0\n;\n"); + printf("; Command Line: %s\n;\n",parms); + if (cflag) { + printf("; %s contents:\n;\n",config); + while (fgets(parms,79,cfg) != NULL) + printf("; %s",parms); + } + printf("\n"); + if (pflag) + printf(" processor 6502\n"); + + for (i=0;i<=0x3d;i++) + if (reserved[i] == 1) { + printf("%s",stella[i]); + for(j=strlen(stella[i]);j<7;j++) + printf(" "); + printf(" = $%0.2X\n",i); + } + + for (i=0x280;i<=0x297;i++) + if (ioresrvd[i-0x280] == 1) { + printf("%s",ioregs[i-0x280]); + for(j=strlen(ioregs[i-0x280]);j<7;j++) + printf(" "); + printf(" = $%0.4X\n",i); + } + + for (i=0;i<0x1000;i++) + if ((labels[i] & 3) == 1) { + printf("L%0.4X = ",i+offset); + printf("$%0.4X\n",i+offset); + } + + printf("\n"); + printf(" %s",orgmnc); + printf("$%0.4X\n",offset); + + fprintf(stderr,"PASS 3\n"); + strcpy(linebuff,""); + strcpy(nextline,""); + disasm(offset,3); + free(mem); +} + +unsigned int filesize(FILE *stream) +{ + unsigned int curpos, length; + + curpos = ftell(stream); + fseek(stream, 0L, SEEK_END); + length = ftell(stream); + fseek(stream, curpos, SEEK_SET); + return length; +} + +unsigned int read_adr() +{ + BYTE d1,d2; + + d1=mem[pc++]; + d2=mem[pc++]; + return (unsigned int) ((d2 << 8)+d1); +} + +int file_load(char file[50]) +{ + FILE *fn; + + fn=fopen(file,"rb"); + if (fn == NULL) return 0; + if (app_data.length == 0) { + app_data.length = filesize(fn); + } + if (app_data.length == 2048) + app_data.end = 0x7ff; + else if (app_data.length == 4096) + app_data.end = 0xfff; + else { + printf("Error: .bin file must be 2048 or 4096 bytes\n"); + exit(1); + } + rewind(fn); + fread(&mem[app_data.load],1,app_data.length,fn); + fclose(fn); + if (app_data.start == 0) + app_data.start = app_data.load; + + return 1; +} + +int load_config(char *file) +{ + char cfg_line[80]; + char cfg_tok[80]; + unsigned int cfg_beg, cfg_end; + + lineno=0; + + if ((cfg=fopen(file,"r")) == NULL) + return 0; + + cfg_beg=cfg_end=0; + + while (fgets(cfg_line,79,cfg)!=NULL) { + strcpy(cfg_tok,""); + sscanf(cfg_line,"%s %x %x",cfg_tok,&cfg_beg,&cfg_end); + if (!strcmp(cfg_tok,"DATA")) { + check_range(cfg_beg,cfg_end); + for(;cfg_beg<=cfg_end;) { + mark(cfg_beg,DATA); + if (cfg_beg == cfg_end) + cfg_end = 0; + else + cfg_beg++; + } + } else if (!strcmp(cfg_tok,"GFX")) { + check_range(cfg_beg,cfg_end); + for(;cfg_beg<=cfg_end;) { + mark(cfg_beg,GFX); + if (cfg_beg == cfg_end) + cfg_end = 0; + else + cfg_beg++; + } + } else if (!strcmp(cfg_tok,"ORG")) { + offset = cfg_beg; + } else if (!strcmp(cfg_tok,"CODE")) { + check_range(cfg_beg,cfg_end); + for(;cfg_beg<=cfg_end;) { + mark(cfg_beg,REACHABLE); + if (cfg_beg == cfg_end) + cfg_end = 0; + else + cfg_beg++; + } + } else { + fprintf(stderr,"Invalid line in config file - line ignored\n",lineno); + } + } + rewind(cfg); + return 1; +} + +void check_range(unsigned int beg, unsigned int end) +{ + lineno++; + if (beg > end) { + fprintf(stderr,"Beginning of range greater than End in config file in line %d\n",lineno); + exit(1); + } + + if (beg > app_data.end + offset) { + fprintf(stderr,"Beginning of range out of range in line %d\n",lineno); + exit(1); + } + + if (beg < offset) { + fprintf(stderr,"Beginning of range out of range in line %d\n",lineno); + exit(1); + } +} + +void disasm(unsigned int distart,int pass) +{ + BYTE op; + BYTE d1,opsrc; + unsigned int ad; + short amode; + int i,bytes,labfound,addbranch; + +/* pc=app_data.start; */ + pc=distart-offset; + while(pc <= app_data.end) { + if(pass == 3) { + if (pc+offset == start_adr) + printf("\nSTART:\n"); + if ((pc+offset == rti_adr) && (intflag)) + printf("\nINTERRUPT:\n"); + } + if(check_bit(labels[pc],GFX)) { +/* && !check_bit(labels[pc],REACHABLE)) { */ + if (pass == 2) + mark(pc+offset,VALID_ENTRY); + if (pass == 3) { + if (check_bit(labels[pc],REFERENCED)) + printf("L%0.4X: ",pc+offset); + else + printf(" ",pc+offset); + printf(".byte $%0.2X ; ",mem[pc]); + showgfx(mem[pc]); + printf(" $%0.4X\n",pc+offset); + } + pc++; + } else + if (check_bit(labels[pc],DATA) && !check_bit(labels[pc],GFX)) { +/* && !check_bit(labels[pc],REACHABLE)) { */ + mark(pc+offset,VALID_ENTRY); + if (pass == 3) { + bytes = 1; + printf("L%0.4X: .byte ",pc+offset); + printf("$%0.2X",mem[pc]); + } + pc++; + + while (check_bit(labels[pc],DATA) && !check_bit(labels[pc],REFERENCED) + && !check_bit(labels[pc],GFX) && pass == 3 && pc <= app_data.end) { + if (pass == 3) { + bytes++; + if (bytes == 17) { + printf("\n .byte $%0.2X",mem[pc]); + bytes = 1; + } else + printf(",$%0.2X",mem[pc]); + } + pc++; + } + if (pass == 3) + printf("\n"); + } else { + op=mem[pc]; + /* version 2.1 bug fix */ + if (pass == 2) + mark(pc+offset,VALID_ENTRY); + if (pass == 3) + if (check_bit(labels[pc],REFERENCED)) { + printf("L%0.4X: ",pc+offset); + } else + printf(" "); + + amode=lookup[op].addr_mode; + if (app_data.disp_data) { + for (i=0; i 0xfff) + addressq=addq(addressq,(ad & app_data.end)+offset); + mark(ad,REACHABLE); + + } + } else if (pass == 3) { + if (ad < 0x100 && fflag) { + sprintf(linebuff,".w "); + strcat(nextline,linebuff); + } + else { + sprintf(linebuff," "); + strcat(nextline,linebuff); + } + if (labfound == 1) { + sprintf(linebuff,"L%0.4X",ad); + strcat(nextline,linebuff); + } + else if (labfound == 3) { + sprintf(linebuff,"%s",ioregs[ad-0x280]); + strcat(nextline,linebuff); + } + else if ((labfound == 4) && rflag) { + sprintf(linebuff,"L%0.4X",(ad & app_data.end)+offset); + strcat(nextline,linebuff); + } + else { + sprintf(linebuff,"$%0.4X",ad); + strcat(nextline,linebuff); + } + } + break; + } + case ZERO_PAGE: { + d1=mem[pc++]; + labfound = mark(d1,REFERENCED); + if (pass == 3) + if (labfound == 2) { + sprintf(linebuff," %s",stella[d1]); + strcat(nextline,linebuff); + } else { + sprintf(linebuff," $%0.2X ",d1); + strcat(nextline,linebuff); + } + break; + } + case IMMEDIATE: { + d1=mem[pc++]; + if (pass == 3) { + sprintf(linebuff," #$%0.2X ",d1); + strcat(nextline,linebuff); + } + break; + } + case ABSOLUTE_X: { + ad=read_adr(); + labfound = mark(ad,REFERENCED); + if (pass == 3) { + if (ad < 0x100 && fflag) { + sprintf(linebuff,".wx "); + strcat(nextline,linebuff); + } + else { + sprintf(linebuff," "); + strcat(nextline,linebuff); + } + if (labfound == 1) { + sprintf(linebuff,"L%0.4X,X",ad); + strcat(nextline,linebuff); + } + else if (labfound == 3) { + sprintf(linebuff,"%s",ioregs[ad-0x280]); + strcat(nextline,linebuff); + } + else if ((labfound == 4) && rflag) { + sprintf(linebuff,"L%0.4X,X",(ad & app_data.end)+offset); + strcat(nextline,linebuff); + } + else { + sprintf(linebuff,"$%0.4X,X",ad); + strcat(nextline,linebuff); + } + } + break; + } + case ABSOLUTE_Y: { + ad=read_adr(); + labfound = mark(ad,REFERENCED); + if (pass == 3) { + if (ad < 0x100 && fflag) { + sprintf(linebuff,".wy "); + strcat(nextline,linebuff); + } + else { + sprintf(linebuff," "); + strcat(nextline,linebuff); + } + if (labfound == 1) { + sprintf(linebuff,"L%0.4X,Y",ad); + strcat(nextline,linebuff); + } + else if (labfound == 3) { + sprintf(linebuff,"%s",ioregs[ad-0x280]); + strcat(nextline,linebuff); + } + else if ((labfound == 4) && rflag) { + sprintf(linebuff,"L%0.4X,Y",(ad & app_data.end)+offset); + strcat(nextline,linebuff); + } + else { + sprintf(linebuff,"$%0.4X,Y",ad); + strcat(nextline,linebuff); + } + } + break; + } + case INDIRECT_X: { + d1=mem[pc++]; + if (pass == 3) { + sprintf(linebuff," ($%0.2X,X)",d1); + strcat(nextline,linebuff); + } + break; + } + case INDIRECT_Y: { + d1=mem[pc++]; + if (pass == 3) { + sprintf(linebuff," ($%0.2X),Y",d1); + strcat(nextline,linebuff); + } + break; + } + case ZERO_PAGE_X: { + d1=mem[pc++]; + labfound = mark(d1,REFERENCED); + if (pass == 3) + if (labfound == 2) { + sprintf(linebuff," %s,X",stella[d1]); + strcat(nextline,linebuff); + } + else { + sprintf(linebuff," $%0.2X,X",d1); + strcat(nextline,linebuff); + } + break; + } + case ZERO_PAGE_Y: { + d1=mem[pc++]; + labfound = mark(d1,REFERENCED); + if (pass == 3) + if (labfound == 2) { + sprintf(linebuff," %s,Y",stella[d1]); + strcat(nextline,linebuff); + } + else { + sprintf(linebuff," $%0.2X,Y",d1); + strcat(nextline,linebuff); + } + break; + } + case RELATIVE: { + d1=mem[pc++]; + ad=d1; + if (d1 >= 128) ad=d1-256; + labfound = mark(pc+ad+offset,REFERENCED); + + if (pass == 1) { + if ((addbranch) && !check_bit(labels[pc+ad],REACHABLE)) { + addressq=addq(addressq,pc+ad+offset); + mark(pc+ad+offset,REACHABLE); + /* addressq=addq(addressq,pc+offset); */ + } + } else if (pass == 3) + if (labfound == 1) { + sprintf(linebuff," L%0.4X",pc+ad+offset); + strcat(nextline,linebuff); + } + else { + sprintf(linebuff," $%0.4X",pc+ad+offset); + strcat(nextline,linebuff); + } + + break; + } + case ABS_INDIRECT: { + ad=read_adr(); + labfound = mark(ad,REFERENCED); + if (pass == 3) + if (ad < 0x100 && fflag) { + sprintf(linebuff,".ind "); + strcat(nextline,linebuff); + } + if (labfound == 1) { + sprintf(linebuff,"(L%04X)",ad); + strcat(nextline,linebuff); + } + else if (labfound == 3) { + sprintf(linebuff,"(%s)",ioregs[ad]); + strcat(nextline,linebuff); + } + else { + sprintf(linebuff,"($%04X)",ad); + strcat(nextline,linebuff); + } + break; + } + } + if (pass == 1) { + if (!strcmp(lookup[op].mnemonic,"RTS") || + !strcmp(lookup[op].mnemonic,"JMP") || +/* !strcmp(lookup[op].mnemonic,"BRK") || */ + !strcmp(lookup[op].mnemonic,"RTI")) { + pcend = (pc-1) + offset; + return; + } + } else if (pass == 3) { + printf("%s",nextline); + for (charcnt=0;charcnt<15-strlen(nextline);charcnt++) + printf(" "); + if (sflag) + printf(";%d",lookup[op].cycles); + printf("\n"); + if (op == 0x40 || op == 0x60) + printf("\n"); + strcpy(nextline,""); + } + } + } +} + +int mark(unsigned int address,int bit) +{ + + if (address >= offset && address <=app_data.end + offset) { + labels[address-offset] = labels[address-offset] | bit; + return 1; + } else if (address >= 0 && address <=0x3d) { + reserved[address] = 1; + return 2; + } else if (address >= 0x280 && address <=0x297) { + ioresrvd[address-0x280] = 1; + return 3; + } else if (address >= 0x1000) { + labels[address & app_data.end] = labels[address & app_data.end] | bit; + return 4; + } else + return 0; +} + +int check_bit(BYTE bitflags, int i) +{ + int j; + + bitflags = bitflags & i; + j = (int) bitflags; + return j; +} + +void showgfx(unsigned char c) +{ + int i; + + printf("|"); + for(i=0;i<8;i++) { + if (c > 127) + printf("X"); + else + printf(" "); + c = c << 1; + } + printf("|"); +} diff --git a/distella.txt b/distella.txt new file mode 100644 index 0000000..c6ae706 --- /dev/null +++ b/distella.txt @@ -0,0 +1,244 @@ + + D i S t e l l a + + v 2.10 + + By + + Dan Boris and Bob Colbert + + Thanks to: + + Alex Hornby, Vesa-Matti Puro, Jarkko Sonninen, Jouko Valta + +---------------------------------------------------------------------------- + +Quick Docs: Type DiStella at the command prompt. + +What is it? + + Distella is a disassembler specifically for the Atari 2600. It creates +source code that is usually recompilable without any human intervention. +It examines the code and performs some basic tracing routines which allow it +to accurately distinguish data from code. + +Features: + + o Written in portable ANSI C - source code is included. + o Very fast + o Distinguishes data from code + o Uses labels for Atari 2600 register locations + o Allows user to override or disable auto data determination + o Optionally includes 6502 cycle times as comments + o Freeware - Use it, Love it, Live it! + +Command format: + + Distella [options] romimage [> sourcefile] + + Distella puts the sourcecode generated to standard output, so to + put it in a file use the '>' redirection. Unlike Distella 1.0, the + .bin suffix is not assumed, and you must use the -c flag to tell + DiStella the name of a config file if you choose to use one. + +Options: + + -a -> Disables the printing of the letter 'A' for opcodes in which + the A register is implied. Some assemblers don't like the 'A' + and others require it. + + Example: LSL A would be just LSL + + -c -> Defines a config file to use for disassembly. The name of the + file must follow the c immediately without any spaces. See the + section called "Config File" for more details about the config + file. + + -d -> Disables automatic code determination. When this flag is used, + DiStella is "dumb" and thinks everything is code unless told + specifically otherwise in the config file. + + -i -> DiStella will read the address indicated in the last 2 bytes of + the ROM file - the interrupt vector - and trace through it to + help determine data areas. Not all programs use the interrupt + vector. The best thing to do is try disassembling the image + without this flag and see if the last two bytes point to an + area that is not disassembled. If that is the case, try the + flag and see if the interrupt routine contains valid code. I + found that a majority of games do not use the interrupt. + + -o# -> ORG mnemonic variation. + + o1 -> ORG $XXXX + o2 -> *=$XXXX + o3 -> .OR $XXXX + + -r -> Relocate calls out of normal address range. Only the lowest 13 + bits in an address are significant in the 2600, so $1000 is + equivalent to $f000. Unfortunately, some ROM images use these + addresses interchangeably. If this flag is NOT used, a section of + code may look like this: + + LF000 lda $D004 ; this actually refers to LF004 + rts + LF004 .byte $3c + + If this flag IS used, the same code would look like this: + + LF000 lda LF004 ; ahh! This is a little clearer :) + rts + LF004 .byte $3c + + It is important to note that if the -r flag is used, the code will + recompile fine, but the ROM image will be altered. If you want your + source to recompile into an exact copy of the original ROM image, do + not use this flag! + + -s -> Includes the cycle count for each instruction. It only includes + the basic cycle count, and does not adjust for page boundries (YET). + +Config File + + The config file is a very simple text file that defines various +parameters for disassembly. Each line in the config file defines either a +range of addresses or the ORG. The addresses should be 4 digit hex numbers +and there should be only 1 space between the command and each address. + +The valid config commands are as follows: + + ORG XXXX + + Defines where the ROM image should be disassembled to. Distella + automatically determines the origin of the ROM image. It takes into + consideration the start address (the address specified in lo/hi byte + format starting from the 4th byte from the end of the ROM image), and + the length of the image. The ORG command will override DiStella's + automatic determination. Be careful, if you are wrong, you won't get + much in the way of code on your output! + + CODE XXXX XXXX + + Defines an address range as being code. Distella is not perfect, + and can mistake code for data. The most common way that this happens + is when Absolute Indirect addressing is used. See the Limitations + section for more information. + + The CODE command overrides DiStella's automatic DATA determination, + but is overridden by all other config commands, so if there are any + conflicts with DATA or ORG commands, the range in question will not be + handled as code. + + GFX XXXX XXXX + + Defines an address range as being graphics. This causes each byte + to be displayed visually in a comment, along with the address of each + byte to the right of the graphic display. + + Here is an example from Pacman: + + + .byte $38 ; | XXX | $FDB5 + .byte $7C ; | XXXXX | $FDB6 + .byte $FE ; |XXXXXXX | $FDB7 + .byte $E0 ; |XXX | $FDB8 + .byte $FE ; |XXXXXXX | $FDB9 + .byte $6C ; | XX XX | $FDBA + .byte $38 ; | XXX | $FDBB + .byte $7E ; | XXXXXX | $FDBC + .byte $E0 ; |XXX | $FDBD + .byte $C0 ; |XX | $FDBE + .byte $E0 ; |XXX | $FDBF + .byte $6C ; | XX XX | $FDC0 + .byte $38 ; | XXX | $FDC1 + + Note that the graphics are upside down, this is common in games + because of the way the code is written. + + The GFX command overrides the DATA command. + + DATA XXXX XXXX + + Defines an address range as being data. Up to 16 bytes will be + put on each line. If an address is reached that is referenced somewhere + else in the code, a new line will be created with its own .byte mnemonic. + Here is an example from Pacman: + + LFF06: .byte $20,$40,$80,$60,$01,$05 + LFF0C: .byte $00 + LFF0D: .byte $00,$00,$01,$00,$00,$01,$06,$05,$04,$03,$02,$01 + LFF19: .byte $00,$02 + +Limitations: + + DiStella does a good job at determining the difference between code and +data. There are a couple of instances that may cause DiStella to confuse +code for data and visa-versa. + + Absolute-Indirect Addressing: + + DiStella traces the code in the ROM image starting at the reset + vector. Each time it sees a relative branch, it puts the branch address + in a queue only if that address hasn't been traced already. It continues + on until it reaches an RTS, RTI, or JMP. It then gets an address from + the queue and traces it repeating the process until no more addresses + are in the queue. Unfortunately, an absolute-indirect JMP - jmp ($ZP) - + doesn't provide enough information for DiStella. It is possible that + an entire section of the ROM image will be determined to be DATA when + it is really CODE. The best thing to do is to look at the code that + loads $ZP and $ZP+1 with the address to jump to (usually there is a list + of address like .byte $00,$f0,$20,$f0,$30,$f0) and use the CODE command + in a config file to force that area to be disassembled. + + Relative "Unconditional" Branches: + + Well, as you may or may not know, the 6502 processor does not have + a relative unconditional branch. Programmers can cheat and use a + relative branch as an absolute branch when they know the status of one + of the status register bits. For example, if this code is executed: + + LDA #$01 + BNE LF034 + LF030 .byte $10,$20,$30,$40 + LF034 RTS + + LF030 will never be reached because 1 is never equal to zero! + DiStella isn't that smart! If you get a large section of code that is + unreadable, look for a relative branch directly before the unreadable + section. Chances are that the data starts directly after that relative + branch. You would need to use the DATA command in a config file to fix + this problem. + + RTS Ending An Interrupt Routing: + + Usually a BRK initiates an interrupt and the code pointed to by the + interrupt vector is executed. The proper way of exiting an interrupt is + to execute an RTI. A problem occurs when an RTS is executed instead. + This is because an RTS pulls the return address off of the stack and then + adds 1 to it, where an RTI does not. What does this mean to you? Well, + look at the following code: + + LF000 LDA #$01 + BRK + .byte $03 ;.SLO + LF004 TAX + + This is an example of what the code might look like if an RTS is + used in the interrupt routine instead of RTI. If an RTI was used, the + code would look like this: + + LF000 LDA #$01 + BRK + LF003 TAX + + The extra byte in the first code segment could cause DiStella to + get off sync, which has numerous side effects. Here it didn't, but keep + in mind that it is possible. + +That's it! Have fun, and report all bugs and errors to: + + Bob Colbert - rcolbert@oasis.novia.net + http://www.novia.net/~rcolbert + + Dan Boris - dan.boris@coat.com + http://www.geocities.com/SiliconValley/9461/ + diff --git a/queue.c b/queue.c new file mode 100644 index 0000000..920d46d --- /dev/null +++ b/queue.c @@ -0,0 +1,105 @@ +/* queue.c */ + +#include +#include +#include +#include + +/* structure to hold list of addresses */ + +struct qnode { + unsigned int address; + struct qnode *next; +}; + +struct qnode *addq(struct qnode *, unsigned int); +struct qnode *delq(struct qnode *); +void qprint(struct qnode *); + +/* allocates enough memory to store 1 q node */ + +struct qnode *qalloc(void) { + return (struct qnode *) malloc(sizeof(struct qnode)); +} + +/* returns pointer to duplicate of s */ + +struct qnode *qdup(struct qnode *s) +{ + struct qnode *temp, *p; + p = NULL; + temp = s; + while (temp != NULL) { + p = addq(p,temp->address); + temp = temp->next; + } + return p; +} + +/* adds q node to tail of p - use form 'p = addq(p,w,t)' */ + +struct qnode *addq(struct qnode *p, unsigned int address) +{ + + if (p == NULL) { + p = qalloc(); + p->address = address; + p->next = NULL; + } + else + p->next = addq(p->next, address); + return p; + +} + +/* remove node from head of p - use form 'p = delq(p)' */ +/* frees memory previously used by node deleted */ + +struct qnode *delq(struct qnode *p) +{ + struct qnode *t; + + t = p; + + if (p != NULL) { + t = p->next; + free(p); + } + + return t; +} + +/* deletes all nodes from queue pointed to by p */ + +void clearq(struct qnode *p) +{ + while (p != NULL) + p = delq(p); +} + +/* prints all bursts in a q - used for debugging */ + +void qprint(struct qnode *p) +{ + if (p != NULL) { + fprintf(stderr,"Address: %0.4X\n",p->address); + qprint(p->next); + } +} + +/* +main() +{ + struct qnode *addresses; + + addresses = NULL; + + addresses = addq(addresses,0xfeac); + addresses = addq(addresses,0xfbba); + qprint(addresses); + printf("--\n"); + printf("%0.4X\n",addresses->address); + addresses = delq(addresses); + printf("%0.4x\n",addresses->address); +} +*/ diff --git a/table.c b/table.c new file mode 100644 index 0000000..122b5b2 --- /dev/null +++ b/table.c @@ -0,0 +1,443 @@ +/***************************************************************************** + + This file is part of Virtual VCS, the Atari 2600 Emulator + =================================================== + + Copyright 1996 Alex Hornby. For contributions see the file CREDITS. + + This software is distributed under the terms of the GNU General Public + License. This is free software with ABSOLUTELY NO WARRANTY. + + See the file COPYING for Details. + + $Id: table.c,v 1.3 1996/03/21 16:36:01 alex Exp $ +******************************************************************************/ + +/* + * $Id: table.c,v 1.3 1996/03/21 16:36:01 alex Exp $ + * + * This was part of the x64 Commodore 64 emulator. + * See README for copyright notice + * + * This file contains lookup-table which is used to translate + * MOS6502 machine instructions. Machine code is used as index + * to array called lookup. Pointer to function is then fetched + * from array "AND" function is called. + * Timing of the undocumented opcodes is based on information + * in an a"RTI"cle in C=Lehti by Kai Lindfors "AND" Topi Maurola. + * + * + * Written by + * Vesa-Matti Puro (vmp@lut.fi) + * Jarkko Sonninen (sonninen@lut.fi) + * Jouko Valta (jopi@stekt.oulu.fi) + * + */ + +struct lookup_tag { + char *mnemonic; /* Selfdocumenting? */ + short addr_mode; + unsigned char source; + unsigned char destination; + unsigned char cycles; + unsigned char pbc_fix; /* Cycle for Page Boundary Crossing */ +}; + + +/* Addressing mode (addr_mode) is used when instruction is diassembled + * or assembled by diassembler or assembler. This is used i.e. + * in function char *sprint_opcode() in the file misc.c. + * + * MOS6502 addressing modes are #defined in the file "vmachine.h". + * + * Mnemonic is character string telling the name of the instruction. + */ + +#define M_NONE 0 +#define M_AC 1 +#define M_XR 2 +#define M_YR 3 +#define M_SP 4 +#define M_SR 5 +#define M_PC 6 +#define M_IMM 7 +#define M_ZERO 8 +#define M_ZERX 9 +#define M_ZERY 10 +#define M_ABS 11 +#define M_ABSX 12 +#define M_ABSY 13 +#define M_AIND 14 +#define M_INDX 15 +#define M_INDY 16 +#define M_REL 17 +#define M_FC 18 +#define M_FD 19 +#define M_FI 20 +#define M_FV 21 +#define M_ADDR 22 +#define M_ 23 + +#define M_ACIM 24 /* Source: AC & IMMED (bus collision) */ +#define M_ANXR 25 /* Source: AC & XR (bus collision) */ +#define M_AXIM 26 /* Source: (AC | #EE) & XR & IMMED (bus collision) */ +#define M_ACNC 27 /* Dest: M_AC and Carry = Negative */ +#define M_ACXR 28 /* Dest: M_AC, M_XR */ + +#define M_SABY 29 /* Source: (ABS_Y & SP) (bus collision) */ +#define M_ACXS 30 /* Dest: M_AC, M_XR, M_SP */ +#define M_STH0 31 /* Dest: Store (src & Addr_Hi+1) to (Addr +0x100) */ +#define M_STH1 32 +#define M_STH2 33 +#define M_STH3 34 + +#define IMPLIED 0 +#define ACCUMULATOR 1 +#define IMMEDIATE 2 + +#define ZERO_PAGE 3 +#define ZERO_PAGE_X 4 +#define ZERO_PAGE_Y 5 + +#define ABSOLUTE 6 +#define ABSOLUTE_X 7 +#define ABSOLUTE_Y 8 + +#define ABS_INDIRECT 9 +#define INDIRECT_X 10 +#define INDIRECT_Y 11 + +#define RELATIVE 12 + +#define ASS_CODE 13 + +int clength[] = {1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 0}; + +struct lookup_tag lookup[] = { + + /**** Positive ****/ + + /* 00 */ { "BRK", IMPLIED, M_NONE, M_PC, 7, 0}, /* Pseudo Absolute */ + /* 01 */ { "ORA", INDIRECT_X, M_INDX, M_AC, 6, 0}, /* (Indirect,X) */ + /* 02 */ { ".JAM", IMPLIED, M_NONE, M_NONE, 0, 0}, /* TILT */ + /* 03 */ { ".SLO", INDIRECT_X, M_INDX, M_INDX, 8, 0}, + + /* 04 */ { ".NOOP", ZERO_PAGE, M_NONE, M_NONE, 3, 0}, + /* 05 */ { "ORA", ZERO_PAGE, M_ZERO, M_AC, 3, 0}, /* Zeropage */ + /* 06 */ { "ASL", ZERO_PAGE, M_ZERO, M_ZERO, 5, 0}, /* Zeropage */ + /* 07 */ { ".SLO", ZERO_PAGE, M_ZERO, M_ZERO, 5, 0}, + + /* 08 */ { "PHP", IMPLIED, M_SR, M_NONE, 3, 0}, + /* 09 */ { "ORA", IMMEDIATE, M_IMM, M_AC, 2, 0}, /* Immediate */ + /* 0a */ { "ASL", ACCUMULATOR, M_AC, M_AC, 2, 0}, /* Accumulator */ + /* 0b */ { ".ANC", IMMEDIATE, M_ACIM, M_ACNC, 2, 0}, + + /* 0c */ { ".NOOP", ABSOLUTE, M_NONE, M_NONE, 4, 0}, + /* 0d */ { "ORA", ABSOLUTE, M_ABS, M_AC, 4, 0}, /* Absolute */ + /* 0e */ { "ASL", ABSOLUTE, M_ABS, M_ABS, 6, 0}, /* Absolute */ + /* 0f */ { ".SLO", ABSOLUTE, M_ABS, M_ABS, 6, 0}, + + /* 10 */ { "BPL", RELATIVE, M_REL, M_NONE, 2, 0}, + /* 11 */ { "ORA", INDIRECT_Y, M_INDY, M_AC, 5, 1}, /* (Indirect),Y */ + /* 12 */ { ".JAM", IMPLIED, M_NONE, M_NONE, 0, 0}, /* TILT */ + /* 13 */ { ".SLO", INDIRECT_Y, M_INDY, M_INDY, 8, 0}, + + /* 14 */ { ".NOOP", ZERO_PAGE_X, M_NONE, M_NONE, 4, 0}, + /* 15 */ { "ORA", ZERO_PAGE_X, M_ZERX, M_AC, 4, 0}, /* Zeropage,X */ + /* 16 */ { "ASL", ZERO_PAGE_X, M_ZERX, M_ZERX, 6, 0},/* Zeropage,X */ + /* 17 */ { ".SLO", ZERO_PAGE_X, M_ZERX, M_ZERX, 6, 0}, + + /* 18 */ { "CLC", IMPLIED, M_NONE, M_FC, 2, 0}, + /* 19 */ { "ORA", ABSOLUTE_Y, M_ABSY, M_AC, 4, 1}, /* Absolute,Y */ + /* 1a */ { ".NOOP", IMPLIED, M_NONE, M_NONE, 2, 0}, + /* 1b */ { ".SLO", ABSOLUTE_Y, M_ABSY, M_ABSY, 7, 0}, + + /* 1c */ { ".NOOP", ABSOLUTE_X, M_NONE, M_NONE, 4, 1}, + /* 1d */ { "ORA", ABSOLUTE_X, M_ABSX, M_AC, 4, 1}, /* Absolute,X */ + /* 1e */ { "ASL", ABSOLUTE_X, M_ABSX, M_ABSX, 7, 0}, /* Absolute,X */ + /* 1f */ { ".SLO", ABSOLUTE_X, M_ABSX, M_ABSX, 7, 0}, + + /* 20 */ { "JSR", ABSOLUTE, M_ADDR, M_PC, 6, 0}, + /* 21 */ { "AND", INDIRECT_X, M_INDX, M_AC, 6, 0}, /* (Indirect ,X) */ + /* 22 */ { ".JAM", IMPLIED, M_NONE, M_NONE, 0, 0}, /* TILT */ + /* 23 */ { ".RLA", INDIRECT_X, M_INDX, M_INDX, 8, 0}, + + /* 24 */ { "BIT", ZERO_PAGE, M_ZERO, M_NONE, 3, 0}, /* Zeropage */ + /* 25 */ { "AND", ZERO_PAGE, M_ZERO, M_AC, 3, 0}, /* Zeropage */ + /* 26 */ { "ROL", ZERO_PAGE, M_ZERO, M_ZERO, 5, 0}, /* Zeropage */ + /* 27 */ { ".RLA", ZERO_PAGE, M_ZERO, M_ZERO, 5, 0}, + + /* 28 */ { "PLP", IMPLIED, M_NONE, M_SR, 4, 0}, + /* 29 */ { "AND", IMMEDIATE, M_IMM, M_AC, 2, 0}, /* Immediate */ + /* 2a */ { "ROL", ACCUMULATOR, M_AC, M_AC, 2, 0}, /* Accumulator */ + /* 2b */ { ".ANC", IMMEDIATE, M_ACIM, M_ACNC, 2, 0}, + + /* 2c */ { "BIT", ABSOLUTE, M_ABS, M_NONE, 4, 0}, /* Absolute */ + /* 2d */ { "AND", ABSOLUTE, M_ABS, M_AC, 4, 0}, /* Absolute */ + /* 2e */ { "ROL", ABSOLUTE, M_ABS, M_ABS, 6, 0}, /* Absolute */ + /* 2f */ { ".RLA", ABSOLUTE, M_ABS, M_ABS, 6, 0}, + + /* 30 */ { "BMI", RELATIVE, M_REL, M_NONE, 2, 0}, + /* 31 */ { "AND", INDIRECT_Y, M_INDY, M_AC, 5, 1}, /* (Indirect),Y */ + /* 32 */ { ".JAM", IMPLIED, M_NONE, M_NONE, 0, 0}, /* TILT */ + /* 33 */ { ".RLA", INDIRECT_Y, M_INDY, M_INDY, 8, 0}, + + /* 34 */ { ".NOOP", ZERO_PAGE_X, M_NONE, M_NONE, 4, 0}, + /* 35 */ { "AND", ZERO_PAGE_X, M_ZERX, M_AC, 4, 0}, /* Zeropage,X */ + /* 36 */ { "ROL", ZERO_PAGE_X, M_ZERX, M_ZERX, 6, 0},/* Zeropage,X */ + /* 37 */ { ".RLA", ZERO_PAGE_X, M_ZERX, M_ZERX, 6, 0}, + + /* 38 */ { "SEC", IMPLIED, M_NONE, M_FC, 2, 0}, + /* 39 */ { "AND", ABSOLUTE_Y, M_ABSY, M_AC, 4, 1}, /* Absolute,Y */ + /* 3a */ { ".NOOP", IMPLIED, M_NONE, M_NONE, 2, 0}, + /* 3b */ { ".RLA", ABSOLUTE_Y, M_ABSY, M_ABSY, 7, 0}, + + /* 3c */ { ".NOOP", ABSOLUTE_X, M_NONE, M_NONE, 4, 1}, + /* 3d */ { "AND", ABSOLUTE_X, M_ABSX, M_AC, 4, 1}, /* Absolute,X */ + /* 3e */ { "ROL", ABSOLUTE_X, M_ABSX, M_ABSX, 7, 0}, /* Absolute,X */ + /* 3f */ { ".RLA", ABSOLUTE_X, M_ABSX, M_ABSX, 7, 0}, + + /* 40 */ { "RTI" , IMPLIED, M_NONE, M_PC, 6, 0}, + /* 41 */ { "EOR", INDIRECT_X, M_INDX, M_AC, 6, 0}, /* (Indirect,X) */ + /* 42 */ { ".JAM", IMPLIED, M_NONE, M_NONE, 0, 0}, /* TILT */ + /* 43 */ { ".SRE", INDIRECT_X, M_INDX, M_INDX, 8, 0}, + + /* 44 */ { ".NOOP", ZERO_PAGE, M_NONE, M_NONE, 3, 0}, + /* 45 */ { "EOR", ZERO_PAGE, M_ZERO, M_AC, 3, 0}, /* Zeropage */ + /* 46 */ { "LSR", ZERO_PAGE, M_ZERO, M_ZERO, 5, 0}, /* Zeropage */ + /* 47 */ { ".SRE", ZERO_PAGE, M_ZERO, M_ZERO, 5, 0}, + + /* 48 */ { "PHA", IMPLIED, M_AC, M_NONE, 3, 0}, + /* 49 */ { "EOR", IMMEDIATE, M_IMM, M_AC, 2, 0}, /* Immediate */ + /* 4a */ { "LSR", ACCUMULATOR, M_AC, M_AC, 2, 0}, /* Accumulator */ + /* 4b */ { ".ASR", IMMEDIATE, M_ACIM, M_AC, 2, 0}, /* (AC & IMM) >>1 */ + + /* 4c */ { "JMP", ABSOLUTE, M_ADDR, M_PC, 3, 0}, /* Absolute */ + /* 4d */ { "EOR", ABSOLUTE, M_ABS, M_AC, 4, 0}, /* Absolute */ + /* 4e */ { "LSR", ABSOLUTE, M_ABS, M_ABS, 6, 0}, /* Absolute */ + /* 4f */ { ".SRE", ABSOLUTE, M_ABS, M_ABS, 6, 0}, + + /* 50 */ { "BVC", RELATIVE, M_REL, M_NONE, 2, 0}, + /* 51 */ { "EOR", INDIRECT_Y, M_INDY, M_AC, 5, 1}, /* (Indirect),Y */ + /* 52 */ { ".JAM", IMPLIED, M_NONE, M_NONE, 0, 0}, /* TILT */ + /* 53 */ { ".SRE", INDIRECT_Y, M_INDY, M_INDY, 8, 0}, + + /* 54 */ { ".NOOP", ZERO_PAGE_X, M_NONE, M_NONE, 4, 0}, + /* 55 */ { "EOR", ZERO_PAGE_X, M_ZERX, M_AC, 4, 0}, /* Zeropage,X */ + /* 56 */ { "LSR", ZERO_PAGE_X, M_ZERX, M_ZERX, 6, 0},/* Zeropage,X */ + /* 57 */ { ".SRE", ZERO_PAGE_X, M_ZERX, M_ZERX, 6, 0}, + + /* 58 */ { "CLI", IMPLIED, M_NONE, M_FI, 2, 0}, + /* 59 */ { "EOR", ABSOLUTE_Y, M_ABSY, M_AC, 4, 1}, /* Absolute,Y */ + /* 5a */ { ".NOOP", IMPLIED, M_NONE, M_NONE, 2, 0}, + /* 5b */ { ".SRE", ABSOLUTE_Y, M_ABSY, M_ABSY, 7, 0}, + + /* 5c */ { ".NOOP", ABSOLUTE_X, M_NONE, M_NONE, 4, 1}, + /* 5d */ { "EOR", ABSOLUTE_X, M_ABSX, M_AC, 4, 1}, /* Absolute,X */ + /* 5e */ { "LSR", ABSOLUTE_X, M_ABSX, M_ABSX, 7, 0}, /* Absolute,X */ + /* 5f */ { ".SRE", ABSOLUTE_X, M_ABSX, M_ABSX, 7, 0}, + + /* 60 */ { "RTS", IMPLIED, M_NONE, M_PC, 6, 0}, + /* 61 */ { "ADC", INDIRECT_X, M_INDX, M_AC, 6, 0}, /* (Indirect,X) */ + /* 62 */ { ".JAM", IMPLIED, M_NONE, M_NONE, 0, 0}, /* TILT */ + /* 63 */ { ".RRA", INDIRECT_X, M_INDX, M_INDX, 8, 0}, + + /* 64 */ { ".NOOP", ZERO_PAGE, M_NONE, M_NONE, 3, 0}, + /* 65 */ { "ADC", ZERO_PAGE, M_ZERO, M_AC, 3, 0}, /* Zeropage */ + /* 66 */ { "ROR", ZERO_PAGE, M_ZERO, M_ZERO, 5, 0}, /* Zeropage */ + /* 67 */ { ".RRA", ZERO_PAGE, M_ZERO, M_ZERO, 5, 0}, + + /* 68 */ { "PLA", IMPLIED, M_NONE, M_AC, 4, 0}, + /* 69 */ { "ADC", IMMEDIATE, M_IMM, M_AC, 2, 0}, /* Immediate */ + /* 6a */ { "ROR", ACCUMULATOR, M_AC, M_AC, 2, 0}, /* Accumulator */ + /* 6b */ { ".ARR", IMMEDIATE, M_ACIM, M_AC, 2, 0}, /* ARR isn't typo */ + + /* 6c */ { "JMP", ABS_INDIRECT, M_AIND, M_PC, 5, 0}, /* Indirect */ + /* 6d */ { "ADC", ABSOLUTE, M_ABS, M_AC, 4, 0}, /* Absolute */ + /* 6e */ { "ROR", ABSOLUTE, M_ABS, M_ABS, 6, 0}, /* Absolute */ + /* 6f */ { ".RRA", ABSOLUTE, M_ABS, M_ABS, 6, 0}, + + /* 70 */ { "BVS", RELATIVE, M_REL, M_NONE, 2, 0}, + /* 71 */ { "ADC", INDIRECT_Y, M_INDY, M_AC, 5, 1}, /* (Indirect),Y */ + /* 72 */ { ".JAM", IMPLIED, M_NONE, M_NONE, 0, 0}, /* TILT relative? */ + /* 73 */ { ".RRA", INDIRECT_Y, M_INDY, M_INDY, 8, 0}, + + /* 74 */ { ".NOOP", ZERO_PAGE_X, M_NONE, M_NONE, 4, 0}, + /* 75 */ { "ADC", ZERO_PAGE_X, M_ZERX, M_AC, 4, 0}, /* Zeropage,X */ + /* 76 */ { "ROR", ZERO_PAGE_X, M_ZERX, M_ZERX, 6, 0}, /* Zeropage,X */ + /* 77 */ { ".RRA", ZERO_PAGE_X, M_ZERX, M_ZERX, 6, 0}, + + /* 78 */ { "SEI", IMPLIED, M_NONE, M_FI, 2, 0}, + /* 79 */ { "ADC", ABSOLUTE_Y, M_ABSY, M_AC, 4, 1}, /* Absolute,Y */ + /* 7a */ { ".NOOP", IMPLIED, M_NONE, M_NONE, 2, 0}, + /* 7b */ { ".RRA", ABSOLUTE_Y, M_ABSY, M_ABSY, 7, 0}, + + /* 7c */ { ".NOOP", ABSOLUTE_X, M_NONE, M_NONE, 4, 1}, + /* 7d */ { "ADC", ABSOLUTE_X, M_ABSX, M_AC, 4, 1}, /* Absolute,X */ + /* 7e */ { "ROR", ABSOLUTE_X, M_ABSX, M_ABSX, 7, 0}, /* Absolute,X */ + /* 7f */ { ".RRA", ABSOLUTE_X, M_ABSX, M_ABSX, 7, 0}, + + /**** Negative ****/ + + /* 80 */ { ".NOOP", IMMEDIATE, M_NONE, M_NONE, 2, 0}, + /* 81 */ { "STA", INDIRECT_X, M_AC, M_INDX, 6, 0}, /* (Indirect,X) */ + /* 82 */ { ".NOOP", IMMEDIATE, M_NONE, M_NONE, 2, 0}, + /* 83 */ { ".SAX", INDIRECT_X, M_ANXR, M_INDX, 6, 0}, + + /* 84 */ { "STY", ZERO_PAGE, M_YR, M_ZERO, 3, 0}, /* Zeropage */ + /* 85 */ { "STA", ZERO_PAGE, M_AC, M_ZERO, 3, 0}, /* Zeropage */ + /* 86 */ { "STX", ZERO_PAGE, M_XR, M_ZERO, 3, 0}, /* Zeropage */ + /* 87 */ { ".SAX", ZERO_PAGE, M_ANXR, M_ZERO, 3, 0}, + + /* 88 */ { "DEY", IMPLIED, M_YR, M_YR, 2, 0}, + /* 89 */ { ".NOOP", IMMEDIATE, M_NONE, M_NONE, 2, 0}, + /* 8a */ { "TXA", IMPLIED, M_XR, M_AC, 2, 0}, + /**** very abnormal: usually AC = AC | #$EE & XR & #$oper ****/ + /* 8b */ { ".ANE", IMMEDIATE, M_AXIM, M_AC, 2, 0}, + + /* 8c */ { "STY", ABSOLUTE, M_YR, M_ABS, 4, 0}, /* Absolute */ + /* 8d */ { "STA", ABSOLUTE, M_AC, M_ABS, 4, 0}, /* Absolute */ + /* 8e */ { "STX", ABSOLUTE, M_XR, M_ABS, 4, 0}, /* Absolute */ + /* 8f */ { ".SAX", ABSOLUTE, M_ANXR, M_ABS, 4, 0}, + + /* 90 */ { "BCC", RELATIVE, M_REL, M_NONE, 2, 0}, + /* 91 */ { "STA", INDIRECT_Y, M_AC, M_INDY, 6, 0}, /* (Indirect),Y */ + /* 92 */ { ".JAM", IMPLIED, M_NONE, M_NONE, 0, 0}, /* TILT relative? */ + /* 93 */ { ".SHA", INDIRECT_Y, M_ANXR, M_STH0, 6, 0}, + + /* 94 */ { "STY", ZERO_PAGE_X, M_YR, M_ZERX, 4, 0}, /* Zeropage,X */ + /* 95 */ { "STA", ZERO_PAGE_X, M_AC, M_ZERX, 4, 0}, /* Zeropage,X */ + /* 96 */ { "STX", ZERO_PAGE_Y, M_XR, M_ZERY, 4, 0}, /* Zeropage,Y */ + /* 97 */ { ".SAX", ZERO_PAGE_Y, M_ANXR, M_ZERY, 4, 0}, + + /* 98 */ { "TYA", IMPLIED, M_YR, M_AC, 2, 0}, + /* 99 */ { "STA", ABSOLUTE_Y, M_AC, M_ABSY, 5, 0}, /* Absolute,Y */ + /* 9a */ { "TXS", IMPLIED, M_XR, M_SP, 2, 0}, + /*** This is very mysterious comm AND ... */ + /* 9b */ { ".SHS", ABSOLUTE_Y, M_ANXR, M_STH3, 5, 0}, + + /* 9c */ { ".SHY", ABSOLUTE_X, M_YR, M_STH2, 5, 0}, + /* 9d */ { "STA", ABSOLUTE_X, M_AC, M_ABSX, 5, 0}, /* Absolute,X */ + /* 9e */ { ".SHX", ABSOLUTE_Y, M_XR, M_STH1, 5, 0}, + /* 9f */ { ".SHA", ABSOLUTE_Y, M_ANXR, M_STH1, 5, 0}, + + /* a0 */ { "LDY", IMMEDIATE, M_IMM, M_YR, 2, 0}, /* Immediate */ + /* a1 */ { "LDA", INDIRECT_X, M_INDX, M_AC, 6, 0}, /* (indirect,X) */ + /* a2 */ { "LDX", IMMEDIATE, M_IMM, M_XR, 2, 0}, /* Immediate */ + /* a3 */ { ".LAX", INDIRECT_X, M_INDX, M_ACXR, 6, 0}, /* (indirect,X) */ + + /* a4 */ { "LDY", ZERO_PAGE, M_ZERO, M_YR, 3, 0}, /* Zeropage */ + /* a5 */ { "LDA", ZERO_PAGE, M_ZERO, M_AC, 3, 0}, /* Zeropage */ + /* a6 */ { "LDX", ZERO_PAGE, M_ZERO, M_XR, 3, 0}, /* Zeropage */ + /* a7 */ { ".LAX", ZERO_PAGE, M_ZERO, M_ACXR, 3, 0}, + + /* a8 */ { "TAY", IMPLIED, M_AC, M_YR, 2, 0}, + /* a9 */ { "LDA", IMMEDIATE, M_IMM, M_AC, 2, 0}, /* Immediate */ + /* aa */ { "TAX", IMPLIED, M_AC, M_XR, 2, 0}, + /* ab */ { ".LXA", IMMEDIATE, M_ACIM, M_ACXR, 2, 0}, /* LXA isn't a typo */ + + /* ac */ { "LDY", ABSOLUTE, M_ABS, M_YR, 4, 0}, /* Absolute */ + /* ad */ { "LDA", ABSOLUTE, M_ABS, M_AC, 4, 0}, /* Absolute */ + /* ae */ { "LDX", ABSOLUTE, M_ABS, M_XR, 4, 0}, /* Absolute */ + /* af */ { ".LAX", ABSOLUTE, M_ABS, M_ACXR, 4, 0}, + + /* b0 */ { "BCS", RELATIVE, M_REL, M_NONE, 2, 0}, + /* b1 */ { "LDA", INDIRECT_Y, M_INDY, M_AC, 5, 1}, /* (indirect),Y */ + /* b2 */ { ".JAM", IMPLIED, M_NONE, M_NONE, 0, 0}, /* TILT */ + /* b3 */ { ".LAX", INDIRECT_Y, M_INDY, M_ACXR, 5, 1}, + + /* b4 */ { "LDY", ZERO_PAGE_X, M_ZERX, M_YR, 4, 0}, /* Zeropage,X */ + /* b5 */ { "LDA", ZERO_PAGE_X, M_ZERX, M_AC, 4, 0}, /* Zeropage,X */ + /* b6 */ { "LDX", ZERO_PAGE_Y, M_ZERY, M_XR, 4, 0}, /* Zeropage,Y */ + /* b7 */ { ".LAX", ZERO_PAGE_Y, M_ZERY, M_ACXR, 4, 0}, + + /* b8 */ { "CLV", IMPLIED, M_NONE, M_FV, 2, 0}, + /* b9 */ { "LDA", ABSOLUTE_Y, M_ABSY, M_AC, 4, 1}, /* Absolute,Y */ + /* ba */ { "TSX", IMPLIED, M_SP, M_XR, 2, 0}, + /* bb */ { ".LAS", ABSOLUTE_Y, M_SABY, M_ACXS, 4, 1}, + + /* bc */ { "LDY", ABSOLUTE_X, M_ABSX, M_YR, 4, 1}, /* Absolute,X */ + /* bd */ { "LDA", ABSOLUTE_X, M_ABSX, M_AC, 4, 1}, /* Absolute,X */ + /* be */ { "LDX", ABSOLUTE_Y, M_ABSY, M_XR, 4, 1}, /* Absolute,Y */ + /* bf */ { ".LAX", ABSOLUTE_Y, M_ABSY, M_ACXR, 4, 1}, + + /* c0 */ { "CPY", IMMEDIATE, M_IMM, M_NONE, 2, 0}, /* Immediate */ + /* c1 */ { "CMP", INDIRECT_X, M_INDX, M_NONE, 6, 0}, /* (Indirect,X) */ + /* c2 */ { ".NOOP", IMMEDIATE, M_NONE, M_NONE, 2, 0}, /* occasional TILT */ + /* c3 */ { ".DCP", INDIRECT_X, M_INDX, M_INDX, 8, 0}, + + /* c4 */ { "CPY", ZERO_PAGE, M_ZERO, M_NONE, 3, 0}, /* Zeropage */ + /* c5 */ { "CMP", ZERO_PAGE, M_ZERO, M_NONE, 3, 0}, /* Zeropage */ + /* c6 */ { "DEC", ZERO_PAGE, M_ZERO, M_ZERO, 5, 0}, /* Zeropage */ + /* c7 */ { ".DCP", ZERO_PAGE, M_ZERO, M_ZERO, 5, 0}, + + /* c8 */ { "INY", IMPLIED, M_YR, M_YR, 2, 0}, + /* c9 */ { "CMP", IMMEDIATE, M_IMM, M_NONE, 2, 0}, /* Immediate */ + /* ca */ { "DEX", IMPLIED, M_XR, M_XR, 2, 0}, + /* cb */ { ".SBX", IMMEDIATE, M_IMM, M_XR, 2, 0}, + + /* cc */ { "CPY", ABSOLUTE, M_ABS, M_NONE, 4, 0}, /* Absolute */ + /* cd */ { "CMP", ABSOLUTE, M_ABS, M_NONE, 4, 0}, /* Absolute */ + /* ce */ { "DEC", ABSOLUTE, M_ABS, M_ABS, 6, 0}, /* Absolute */ + /* cf */ { ".DCP", ABSOLUTE, M_ABS, M_ABS, 6, 0}, + + /* d0 */ { "BNE", RELATIVE, M_REL, M_NONE, 2, 0}, + /* d1 */ { "CMP", INDIRECT_Y, M_INDY, M_NONE, 5, 1}, /* (Indirect),Y */ + /* d2 */ { ".JAM", IMPLIED, M_NONE, M_NONE, 0, 0}, /* TILT */ + /* d3 */ { ".DCP", INDIRECT_Y, M_INDY, M_INDY, 8, 0}, + + /* d4 */ { ".NOOP", ZERO_PAGE_X, M_NONE, M_NONE, 4, 0}, + /* d5 */ { "CMP", ZERO_PAGE_X, M_ZERX, M_NONE, 4, 0},/* Zeropage,X */ + /* d6 */ { "DEC", ZERO_PAGE_X, M_ZERX, M_ZERX, 6, 0},/* Zeropage,X */ + /* d7 */ { ".DCP", ZERO_PAGE_X, M_ZERX, M_ZERX, 6, 0}, + + /* d8 */ { "CLD", IMPLIED, M_NONE, M_FD, 2, 0}, + /* d9 */ { "CMP", ABSOLUTE_Y, M_ABSY, M_NONE, 4, 1}, /* Absolute,Y */ + /* da */ { ".NOOP", IMPLIED, M_NONE, M_NONE, 2, 0}, + /* db */ { ".DCP", ABSOLUTE_Y, M_ABSY, M_ABSY, 7, 0}, + + /* dc */ { ".NOOP", ABSOLUTE_X, M_NONE, M_NONE, 4, 1}, + /* dd */ { "CMP", ABSOLUTE_X, M_ABSX, M_NONE, 4, 1}, /* Absolute,X */ + /* de */ { "DEC", ABSOLUTE_X, M_ABSX, M_ABSX, 7, 0}, /* Absolute,X */ + /* df */ { ".DCP", ABSOLUTE_X, M_ABSX, M_ABSX, 7, 0}, + + /* e0 */ { "CPX", IMMEDIATE, M_IMM, M_NONE, 2, 0}, /* Immediate */ + /* e1 */ { "SBC", INDIRECT_X, M_INDX, M_AC, 6, 0}, /* (Indirect,X) */ + /* e2 */ { ".NOOP", IMMEDIATE, M_NONE, M_NONE, 2, 0}, + /* e3 */ { ".ISB", INDIRECT_X, M_INDX, M_INDX, 8, 0}, + + /* e4 */ { "CPX", ZERO_PAGE, M_ZERO, M_NONE, 3, 0}, /* Zeropage */ + /* e5 */ { "SBC", ZERO_PAGE, M_ZERO, M_AC, 3, 0}, /* Zeropage */ + /* e6 */ { "INC", ZERO_PAGE, M_ZERO, M_ZERO, 5, 0}, /* Zeropage */ + /* e7 */ { ".ISB", ZERO_PAGE, M_ZERO, M_ZERO, 5, 0}, + + /* e8 */ { "INX", IMPLIED, M_XR, M_XR, 2, 0}, + /* e9 */ { "SBC", IMMEDIATE, M_IMM, M_AC, 2, 0}, /* Immediate */ + /* ea */ { "NOP", IMPLIED, M_NONE, M_NONE, 2, 0}, + /* eb */ { ".USBC", IMMEDIATE, M_IMM, M_AC, 2, 0}, /* same as e9 */ + + /* ec */ { "CPX", ABSOLUTE, M_ABS, M_NONE, 4, 0}, /* Absolute */ + /* ed */ { "SBC", ABSOLUTE, M_ABS, M_AC, 4, 0}, /* Absolute */ + /* ee */ { "INC", ABSOLUTE, M_ABS, M_ABS, 6, 0}, /* Absolute */ + /* ef */ { ".ISB", ABSOLUTE, M_ABS, M_ABS, 6, 0}, + + /* f0 */ { "BEQ", RELATIVE, M_REL, M_NONE, 2, 0}, + /* f1 */ { "SBC", INDIRECT_Y, M_INDY, M_AC, 5, 1}, /* (Indirect),Y */ + /* f2 */ { ".JAM", IMPLIED, M_NONE, M_NONE, 0, 0}, /* TILT */ + /* f3 */ { ".ISB", INDIRECT_Y, M_INDY, M_INDY, 8, 0}, + + /* f4 */ { ".NOOP", ZERO_PAGE_X, M_NONE, M_NONE, 4, 0}, + /* f5 */ { "SBC", ZERO_PAGE_X, M_ZERX, M_AC, 4, 0}, /* Zeropage,X */ + /* f6 */ { "INC", ZERO_PAGE_X, M_ZERX, M_ZERX, 6, 0}, /* Zeropage,X */ + /* f7 */ { ".ISB", ZERO_PAGE_X, M_ZERX, M_ZERX, 6, 0}, + + /* f8 */ { "SED", IMPLIED, M_NONE, M_FD, 2, 0}, + /* f9 */ { "SBC", ABSOLUTE_Y, M_ABSY, M_AC, 4, 1}, /* Absolute,Y */ + /* fa */ { ".NOOP", IMPLIED, M_NONE, M_NONE, 2, 0}, + /* fb */ { ".ISB", ABSOLUTE_Y, M_ABSY, M_ABSY, 7, 0}, + + /* fc */ { ".NOOP", ABSOLUTE_X, M_NONE, M_NONE, 4, 1}, + /* fd */ { "SBC", ABSOLUTE_X, M_ABSX, M_AC, 4, 1}, /* Absolute,X */ + /* fe */ { "INC", ABSOLUTE_X, M_ABSX, M_ABSX, 7, 0}, /* Absolute,X */ + /* ff */ { ".ISB", ABSOLUTE_X, M_ABSX, M_ABSX, 7, 0} +}; diff --git a/vcs.c b/vcs.c new file mode 100644 index 0000000..baba540 --- /dev/null +++ b/vcs.c @@ -0,0 +1,89 @@ +char stella[62][10] = +{"VSYNC", +"VBLANK", +"WSYNC", +"RSYNC", +"NUSIZ0", +"NUSIZ1", +"COLUP0", +"COLUP1", +"COLUPF", +"COLUBK", +"CTRLPF", +"REFP0", +"REFP1", +"PF0", +"PF1", +"PF2", +"RESP0", +"RESP1", +"RESM0", +"RESM1", +"RESBL", +"AUDC0", +"AUDC1", +"AUDF0", +"AUDF1", +"AUDV0", +"AUDV1", +"GRP0", +"GRP1", +"ENAM0", +"ENAM1", +"ENABL", +"HMP0", +"HMP1", +"HMM0", +"HMM1", +"HMBL", +"VDELP0", +"VDEL01", +"VDELBL", +"RESMP0", +"RESMP1", +"HMOVE", +"HMCLR", +"CXCLR", +"$2D", +"$2E", +"$2F", +"CXM0P", +"CXM1P", +"CXP0FB", +"CXP1FB", +"CXM0FB", +"CXM1FB", +"CXBLPF", +"CXPPMM", +"INPT0", +"INPT1", +"INPT2", +"INPT3", +"INPT4", +"INPT5"}; + +char ioregs[24][10] = +{"SWCHA", +"SWACNT", +"SWCHB", +"SWBCNT", +"INTIM", +"$0285", +"$0286", +"$0287", +"$0288", +"$0289", +"$028A", +"$028B", +"$028C", +"$028D", +"$028E", +"$028F", +"$0290", +"$0291", +"$0292", +"$0293", +"TIM1T", +"TIM8T", +"TIM64T", +"T1024T"};