C C ****************************************************************** C * * C * Y 6 5 0 2 S I M U L A T O R * C * * C * (c) Peter Coghlan 1991 * C * * C * converted from the Z80 Simulator * C * (c) Peter Coghlan & Niall Downey 1988 * C * * C ****************************************************************** C C This is a 6502 simulator. It takes a single parameter which gives C the name of a file containing 6502 code in hex format. The file C should be in VC format with records less than 105 characters long. C It should have a # in the second position of each line followed by C four hex digits specifying an address followed by an even number C of hex digits specifying code to be loaded starting at that C addressa. The code is followed by an & and two hex digits which C form a mod 255 checksum for code in this record. The file may be C terminated with a $ in the second position of the last line. C Records containing a % in the second position are used to describe C the particular flavour of 6502 to be simulated and to specify C memory addresses which should be intercepted to perform I/O. C C The following comments relate to leftovers from the Z80 simulator C which don't do anything in the 6502 simulator: C C Options to be used by the simulator can be passed to the program C on the command line when it is called, these options are, C C name1 IN name2 OUT name3 LIST name4 NMI nnn IRQ nnn EX nnnn DEBUG C C Where 'name1' is the name of the hexadecimal machine code file, C 'name2' is the name of a file from which input data is read, C 'name3' is the name of a file to which output data goes, C 'name4' is the name of a trace file, C NMI nnn causes NMI's every nnn instructions, C IRQ nnn causes IRQ's every nnn instructions, C EX nnnn starts the machine code program at address nnnn hex. C DEBUG enables screen assembly listing and trace file C The name of the machine code file must be specified i.e. 'name1', C C Initialise variables and 'memory' C IMPLICIT INTEGER (A-Z) INTEGER OPTLEN(7),TRAP(2) integer nbytes,bytes(48),debug /0/,extend /0/ CHARACTER BUFFER*104,DESC(3)*5 CHARACTER PFILE*22,IPFILE*22,OPFILE*22,SCRFLE*22,OPTS*29 LOGICAL FLAG LOGICAL FOUND,ORGFLG,EXFLAG,GOTMEM(2),BADFRM C COMMON /COUNT/ BYTES C COMMON /Y6502_REGisterS/ PC,A,X,Y,P,SP,Flags C COMMON /Y6502_TRAPS/ TRAP DATA NMI,INT/2*0/ C DATA PFILE,IPFILE/' ','Y6502.IN'/ DATA OPFILE/'Y6502.OUT'/ DATA OPTS/'NMI IRQ OUT IN EX LIST DEBUG '/ DATA DESC /'OPSYS','IO ','MODE '/ DATA BUFFER /' '/, OPTLEN /4,4,4,3,3,5,6/ DATA ORGFLG,EXFLAG,GOTMEM,BADFRM /5*.FALSE./ DATA DESCS /3/ External BBC_Mos, BBC_Trace, BBC_Invalid A=0 X=0 Y=0 P=32 S=0 TRAP(1)=-1 TRAP(2)=-1 C C ###################################################################### C C Get option parameters from command line. C call lib$get_foreign(buffer, 'Program file :', len) IF (LEN.EQ.0) THEN WRITE(6,1000) 1000 FORMAT(/' *Error : program file not specified') CALL exit(80) ENDIF BUFPOS=1 CALL FILNME(BUFFER,BUFPOS,PFILE) 1001 IF (BUFPOS.GE.LEN) GOTO 1099 IF (BUFFER(BUFPOS:BUFPOS).NE.' ') GOTO 1002 BUFPOS=BUFPOS+1 IF (BUFPOS.GT.80) GOTO 1006 GOTO 1001 1002 OPTPOS=1 OPTN=1 1003 IF (OPTN.GT.7) GOTO 1004 flag = (buffer(bufpos:bufpos+optlen(optn)-1).eq. & opts(optpos:optpos+optlen(optn)-1)) OPTPOS=OPTPOS+OPTLEN(OPTN) OPTN=OPTN+1 IF (.NOT.FLAG) GOTO 1003 BUFPOS=BUFPOS+OPTLEN(OPTN-1) IF (BUFPOS.GT.80) GOTO 1006 IF (OPTN.EQ.2) NMI=NUMBER(10,BUFFER,BUFPOS) IF (OPTN.EQ.3) INT=NUMBER(10,BUFFER,BUFPOS) IF (OPTN.EQ.4) CALL FILNME(BUFFER,BUFPOS,OPFILE) IF (OPTN.EQ.5) CALL FILNME(BUFFER,BUFPOS,IPFILE) IF (OPTN.EQ.6) THEN PC=NUMBER(16,BUFFER,BUFPOS) EXFLAG=.TRUE. ENDIF IF (OPTN.EQ.7) THEN CALL FILNME(BUFFER,BUFPOS,SCRFLE) open(unit=8,iostat=ret,name=scrfle,status='unknown') if (ret .ne. 0) then write (6,'(/'' *Error : Cannot open trace file'')') call exit(ret) endif ENDIF IF (OPTN.EQ.8) DEBUG=128 GOTO 1001 1004 WRITE (6,1005) 1005 FORMAT(/' *Error : Unknown options on command line') CALL exit(81) 1006 WRITE (6,1007) 1007 FORMAT(/' *Error : Command line too long') CALL exit(82) 1099 CONTINUE C C ###################################################################### C C Read machine code program into memory at appropriate location open(unit=10,iostat=rc,name=pfile,status='old',readonly) C IF (RC.EQ.30) THEN C WRITE(6,2000)PFILE C 2000 FORMAT(/' *Error : File not found ',A22) C CALL exit(rc) if (rc.ne.0) then write(6,'(/'' *Error : Cannot open program file : '',A22)') & pfile call exit(rc) ENDIF 2001 read (10,'(A104)',iostat=rc) buffer IF (BUFFER(1:1).EQ.'1') BUFFER(1:1)=' ' IF (BUFFER(1:2).EQ.' #'.AND.BUFFER(3:3).NE.'R'.AND.RC.EQ.0) THEN cksum = index(buffer,'&') nbytes = (cksum - 7) / 2 if (nbytes .gt. 0 .and. mod(cksum, 2) .eq. 1) then read (buffer,'(2X,Z4,48Z2)') ptr, (bytes(i),i = 1, nbytes) read (buffer(cksum:),'(1x,Z2)') cksum CKSUM=CKSUM-iAND(PTR,255) CKSUM=CKSUM-PTR/256 DO 2004 I=0,nbytes-1 CALL Y6502_Write_Memory(ptr+i,bytes(i+1)) CKSUM=CKSUM-bytes(I+1) IF (ptr+I.EQ.65532.OR.ptr+I.EQ.65533) GOTMEM(ptr+i-65531)=.TRUE. 2004 CONTINUE IF (.NOT.ORGFLG) THEN ORG=PTR ORGFLG=.TRUE. ENDIF ELSE BADFRM=.TRUE. ENDIF ELSE IF (BUFFER(3:3).EQ.'R'.AND.RC.EQ.0) THEN WRITE(6,2005) 2005 FORMAT(/' *Error : Code requiring relocation not usable') CALL exit(84) ELSE IF (BUFFER(1:2).EQ.' %'.AND.RC.EQ.0) THEN LENGTH=INDEX(BUFFER(3:),'=')-1 IF (LENGTH.LT.1) THEN WRITE(6,2006) BUFFER(:28) 2006 FORMAT(/' *Error : Invalid system descriptor - ',A28) CALL exit(88) ENDIF DO 2007 I=1,DESCS IF (BUFFER(3:LENGTH+2).EQ.DESC(I)(1:LENGTH)) THEN IF (I.NE.3) THEN TRAP(I)=NUMBER(16,BUFFER,LENGTH+4) ELSE IF (BUFFER(LENGTH+4:LENGTH+12).EQ.'STANDARD ') THEN EXTEND= 0 ELSEIF (BUFFER(LENGTH+4:LENGTH+12).EQ.'EXTENDED ') THEN EXTEND= 64 ELSE WRITE (6,2008) BUFFER(:28)//' ' ENDIF ENDIF FOUND=.TRUE. ENDIF 2007 CONTINUE IF (.NOT.FOUND) THEN WRITE(6,2008) BUFFER(:LENGTH)//' ' 2008 FORMAT(/' *Error : urecognised system descriptor - ',A28) CALL exit(89) ENDIF ELSE IF (RC.EQ.0) THEN BADFRM=.TRUE. ELSE IF (RC.NE.-1) THEN write (6,'(/'' *Error : Bad return code reading from file : '',A22)') & pfile CALL exit(RC) ENDIF IF (BADFRM) THEN WRITE(6,2009) 2009 FORMAT(/' *Error : Assembly program format incorrect') CALL exit(83) ELSE IF (iAND(CKSUM,255).NE.0) THEN WRITE(6,2010) iAND(CKSUM,255) 2010 FORMAT(/' *Error : Assembly program checksum error : ',Z2) CALL exit(83) ENDIF IF (BUFFER(1:1).NE.'$'.AND.RC.EQ.0) GOTO 2001 close (unit=10) IF (.NOT.EXFLAG) THEN IF (GOTMEM(1).AND.GOTMEM(2)) THEN PC=Y6502_Read_Memory(65532)+Y6502_Read_Memory(65533)*256 ELSE PC=ORG ENDIF ENDIF WRITE(6,2011)PFILE,PC C WRITE(8,2011)PFILE,PC 2011 FORMAT(/' Program file : ',A22/ * ' Starting execution at ',Z4,' hex'/) Call BBC_Screen_Init Status = Y6502_Execute(PC, , , , , , DEBUG+EXTEND, Trap(1), * BBC_Mos, BBC_Trace, BBC_Invalid) CALL exit(Status) C C Clear screen and print header C IF (DEBUG) THEN WRITE(6,3001) C WRITE(8,3001) 3001 FORMAT('1 PC Instruction Assembly',T37, $ 'Addr Data A X Y SP N V B D I Z C'/) ENDIF STOP END C C C ###################################################################### C C Subroutines and functions C C Report execution of an invalid opcode C Subroutine BBC_Invalid(Address) Implicit None Integer Address, Y6502_Read_Memory Write (*, 1) Y6502_Read_Memory(Address), Address 1 Format(' * Unrecognised opcode ',Z2,' at address ',Z4,' *') Return End C C List Debug information on the screen C SUBROUTINE BBC_Trace(OPADDR,a,x,y,p,sp,ADDR) IMPLICIT INTEGER (A-Z) INTEGER FLAGS(0:6) CHARACTER OPCSTR*3, OPDSTR*9,ADDSTR*4,DATSTR*2 CHARACTER*3 OPCODS(0:128) / + 'BRK', 'ORA', '???', '???', 'TSB', 'ORA', 'ASL', 'RMB', + 'PHP', 'ORA', 'ASL', '???', 'TSB', 'ORA', 'ASL', 'BBR', + 'BPL', 'ORA', 'ORA', '???', 'TRB', 'ORA', 'ASL', 'RMB', + 'CLC', 'ORA', 'INA', '???', 'TRB', 'ORA', 'ASL', 'BBR', + 'JSR', 'AND', '???', '???', 'BIT', 'AND', 'ROL', 'RMB', + 'PLP', 'AND', 'ROL', '???', 'BIT', 'AND', 'ROL', 'BBR', + 'BMI', 'AND', 'AND', '???', 'BIT', 'AND', 'ROL', 'RMB', + 'SEC', 'AND', 'DEA', '???', 'BIT', 'AND', 'ROL', 'BBR', + 'RTI', 'EOR', '???', '???', '???', 'EOR', 'LSR', 'RMB', + 'PHA', 'EOR', 'LSR', '???', 'JMP', 'EOR', 'LSR', 'BBR', + 'BVC', 'EOR', 'EOR', '???', '???', 'EOR', 'LSR', 'RMB', + 'CLI', 'EOR', 'PHY', '???', '???', 'EOR', 'LSR', 'BBR', + 'RTS', 'ADC', '???', '???', 'STZ', 'ADC', 'ROR', 'RMB', + 'PLA', 'ADC', 'ROR', '???', 'JMP', 'ADC', 'ROR', 'BBR', + 'BVS', 'ADC', 'ADC', '???', 'STZ', 'ADC', 'ROR', 'RMB', + 'SEI', 'ADC', 'PLY', '???', 'JMP', 'ADC', 'ROR', 'BBR', 'BRA' / CHARACTER*3 DUMMY(128) / + 'BRA', 'STA', '???', '???', 'STY', 'STA', 'STX', 'SMB', + 'DEY', 'BIT', 'TXA', '???', 'STY', 'STA', 'STX', 'BBS', + 'BCC', 'STA', 'STA', '???', 'STY', 'STA', 'STX', 'SMB', + 'TYA', 'STA', 'TXS', '???', 'STZ', 'STA', 'STZ', 'BBS', + 'LDY', 'LDA', 'LDX', '???', 'LDY', 'LDA', 'LDX', 'SMB', + 'TAY', 'LDA', 'TAX', '???', 'LDY', 'LDA', 'LDX', 'BBS', + 'BCS', 'LDA', 'LDA', '???', 'LDY', 'LDA', 'LDX', 'SMB', + 'CLV', 'LDA', 'TSX', '???', 'LDY', 'LDA', 'LDX', 'BBS', + 'CPY', 'CMP', '???', '???', 'CPY', 'CMP', 'DEC', 'SMB', + 'INY', 'CMP', 'DEX', '???', 'CPY', 'CMP', 'DEC', 'BBS', + 'BNE', 'CMP', 'CMP', '???', '???', 'CMP', 'DEC', 'SMB', + 'CLD', 'CMP', 'PHX', '???', '???', 'CMP', 'DEC', 'BBS', + 'CPX', 'SBC', '???', '???', 'CPX', 'SBC', 'INC', 'SMB', + 'INX', 'SBC', 'NOP', '???', 'CPX', 'SBC', 'INC', 'SMB', + 'BEQ', 'SBC', 'SBC', '???', '???', 'SBC', 'INC', 'SMB', + 'SED', 'SBC', 'PLX', '???', '???', 'SBC', 'INC', 'SMB' / EQUIVALENCE (OPCODS(128),DUMMY(1)) Integer*2 Modes(0:255) / + 02, 00, 14, 14, 01, 01, 01, 14, 14, 02, 13, 14, 03, 03, 03, 14, + 09, 04, 11, 14, 01, 05, 05, 14, 14, 06, 13, 14, 03, 07, 07, 14, + 03, 00, 14, 14, 01, 01, 01, 14, 14, 02, 13, 14, 03, 03, 03, 14, + 09, 04, 11, 14, 05, 05, 05, 14, 14, 06, 13, 14, 07, 07, 07, 14, + 14, 00, 14, 14, 14, 01, 01, 14, 14, 02, 13, 14, 03, 03, 03, 14, + 09, 04, 11, 14, 14, 05, 05, 14, 14, 06, 13, 14, 14, 07, 07, 14, + 14, 00, 14, 14, 01, 01, 01, 14, 14, 02, 13, 14, 08, 03, 03, 14, + 09, 04, 11, 14, 05, 05, 05, 14, 14, 06, 13, 14, 12, 07, 07, 14, + 09, 00, 14, 14, 01, 01, 01, 14, 14, 02, 14, 14, 03, 03, 03, 14, + 09, 04, 11, 14, 05, 05, 10, 14, 14, 06, 14, 14, 03, 07, 07, 14, + 02, 00, 02, 14, 01, 01, 01, 14, 14, 02, 14, 14, 03, 03, 03, 14, + 09, 04, 11, 14, 05, 05, 10, 14, 14, 06, 14, 14, 07, 07, 06, 14, + 02, 00, 14, 14, 01, 01, 01, 14, 14, 02, 14, 14, 03, 03, 03, 14, + 09, 04, 11, 14, 14, 05, 05, 14, 14, 06, 14, 14, 14, 07, 07, 14, + 02, 00, 14, 14, 01, 01, 01, 14, 14, 02, 14, 14, 03, 03, 03, 14, + 09, 04, 11, 14, 14, 05, 05, 14, 14, 06, 14, 14, 14, 07, 07, 14 / C OPCODE=Y6502_Read_Memory(OPADDR) OPCSTR=OPCODS(OPCODE) Mode = Modes(Opcode) CALL GTOPND(OPADDR,MODE,BYTES,OPDSTR) DO 3101 I=7,0,-1 IF (I.GE.5) THEN J=7-I ELSE J=6-I ENDIF FLAGS(J)=iAND(p,2**I)/2**I 3101 CONTINUE C IF (MODE.NE.13.AND.MODE.NE.14.AND.OPCODE.NE.0) THEN write (addstr,'(z4.4)') addr DATSTR=' ' ELSE ADDSTR=' ' DATSTR=' ' ENDIF C GOTO (3102,3103,3104),BYTES C 3102 WRITE(6,3106)OPADDR,OPCODE,OPCSTR,OPDSTR, * ADDSTR,DATSTR,a,x,y,sp,FLAGS C WRITE(8,3106)OPADDR,OPCODE,OPCSTR,OPDSTR,ADDSTR, C * DATSTR,a,x,y,sp,FLAGS RETURN 3103 WRITE(6,3107)OPADDR,(Y6502_Read_Memory(OPADDR+J),J=0,1),OPCSTR,OPDSTR, * ADDSTR,DATSTR,a,x,y,sp,FLAGS C WRITE(8,3107)OPADDR,(Y6502_Read_Memory(OPADDR+J),J=0,1),OPCSTR,OPDSTR, C * ADDSTR,DATSTR,a,x,y,sp,FLAGS RETURN 3104 WRITE(6,3108)OPADDR,(Y6502_Read_Memory(OPADDR+J),J=0,2),OPCSTR,OPDSTR, * ADDSTR,DATSTR,a,x,y,sp,FLAGS C WRITE(8,3108)OPADDR,(Y6502_Read_Memory(OPADDR+J),J=0,2),OPCSTR,OPDSTR, C * ADDSTR,DATSTR,a,x,y,sp,FLAGS RETURN 3106 FORMAT(1X,Z4.4,1(2X,Z2.2),11X,A3,1X,A9,3X,A4,2X,A2,1X,1X,3X, * 3(Z2.2,2X),'1',Z2.2,4X,7(I1,1X)) 3107 FORMAT(1X,Z4.4,2(2X,Z2.2),07X,A3,1X,A9,3X,A4,2X,A2,1X,1X,3X, * 3(Z2.2,2X),'1',Z2.2,4X,7(I1,1X)) 3108 FORMAT(1X,Z4.4,3(2X,Z2.2),03X,A3,1X,A9,3X,A4,2X,A2,1X,1X,3X, * 3(Z2.2,2X),'1',Z2.2,4X,7(I1,1X)) END C C Function to extract a decimal or hex number from the command line C options or a string read in from the keyboard C FUNCTION NUMBER(BASE,BUFFER,BUFPOS) INTEGER NUMBER,BASE,BUFPOS,CHAR CHARACTER BUFFER*104 NUMBER=0 CHAR=0 1 if (buffer(bufpos:bufpos).ne.' ') goto 2 BUFPOS=BUFPOS+1 IF (BUFPOS.GT.80) GOTO 3 GOTO 1 2 char = ichar(buffer(bufpos:bufpos)) BUFPOS=BUFPOS+1 IF (CHAR.EQ.32.OR.CHAR.EQ.107) GOTO 3 IF (CHAR.LT.48.OR.(CHAR.GT.57.AND.CHAR.LT.65).OR.CHAR.GT.70) * GOTO 4 IF (CHAR.GT.57.AND.BASE.EQ.10) GOTO 4 NUMBER=NUMBER*BASE+iAND(CHAR,15) IF (CHAR.GT.57) NUMBER=NUMBER+9 IF (BUFPOS.LE.80) GOTO 2 3 RETURN 4 WRITE(6,5) 5 FORMAT(/' *Error : Input contains illegal numerical characters') CALL exit(87) END C C Subroutine to extract a filename from the command line options C SUBROUTINE FILNME(BUFFER,I,FNAME) character buffer*104, fname*22 fname = ' ' 1 if (buffer(i:i).ne.' ') goto 2 I=I+1 IF (I.GT.80) GOTO 4 GOTO 1 2 J=1 3 FNAME(J:j)=BUFFER(I:i) I=I+1 J=J+1 if (buffer(i:i).eq.' '.or. buffer(i:i).eq.',') goto 6 IF (J.EQ.23) GOTO 4 IF (I.LE.80) GOTO 3 4 WRITE(6,5) 5 FORMAT(/' *Error : Invalid filename') CALL exit(12) 6 I=I+1 RETURN END C C Subroutine to return the operand of an instruction in a printable C form suitable for display in the assembly language listing. C Also return the number of bytes in the instruction. C SUBROUTINE GTOPND(OPADDR,MODE,BYTES,OPDSTR) IMPLICIT INTEGER (A-Z) Integer *2 Mode CHARACTER HEXSTR*8,OPDSTR*9 PC=iAND(OPADDR+1,65535) LOBYTE=Y6502_Read_Memory(PC) PC=iAND(PC+1,65535) HIBYTE=Y6502_Read_Memory(PC) write (hexstr,'(Z8.8)') lobyte+hibyte*256 GOTO (100,101,102,103,104,105,106,107,108,109,110,111,112,113,114) + , MODE+1 C C (ind,x) C 100 OPDSTR='(&'//HEXSTR(7:8)//',X)' BYTES=2 RETURN C C zp C 101 OPDSTR='&'//HEXSTR(7:8) BYTES=2 RETURN C C # imm C 102 OPDSTR='# &'//HEXSTR(7:8) BYTES=2 RETURN C C abs C 103 OPDSTR='&'//HEXSTR(5:8) BYTES=3 RETURN C C (ind),y C 104 OPDSTR='(&'//HEXSTR(7:8)//'),Y' BYTES=2 RETURN C C zp,x C 105 OPDSTR='&'//HEXSTR(7:8)//',X' BYTES=2 RETURN C C abs,y C 106 OPDSTR='&'//HEXSTR(5:8)//',Y' BYTES=3 RETURN C C abs,x C 107 OPDSTR='&'//HEXSTR(5:8)//',X' BYTES=3 RETURN C C (abs) C 108 OPDSTR='(&'//HEXSTR(5:8)//')' BYTES=3 RETURN C C rel C 109 OPAND=LOBYTE IF (LOBYTE.GE.128) LOBYTE=LOBYTE-256 ADDR=iAND(PC+LOBYTE,65535) write (opdstr,'(''&''z4.4)') addr BYTES=2 RETURN C C zp,y C 110 OPDSTR='&'//HEXSTR(7:8)//',Y' BYTES=2 RETURN C C (ind) C 111 OPDSTR='(&'//HEXSTR(7:8)//')' BYTES=2 RETURN C C (abs,x) C 112 OPDSTR='(&'//HEXSTR(5:8)//',X)' BYTES=3 RETURN C C accum C 113 OPDSTR='A' BYTES=1 RETURN C C implied C 114 OPDSTR=' ' BYTES=1 RETURN END