/* * 65C02 Disassembler for DOS, Version 1.11 * (C)Copyright Steve Nickolas and Dapple Project, 2002 * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the author nor the names of other contributors may * be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include /* This program is designed with Marat Fayzullin's M6502 in mind. Standalone, we just read the opcodes from an array. */ #define Rd6502(x) data[x] #ifdef __TURBOC__ #define MAXRAM 53248 /* 52K */ #else #define MAXRAM 65536 /* Full 64K */ #endif static unsigned char data[MAXRAM]; /* File limit */ /* The length of the file. */ unsigned long int fl; /* Information on the opcodes is defined in two components: the name of the opcode in printf() format, and a method used to determine what variables should be fed into the printf() and how far the IP should be pushed ahead. Opcodes are given in the format used by the Apple ][ disassembler. */ typedef enum {Nothing, Byte, Word, Relative} NumberSize; typedef struct { char Description[20]; NumberSize Method; } Opcode; Opcode Ops[256]= { "BRK",Nothing, /* $00 */ "ORA ($%02X,X)",Byte, "???",Nothing, "???",Nothing, "TSB $%02X",Byte, "ORA $%02X",Byte, "ASL $%02X",Byte, "???",Nothing, "PHP",Nothing, /* $08 */ "ORA #$%02X",Byte, "ASL",Nothing, "???",Nothing, "TSB $%04X",Word, "ORA $%04X",Word, "ASL $%04X",Word, "???",Nothing, "BPL $%04X",Relative, /* $10 */ "ORA ($%02X),Y",Byte, "ORA ($%02X)",Byte, "???",Nothing, "TRB $%02X",Byte, "ORA $%02X,X",Byte, "ASL $%02X,X",Byte, "???",Nothing, "CLC",Nothing, /* $18 */ "ORA $%04X,Y",Word, "INC",Nothing, "???",Nothing, "TRB $%04X",Word, "ORA $%04X,X",Word, "ASL $%04X,X",Word, "???",Nothing, "JSR $%04X",Word, /* $20 */ "AND ($%02X,X)",Byte, "???",Nothing, "???",Nothing, "BIT $%02X",Byte, "AND $%02X",Byte, "ROL $%02X",Byte, "???",Nothing, "PLP",Nothing, /* $28 */ "AND #$%02X",Byte, "ROL",Byte, "???",Nothing, "BIT $%04X",Word, "AND $%04X",Word, "ROL $%04X",Word, "???",Nothing, "BMI $%04X",Relative, /* $30 */ "AND ($%02X),Y",Byte, "AND ($%02X)",Byte, "???",Nothing, "BIT $%02X,X",Byte, "AND $%02X,X",Byte, "ROL $%02X,X",Byte, "???",Nothing, "SEC",Nothing, /* $38 */ "AND $%04X,Y",Word, "DEC",Nothing, "???",Nothing, "BIT $%04X,X",Word, "AND $%04X,X",Word, "ROL $%04X,X",Word, "???",Nothing, "RTI",Nothing, /* $40 */ "EOR ($%02X,X)",Byte, "???",Nothing, "???",Nothing, "???",Nothing, "EOR $%02X",Byte, "LSR $%02X",Byte, "???",Nothing, "PHA",Nothing, /* $48 */ "EOR #$%02X",Byte, "LSR",Nothing, "???",Nothing, "JMP $%04X",Word, "EOR $%04X",Word, "LSR $%04X",Word, "???",Nothing, "BVC $%04X",Relative, /* $50 */ "EOR ($%02X),Y",Byte, "EOR ($%02X)",Byte, "???",Nothing, "???",Nothing, "EOR $%02X,X",Byte, "LSR $%02X,X",Byte, "???",Nothing, "CLI",Nothing, /* $58 */ "EOR $%04X,Y",Word, "PHY",Nothing, "???",Nothing, "???",Nothing, "EOR $%04X,X",Word, "LSR $%04X,X",Word, "???",Nothing, "RTS",Nothing, /* $60 */ "ADC ($%02X,X)",Byte, "???",Nothing, "???",Nothing, "STZ $%02X",Byte, "ADC $%02X",Byte, "ROR $%02X",Byte, "???",Nothing, "PLA",Nothing, /* $68 */ "ADC #$%02X",Byte, "ROR",Nothing, "???",Nothing, "JMP ($%04X)",Word, "ADC $%04X",Word, "ROR $%04X",Word, "???",Nothing, "BVS $%04X",Relative, /* $70 */ "ADC ($%02X),Y",Byte, "ADC ($%02X)",Byte, "???",Nothing, "STZ $%02X,X",Byte, "ADC $%02X,X",Byte, "ROR $%02X,X",Byte, "???",Nothing, "SEI",Nothing, /* $78 */ "ADC $%04X,Y",Word, "PLY",Nothing, "???",Nothing, "JMP ($%04X,X)",Word, "ADC $%04X,X",Word, "ROR $%04X,X",Word, "???",Nothing, "BRA $%04X",Relative, /* $80 */ "STA ($%02X,X)",Byte, "???",Nothing, "???",Nothing, "STY $%02X",Byte, "STA $%02X",Byte, "STX $%02X",Byte, "???",Nothing, "DEY",Nothing, /* $88 */ "BIT #$%02X",Byte, "TXA",Nothing, "???",Nothing, "STY $%04X",Word, "STA $%04X",Word, "STX $%04X",Word, "???",Nothing, "BCC $%04X",Relative, /* $90 */ "STA ($%02X),Y",Byte, "STA ($%02X)",Byte, "???",Nothing, "STY $%02X,X",Byte, "STA $%02X,X",Byte, "STX $%02X,Y",Byte, "???",Nothing, "TYA",Nothing, /* $98 */ "STA $%04X,Y",Word, "TXS",Nothing, "???",Nothing, "STZ $%04X",Word, "STA $%04X,X",Word, "STZ $%04X,X",Word, "???",Nothing, "LDY #$%02X",Byte, /* $A0 */ "LDA ($%02X,X)",Byte, "LDX #$%02X",Byte, "???",Nothing, "LDY $%02X",Byte, "LDA $%02X",Byte, "LDX $%02X",Byte, "???",Nothing, "TAY",Nothing, /* $A8 */ "LDA #$%02X",Byte, "TAX",Nothing, "???",Nothing, "LDY $%04X",Word, "LDA $%04X",Word, "LDX $%04X",Word, "???",Nothing, "BCS $%04X",Relative, /* $B0 */ "LDA ($%02X),Y",Byte, "LDA ($%02X)",Byte, "???",Nothing, "LDY $%02X,X",Byte, "LDA $%02X,X",Byte, "LDX $%02X,Y",Byte, "???",Nothing, "CLV",Nothing, /* $B8 */ "LDA $%04X,Y",Word, "TSX",Nothing, "???",Nothing, "LDY $%04X,X",Word, "LDA $%04X,X",Word, "LDX $%04X,Y",Word, "???",Nothing, "CPY #$%02X",Byte, /* $C0 */ "CMP ($%02X,X)",Byte, "???",Nothing, "???",Nothing, "CPY $%02X",Byte, "CMP $%02X",Byte, "DEC $%02X",Byte, "???",Nothing, "INY",Nothing, /* $C8 */ "CMP #$%02X",Byte, "DEX",Nothing, "???",Nothing, "CPY $%04X",Word, "CMP $%04X",Word, "DEC $%04X",Word, "???",Nothing, "BNE $%04X",Relative, /* $D0 */ "CMP ($%02X),Y",Byte, "CMP ($%02X)",Byte, "???",Nothing, "???",Nothing, "CMP $%02X,X",Byte, "DEC $%02X,X",Byte, "???",Nothing, "CLD",Nothing, /* $D8 */ "CMP $%04X,Y",Word, "PHX",Nothing, "???",Nothing, "???",Nothing, "CMP $%04X,X",Word, "DEC $%04X,X",Word, "???",Nothing, "CPX #$%02X",Byte, /* $E0 */ "SBC ($%02X,X)",Byte, "???",Nothing, "???",Nothing, "CPX $%02X",Byte, "SBC $%02X",Byte, "INC $%02X",Byte, "???",Nothing, "INX",Nothing, /* $E8 */ "SBC #$%02X",Byte, "NOP",Nothing, "???",Nothing, "CPX $%04X",Word, "SBC $%04X",Word, "INC $%04X",Word, "???",Nothing, "BEQ $%04X",Relative, /* $F0 */ "SBC ($%02X),Y",Byte, "SBC ($%02X)",Byte, "???",Nothing, "???",Nothing, "SBC $%02X,X",Byte, "INC $%02X,X",Byte, "???",Nothing, "SED",Nothing, /* $F8 */ "SBC $%04X,Y",Word, "PLX",Nothing, "???",Nothing, "???",Nothing, "SBC $%04X,X",Word, "INC $%04X,X",Word, "???",Nothing }; /* dasm() Disassemble one opcode: given a buffer in which to copy the disassembly and the current IP, returns the new IP. */ int dasm(char *output, unsigned short int address) { /* Addresses used by branch ops are relative to address+2, signed char */ unsigned int num; signed char Rel; unsigned int p1,p2; unsigned int op; char Desc[128]; /* We must keep the compiler from promoting signed values, so often we explicitly cast to unsigned char. */ op=(unsigned char)Rd6502(address); if (Ops[op].Method==Nothing) /* Just the opcode */ { sprintf (output,"%04X: %02X %s",address,op,Ops[op].Description); return address+1; } if (Ops[op].Method==Byte) /* Two byte opcode */ { sprintf (Desc,Ops[op].Description,(unsigned char)(p1=Rd6502(address+1))); sprintf (output,"%04X: %02X%02X %s",address,op,(unsigned char)p1,Desc); return address+2; } if (Ops[op].Method==Word) /* Three byte opcode */ { p1=(unsigned char)Rd6502(address+1); p2=(unsigned char)Rd6502(address+2); num=p1+(p2<<8); /* Construct low-endian number */ sprintf (Desc,Ops[op].Description,num); sprintf (output,"%04X: %02X%02X%02X %s",address,op,p1,p2,Desc); return address+3; } if (Ops[op].Method==Relative) /* Two byte opcode */ { /* This is a little tricky... The relative address is a signed char. */ Rel=Rd6502(address+1); num=(address+2); num+=Rel; /* this CAN subtract; remember a+(-b) is the same as a-b */ sprintf (Desc,Ops[op].Description,(unsigned int)num); sprintf (output,"%04X: %02X%02X %s",address,op,(unsigned char)Rel,Desc); return address+2; } /* error : should never reach this point but placate compilers */ return -1; } /* Gives syntax for program */ void usage(char *progname) { fprintf (stderr,"\ dasmc02 - 65C02 Disassembler, version 1.11\n\ (C)Copyright 2002 Steve Nickolas and Dapple Project\n\ \n"); fprintf (stderr,"\ usage: %s [-h] [-oxxxx] file\n\ \n\ -h ignored - for compatibility with Marat Fayzullin's disassembler\n\ -oxxxx starting address of program is xxxx (in hexadecimal)\n\ ",progname); } int main(int argc, char **argv) { unsigned long int addr=0, ip; FILE *file; int travel; for (travel=1; (travel, steve@dosius.zzn.com (Dosius) wrote: > file=fopen(argv[travel],"rb"); > > > fseek(file,0,SEEK_END); > Sore thumb, out, stick. I haven't read the code yet but with a very quick glance I immediately noticed some Undefined Behavior. The ANSI C Standard X3.159-1989 (superceeded by ISO/IEC 9899:1990 but with exactly the same language) cleary states in paragraph 4.9.9.2 that "A binary stream need not meaningfully support fseek calls with a whence value of SEEK_END." The prototype of fseek being, of course: int fseek(FILE *stream, long int offset, int whence); This is a commonly used technique by Unix programmers but it is a serious error in C. It renders your program unportable (by standard C definitions, if not in practice). There is an explanation in the C Rationale as to why fseek() and ftell() were left in Standard C even though their behaviors can have very little meaning outside of Unix. Just like the abominable gets() function, fseek() and ftell() were left in the language due to the volume of programs using them written prior to the standardization of the language, plus some clear value when used properly. Just because it is there doesn't mean you should use it. Because the normative text is slightly ambiguous the current C Standard (ISO/IEC 9899:1999) has added a footnote: Footnote 225: Setting the file position indicator to end-of-file, as with fseek(file,0,SEEK_END), has undefined behavior for a binary stream (because of possible trailing null characters) or for any stream with state-dependent encoding that does not assuredly end in the initial shift state. (OT: One example of a text file with state-dependent encoding is one that uses utf-8.) My suggestion is that you needn't change this program if you're not so inclined, just heavily document that it invokes Undefined Behavior, and then correct your (common but) faulty C programming practice in future programs. C is very dangerous when not clearly understood. It's syntactical simplicity masks the complexities of it's subtleties.