From fa6e4f49f176dfb2d49d5f00c101dc33e571b8a2 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 28 Jun 2023 10:12:09 +0200 Subject: [PATCH] Compile functional test from sources --- .../functional_test/6502_functional_test.bin | Bin 65536 -> 13154 bytes .../functional_test/6502_functional_test.ca65 | 6125 +++++++++++++++++ examples/asm/functional_test/Makefile | 3 + examples/asm/functional_test/README.md | 8 + 4 files changed, 6136 insertions(+) create mode 100644 examples/asm/functional_test/6502_functional_test.ca65 create mode 100644 examples/asm/functional_test/Makefile diff --git a/examples/asm/functional_test/6502_functional_test.bin b/examples/asm/functional_test/6502_functional_test.bin index 8a20227a6decf69bcdf87155cace7278caa2996a..24ce1ce199eac3e55e943569f1d254cc83846adb 100644 GIT binary patch literal 13154 zcmcIreQ+G*ect<$SN2JFmLpk~{2>%$)<75mwnH)`K^|aEWJ?@UrAazTop#!aHf^10 z+O#tWofMy|dN6TjfMRAcbKIbeR>Lu3#-_0+3QA;ad^dJ-X`5o>5uAf91miZM1CCp& zyFS17W8e4PTTT9|HAwINKAz|I_3rNNEtVzz?X>m}So@o;{f}Dv-?8@3jrCh2*{l_{ zqKD#RRwVWhiSI=2v%cQnW6fqS|0JJ{S)-9`G(x{ED;BXL(d_7Gb~HPZty>W*n~hr5 zwr^%rbsVR@`(5k3ZPs!f|CX0i7G%+^mAz1?Kj&FTtmxkr67H^eK2fpV4e@dR-||Mc zH(vJtEx+O38LzzW?ui$zXvwP7ya;@09l<(^^=zaPa|h#|McSjazFH}=T$`??Yg1Jr zs0zX4tPso!0l`E869Wu|HJI_HGUsbRK6SXXQmGA{dL^^uekd`+&)#U5f~sCju+23 zeO(8-r#CNeNyprd#J-r@oj4kEdlHqDdvn5d67FpY_l|^nXTlyT?E+;}#;HV?WA5I> zbQFdIiN0t$=I%=zi0+HIznAb58D}|KiMfB6I2xUfxsP)F0j~dc!aFGRW8D8A6X|GQ z%stBW|CI3V7y1I%d)$9Iu`hZc=KgcSTM_!7a{LnauWvrP9OTmkpEXpk?fJdr*Xg`# zmpc98RJQ|uZ>3hal%JtOy-n)3{X$hQK|RCS*8s`p?2h+fp+EnKTiE^XyOr9~(lR#N z(b~RR=~|^$1={TADz)<2vu6jU-0N$kNgp?P5GMUFd4!|dKy8+&1BHk4_kW|Z8=&Lk zHqHo+kNg(gfI9nWZ5KAfcdhh&sPKjSBR#1-rdt(mzY8~4b6X3yb>wDeynM#nm7%)q zEj*I{tmA%l592K@!S4(3djO@NDdIa84nM~*WisAOp_o6e%s0aPC79#CA%BAxQT}z{ zN)i`N6rDE452pa<9$L_ZU7*Epu(%;5KcvO0kRT?9)-(gmem54KQo`)-h2(;i(B7r> zw;=qX6jB69M9_i=3N?yh$)-~n>}Ygxz=wrq4+m;o%en>!%BM_$`6$PA6dHXWh2r*6 zNaLfBPo4#H)}B?sI8edhKm{|dYjB`^#wi%satQ(BK+Qi6I0^*=N2S4m@+s7*GKb}E zqRQAybgbgx$0{V^g@Ho1%gTYW<;RL6&p4hnIL z*qM~M@kE$FV_9MXgT}Ko8P&=zYhFAZCNQ}2^=F+aeKhQ|OrUXbL4b=H=~-!BJS`2E zdP-B=f+=pLDUMLDp0kUQ2<;&113y66WudZk6%I62U02~?S0VBod7WazPoTp9 zEGptTjM&E0Z&Bzegr26*S6=Bpe5^S#KKnn@ALLhlyfyMn8?CPk6czx(U>R|Qw+SH) zg!Mouu;h=o+s`8T@3Z6=MDh!id^!ClOptt-$*^XEM-u%^NgF+VcfX@lJxQ976@_tFq=pky;8Qvy8 z?Uy!*2|m<9+72`8m%603m9)6nV!SyqA$Ln{inMfZ1Fg*2-z+m9t4bb%k-OoC|aIH8IwHriC&4+9vdaVw7Di^xN0EgiN-wVAF!a=iL&+iy{dtNMQ#sMX8*r$l7$-3Wi)ox_<&{6wT1=3b;(am^ zok;PLA4(OQNK7F=*~C7cZNSidL`?k)Y^zT?Pq3+EWx0-+#<;m$FP5tEQz}UZtv1jM5 zgEaP&rvr2UBIcf}Y3?Ua2L}H|3_e%W;7^_oOn!iyZ#k8BfSd0*m3M%fZ#tECfSd0+ zm3My5J{O$){Fr?%IQjX0`&@AH^Ok)sIQa@sC7|$B)Cy09s_=Mxd4KW`YLk!2l#_o@ zn|w^9ocx2@hA2qwRst{3bwccLBL3BSr!`Bg5&LSCN80_V6H%n?AI zZoI@@<`9hr5&;Ik^#X^n^g0kQzbScn2XTeolT`W5M2LmFm4F3qB?7k(zvjRvc^2Qh z3;4g-!S=Ygk-5xq8x15Z3_@TFO&s`n7V#+I8aGkpGH#=o5iU@TMjV_Og~Atc?Z%e$ zHc~HFPIQY)|MkMk%+|=$zxC3e|FBSd{*GhoB4C)kG5x;a&~A#}%YS&XC-S{dlA7N= z#iFLVPrei1A zQsDeL(aCh|?8-i`XeZZ3KD9v``P4>jo3XVErY^SaVtC(ufkJtDm)(F|Nkumu3+9KKFN4wx4lz-T;79EftYSQ?AyLk zej@&sl66SgD@xWbW$%-WJpxpCpAj-04TU!U$Tso<+aUGA5^RvDypV0jeOQQ?N?)*FA1NPvqm+1OTrhHgs)kOm|=NN zRbyPN87i$CV1)t06>A2p87t&jzZ&Cw$#Ai3faPk2%9jjOS;}g3qZ*reHN32IW|LjY zvMHeXpcx-wGB{&0h>T>9Gnr8z!03$%53%S1~JJMXY>@w-Bc~W}RQ+oD!@z$%4!z3oq!3R}pCLDgw=2<^Pi% zjpQT?a+54bt$Y=;@>RsjSMmNLgqLJNUXlfQm9OGezKU1*5-*+cHe-)(!5%LpQ2P@b zoVE7CP56+Gn}mf=;BPs-1Wn!0Jf>-w&qMaY$Ar%VrjM_AY(Z(5&#hrRHRAC#I!{7q zZV^16F?ry_HIFSQ4deN&ET{`)@vy)! z8=+&E7BV9p2a7A zvPV_gBiqoU9@V3#lTJs{KC#*!38-aNf;CoQXyYnURk1=~d&Pk`RVzp~pd0(JjoT=y6C4voCrakFT1GjKj3h zbCGdK3$sCb9Hxcr5!)j-p+~*cBg=a{m&AFDs@%60U)R6ojpmY-1p$X=TsqwFLMR-pbU1Ehz|n5*MWi1Uk>V6Zq5dbTSHv_0 zgT-qenc0GDbKiFb^mc7`7&`Z>s@Q z^fx2=9Ta`+#MUEgDf*c|k2*cgQG4O9#Yle}f6Hk+#^x6FpvCZlX2JFZLEM}SBK(RF zpyvQeFa4RGMvBMqF%%{=23889&@3bde_&6_-CKj+i|jeOsgr)B$rP>yu#o1UxT_qFtntjHgLi&jggiQYoH&llrE6O;>uNeMLO>Huc<_>3!xq1K#85 zMW0ydB<~{i9nbE)TX^4lkF>Cw{}uY4Y)GE^&MeS2(?V8&S@6EY;DOM-_wv3Qdg)Jn z?-hNA!S96komuc0Gz*C#`VRNO-c|Z;=%kBu-_UnxKOgY+`)=qYFYEg)=)1%F-gP9+`u?Zrd#WLM>MFB9%}ong z0cOGb4ueE!-@AC<4ZZZIzITbf!=NR+@63Y7pjk)^(RaAFcdgQQLnr-2-=W*``wgj! zzMD$vDf$lOCU}PT-Bil@`+bKx;+n2T-wl03-=SR}@b>#|=p--eyMw;(MBgtITJa*A zl}qMYa;+$SE|p7r^;{diQ}Uv@^_@L~yB3p+Dcc^}Ifgft7t@J!Zv8!w&LXlLXM!9jAhEhza5%zVJ;Kk((ADQZ2k0bgwQ zv<2mDM>!K>>gX3Ovgk)UYhdV}ci;|-$!PBe(V{$LR5awaJG=9hw! z_r79C_D`(e`JK!}%LA=_YwCb>?v9RCNVoNw3~JLrZwS`(M{w-0j%zzwHjX4d9jNhw(}@zD#a(`^LZQcGv8| zs0Y0s67OA0vJd$?mv~FOSc{athHXN`Q$)Sr9UuhvbL{t^LMzQI?=9^G5MD8#%~Wc+ zwTIT7nu)R(LB-45csNS0athcygZ;s}rHQozQ_g+v^Mq9`!uw^-yNtw^(VNln&fLnN!VM+9YxlS900Zb(YTiur0-~LLIun z`LH1aqDXcvO)#P~0g}KDP?ROYR9A=sWvGkTs~dH)1&gx-W4!Irlp~&Ti-P&I8Va&K~C;=d;e|oO>OzXox4{%lzjzfPkL9 z82Zpq%o%Ao9Q&K(x8h&x>5N_eNnz+ve8|Cnv3M-*92z<_gq6VY(2x_eS0wh~*4PrY zyR_6A!`e7Qv7z69?zzN^#pM^h*jUV&j5%|$O3jPoxKJBCHjzzqe8cG*92;-&V)^ek zlz**%mhSzvrZ1P568Srl(=q&5-$1D1K;;0{bi9(tKalid1n-$zS8Xo7RGX@0YZFx^ zs4Btuv=U4!0fli8CV?n%cayC7Z}6Q~MJ6rqnZud~2%GnqQO3uTSMSrt(`-`OcKvKi5Uw_U0YRZA(u2h$kP2}%O9d!03@_SNVyf?SxR1*2Gq@Ho668QsC|Ho4Q^^`ZH^p8pZ z$5UCSE0G_R`jM1(ztTS|^~a?DcT@YEgNgjhDetDzzb50qF8$w-`q`9MQ}w+i{Vz)W zyHdZ9@&=XuL+O7*#$T5D)GF@{rEgm$>hD-3>f5?X^mqFzBHsWENt)Ipg$dC2;<;Xz zHle?lz~%tei;)O}h6vDdWVK8p=q)SDQCnG#I+o>Vton53FXoP$Va{4JHSMj~0) zfM1vIIOW&nI}R!<%aJlxv3t{4Lh3z<-cz)7X!5hUz8PJHhz8k=keNEjCk3BX#A{n& zH~DqsK5*wcd^pu@2*0~hD_$y`CQ$E_`h6c%^%B&lW%gBw79TF`?;Pn4x>e!!dvMEJZcT7&)^59t z`wL%;iDYw&Fn$=u52Als9shK1hDVw_aJqN6SR$Orxm|QXdvbsdKE}c0T5^#N{u`1T zTJky_ya5TF^>j?L-9-m?;h;lH9;bu5AvvHWH|Ss&Bp0=W&Qdx)2;u8mIEhiGQDqCN zEY_&bMVBsCu%ji#3%M4wWq4s0vutbd!uXV_R6h#sm>-3f+>b(?_)$pXM0YA(;GEAh6jsu%w>|{EooL@+vOgO5Z zqo{2(`yQ3viqf-G`pqA1IC{J>K05usQy&#>{x}nVp`DJet0aO^vc@p2W77jOP36MwFqj;|LJ6oYb$ z2$ugZ$iE%_tKm-*e_2d?BKNfDdZIcb>b*c+3-uZ~$)_7=BPObcr8=Y4Yn3`F)jd+( zs@3b1IwjR|5iVp&4GxjH2V`ziN97*4AaavBD)+zzk(<;}xd&u!^!=*d(GeQcSve-Z z?N=Ms29LCmc1^JBSDUmpL)x{$reAH=+E&u8lUm(#Fz>__k=XPcOgwQ#WHvnqGf!L* zsZGzp)Du@kZqsvd#=WN2+V8Y@*uB<>dEf+uw4>np}eSbCbnNP3c%{sL8stt_?30gSXvHjtQ##Wt2QlKe$8%4KtskEbZT(AY;f25V$)vjMN@i_;O>`tdWNwSNU`FRooj zXzj<(g!cXw?7dXe-jAONE&eN5e5t0zA3qb?{7`Or%9*}Hx#c-$`VQrmC!Og#lv|#4 zrtiXxdp^ARg~RUo@a7kK-1FhhFT~vQ;mtSkOamsKsoKOdrJ8uMzJkB`N36}qX6ns9 zVr@P)Qg8kdYxAEnn}5XG{HNsRV~pNfx?hOf+=ehd~c=L27{`@b!^%p)a*1osncyk;Xrf*FBm&##16n$9u_+&Qz>@HHv zr>BGyR2OTn-Fax24DeAz04i}kO6)+1v?wL9Y-8dwI~Tgjme$Z*$HX;uE_8`4tra?P zft?FoUrXzRPF!8Z`xWcrTA$CHp!NC8iCUk}oUrxz%!OrAuF!DQL&PMmrf6_D>JefR zmr~M2JwQz2I!d~z$A|iSroZ}proZ}proZ}pray1S6*ppfJkZaC-eFhVh3!WMug4X) zV3O%a1~2A{`!C7#&CkR$b(?smUK7vMY2wNHe)W$G_yo$^AU7Ew&TU(#-!9YJLTooa z*4wsLzau_qWNWnSZ;foVmMxJ?eAs8=-KS)BJiPG}c98k;-Y_yh9=)rkT9$J=us7=C zLsQ@mESh@HZYtb?%~V2D;ts5`5}F#Vn5IZ8rm513Y09)>Qk`^l*Z`&*CDYahAGuE)J|Q`8+kN6?m(PgxEIE&q=!V`Or1*3=9iZV`Qha4e z@hwYLGc3;=8l&o1M5W`pu*yKhRmUQ*Wo#l(`!q)7p@@say0B6$qRK-N)L3d7n?Pf; zq~Q(a&aQXohQt=oe$b8&3K^WS8I+7V zayJ!N?xq6E-Sq#HZS~1X7S2twaBAagG858`*=lB+!Q8Zs;=ZP!LT9xOvU>^8L%@gxRBl7%NRBa-Uud!4hd{okK!()1OHxzCQ121mL8O+qt=6?$-nXxio0>*&TW#m2 z&V6+dvz1$V2U%OWzrI4{Dz~k+D_6OtI*8uNZL2kZ@yYWo;J?-I$J?7+25)ijxm9Qc z@(uNg)%6ydgnUCSEgn)af`hPQ1TAz&c8oh$tb>qH9YG7b4x(aP1czeRp<@K+V%PB( zEB2!62wK>^=sL7Ta6on)K?^;jDxD)+F{4g1qvzAPb!qp+O6N#Ot*PiugZit&J2)D> zDQ1zv<=SaJMj<73t(Qk&| z^$t}oQOz(!A$CAnMnpi^*#Whk6_BdMr=Z?#s#>f$(W|K~wF^F-a7@KNL&eJWooHK` z8C6ZjC?^pqKteul&2oRLQ zvd}T)6EZ13vpMX&ARg_4I_*a~nf7z{)up}aJ6cNZc~yLReG(ae2cB;$W^WUc(JU#K zdqc_rtyG_Ulls=*4Z70j*;mwu-a)aS`lsa7JI7wT zlbZjslk1C|dgs_b-bqsn80h|KEjs1Yp?@%P>ZF(cMz8&8>YZxp2=JxosS67k!m`jY z)YRerNaqSu59qX?nmTlU`1zHl9#m>iHFYTe0G`oP4=OeNt9t5C_v{Y32Bsd+*G(PT zuY|n)sRwjAubBF$F!h|AddJxJyJ+fPaB_{2Q|}l%*F{qc80h|K>K$_G&?h6OPI~EY z^xB`M-l3+B04>o|7Zx&vWuarJsl$D3#|l#q=(L}jI&@p^S!wD)rS?=)hjIfvqo*EJ zYWn`vp^onjx(22m(AP~J+P09lKlOl4=Uq5JCwmTK-;S|gEN1W>w%BlbxMes4`opcm zS#No`9luWUoZ+^P&dS{j>4jF;?caVFUq@cZCbPqByC9uSFSJyu@XX>)zmOsQaNBJA zoAis-;nvD5CE1Q7cOc0Pk>t3a!iytc7QU2bSLb(k&ZbMP6?Zo49fxcC_SQ;zw$=XI z0h@(%sby)c(6!83fAeiS;nq^hEOkNIF`F@e7uwC=*~~(FiSm_Xn+w^>@I@-}4iveB zBJ;<0=3@P`S3tM z@@T1T`?q>aEe~e8n(%LHwxPfA{@(uKHXG#3S$x$h0MeHjkiN)()S}GTJ)f~aYH|D6 zeu#aLT5Lf25+9^5F(7@B0qKhjNMB??`XV2sFY!V85+9^5@j*(-qv|`*Q?-NmpBk2G zbNZ{(_#Slc(cV+ln^;Nu5`3G(>EgVjqAx;eC|s)K`8A6C4MwN-JPH5&d;hKbGzZYAL)84wc)0b z=2NE~@y*PB`eO5`Dn6~WA{~*DUT$72HT6y0pKtzc{{MlP025#WOn?b60Vco%m;e)C z0!)AjFaajO1egF5U;<2l2`~XBzyz286JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C z0!)AjFaajO1egF5U;<2l2`~XBzyz286JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C z0!)AjFaajO1egF5U;<2l2`~XBzyz286JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C z0!)AjFaajO1egF5U;<2l2`~XBzyz286JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C z0!)AjFaajO1egF5U;<2l2`~XBzyz286JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C z0!)AjFaajO1egF5U;<2l2`~XBzyz286JP>NfC(@GCcp%k025#WOn?b6fnP0wneN%{ Hf9U>yF)>V1 diff --git a/examples/asm/functional_test/6502_functional_test.ca65 b/examples/asm/functional_test/6502_functional_test.ca65 new file mode 100644 index 0000000..255f765 --- /dev/null +++ b/examples/asm/functional_test/6502_functional_test.ca65 @@ -0,0 +1,6125 @@ +; +; 6 5 0 2 F U N C T I O N A L T E S T +; +; Copyright (C) 2012-2020 Klaus Dormann +; +; This program is free software: you can redistribute it and/or modify +; it under the terms of the GNU General Public License as published by +; the Free Software Foundation, either version 3 of the License, or +; (at your option) any later version. +; +; This program is distributed in the hope that it will be useful, +; but WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +; GNU General Public License for more details. +; +; You should have received a copy of the GNU General Public License +; along with this program. If not, see . + + +; This program is designed to test all opcodes of a 6502 emulator using all +; addressing modes with focus on propper setting of the processor status +; register bits. +; +; version 05-jan-2020 +; contact info at http://2m5.de or email K@2m5.de +; +; assembled with CA65, linked with LD65 (cc65.github.io): +; ca65 -l 6502_functional_test.lst 6502_functional_test.ca65 +; ld65 6502_functional_test.o -o 6502_functional_test.bin \ +; -m 6502_functional_test.map -C example.cfg +; example linker config (example.cfg): +; MEMORY { +; RAM: start = $0000, size=$8000, type = rw, fill = yes, \ +; fillval = $FF, file = %O; +; ROM: start = $8000, size=$7FFA, type = ro, fill = yes, \ +; fillval = $FF, file = %O; +; ROM_VECTORS: start = $FFFA, size=6, type = ro, fill = yes, \ +; fillval = $FF, file = %O; +; } +; SEGMENTS { +; ZEROPAGE: load=RAM, type=rw; +; DATA: load=RAM, type=rw, offset=$0200; +; CODE: load=RAM, type=rw, offset=$0400; +; VECTORS: load=ROM_VECTORS, type=ro; +; } +; +; No IO - should be run from a monitor with access to registers. +; To run load intel hex image with a load command, than alter PC to 400 hex +; (code_segment) and enter a go command. +; Loop on program counter determines error or successful completion of test. +; Check listing for relevant traps (jump/branch *). +; Please note that in early tests some instructions will have to be used before +; they are actually tested! +; +; RESET, NMI or IRQ should not occur and will be trapped if vectors are enabled. +; Tests documented behavior of the original NMOS 6502 only! No unofficial +; opcodes. Additional opcodes of newer versions of the CPU (65C02, 65816) will +; not be tested. Decimal ops will only be tested with valid BCD operands and +; N V Z flags will be ignored. +; +; Debugging hints: +; Most of the code is written sequentially. if you hit a trap, check the +; immediately preceeding code for the instruction to be tested. Results are +; tested first, flags are checked second by pushing them onto the stack and +; pulling them to the accumulator after the result was checked. The "real" +; flags are no longer valid for the tested instruction at this time! +; If the tested instruction was indexed, the relevant index (X or Y) must +; also be checked. Opposed to the flags, X and Y registers are still valid. +; +; versions: +; 28-jul-2012 1st version distributed for testing +; 29-jul-2012 fixed references to location 0, now #0 +; added license - GPLv3 +; 30-jul-2012 added configuration options +; 01-aug-2012 added trap macro to allow user to change error handling +; 01-dec-2012 fixed trap in branch field must be a branch +; 02-mar-2013 fixed PLA flags not tested +; 19-jul-2013 allowed ROM vectors to be loaded when load_data_direct = 0 +; added test sequence check to detect if tests jump their fence +; 23-jul-2013 added RAM integrity check option +; 16-aug-2013 added error report to standard output option +; 13-dec-2014 added binary/decimal opcode table switch test +; 14-dec-2014 improved relative address test +; 23-aug-2015 added option to disable self modifying tests +; 24-aug-2015 all self modifying immediate opcodes now execute in data RAM +; added small branch offset pretest +; 21-oct-2015 added option to disable decimal mode ADC & SBC tests +; 04-dec-2017 fixed BRK only tested with interrupts enabled +; added option to skip the remainder of a failing test +; in report.i65 +; 05-jan-2020 fixed shifts not testing zero result and flag when last 1-bit +; is shifted out + +; C O N F I G U R A T I O N + +;ROM_vectors writable (0=no, 1=yes) +;if ROM vectors can not be used interrupts will not be trapped +;as a consequence BRK can not be tested but will be emulated to test RTI +ROM_vectors = 0 + +;load_data_direct (0=move from code segment, 1=load directly) +;loading directly is preferred but may not be supported by your platform +;0 produces only consecutive object code, 1 is not suitable for a binary image +load_data_direct = 1 + +;I_flag behavior (0=force enabled, 1=force disabled, 2=prohibit change, 3=allow +;change) 2 requires extra code and is not recommended. SEI & CLI can only be +;tested if you allow changing the interrupt status (I_flag = 3) +I_flag = 1 + +;configure memory - try to stay away from memory used by the system +;zero_page memory start address, $52 (82) consecutive Bytes required +; add 2 if I_flag = 2 +zero_page = $a + +;data_segment memory start address, $7B (123) consecutive Bytes required +; check that this matches the linker configuration file +data_segment = $200 + .if (data_segment & $ff) <> 0 + .error "low byte of data_segment MUST be $00 !!" + .endif + +;code_segment memory start address, 13.1kB of consecutive space required +; add 2.5 kB if I_flag = 2 +; check that this matches the linker configuration file +code_segment = $400 + +;self modifying code may be disabled to allow running in ROM +;0=part of the code is self modifying and must reside in RAM +;1=tests disabled: branch range +disable_selfmod = 1 + +;report errors through I/O channel (0=use standard self trap loops, 1=include +;report.i65 as I/O channel, add 3.5 kB) +report = 0 + +;RAM integrity test option. Checks for undesired RAM writes. +;set lowest non RAM or RAM mirror address page (-1=disable, 0=64k, $40=16k) +;leave disabled if a monitor, OS or background interrupt is allowed to alter RAM +ram_top = -1 + +;disable test decimal mode ADC & SBC, 0=enable, 1=disable, +;2=disable including decimal flag in processor status +disable_decimal = 0 + +;macros for error & success traps to allow user modification +;example: +; .macro trap +; jsr my_error_handler +; .endmacro +; .macro trap_eq +; bne :+ +; trap ;failed equal (zero) +;: +; .endmacro +; +; my_error_handler should pop the calling address from the stack and report it. +; putting larger portions of code (more than 3 bytes) inside the trap macro +; may lead to branch range problems for some tests. + .if report = 0 + .macro trap + jmp * ;failed anyway + .endmacro + .macro trap_eq + beq * ;failed equal (zero) + .endmacro + .macro trap_ne + bne * ;failed not equal (non zero) + .endmacro + .macro trap_cs + bcs * ;failed carry set + .endmacro + .macro trap_cc + bcc * ;failed carry clear + .endmacro + .macro trap_mi + bmi * ;failed minus (bit 7 set) + .endmacro + .macro trap_pl + bpl * ;failed plus (bit 7 clear) + .endmacro + .macro trap_vs + bvs * ;failed overflow set + .endmacro + .macro trap_vc + bvc * ;failed overflow clear + .endmacro +; please observe that during the test the stack gets invalidated +; therefore a RTS inside the success macro is not possible + .macro success + jmp * ;test passed, no errors + .endmacro + .endif + .if report = 1 + .macro trap + jsr report_error + .endmacro + .macro trap_eq + bne :+ + trap ;failed equal (zero) +: + .endmacro + .macro trap_ne + beq :+ + trap ;failed not equal (non zero) +: + .endmacro + .macro trap_cs + bcc :+ + trap ;failed carry set +: + .endmacro + .macro trap_cc + bcs :+ + trap ;failed carry clear +: + .endmacro + .macro trap_mi + bpl :+ + trap ;failed minus (bit 7 set) +: + .endmacro + .macro trap_pl + bmi :+ + trap ;failed plus (bit 7 clear) +: + .endmacro + .macro trap_vs + bvc :+ + trap ;failed overflow set +: + .endmacro + .macro trap_vc + bvs :+ + trap ;failed overflow clear +: + .endmacro +; please observe that during the test the stack gets invalidated +; therefore a RTS inside the success macro is not possible + .macro success + jsr report_success + .endmacro + .endif + + .define equ = + +carry equ %00000001 ;flag bits in status +zero equ %00000010 +intdis equ %00000100 +decmode equ %00001000 +break equ %00010000 +reserv equ %00100000 +overfl equ %01000000 +minus equ %10000000 + +fc equ carry +fz equ zero +fzc equ carry+zero +fv equ overfl +fvz equ overfl+zero +fn equ minus +fnc equ minus+carry +fnz equ minus+zero +fnzc equ minus+zero+carry +fnv equ minus+overfl + +fao equ break+reserv ;bits always on after PHP, BRK +fai equ fao+intdis ;+ forced interrupt disable +faod equ fao+decmode ;+ ignore decimal +faid equ fai+decmode ;+ ignore decimal +m8 equ $ff ;8 bit mask +m8i equ $ff&~intdis ;8 bit mask - interrupt disable + +;macros to allow masking of status bits. +;masking test of decimal bit +;masking of interrupt enable/disable on load and compare +;masking of always on bits after PHP or BRK (unused & break) on compare + .if disable_decimal < 2 + .if I_flag = 0 + .macro load_flag p1 + lda #p1&m8i ;force enable interrupts (mask I) + .endmacro + .macro cmp_flag p1 + cmp #(p1|fao)&m8i ;I_flag is always enabled + always on bits + .endmacro + .macro eor_flag p1 + eor #(p1&m8i|fao) ;mask I, invert expected flags + always on bits + .endmacro + .endif + .if I_flag = 1 + .macro load_flag p1 + lda #p1|intdis ;force disable interrupts + .endmacro + .macro cmp_flag p1 + cmp #(p1|fai)&m8 ;I_flag is always disabled + always on bits + .endmacro + .macro eor_flag p1 + eor #(p1|fai) ;invert expected flags + always on bits + I + .endmacro + .endif + .if I_flag = 2 + .macro load_flag p1 + lda #p1 + ora flag_I_on ;restore I-flag + and flag_I_off + .endmacro + .macro cmp_flag p1 + eor flag_I_on ;I_flag is never changed + cmp #(p1|fao)&m8i ;expected flags + always on bits, mask I + .endmacro + .macro eor_flag p1 + eor flag_I_on ;I_flag is never changed + eor #(p1&m8i|fao) ;mask I, invert expected flags + always on bits + .endmacro + .endif + .if I_flag = 3 + .macro load_flag p1 + lda #p1 ;allow test to change I-flag (no mask) + .endmacro + .macro cmp_flag p1 + cmp #(p1|fao)&m8 ;expected flags + always on bits + .endmacro + .macro eor_flag p1 + eor #p1|fao ;invert expected flags + always on bits + .endmacro + .endif + .else + .if I_flag = 0 + .macro load_flag p1 + lda #p1&m8i ;force enable interrupts (mask I) + .endmacro + .macro cmp_flag p1 + ora #decmode ;ignore decimal mode bit + cmp #(p1|faod)&m8i ;I_flag is always enabled + always on bits + .endmacro + .macro eor_flag p1 + ora #decmode ;ignore decimal mode bit + eor #(p1&m8i|faod) ;mask I, invert expected flags + always on bits + .endmacro + .endif + .if I_flag = 1 + .macro load_flag p1 + lda #p1|intdis ;force disable interrupts + .endmacro + .macro cmp_flag p1 + ora #decmode ;ignore decimal mode bit + cmp #(p1|faid)&m8 ;I_flag is always disabled + always on bits + .endmacro + .macro eor_flag p1 + ora #decmode ;ignore decimal mode bit + eor #(p1|faid) ;invert expected flags + always on bits + I + .endmacro + .endif + .if I_flag = 2 + .macro load_flag p1 + lda #p1 + ora flag_I_on ;restore I-flag + and flag_I_off + .endmacro + .macro cmp_flag p1 + eor flag_I_on ;I_flag is never changed + ora #decmode ;ignore decimal mode bit + cmp #(p1|faod)&m8i ;expected flags + always on bits, mask I + .endmacro + .macro eor_flag p1 + eor flag_I_on ;I_flag is never changed + ora #decmode ;ignore decimal mode bit + eor #(p1&m8i|faod) ;mask I, invert expected flags + always on bits + .endmacro + .endif + .if I_flag = 3 + .macro load_flag p1 + lda #p1 ;allow test to change I-flag (no mask) + .endmacro + .macro cmp_flag p1 + ora #decmode ;ignore decimal mode bit + cmp #(p1|faod)&m8 ;expected flags + always on bits + .endmacro + .macro eor_flag p1 + ora #decmode ;ignore decimal mode bit + eor #p1|faod ;invert expected flags + always on bits + .endmacro + .endif + .endif + +;macros to set (register|memory|zeropage) & status + .macro set_stat p1 ;setting flags in the processor status register + load_flag p1 + pha ;use stack to load status + plp + .endmacro + + .macro set_a p1,p2 ;precharging accu & status + load_flag p2 + pha ;use stack to load status + lda #p1 ;precharge accu + plp + .endmacro + + .macro set_x p1,p2 ;precharging index & status + load_flag p2 + pha ;use stack to load status + ldx #p1 ;precharge index x + plp + .endmacro + + .macro set_y p1,p2 ;precharging index & status + load_flag p2 + pha ;use stack to load status + ldy #p1 ;precharge index y + plp + .endmacro + + .macro set_ax p1,p2 ;precharging indexed accu & immediate status + load_flag p2 + pha ;use stack to load status + lda p1,x ;precharge accu + plp + .endmacro + + .macro set_ay p1,p2 ;precharging indexed accu & immediate status + load_flag p2 + pha ;use stack to load status + lda p1,y ;precharge accu + plp + .endmacro + + .macro set_z p1,p2 ;precharging indexed zp & immediate status + load_flag p2 + pha ;use stack to load status + lda p1,x ;load to zeropage + sta zpt + plp + .endmacro + + .macro set_zx p1,p2 ;precharging zp,x & immediate status + load_flag p2 + pha ;use stack to load status + lda p1,x ;load to indexed zeropage + sta zpt,x + plp + .endmacro + + .macro set_abs p1,p2 ;precharging indexed memory & immediate status + load_flag p2 + pha ;use stack to load status + lda p1,x ;load to memory + sta abst + plp + .endmacro + + .macro set_absx p1,p2 ;precharging abs,x & immediate status + load_flag p2 + pha ;use stack to load status + lda p1,x ;load to indexed memory + sta abst,x + plp + .endmacro + +;macros to test (register|memory|zeropage) & status & (mask) + .macro tst_stat p1 ;testing flags in the processor status register + php ;save status + pla ;use stack to retrieve status + pha + cmp_flag p1 + trap_ne + plp ;restore status + .endmacro + + .macro tst_a p1,p2 ;testing result in accu & flags + php ;save flags + cmp #p1 ;test result + trap_ne + pla ;load status + pha + cmp_flag p2 + trap_ne + plp ;restore status + .endmacro + + .macro tst_x p1,p2 ;testing result in x index & flags + php ;save flags + cpx #p1 ;test result + trap_ne + pla ;load status + pha + cmp_flag p2 + trap_ne + plp ;restore status + .endmacro + + .macro tst_y p1,p2 ;testing result in y index & flags + php ;save flags + cpy #p1 ;test result + trap_ne + pla ;load status + pha + cmp_flag p2 + trap_ne + plp ;restore status + .endmacro + + .macro tst_ax p1,p2,p3 ;indexed testing result in accu & flags + php ;save flags + cmp p1,x ;test result + trap_ne + pla ;load status + eor_flag p3 + cmp p2,x ;test flags + trap_ne ; + .endmacro + + .macro tst_ay p1,p2,p3 ;indexed testing result in accu & flags + php ;save flags + cmp p1,y ;test result + trap_ne ; + pla ;load status + eor_flag p3 + cmp p2,y ;test flags + trap_ne + .endmacro + + .macro tst_z p1,p2,p3 ;indexed testing result in zp & flags + php ;save flags + lda zpt + cmp p1,x ;test result + trap_ne + pla ;load status + eor_flag p3 + cmp p2,x ;test flags + trap_ne + .endmacro + + .macro tst_zx p1,p2,p3 ;testing result in zp,x & flags + php ;save flags + lda zpt,x + cmp p1,x ;test result + trap_ne + pla ;load status + eor_flag p3 + cmp p2,x ;test flags + trap_ne + .endmacro + + .macro tst_abs p1,p2,p3 ;indexed testing result in memory & flags + php ;save flags + lda abst + cmp p1,x ;test result + trap_ne + pla ;load status + eor_flag p3 + cmp p2,x ;test flags + trap_ne + .endmacro + + .macro tst_absx p1,p2,p3 ;testing result in abs,x & flags + php ;save flags + lda abst,x + cmp p1,x ;test result + trap_ne + pla ;load status + eor_flag p3 + cmp p2,x ;test flags + trap_ne + .endmacro + +; RAM integrity test +; verifies that none of the previous tests has altered RAM outside of the +; designated write areas. +; uses zpt word as indirect pointer, zpt+2 word as checksum + .if ram_top > -1 +check_ram macro + cld + lda #0 + sta zpt ;set low byte of indirect pointer + sta zpt+3 ;checksum high byte + .if disable_selfmod = 0 + sta range_adr ;reset self modifying code + .endif + clc + ldx #zp_bss-zero_page ;zeropage - write test area +ccs3: adc zero_page,x + bcc ccs2 + inc zpt+3 ;carry to high byte + clc +ccs2: inx + bne ccs3 + ldx #hi(abs1) ;set high byte of indirect pointer + stx zpt+1 + ldy #lo(abs1) ;data after write & execute test area +ccs5: adc (zpt),y + bcc ccs4 + inc zpt+3 ;carry to high byte + clc +ccs4: iny + bne ccs5 + inx ;advance RAM high address + stx zpt+1 + cpx #ram_top + bne ccs5 + sta zpt+2 ;checksum low is + cmp ram_chksm ;checksum low expected + trap_ne ;checksum mismatch + lda zpt+3 ;checksum high is + cmp ram_chksm+1 ;checksum high expected + trap_ne ;checksum mismatch + .endmacro + .else + .macro check_ram + ;RAM check disabled - RAM size not set + .endmacro + .endif + + .macro next_test ;make sure, tests don't jump the fence + lda test_case ;previous test + cmp #test_num + trap_ne ;test is out of sequence +test_num .set test_num + 1 + lda #test_num ;*** next tests' number + sta test_case + ;check_ram ;uncomment to find altered RAM after each test + .endmacro + + .ZEROPAGE + .res zero_page, 0 + .org zero_page + +;break test interrupt save +irq_a: .res 1,0 ;a register +irq_x: .res 1,0 ;x register + .if I_flag = 2 +;masking for I bit in status +flag_I_on: .res 1,0 ;or mask to load flags +flag_I_off: .res 1,0 ;and mask to load flags + .endif +zpt: ;6 bytes store/modify test area +;add/subtract operand generation and result/flag prediction +adfc: .res 1,0 ;carry flag before op +ad1: .res 1,0 ;operand 1 - accumulator +ad2: .res 1,0 ;operand 2 - memory / immediate +adrl: .res 1,0 ;expected result bits 0-7 +adrh: .res 1,0 ;expected result bit 8 (carry) +adrf: .res 1,0 ;expected flags NV0000ZC (only binary mode) +sb2: .res 1,0 ;operand 2 complemented for subtract +zp_bss: +zps: .byte $80,1 ;additional shift pattern to test zero result & flag +zp1: .byte $c3,$82,$41,0 ;test patterns for LDx BIT ROL ROR ASL LSR +zp7f: .byte $7f ;test pattern for compare +;logical zeropage operands +zpOR: .byte 0,$1f,$71,$80 ;test pattern for OR +zpAN: .byte $0f,$ff,$7f,$80 ;test pattern for AND +zpEO: .byte $ff,$0f,$8f,$8f ;test pattern for EOR +;indirect addressing pointers +ind1: .word abs1 ;indirect pointer to pattern in absolute memory + .word abs1+1 + .word abs1+2 + .word abs1+3 + .word abs7f +inw1: .word abs1-$f8 ;indirect pointer for wrap-test pattern +indt: .word abst ;indirect pointer to store area in absolute memory + .word abst+1 + .word abst+2 + .word abst+3 +inwt: .word abst-$f8 ;indirect pointer for wrap-test store +indAN: .word absAN ;indirect pointer to AND pattern in absolute memory + .word absAN+1 + .word absAN+2 + .word absAN+3 +indEO: .word absEO ;indirect pointer to EOR pattern in absolute memory + .word absEO+1 + .word absEO+2 + .word absEO+3 +indOR: .word absOR ;indirect pointer to OR pattern in absolute memory + .word absOR+1 + .word absOR+2 + .word absOR+3 +;add/subtract indirect pointers +adi2: .word ada2 ;indirect pointer to operand 2 in absolute memory +sbi2: .word sba2 ;indirect pointer to complemented operand 2 (SBC) +adiy2: .word ada2-$ff ;with offset for indirect indexed +sbiy2: .word sba2-$ff +zp_bss_end: + + .DATA + .org data_segment + +test_case: .res 1,0 ;current test number +ram_chksm: .res 2,0 ;checksum for RAM integrity test +;add/subtract operand copy - abs tests write area +abst: ;6 bytes store/modify test area +ada2: .res 1,0 ;operand 2 +sba2: .res 1,0 ;operand 2 complemented for subtract + .res 4,0 ;fill remaining bytes +data_bss: + .if load_data_direct = 1 +ex_andi:and #0 ;execute immediate opcodes + rts +ex_eori:eor #0 ;execute immediate opcodes + rts +ex_orai:ora #0 ;execute immediate opcodes + rts +ex_adci:adc #0 ;execute immediate opcodes + rts +ex_sbci:sbc #0 ;execute immediate opcodes + rts + .else +ex_andi:.res 3 +ex_eori:.res 3 +ex_orai:.res 3 +ex_adci:.res 3 +ex_sbci:.res 3 + .endif +;zps .byte $80,1 ;additional shift patterns test zero result & flag +abs1: .byte $c3,$82,$41,0 ;test patterns for LDx BIT ROL ROR ASL LSR +abs7f: .byte $7f ;test pattern for compare +;loads +fLDx: .byte fn,fn,0,fz ;expected flags for load +;shifts +rASL: ;expected result ASL & ROL -carry +rROL: .byte 0,2,$86,$04,$82,0 +rROLc: .byte 1,3,$87,$05,$83,1 ;expected result ROL +carry +rLSR: ;expected result LSR & ROR -carry +rROR: .byte $40,0,$61,$41,$20,0 +rRORc: .byte $c0,$80,$e1,$c1,$a0,$80 ;expected result ROR +carry +fASL: ;expected flags for shifts +fROL: .byte fzc,0,fnc,fc,fn,fz ;no carry in +fROLc: .byte fc,0,fnc,fc,fn,0 ;carry in +fLSR: +fROR: .byte 0,fzc,fc,0,fc,fz ;no carry in +fRORc: .byte fn,fnc,fnc,fn,fnc,fn ;carry in +;increments (decrements) +rINC: .byte $7f,$80,$ff,0,1 ;expected result for INC/DEC +fINC: .byte 0,fn,fn,fz,0 ;expected flags for INC/DEC +;logical memory operand +absOR: .byte 0,$1f,$71,$80 ;test pattern for OR +absAN: .byte $0f,$ff,$7f,$80 ;test pattern for AND +absEO: .byte $ff,$0f,$8f,$8f ;test pattern for EOR +;logical accu operand +absORa: .byte 0,$f1,$1f,0 ;test pattern for OR +absANa: .byte $f0,$ff,$ff,$ff ;test pattern for AND +absEOa: .byte $ff,$f0,$f0,$0f ;test pattern for EOR +;logical results +absrlo: .byte 0,$ff,$7f,$80 +absflo: .byte fz,fn,0,fn +data_bss_end: + + + .CODE + .org code_segment + .P02 ; disable 65SC02, 65C02 and 65816 instructions +start: cld + ldx #$ff + txs + lda #0 ;*** test 0 = initialize + sta test_case +test_num .set 0 + +;stop interrupts before initializing BSS + .if I_flag = 1 + sei + .endif + +;initialize I/O for report channel + .if report = 1 + jsr report_init + .endif + +;pretest small branch offset + ldx #5 + jmp psb_test +psb_bwok: + ldy #5 + bne psb_forw + trap ;branch should be taken + dey ;forward landing zone + dey + dey + dey + dey +psb_forw: + dey + dey + dey + dey + dey + beq psb_fwok + trap ;forward offset + + dex ;backward landing zone + dex + dex + dex + dex +psb_back: + dex + dex + dex + dex + dex + beq psb_bwok + trap ;backward offset +psb_test: + bne psb_back + trap ;branch should be taken +psb_fwok: + +;initialize BSS segment + .if load_data_direct <> 1 + ldx #zp_end-zp_init-1 +ld_zp: lda zp_init,x + sta zp_bss,x + dex + bpl ld_zp + ldx #data_end-data_init-1 +ld_data:lda data_init,x + sta data_bss,x + dex + bpl ld_data + .if ROM_vectors = 1 + ldx #5 +ld_vect:lda vec_init,x + sta vec_bss,x + dex + bpl ld_vect + .endif + .endif + +;retain status of interrupt flag + .if I_flag = 2 + php + pla + and #4 ;isolate flag + sta flag_I_on ;or mask + eor #lo(~4) ;reverse + sta flag_I_off ;and mask + .endif + +;generate checksum for RAM integrity test + .if ram_top > -1 + lda #0 + sta zpt ;set low byte of indirect pointer + sta ram_chksm+1 ;checksum high byte + .if disable_selfmod = 0 + sta range_adr ;reset self modifying code + .endif + clc + ldx #zp_bss-zero_page ;zeropage - write test area +gcs3: adc zero_page,x + bcc gcs2 + inc ram_chksm+1 ;carry to high byte + clc +gcs2: inx + bne gcs3 + ldx #hi(abs1) ;set high byte of indirect pointer + stx zpt+1 + ldy #lo(abs1) ;data after write & execute test area +gcs5: adc (zpt),y + bcc gcs4 + inc ram_chksm+1 ;carry to high byte + clc +gcs4: iny + bne gcs5 + inx ;advance RAM high address + stx zpt+1 + cpx #ram_top + bne gcs5 + sta ram_chksm ;checksum complete + .endif + next_test + + .if disable_selfmod = 0 +;testing relative addressing with BEQ + ldy #$fe ;testing maximum range, not -1/-2 (invalid/self adr) +range_loop: + dey ;next relative address + tya + tax ;precharge count to end of loop + bpl range_fw ;calculate relative address + clc ;avoid branch self or to relative address of branch + adc #2 + nop ;offset landing zone - tolerate +/-5 offset to branch + nop + nop + nop + nop +range_fw: + nop + nop + nop + nop + nop + eor #$7f ;complement except sign + sta range_adr ;load into test target + lda #0 ;should set zero flag in status register + jmp range_op + + dex ; offset landing zone - backward branch too far + dex + dex + dex + dex + ;relative address target field with branch under test in the middle + dex ;-128 - max backward + dex + dex + dex + dex + dex + dex + dex + dex ;-120 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;-110 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;-100 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;-90 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;-80 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;-70 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;-60 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;-50 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;-40 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;-30 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;-20 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;-10 + dex + dex + dex + dex + dex + dex + dex ;-3 +range_op: ;test target with zero flag=0, z=1 if previous dex +range_adr = *+1 ;modifiable relative address + beq *+64 ;+64 if called without modification + dex ;+0 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;+10 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;+20 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;+30 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;+40 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;+50 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;+60 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;+70 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;+80 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;+90 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;+100 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;+110 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;+120 + dex + dex + dex + dex + dex + dex + nop ;offset landing zone - forward branch too far + nop + nop + nop + nop + beq range_ok ;+127 - max forward + trap ; bad range + nop ;offset landing zone - tolerate +/-5 offset to branch + nop + nop + nop + nop +range_ok: + nop + nop + nop + nop + nop + cpy #0 + beq range_end + jmp range_loop +range_end: ;range test successful + .endif + next_test + +;partial test BNE & CMP, CPX, CPY immediate + cpy #1 ;testing BNE true + bne test_bne + trap +test_bne: + lda #0 + cmp #0 ;test compare immediate + trap_ne + trap_cc + trap_mi + cmp #1 + trap_eq + trap_cs + trap_pl + tax + cpx #0 ;test compare x immediate + trap_ne + trap_cc + trap_mi + cpx #1 + trap_eq + trap_cs + trap_pl + tay + cpy #0 ;test compare y immediate + trap_ne + trap_cc + trap_mi + cpy #1 + trap_eq + trap_cs + trap_pl + next_test +;testing stack operations PHA PHP PLA PLP + + ldx #$ff ;initialize stack + txs + lda #$55 + pha + lda #$aa + pha + cmp $1fe ;on stack ? + trap_ne + tsx + txa ;overwrite accu + cmp #$fd ;sp decremented? + trap_ne + pla + cmp #$aa ;successful retreived from stack? + trap_ne + pla + cmp #$55 + trap_ne + cmp $1ff ;remains on stack? + trap_ne + tsx + cpx #$ff ;sp incremented? + trap_ne + next_test + +;testing branch decisions BPL BMI BVC BVS BCC BCS BNE BEQ + set_stat $ff ;all on + bpl nbr1 ;branches should not be taken + bvc nbr2 + bcc nbr3 + bne nbr4 + bmi br1 ;branches should be taken + trap +br1: bvs br2 + trap +br2: bcs br3 + trap +br3: beq br4 + trap +nbr1: + trap ;previous bpl taken +nbr2: + trap ;previous bvc taken +nbr3: + trap ;previous bcc taken +nbr4: + trap ;previous bne taken +br4: php + tsx + cpx #$fe ;sp after php? + trap_ne + pla + cmp_flag $ff ;returned all flags on? + trap_ne + tsx + cpx #$ff ;sp after php? + trap_ne + set_stat 0 ;all off + bmi nbr11 ;branches should not be taken + bvs nbr12 + bcs nbr13 + beq nbr14 + bpl br11 ;branches should be taken + trap +br11: bvc br12 + trap +br12: bcc br13 + trap +br13: bne br14 + trap +nbr11: + trap ;previous bmi taken +nbr12: + trap ;previous bvs taken +nbr13: + trap ;previous bcs taken +nbr14: + trap ;previous beq taken +br14: php + pla + cmp_flag 0 ;flags off except break (pushed by sw) + reserved? + trap_ne + ;crosscheck flags + set_stat zero + bne brzs1 + beq brzs2 +brzs1: + trap ;branch zero/non zero +brzs2: bcs brzs3 + bcc brzs4 +brzs3: + trap ;branch carry/no carry +brzs4: bmi brzs5 + bpl brzs6 +brzs5: + trap ;branch minus/plus +brzs6: bvs brzs7 + bvc brzs8 +brzs7: + trap ;branch overflow/no overflow +brzs8: + set_stat carry + beq brcs1 + bne brcs2 +brcs1: + trap ;branch zero/non zero +brcs2: bcc brcs3 + bcs brcs4 +brcs3: + trap ;branch carry/no carry +brcs4: bmi brcs5 + bpl brcs6 +brcs5: + trap ;branch minus/plus +brcs6: bvs brcs7 + bvc brcs8 +brcs7: + trap ;branch overflow/no overflow + +brcs8: + set_stat minus + beq brmi1 + bne brmi2 +brmi1: + trap ;branch zero/non zero +brmi2: bcs brmi3 + bcc brmi4 +brmi3: + trap ;branch carry/no carry +brmi4: bpl brmi5 + bmi brmi6 +brmi5: + trap ;branch minus/plus +brmi6: bvs brmi7 + bvc brmi8 +brmi7: + trap ;branch overflow/no overflow +brmi8: + set_stat overfl + beq brvs1 + bne brvs2 +brvs1: + trap ;branch zero/non zero +brvs2: bcs brvs3 + bcc brvs4 +brvs3: + trap ;branch carry/no carry +brvs4: bmi brvs5 + bpl brvs6 +brvs5: + trap ;branch minus/plus +brvs6: bvc brvs7 + bvs brvs8 +brvs7: + trap ;branch overflow/no overflow +brvs8: + set_stat $ff-zero + beq brzc1 + bne brzc2 +brzc1: + trap ;branch zero/non zero +brzc2: bcc brzc3 + bcs brzc4 +brzc3: + trap ;branch carry/no carry +brzc4: bpl brzc5 + bmi brzc6 +brzc5: + trap ;branch minus/plus +brzc6: bvc brzc7 + bvs brzc8 +brzc7: + trap ;branch overflow/no overflow +brzc8: + set_stat $ff-carry + bne brcc1 + beq brcc2 +brcc1: + trap ;branch zero/non zero +brcc2: bcs brcc3 + bcc brcc4 +brcc3: + trap ;branch carry/no carry +brcc4: bpl brcc5 + bmi brcc6 +brcc5: + trap ;branch minus/plus +brcc6: bvc brcc7 + bvs brcc8 +brcc7: + trap ;branch overflow/no overflow +brcc8: + set_stat $ff-minus + bne brpl1 + beq brpl2 +brpl1: + trap ;branch zero/non zero +brpl2: bcc brpl3 + bcs brpl4 +brpl3: + trap ;branch carry/no carry +brpl4: bmi brpl5 + bpl brpl6 +brpl5: + trap ;branch minus/plus +brpl6: bvc brpl7 + bvs brpl8 +brpl7: + trap ;branch overflow/no overflow +brpl8: + set_stat $ff-overfl + bne brvc1 + beq brvc2 +brvc1: + trap ;branch zero/non zero +brvc2: bcc brvc3 + bcs brvc4 +brvc3: + trap ;branch carry/no carry +brvc4: bpl brvc5 + bmi brvc6 +brvc5: + trap ;branch minus/plus +brvc6: bvs brvc7 + bvc brvc8 +brvc7: + trap ;branch overflow/no overflow +brvc8: + next_test + +; test PHA does not alter flags or accumulator but PLA does + ldx #$55 ;x & y protected + ldy #$aa + set_a 1,$ff ;push + pha + tst_a 1,$ff + set_a 0,0 + pha + tst_a 0,0 + set_a $ff,$ff + pha + tst_a $ff,$ff + set_a 1,0 + pha + tst_a 1,0 + set_a 0,$ff + pha + tst_a 0,$ff + set_a $ff,0 + pha + tst_a $ff,0 + set_a 0,$ff ;pull + pla + tst_a $ff,$ff-zero + set_a $ff,0 + pla + tst_a 0,zero + set_a $fe,$ff + pla + tst_a 1,$ff-zero-minus + set_a 0,0 + pla + tst_a $ff,minus + set_a $ff,$ff + pla + tst_a 0,$ff-minus + set_a $fe,0 + pla + tst_a 1,0 + cpx #$55 ;x & y unchanged? + trap_ne + cpy #$aa + trap_ne + next_test + +; partial pretest EOR # + set_a $3c,0 + eor #$c3 + tst_a $ff,fn + set_a $c3,0 + eor #$c3 + tst_a 0,fz + next_test + +; PC modifying instructions except branches (NOP, JMP, JSR, RTS, BRK, RTI) +; testing NOP + ldx #$24 + ldy #$42 + set_a $18,0 + nop + tst_a $18,0 + cpx #$24 + trap_ne + cpy #$42 + trap_ne + ldx #$db + ldy #$bd + set_a $e7,$ff + nop + tst_a $e7,$ff + cpx #$db + trap_ne + cpy #$bd + trap_ne + next_test + +; jump absolute + set_stat $0 + lda #'F' + ldx #'A' + ldy #'R' ;N=0, V=0, Z=0, C=0 + jmp test_far + nop + nop + trap_ne ;runover protection + inx + inx +far_ret: + trap_eq ;returned flags OK? + trap_pl + trap_cc + trap_vc + cmp #('F'^$aa) ;returned registers OK? + trap_ne + cpx #('A'+1) + trap_ne + cpy #('R'-3) + trap_ne + dex + iny + iny + iny + eor #$aa ;N=0, V=1, Z=0, C=1 + jmp test_near + nop + nop + trap_ne ;runover protection + inx + inx +test_near: + trap_eq ;passed flags OK? + trap_mi + trap_cc + trap_vc + cmp #'F' ;passed registers OK? + trap_ne + cpx #'A' + trap_ne + cpy #'R' + trap_ne + next_test + +; jump indirect + set_stat 0 + lda #'I' + ldx #'N' + ldy #'D' ;N=0, V=0, Z=0, C=0 + jmp (ptr_tst_ind) + nop + trap_ne ;runover protection + dey + dey +ind_ret: + php ;either SP or Y count will fail, if we do not hit + dey + dey + dey + plp + trap_eq ;returned flags OK? + trap_pl + trap_cc + trap_vc + cmp #('I'^$aa) ;returned registers OK? + trap_ne + cpx #('N'+1) + trap_ne + cpy #('D'-6) + trap_ne + tsx ;SP check + cpx #$ff + trap_ne + next_test + +; jump subroutine & return from subroutine + set_stat 0 + lda #'J' + ldx #'S' + ldy #'R' ;N=0, V=0, Z=0, C=0 + jsr test_jsr +jsr_ret = *-1 ;last address of jsr = return address + php ;either SP or Y count will fail, if we do not hit + dey + dey + dey + plp + trap_eq ;returned flags OK? + trap_pl + trap_cc + trap_vc + cmp #('J'^$aa) ;returned registers OK? + trap_ne + cpx #('S'+1) + trap_ne + cpy #('R'-6) + trap_ne + tsx ;sp? + cpx #$ff + trap_ne + next_test + +; break & return from interrupt + .if ROM_vectors = 1 + load_flag 0 ;with interrupts enabled if allowed! + pha + lda #'B' + ldx #'R' + ldy #'K' + plp ;N=0, V=0, Z=0, C=0 + brk + .else + lda #>brk_ret0 ;emulated break + pha + lda #brk_ret1 ;emulated break + pha + lda #bin_rti_ret ;emulated interrupt for rti + pha + lda #dec_rti_ret ;emulated interrupt for rti + pha + lda #jsr_ret + trap_ne + lda $1fe + cmp #brk_ret0 + trap_ne + lda $1fe + cmp #brk_ret1 + trap_ne + lda $1fe + cmp # 1 +zp_init: +zps_: .byte $80,1 ;additional shift pattern to test zero result & flag +zp1_: .byte $c3,$82,$41,0 ;test patterns for LDx BIT ROL ROR ASL LSR +zp7f_: .byte $7f ;test pattern for compare +;logical zeropage operands +zpOR_: .byte 0,$1f,$71,$80 ;test pattern for OR +zpAN_: .byte $0f,$ff,$7f,$80 ;test pattern for AND +zpEO_: .byte $ff,$0f,$8f,$8f ;test pattern for EOR +;indirect addressing pointers +ind1_: .word abs1 ;indirect pointer to pattern in absolute memory + .word abs1+1 + .word abs1+2 + .word abs1+3 + .word abs7f +inw1_: .word abs1-$f8 ;indirect pointer for wrap-test pattern +indt_: .word abst ;indirect pointer to store area in absolute memory + .word abst+1 + .word abst+2 + .word abst+3 +inwt_: .word abst-$f8 ;indirect pointer for wrap-test store +indAN_: .word absAN ;indirect pointer to AND pattern in absolute memory + .word absAN+1 + .word absAN+2 + .word absAN+3 +indEO_: .word absEO ;indirect pointer to EOR pattern in absolute memory + .word absEO+1 + .word absEO+2 + .word absEO+3 +indOR_: .word absOR ;indirect pointer to OR pattern in absolute memory + .word absOR+1 + .word absOR+2 + .word absOR+3 +;add/subtract indirect pointers +adi2_: .word ada2 ;indirect pointer to operand 2 in absolute memory +sbi2_: .word sba2 ;indirect pointer to complemented operand 2 (SBC) +adiy2_: .word ada2-$ff ;with offset for indirect indexed +sbiy2_: .word sba2-$ff +zp_end: + .if (zp_end - zp_init) <> (zp_bss_end - zp_bss) + ;force assembler error if size is different + .error "mismatch between bss and zeropage data" + .endif +data_init: +ex_and_:and #0 ;execute immediate opcodes + rts +ex_eor_:eor #0 ;execute immediate opcodes + rts +ex_ora_:ora #0 ;execute immediate opcodes + rts +ex_adc_:adc #0 ;execute immediate opcodes + rts +ex_sbc_:sbc #0 ;execute immediate opcodes + rts +;zps: .byte $80,1 ;additional shift patterns test zero result & flag +abs1_: .byte $c3,$82,$41,0 ;test patterns for LDx BIT ROL ROR ASL LSR +abs7f_: .byte $7f ;test pattern for compare +;loads +fLDx_: .byte fn,fn,0,fz ;expected flags for load +;shifts +rASL_: ;expected result ASL & ROL -carry +rROL_: .byte 0,2,$86,$04,$82,0 +rROLc_: .byte 1,3,$87,$05,$83,1 ;expected result ROL +carry +rLSR_: ;expected result LSR & ROR -carry +rROR_: .byte $40,0,$61,$41,$20,0 +rRORc_: .byte $c0,$80,$e1,$c1,$a0,$80 ;expected result ROR +carry +fASL_: ;expected flags for shifts +fROL_: .byte fzc,0,fnc,fc,fn,fz ;no carry in +fROLc_: .byte fc,0,fnc,fc,fn,0 ;carry in +fLSR_: +fROR_: .byte 0,fzc,fc,0,fc,fz ;no carry in +fRORc_: .byte fn,fnc,fnc,fn,fnc,fn ;carry in +;increments (decrements) +rINC_: .byte $7f,$80,$ff,0,1 ;expected result for INC/DEC +fINC_: .byte 0,fn,fn,fz,0 ;expected flags for INC/DEC +;logical memory operand +absOR_: .byte 0,$1f,$71,$80 ;test pattern for OR +absAN_: .byte $0f,$ff,$7f,$80 ;test pattern for AND +absEO_: .byte $ff,$0f,$8f,$8f ;test pattern for EOR +;logical accu operand +absORa_:.byte 0,$f1,$1f,0 ;test pattern for OR +absANa_:.byte $f0,$ff,$ff,$ff ;test pattern for AND +absEOa_:.byte $ff,$f0,$f0,$0f ;test pattern for EOR +;logical results +absrlo_:.byte 0,$ff,$7f,$80 +absflo_:.byte fz,fn,0,fn +data_end + .if (data_end - data_init) <> (data_bss_end - data_bss) + ;force assembler error if size is different + .error "mismatch between bss and data" + .endif + +vec_init + .word nmi_trap + .word res_trap + .word irq_trap +vec_bss equ $fffa + .endif ;end of RAM init data + + .if (load_data_direct = 1) & (ROM_vectors = 1) + .segment "VECTORS" + .org $fffa ;vectors + .word nmi_trap + .word res_trap + .word irq_trap + .endif diff --git a/examples/asm/functional_test/Makefile b/examples/asm/functional_test/Makefile new file mode 100644 index 0000000..ff35eea --- /dev/null +++ b/examples/asm/functional_test/Makefile @@ -0,0 +1,3 @@ +build: + ca65 6502_functional_test.ca65 + ld65 -C ../linker.cfg -o 6502_functional_test.bin 6502_functional_test.o diff --git a/examples/asm/functional_test/README.md b/examples/asm/functional_test/README.md index 8b457be..907825d 100644 --- a/examples/asm/functional_test/README.md +++ b/examples/asm/functional_test/README.md @@ -17,6 +17,14 @@ make build This will create a `6502_functional_test.bin` file in the `build` directory, which the emulator will load. +## Running + +Then, from the root of the repository, run: + +```bash +cargo run --release --example functional +``` + ## Credits Taken from