/FILE SYSPRINT NAME($Y65:Y6502F.LIS) NR(4000) NEW(REPLACE) LRECL(133) /FILE SYSPUNCH NAME($Y65:Y6502F.OBJ) NR(4000) NEW(REPLACE) /SYS REGION=700K /LOAD VSFORT /OPT OPT(1),DECK,SOURCE,LIST /JOB NOGO 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 which reads in hexadecimal machine code C from a file which is VC and records less than 105 characters long C and has a # in the second position of each line and an & as the C third last character on each line. The file may be terminated C with a $ in the second position of the last line. 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 POS(104),FLAGS*2(0:6),OPTLEN(7),TRAP(2) CHARACTER OPCSTR*3,OPDSTR*9,BUFFER*104,PREFIX,DESC*5(3),ADDSTR*8 CHARACTER PFILE*22,IPFILE*22,OPFILE*22,SCRFLE*22,OPTS*29,DATSTR*8 CHARACTER*8 PCODE LOGICAL COND,IPFLAG,OPFLAG,EQUAL,EQUALB,FLAG LOGICAL BRANCH,EXTEND,FOUND,DEBUG,ORGFLG,EXFLAG,GOTMEM(2),BADFRM COMMON /COUNT/ BYTES COMMON /REGS/ A,X,Y,P,PC,S COMMON /TRAPS/ TRAP DATA NMICNT,IRQCNT,NMI,INT/4*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 IPFLAG,OPFLAG/2*.FALSE./ DATA ORGFLG,EXFLAG,GOTMEM,BADFRM /5*.FALSE./ DATA DESCS /3/ A=0 X=0 Y=0 P=32 S=0 TRAP(1)=-1 TRAP(2)=-1 C CALL NPRMPT C C ###################################################################### C C Get option parameters from command line. C CALL PARM(BUFFER,80,LEN) IF (LEN.EQ.0) THEN WRITE(6,1000) 1000 FORMAT(/' *Error : program file not specified') CALL EOJ(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=EQUALB(BUFFER,BUFPOS,OPTS,OPTPOS,OPTLEN(OPTN)) 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 SETINF(6,-100,80,'FC','PRIV.') CALL FILNME(BUFFER,BUFPOS,SCRFLE) CALL OPNFIL(8,RET,SCRFLE,'OKNEW OKOLD WROK.') CALL FILMSG(RET,6) ENDIF IF (OPTN.EQ.8) DEBUG=.TRUE. GOTO 1001 1004 WRITE (6,1005) 1005 FORMAT(/' *Error : Unknown options on command line') CALL EOJ(81) 1006 WRITE (6,1007) 1007 FORMAT(/' *Error : Command line too long') CALL EOJ(82) 1099 CONTINUE C C ###################################################################### C C Read machine code program into memory at appropriate location CALL MFACT(RC,'OPEN OKOLD RDOK.',PFILE) IF (RC.EQ.30) THEN WRITE(6,2000)PFILE 2000 FORMAT(/' *Error : File not found ',A22) CALL EOJ(30) ENDIF CALL FILMSG(RC,10) 2001 CALL MFIO(RC,'*.',0,104,BUFFER) IF (BUFFER(1:1).EQ.'1') BUFFER(1:1)=' ' CALL LJUST(BUFFER,104,LEN,' ') IF (BUFFER(1:1).EQ.'#'.AND.BUFFER(2:2).NE.'R'.AND.RC.EQ.0) THEN CALL VERALL(BUFFER,104,'0123456789ABCDEF ',17,POS,NUMPOS) IF (NUMPOS.EQ.2.AND.BUFFER(POS(2):POS(2)).EQ.'&') THEN END=(POS(2)-6)/2-1 CALL CORE(BUFFER,104) READ(10,2002) PTR,(POS(PTR+I+1),I=0,END) 2002 FORMAT(1X,Z4,48Z2) CALL CORE(BUFFER(POS(2):),104) READ (10,2003) CKSUM 2003 FORMAT(1X,Z2) CKSUM=CKSUM-LAND(PTR,255) CKSUM=CKSUM-PTR/256 DO 2004 I=PTR,PTR+END CALL WRMEM(I,POS(I+1)) CKSUM=CKSUM-POS(I+1) IF (I.EQ.65532.OR.I.EQ.65533) GOTMEM(I-65531)=.TRUE. 2004 CONTINUE IF (.NOT.ORGFLG) THEN ORG=PTR ORGFLG=.TRUE. ENDIF ELSE BADFRM=.TRUE. ENDIF ELSE IF (BUFFER(2:2).EQ.'R'.AND.RC.EQ.0) THEN WRITE(6,2005) 2005 FORMAT(/' *Error : Code requiring relocation not usable') CALL EOJ(84) ELSE IF (BUFFER(1:1).EQ.'%'.AND.RC.EQ.0) THEN BUFFER(1:1)=' ' CALL LJUST(BUFFER,104,LEN,' ') CALL FNDALL(BUFFER,104,' =',2,POS,NUMPOS) LENGTH=POS(1)-1 DO 2007 I=1,DESCS IF (BUFFER(:LENGTH).EQ.DESC(I)(:LENGTH)) THEN CALL LJUST(BUFFER(LENGTH:),105-LENGTH,LEN,' ') IF (BUFFER(LENGTH+1:LENGTH+1).NE.'=') THEN WRITE(6,2006) 2006 FORMAT(/' *Error : missing = in system descriptor') CALL EOJ(88) ENDIF CALL LJUST(BUFFER(LENGTH+2:),103-LENGTH,LEN,' ') IF (I.NE.3) THEN TRAP(I)=NUMBER(16,BUFFER,LENGTH+2) ELSE IF (BUFFER(LENGTH+2:LENGTH+2+LEN).EQ.'STANDARD') THEN EXTEND=.FALSE. ELSEIF (BUFFER(LENGTH+2:LENGTH+2+LEN).EQ.'EXTENDED') THEN EXTEND=.TRUE. ELSE WRITE (6,2008) BUFFER(:LENGTH+2)//' ' ENDIF ENDIF FOUND=.TRUE. ENDIF 2007 CONTINUE IF (.NOT.FOUND) THEN WRITE(6,2008) BUFFER(:LENGTH)//' ' 2008 FORMAT(/' *Error : urecognised system descriptor %',A28) CALL EOJ(89) ENDIF ELSE IF (RC.EQ.0) THEN BADFRM=.TRUE. ELSE IF (RC.NE.1) THEN CALL FILMSG(10,RC) CALL EOJ(RC) ENDIF IF (BADFRM) THEN WRITE(6,2009) 2009 FORMAT(/' *Error : Assembly program format incorrect') CALL EOJ(83) ELSE IF (LAND(CKSUM,255).NE.0) THEN WRITE(6,2010) LAND(CKSUM,255) 2010 FORMAT(/' *Error : Assembly program checksum error : ',Z2) CALL EOJ(83) ENDIF IF (BUFFER(1:1).NE.'$'.AND.RC.EQ.0) GOTO 2001 CALL MFACT(RC,'CLOSE.',PFILE) IF (.NOT.EXFLAG) THEN IF (GOTMEM(1).AND.GOTMEM(2)) THEN PC=RDMEM(65532)+RDMEM(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 Y6502(PC,DEBUG,EXTEND) CALL EOJ(0) C C Clear screen and print header C IF (DEBUG) THEN WRITE(6,3001) 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 INVOPC(ADDR) IMPLICIT INTEGER (A-Z) WRITE (6,1) RDMEM(ADDR),ADDR 1 FORMAT(' * Unrecognised opcode ',Z2,' at address ',Z4,' *') RETURN END C C List Debug information on the screen C SUBROUTINE DBGLST(OPADDR,MODE,ADDR,DATA,REGS) IMPLICIT INTEGER (A-Z) INTEGER REGS(6),FLAGS(0:6) CHARACTER OPCSTR*3, OPDSTR*9,ADDSTR*8,DATSTR*8 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)) C OPCODE=RDMEM(OPADDR) OPCSTR=OPCODS(OPCODE) BYTES=1 OPDSTR=' ' IF (MODE.NE.13) 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)=LAND(REGS(4),2**I)/2**I 3101 CONTINUE C IF (MODE.NE.13.AND.OPDSTR.NE.'A'.AND.OPCODE.NE.0) THEN CALL I2X(ADDR,ADDSTR) ADDSTR(1:4)=ADDSTR(5:8) IF (MODE.NE.8) THEN CALL I2X(DATA,DATSTR) DATSTR(1:2)=DATSTR(7:8) ELSE DATSTR=' ' ENDIF ELSE ADDSTR=' ' DATSTR=' ' ENDIF C GOTO (3102,3103,3104),BYTES C 3102 WRITE(6,3106)OPADDR,OPCODE,OPCSTR,OPDSTR, * ADDSTR(:4),DATSTR(:2),REGS(1),REGS(2),REGS(3),REGS(6),FLAGS C WRITE(8,3106)OPADDR,OPCODE,OPCSTR,OPDSTR,ADDSTR(:4), C * DATSTR(:2),REGS(1),REGS(2),REGS(3),REGS(6),FLAGS RETURN 3103 WRITE(6,3107)OPADDR,(RDMEM(OPADDR+J),J=0,1),OPCSTR,OPDSTR, * ADDSTR(:4),DATSTR(:2),REGS(1),REGS(2),REGS(3),REGS(6),FLAGS C WRITE(8,3107)OPADDR,(RDMEM(OPADDR+J),J=0,1),OPCSTR,OPDSTR, C * ADDSTR(:4),DATSTR(:2),REGS(1),REGS(2),REGS(3),REGS(6),FLAGS RETURN 3104 WRITE(6,3108)OPADDR,(RDMEM(OPADDR+J),J=0,2),OPCSTR,OPDSTR, * ADDSTR(:4),DATSTR(:2),REGS(1),REGS(2),REGS(3),REGS(6),FLAGS C WRITE(8,3108)OPADDR,(RDMEM(OPADDR+J),J=0,2),OPCSTR,OPDSTR, C * ADDSTR(:4),DATSTR(:2),REGS(1),REGS(2),REGS(3),REGS(6),FLAGS RETURN 3106 FORMAT(1X,Z4,1(2X,Z2),11X,A3,1X,A9,3X,A4,2X,A2,1X,1X,3X, * 3(Z2,2X),'1',Z2,4X,7(I1,1X)) 3107 FORMAT(1X,Z4,2(2X,Z2),07X,A3,1X,A9,3X,A4,2X,A2,1X,1X,3X, * 3(Z2,2X),'1',Z2,4X,7(I1,1X)) 3108 FORMAT(1X,Z4,3(2X,Z2),03X,A3,1X,A9,3X,A4,2X,A2,1X,1X,3X, * 3(Z2,2X),'1',Z2,4X,7(I1,1X)) END C C Take appropriate action when the user interrupts the program C SUBROUTINE USRATN(REG) IMPLICIT INTEGER (A-Z) INTEGER REG(6), DBGFLG /Z80000000/ CHARACTER BUFFER*80 CALL PSTCOD(BUFFER) 3300 WRITE(6,3301) 3301 FORMAT(/' Press return to continue, S for storage dump,'/ * ' D to toggle debug, R to reset or Q to finish.'/) READ (9,3302)BUFFER(1:80) 3302 FORMAT(A80) CALL LJUST(BUFFER,80,LEN,' ') IF (BUFFER(1:2).EQ.'Q ') CALL EOJ(0) IF (BUFFER(1:2).EQ.' ') RETURN IF (BUFFER(1:2).EQ.'D ') THEN REG(4)=LXOR(REG(4),DBGFLG) ELSE IF (BUFFER(1:2).EQ.'R ') THEN REG(5)=RDMEM(65532)+RDMEM(65533)*256 ELSE IF (BUFFER(1:2).EQ.'S ') THEN BUFPOS=2 CALL VERIFY(BUFFER(2:),79,' ',1,NONSPC) IF (NONSPC.EQ.0) THEN WRITE(6,3303) 3303 FORMAT(' Enter start address for memory dump ( hex ) :'/) READ(9,3302)BUFFER(1:80) BUFPOS=1 ENDIF PTR=NUMBER(16,BUFFER,BUFPOS) IF (PTR.GT.65535) GOTO 3308 CALL VERIFY(BUFFER(BUFPOS:),80-BUFPOS,' ',1,NONSPC) IF (NONSPC.EQ.0) THEN WRITE(6,3304) 3304 FORMAT(' Enter finish address ( hex ) :'/) READ(9,3302)BUFFER(1:80) BUFPOS=1 ENDIF END=NUMBER(16,BUFFER,BUFPOS) IF (END.GT.65536.OR.PTR.GE.END) GOTO 3308 WRITE(8,3305)PTR,END 3305 FORMAT(/,' Memory dump ',Z4,' - ',Z4/) 3306 PTR1=PTR PTR2=PTR+15 IF (PTR2.GE.65535) PTR2=65535 WRITE(6,3307)PTR,(RDMEM(J),J=PTR1,PTR2) WRITE(8,3307)PTR,(RDMEM(J),J=PTR1,PTR2) 3307 FORMAT(1X,Z4,5X,4(4(1X,Z2),3X)) PTR=PTR+16 IF (PTR.LT.END+1) GOTO 3306 ENDIF GOTO 3300 3308 WRITE(6,3309) 3309 FORMAT(/' *Error : Address out of range') GOTO 3300 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 LOGICAL*1 EQUALB CHARACTER BUFFER*104 NUMBER=0 CHAR=0 1 IF (.NOT.EQUALB(BUFFER,BUFPOS,' ',1,1)) GOTO 2 BUFPOS=BUFPOS+1 IF (BUFPOS.GT.80) GOTO 3 GOTO 1 2 CALL LBMOVE(BUFFER,BUFPOS,CHAR,4,1) BUFPOS=BUFPOS+1 IF (CHAR.EQ.64.OR.CHAR.EQ.107) GOTO 3 IF (CHAR.LT.193.OR.(CHAR.GT.198.AND.CHAR.LT.240).OR.CHAR.GT.249) * GOTO 4 IF (CHAR.LT.240.AND.BASE.EQ.10) GOTO 4 NUMBER=NUMBER*BASE+LAND(CHAR,15) IF (CHAR.LE.198) NUMBER=NUMBER+9 IF (BUFPOS.LE.80) GOTO 2 3 RETURN 4 WRITE(6,5) 5 FORMAT(/' *Error : Input contains illegal numerical characters') CALL EOJ(87) END C C Subroutine to extract a filename from the command line options C SUBROUTINE FILNME(BUFFER,I,FNAME) LOGICAL*1 BUFFER(104),FNAME(22),EQUAL CALL FILL(FNAME,22,' ') 1 IF (.NOT.EQUAL(BUFFER(I),' ',1)) GOTO 2 I=I+1 IF (I.GT.80) GOTO 4 GOTO 1 2 J=1 3 FNAME(J)=BUFFER(I) I=I+1 J=J+1 IF (EQUAL(BUFFER(I),' ',1).OR.EQUAL(BUFFER(I),',',1)) GOTO 6 IF (J.EQ.23) GOTO 4 IF (I.LE.80) GOTO 3 4 WRITE(6,5) 5 FORMAT(/' *Error : Invalid filename') CALL EOJ(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) CHARACTER HEXSTR*8,OPDSTR*9 PC=LAND(OPADDR+1,65535) LOBYTE=RDMEM(PC) PC=LAND(PC+1,65535) HIBYTE=RDMEM(PC) CALL I2X(LOBYTE+HIBYTE*256,HEXSTR) GOTO (100,101,102,103,104,105,106,107,108,109,110,111,112),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=LAND(PC+LOBYTE,65535) CALL I2X(ADDR,HEXSTR) OPDSTR='&'//HEXSTR(5:8) 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 END