From 7522d3cb3b44a563c0021a47b33fd92b874f6cb1 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Sun, 1 Mar 2020 18:38:00 +0100 Subject: [PATCH] added joystick to C64 emulation (via numpad keys) --- c64testprgs/joytest.prg | Bin 0 -> 349 bytes free-c64-roms/basic | Bin 0 -> 8192 bytes free-c64-roms/chargen | Bin 0 -> 4096 bytes free-c64-roms/kernal | Bin 0 -> 8192 bytes free-c64-roms/readme.txt | 24 +++ src/main/kotlin/razorvine/c64emu/Cia.kt | 192 +++++++++++------- src/main/kotlin/razorvine/c64emu/GUI.kt | 38 +++- src/main/kotlin/razorvine/c64emu/c64Main.kt | 5 + .../razorvine/examplemachines/DebugWindow.kt | 1 + .../kotlin/razorvine/examplemachines/GUI.kt | 4 + .../razorvine/examplemachines/machineMain.kt | 5 + .../razorvine/ksim65/IVirtualMachine.kt | 1 + src/main/resources/version.properties | 2 +- 13 files changed, 192 insertions(+), 80 deletions(-) create mode 100644 c64testprgs/joytest.prg create mode 100644 free-c64-roms/basic create mode 100644 free-c64-roms/chargen create mode 100644 free-c64-roms/kernal create mode 100644 free-c64-roms/readme.txt diff --git a/c64testprgs/joytest.prg b/c64testprgs/joytest.prg new file mode 100644 index 0000000000000000000000000000000000000000..aa1a273ac86feab40b96e0e7404701c2ebdca2b8 GIT binary patch literal 349 zcmX|+F-rt75QX1qGiPll$QZV`g)25Aw?$UL;%-m(WG@_wVB`O=QwtGGtL+8tgjCl4 z27iQBwwBH&DukK$=6e~E0d3I$SG))XKe8>{El*e3Xpy^XWVD9PJQy6%2E2r~(WKm! zB6r^YYMbMNHqqGd%--?~+lk{wdMDB0lD1LXkYMS!mXJ`1jz)^IV2rzG%@q8kO}*9~ zgI5yN@`oki-s=v<@3e=Gjko)z^0w+9-PpX+1!H_s9hvk@5));T;hQ|}^qiSlng4anBnRLJaD7yC literal 0 HcmV?d00001 diff --git a/free-c64-roms/basic b/free-c64-roms/basic new file mode 100644 index 0000000000000000000000000000000000000000..7828da6c6598c21531abe94742894e9f0a97f4ae GIT binary patch literal 8192 zcmeHM4Rlk-l^*$7NFW=@#uz0OnArT-7>q-(D}Lmd9|sX0fdC1R?n$0x%a!~?SlaGM z&F%_dP)kKs*XM}oRwgRwWoj^r)ng?QPLuvtLJ!DIu^#y}{I-`;tK z1P;yFv+bUqJ*SMVdGmAcotZoL&b#xypG6NwmlZCazhM5Vndm`!pn(S(_>0tlK#@40i_=NA7m3*(G2bKF{c$9v#vUf4YwSJ7dP6IU>oJ6D?8MMv zl$B1SRbaXk0^@OqMCL?PB_S|DE7s4(ubsmuV*KBc`?V_&XNOl=5nN8ZwwN=L5zLJk zedCDJ>NSc^;ubfMV$n^C#A4zSizFnvlv6!UYl*9*sHC{WU9y3(%byFZM+7R7ad<^1 zh>dw8I5-2OJw&`rnwOM|6{JioC#7N;(3dJ7_js)(r6px00!y3}+3ZWFj#`IQAm&OtM0Uz?Vs}_>m;N4~RKxNi{zME3 zJDB;GY35%}%GkupNI6WboJ?mGFUf{VDO6&1u$AmszPO%Qa0T>~YCUDFa+{}TeO*O( zn+YFl^CMzsK9pZ$_oI+qEVH^zqBm@WVtIxaBm#k#5tM>sc||)Z74xy$5K-t!&4;Os^WIl z$MNGNCh9~Cm+#ZeOn#w32i7L9<;O_OxJD}+KRHTLB1Vfo$njZGu?494oc$8IOGdzq zJBOtCQTqxd1s_ypGq`~^z#bGA=EZ#i3<0bGv^{4ySU>E|MR@>5kx+e$AG}9p(`r|P zYrugz)!+)|MN3)s1aSG)Fe}alSs>rS#eV3Y1oE%f)!47=wdS9x9fFOXj*#mLg z@&k4-Fyd2w1uF!jbHKwxyLd+wgn^Q7)z(+mZQ@(iJNEkj<0(ig%~INxjpM=m3xL47JLaJOY&#fm8fK%o`FjA(WeGjv$Z|%8d-l zab001Gz>z|_2lsu3g=v|m`n1&SQ!TpA?3-)pt2bJY;mTdecFg-WjKuSu7Ka5?8on<;aW#17kEAU1V&-?js#wQ z6CKjKaOqD@yczuCo72`jF?WZa-5BY zBu3H1PC#cP_~p~YBtLYK7$Q1_lhkR_U>E+ff%Sgbz%OpBGd3gLaPuCd>tXMGjSV_J zYZ&5)?*f`GIEue)5bBIT72awTB9`F9I+JV~0ZD6=jdjL{kPfuAiL85r~caw#H4UNMi3lk=PJxV@T1c|KOkG$M+UiYtIU&l6+MEL zcuGZ&pKErZ(mgJCkzN;wtE1F6R~|=-I?8;H$)ibeN4YOcP9*M*3SYXchdDR}8*#&{ z-A#&PKGbHDP;3K4p%pPV5S7P@fv7B21Vp7Vmqx@aM1X%k z3j7t{eHaON%>KwUWjMTUui>=)Ho+=D_k;96155+HD#!%*-Iu_CLX{B355r^k^X(ya z8HvnM!18Uv?TDEX;Pz1P306=FAPgQkg0_BdzoT{Ieg|*~U~(qln+$v$pBZI>J_<47 zo=Q2cXOloGhhp+gy(}hY*E(4X$Y+al$sBPWmZHS2&)3@ghy|)zuifg6hd>^#LE!;rD-Lq**Y+DeboHrh`Ag}z6BMgQ^O&ki1=9ki3aPk()QRb7}y=&*BL^k3<( z>2dlSdV^Dp`=#8p=IH&4IaH=UmO*D*D&6XrJSsi6bQAeka zQ4_7|Lr{2F9s4bH+<0{YRv$@AR}&|ylP0TEP=?BlQ8Uq0HEWtWcDkCa&QNpIN7b2+ z%~WUQ=BeLSXWC}ZQ6HYG>gTEHIiD_Hhni}tnre_e1uet}j``oZ_QFETDQgN|eFo2) z{vL{LU)>KRi_f8THT^$C{FMc%X#N-Y=F02y@Bv}xx&@opBImSX)Nebn0L{a5(cUTO z_wO%Qc@;hWY-;o4YtXsqi|f#_x7w!ow`?4rf(I(mPX=}_be}{{a%GDDZ@zmKk!x#L z6%1TO>T{_%#My^utos1PUftMt9c^(hUyOFa@4))VhQ9pM6V{@>@Ba?>&6>npjx1j| zX%@Pc`kkw2S0!>*URARP69=4|mm;(p4ZOG%ZA3RvYLPJT(hCdGz|}{pewl>&^2$%5 z=TDDMdBmUMKOn$5V79Bz%t(g}le5lBRD=6x=9CwF2RXOoS+q5=#W~Mzv7=e&<_|Co z^~yDiHtJi!4{upctp?^(t6@rb_ZHOm52)`F+PSU@wAr`oWz?UH=HEn91_m?I(JxSS z-(bcJ2h;PGacD4WaQfiX!5M?kf8U>n2J;8)gY$MSE?A8>FQ^I&zqEWo%1<(KXMhF8J=YI*|51$miKcF>*%YlQt zjg%m!NJeqA%)y`yt?b4CG>>GoQZ zAPVL9pC8h~N3F@$TLqCMEJ7ebeMm_TU5H2pJ$4e-JIbYoRaSRlc`al^cI` z*2^rbs)C-XM>mf7f`tlp(ZpWEW@2KXm5G=Ev@#QOFMxNR|8@Ee|e0$VzHt7kLd?ehDhXee?kQ}=P}OuI8$c)Q zwqVniDMYem&N9^5wL&+#s;3# z&LbU&d)Wuyog;vcM*x>-LksfD&5d~)gkSp*A zdjeJoDB|Eh4LKBi>VPX|zF5A)0f0TAwJb>0a9pp*aNJV3bXF{ZOE1&_&joPyYdqHk zoEiqq@LYVV4Lk)-0SJ1*R+xWj5KGW8FDimCj4uf>)nyg zfZuA1t~yyKb`wMOC|!d^cYKsiHa8~gG)QYEN=umP-_gDUBk#{*SEl1?<>HyemCV(|eQ9L1ZD`X2sGl1@GXz3(J5F#xGvPS#zv z==jfDVX)WXTgI!&x*?byCSk2e(!s=_6UH~dVPg~%>P+>99uqM|K9)KD%+Xe)J6+Hu z$1a9D6XgDSQx~9xqq~`@!ru|@aM1ag|9U7 zKNqSP3CRN7eVUlhzA|)-R^o#xa^iLPsrd~^C))Y(ZOd2c z78O35Ju@d~=FG>kpIDgv=-2WD%_|^h&cPmkR05+JHuRDeUbfM>nZ}T}rfj=>v$M3b iymP(Wmmz2^VfkiHY0vEz?OmbG*P+c8KIRE*@c#yUti=xi literal 0 HcmV?d00001 diff --git a/free-c64-roms/chargen b/free-c64-roms/chargen new file mode 100644 index 0000000000000000000000000000000000000000..11cbd8c256dc027877fe78450aee9ff00a3065bb GIT binary patch literal 4096 zcmeHKJ%}4e6dp&gy5cywoMMT3lS1GiR4h_(VN#|^oh1ib0dxb5ww2muI3yLS-_h$F(&VdvLC!eH!_uiX%KeM~@&3VCS zEt#s?&Rh!LWHH|$pV1J(B*d3$@vD{(dR1i{Fo{LIr@?e8Cq$B!A$ z(})E*f>|T~uI?P7{tn?Gu7A|&K;I=iwd3|(K&iWf@!+nyMUPNDH5`6RZMQ9X4`Xoo`78TjWd!2%54&Au#)EdN*|-B;u&zMG5Y}8@t7_Bq+1K`Ct8O$n zfEM#+yIHT&No_}}cH8DtEkJY|ZL_KD)uPHcnChHTH*^_iS1C^4t}Rnlx_1{ zFI!@6rj>Y*v{A2pSv;L4@!Vu_9K~^Lj^h~LhfW(h5Li8VJmsS4Q%#*Y1>I!)!Zoxcp zJk2Qo$cn;TT%1oPPZ=*O5DRh&bAkNV#qt#OFNq9slau8V`ZbZ6n|z)F#=K4^>1*C- ziv?j8W;Xj(440P^d@fB9B~cj_<|oFDpR>;Fhw0oDg`J#Bp4==X{=(e6$|w1&8-oET z-Nr5o=*DDL!xfIdO2kWkL%eBp8fT_hu+a$pm-!Ow1$kXUCdn*9Xs-KliFL)gchT;+?i_5ha3vlW;u0;(lKMVFHa>bXJx z76`Rj=SAV^D9a-4q45_lxNb#+bA)3m$L$J%uY-q2LRc4xIcq&&dL7<%mzS$Nx@^H;{;H}E6Geq4EvnKi}^eO;nBfhx#W)zCsFgaef#h8 w!4dF1A*jepZ_V}fz5H+A{+IEG=70Pj@_e?iXH)jW_nb|j^=1>Y?y>g$4cOiGUH||9 literal 0 HcmV?d00001 diff --git a/free-c64-roms/kernal b/free-c64-roms/kernal new file mode 100644 index 0000000000000000000000000000000000000000..0df4af2e1bc8d88ee7bedab16967ce4ab48278c2 GIT binary patch literal 8192 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 literal 0 HcmV?d00001 diff --git a/free-c64-roms/readme.txt b/free-c64-roms/readme.txt new file mode 100644 index 0000000..031c2fa --- /dev/null +++ b/free-c64-roms/readme.txt @@ -0,0 +1,24 @@ +Free/open source roms for the C64 + +See https://github.com/MEGA65/open-roms + +The following copyright notices apply to the entirety of this package, +including each source file, unless otherwise noted in each file or directory. + + Copyright Paul Gardner-Stephen, 2019. + Copyright Roman Standzikowski (FeralChil64), 2019-2020. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . + + diff --git a/src/main/kotlin/razorvine/c64emu/Cia.kt b/src/main/kotlin/razorvine/c64emu/Cia.kt index a948315..6eca49d 100644 --- a/src/main/kotlin/razorvine/c64emu/Cia.kt +++ b/src/main/kotlin/razorvine/c64emu/Cia.kt @@ -9,12 +9,20 @@ import java.awt.event.KeyEvent /** * Minimal simulation of the MOS 6526 CIA chip. * Depending on what CIA it is (1 or 2), some registers do different things on the C64. - * This implementation provides a working keyboard matrix, TOD clock, and the essentials of the timer A and B. + * This implementation provides a working keyboard matrix, joystick in port#2 (cia 1), + * time of day clock, and the essentials of the timer A and B. */ class Cia(val number: Int, startAddress: Address, endAddress: Address, val cpu: Cpu6502) : MemMappedComponent(startAddress, endAddress) { private var ramBuffer = Array(endAddress-startAddress+1) { 0 } private var regPRA = 0xff + // joystick in port 2 configuration (only works on cia#1) + private var joy2up = false + private var joy2down = false + private var joy2left = false + private var joy2right = false + private var joy2fire = false + class TimeOfDay { private var updatedAt = 0L private var startedAt = 0L @@ -125,6 +133,11 @@ class Cia(val number: Int, startAddress: Address, endAddress: Address, val cpu: timerAset = 0 timerBactual = 0 timerBset = 0 + joy2up = false + joy2down = false + joy2left = false + joy2right = false + joy2fire = false } override operator fun get(offset: Int): UByte { @@ -139,71 +152,85 @@ class Cia(val number: Int, startAddress: Address, endAddress: Address, val cpu: } val register = offset and 15 - if (number == 1 && register == 0x01) { - // register 1 on CIA#1 is the keyboard data port - // if bit is cleared in PRA, contains keys pressed in that column of the matrix - return when (regPRA) { - 0b00000000 -> { - // check if any keys are pressed at all (by checking all columns at once) - if (hostKeyPresses.isEmpty()) 0xff.toShort() else 0x00.toShort() - } - 0b11111110 -> { - // read column 0 - scanColumn(HostKeyPress(KeyEvent.VK_DOWN), HostKeyPress(KeyEvent.VK_F5), HostKeyPress(KeyEvent.VK_F3), - HostKeyPress(KeyEvent.VK_F1), HostKeyPress(KeyEvent.VK_F7), HostKeyPress(KeyEvent.VK_RIGHT), - HostKeyPress(KeyEvent.VK_ENTER), HostKeyPress(KeyEvent.VK_BACK_SPACE)) - } - 0b11111101 -> { - // read column 1 - scanColumn(HostKeyPress(KeyEvent.VK_SHIFT), // left shift - HostKeyPress(KeyEvent.VK_E), HostKeyPress(KeyEvent.VK_S), HostKeyPress(KeyEvent.VK_Z), - HostKeyPress(KeyEvent.VK_4), HostKeyPress(KeyEvent.VK_A), HostKeyPress(KeyEvent.VK_W), - HostKeyPress(KeyEvent.VK_3)) - } - 0b11111011 -> { - // read column 2 - scanColumn(HostKeyPress(KeyEvent.VK_X), HostKeyPress(KeyEvent.VK_T), HostKeyPress(KeyEvent.VK_F), - HostKeyPress(KeyEvent.VK_C), HostKeyPress(KeyEvent.VK_6), HostKeyPress(KeyEvent.VK_D), - HostKeyPress(KeyEvent.VK_R), HostKeyPress(KeyEvent.VK_5)) - } - 0b11110111 -> { - // read column 3 - scanColumn(HostKeyPress(KeyEvent.VK_V), HostKeyPress(KeyEvent.VK_U), HostKeyPress(KeyEvent.VK_H), - HostKeyPress(KeyEvent.VK_B), HostKeyPress(KeyEvent.VK_8), HostKeyPress(KeyEvent.VK_G), - HostKeyPress(KeyEvent.VK_Y), HostKeyPress(KeyEvent.VK_7)) - } - 0b11101111 -> { - // read column 4 - scanColumn(HostKeyPress(KeyEvent.VK_N), HostKeyPress(KeyEvent.VK_O), HostKeyPress(KeyEvent.VK_K), - HostKeyPress(KeyEvent.VK_M), HostKeyPress(KeyEvent.VK_0), HostKeyPress(KeyEvent.VK_J), - HostKeyPress(KeyEvent.VK_I), HostKeyPress(KeyEvent.VK_9)) - } - 0b11011111 -> { - // read column 5 - scanColumn(HostKeyPress(KeyEvent.VK_COMMA), HostKeyPress(KeyEvent.VK_AT), HostKeyPress(KeyEvent.VK_COLON), - HostKeyPress(KeyEvent.VK_PERIOD), HostKeyPress(KeyEvent.VK_MINUS), HostKeyPress(KeyEvent.VK_L), - HostKeyPress(KeyEvent.VK_P), HostKeyPress(KeyEvent.VK_PLUS)) - } - 0b10111111 -> { - // read column 6 - scanColumn(HostKeyPress(KeyEvent.VK_SLASH), HostKeyPress(KeyEvent.VK_CIRCUMFLEX), HostKeyPress(KeyEvent.VK_EQUALS), - HostKeyPress(KeyEvent.VK_SHIFT, rightSide = true), // right shift - HostKeyPress(KeyEvent.VK_HOME), HostKeyPress(KeyEvent.VK_SEMICOLON), HostKeyPress(KeyEvent.VK_ASTERISK), - HostKeyPress(KeyEvent.VK_DEAD_TILDE) // pound sign - ) - } - 0b01111111 -> { - // read column 7 - scanColumn(HostKeyPress(KeyEvent.VK_ESCAPE), HostKeyPress(KeyEvent.VK_Q), HostKeyPress(KeyEvent.VK_ALT), - HostKeyPress(KeyEvent.VK_SPACE), HostKeyPress(KeyEvent.VK_2), HostKeyPress(KeyEvent.VK_CONTROL), - HostKeyPress(KeyEvent.VK_BACK_QUOTE), HostKeyPress(KeyEvent.VK_1)) - } - else -> { - // invalid column selection - 0xff + + if(number==1) { + // first CIA has keyboard matrix + if(register==0x00) { + // reading $dc00 is joystick in port #2 + return (0b01100000 + or (if(joy2up) 0 else 0b00000001) + or (if(joy2down) 0 else 0b00000010) + or (if(joy2left) 0 else 0b00000100) + or (if(joy2right) 0 else 0b00001000) + or (if(joy2fire) 0 else 0b00010000)).toShort() + } else if(register==0x01) { + // register 1 on CIA#1 is the keyboard data port (and joystick #1...) + // if bit is cleared in PRA, contains keys pressed in that column of the matrix + // NOTE: we do not emulate a joystick in port #1 as this conflicts with the keyboard. + // just use the joystick in port #2 for now... + return when (regPRA) { + 0b00000000 -> { + // check if any keys are pressed at all (by checking all columns at once) + if (hostKeyPresses.isEmpty()) 0xff.toShort() else 0x00.toShort() + } + 0b11111110 -> { + // read column 0 + scanColumn(HostKeyPress(KeyEvent.VK_DOWN), HostKeyPress(KeyEvent.VK_F5), HostKeyPress(KeyEvent.VK_F3), + HostKeyPress(KeyEvent.VK_F1), HostKeyPress(KeyEvent.VK_F7), HostKeyPress(KeyEvent.VK_RIGHT), + HostKeyPress(KeyEvent.VK_ENTER), HostKeyPress(KeyEvent.VK_BACK_SPACE)) + } + 0b11111101 -> { + // read column 1 + scanColumn(HostKeyPress(KeyEvent.VK_SHIFT), // left shift + HostKeyPress(KeyEvent.VK_E), HostKeyPress(KeyEvent.VK_S), HostKeyPress(KeyEvent.VK_Z), + HostKeyPress(KeyEvent.VK_4), HostKeyPress(KeyEvent.VK_A), HostKeyPress(KeyEvent.VK_W), + HostKeyPress(KeyEvent.VK_3)) + } + 0b11111011 -> { + // read column 2 + scanColumn(HostKeyPress(KeyEvent.VK_X), HostKeyPress(KeyEvent.VK_T), HostKeyPress(KeyEvent.VK_F), + HostKeyPress(KeyEvent.VK_C), HostKeyPress(KeyEvent.VK_6), HostKeyPress(KeyEvent.VK_D), + HostKeyPress(KeyEvent.VK_R), HostKeyPress(KeyEvent.VK_5)) + } + 0b11110111 -> { + // read column 3 + scanColumn(HostKeyPress(KeyEvent.VK_V), HostKeyPress(KeyEvent.VK_U), HostKeyPress(KeyEvent.VK_H), + HostKeyPress(KeyEvent.VK_B), HostKeyPress(KeyEvent.VK_8), HostKeyPress(KeyEvent.VK_G), + HostKeyPress(KeyEvent.VK_Y), HostKeyPress(KeyEvent.VK_7)) + } + 0b11101111 -> { + // read column 4 + scanColumn(HostKeyPress(KeyEvent.VK_N), HostKeyPress(KeyEvent.VK_O), HostKeyPress(KeyEvent.VK_K), + HostKeyPress(KeyEvent.VK_M), HostKeyPress(KeyEvent.VK_0), HostKeyPress(KeyEvent.VK_J), + HostKeyPress(KeyEvent.VK_I), HostKeyPress(KeyEvent.VK_9)) + } + 0b11011111 -> { + // read column 5 + scanColumn(HostKeyPress(KeyEvent.VK_COMMA), HostKeyPress(KeyEvent.VK_AT), HostKeyPress(KeyEvent.VK_COLON), + HostKeyPress(KeyEvent.VK_PERIOD), HostKeyPress(KeyEvent.VK_MINUS), HostKeyPress(KeyEvent.VK_L), + HostKeyPress(KeyEvent.VK_P), HostKeyPress(KeyEvent.VK_PLUS)) + } + 0b10111111 -> { + // read column 6 + scanColumn(HostKeyPress(KeyEvent.VK_SLASH), HostKeyPress(KeyEvent.VK_CIRCUMFLEX), HostKeyPress(KeyEvent.VK_EQUALS), + HostKeyPress(KeyEvent.VK_SHIFT, rightSide = true), // right shift + HostKeyPress(KeyEvent.VK_HOME), HostKeyPress(KeyEvent.VK_SEMICOLON), HostKeyPress(KeyEvent.VK_ASTERISK), + HostKeyPress(KeyEvent.VK_DEAD_TILDE) // pound sign + ) + } + 0b01111111 -> { + // read column 7 + scanColumn(HostKeyPress(KeyEvent.VK_ESCAPE), HostKeyPress(KeyEvent.VK_Q), HostKeyPress(KeyEvent.VK_ALT), + HostKeyPress(KeyEvent.VK_SPACE), HostKeyPress(KeyEvent.VK_2), HostKeyPress(KeyEvent.VK_CONTROL), + HostKeyPress(KeyEvent.VK_BACK_QUOTE), HostKeyPress(KeyEvent.VK_1)) + } + else -> { + // invalid column selection + 0xff + } } } - } else ramBuffer[register] + } return when (register) { 0x04 -> (timerAactual and 0xff).toShort() @@ -224,19 +251,6 @@ class Cia(val number: Int, startAddress: Address, endAddress: Address, val cpu: } } - private fun toBCD(data: Int): UByte { - val tens = data/10 - val ones = data-tens*10 - return ((tens shl 4) or ones).toShort() - } - - private fun fromBCD(bcd: UByte): Int { - val ibcd = bcd.toInt() - val tens = ibcd ushr 4 - val ones = ibcd and 0x0f - return tens*10+ones - } - override operator fun set(offset: Int, data: UByte) { val register = offset and 15 if (number == 1 && register == 0x00) { @@ -296,6 +310,19 @@ class Cia(val number: Int, startAddress: Address, endAddress: Address, val cpu: } } + private fun toBCD(data: Int): UByte { + val tens = data/10 + val ones = data-tens*10 + return ((tens shl 4) or ones).toShort() + } + + private fun fromBCD(bcd: UByte): Int { + val ibcd = bcd.toInt() + val tens = ibcd ushr 4 + val ones = ibcd and 0x0f + return tens*10+ones + } + fun hostKeyPressed(event: KeyEvent) { val rightSide = event.keyLocation == KeyEvent.KEY_LOCATION_RIGHT val numpad = event.keyLocation == KeyEvent.KEY_LOCATION_NUMPAD @@ -374,4 +401,15 @@ class Cia(val number: Int, startAddress: Address, endAddress: Address, val cpu: } } } + + fun setJoystick2(up: Boolean, down: Boolean, left: Boolean, right: Boolean, fire: Boolean) { + if(number!=1) + throw NotImplementedError("joystick port 2 is connected to cia#1") + + joy2up = up + joy2down = down + joy2left = left + joy2right = right + joy2fire = fire + } } diff --git a/src/main/kotlin/razorvine/c64emu/GUI.kt b/src/main/kotlin/razorvine/c64emu/GUI.kt index f43583b..cfaccdf 100644 --- a/src/main/kotlin/razorvine/c64emu/GUI.kt +++ b/src/main/kotlin/razorvine/c64emu/GUI.kt @@ -82,16 +82,50 @@ class MainC64Window(title: String, chargen: Rom, val ram: MemoryComponent, val c // keyboard events: override fun keyTyped(event: KeyEvent) {} + private var joy2up = false + private var joy2down = false + private var joy2left = false + private var joy2right = false + private var joy2fire = false + override fun keyPressed(event: KeyEvent) { // '\' is mapped as RESTORE, this causes a NMI on the cpu if (event.keyChar == '\\') { cpu.nmiAsserted = true } else { - keypressCia.hostKeyPressed(event) + if(event.keyLocation==KeyEvent.KEY_LOCATION_NUMPAD) { + // numpad is joystick #2 + if (event.keyChar in "789") joy2up = true + if (event.keyChar in "123") joy2down = true + if (event.keyChar in "741") joy2left = true + if (event.keyChar in "963") joy2right = true + if (event.keyChar in "05\n") joy2fire = true + keypressCia.setJoystick2(joy2up, joy2down, joy2left, joy2right, joy2fire) + } else { + keypressCia.hostKeyPressed(event) + } } } override fun keyReleased(event: KeyEvent) { - keypressCia.hostKeyPressed(event) + if(event.keyLocation==KeyEvent.KEY_LOCATION_NUMPAD) { + // numpad is joystick #2 + if (event.keyChar in "789") joy2up = false + if (event.keyChar in "123") joy2down = false + if (event.keyChar in "741") joy2left = false + if (event.keyChar in "963") joy2right = false + if (event.keyChar in "05\n") joy2fire = false + keypressCia.setJoystick2(joy2up, joy2down, joy2left, joy2right, joy2fire) + } else { + keypressCia.hostKeyPressed(event) + } + } + + fun reset() { + joy2up = false + joy2down = false + joy2left = false + joy2right = false + joy2fire = false } } diff --git a/src/main/kotlin/razorvine/c64emu/c64Main.kt b/src/main/kotlin/razorvine/c64emu/c64Main.kt index 8dbb402..f1f78cc 100644 --- a/src/main/kotlin/razorvine/c64emu/c64Main.kt +++ b/src/main/kotlin/razorvine/c64emu/c64Main.kt @@ -216,6 +216,11 @@ class C64Machine(title: String) : IVirtualMachine { while (cpu.instrCycles > 0) bus.clock() } + override fun reset() { + bus.reset() + hostDisplay.reset() + } + override fun executeMonitorCommand(command: String) = monitor.command(command) fun start() { diff --git a/src/main/kotlin/razorvine/examplemachines/DebugWindow.kt b/src/main/kotlin/razorvine/examplemachines/DebugWindow.kt index c50f293..43ab6d1 100644 --- a/src/main/kotlin/razorvine/examplemachines/DebugWindow.kt +++ b/src/main/kotlin/razorvine/examplemachines/DebugWindow.kt @@ -182,6 +182,7 @@ class DebugWindow(private val vm: IVirtualMachine) : JFrame("Debugger - ksim65 v } "reset" -> { + vm.reset() vm.bus.reset() updateCpu(vm.cpu, vm.bus) } diff --git a/src/main/kotlin/razorvine/examplemachines/GUI.kt b/src/main/kotlin/razorvine/examplemachines/GUI.kt index f845107..9c07468 100644 --- a/src/main/kotlin/razorvine/examplemachines/GUI.kt +++ b/src/main/kotlin/razorvine/examplemachines/GUI.kt @@ -242,4 +242,8 @@ class MainWindow(title: String) : JFrame(title), KeyListener, MouseInputListener else keyboardBuffer.pop() } + fun reset() { + // when the reset button is pressed + } + } diff --git a/src/main/kotlin/razorvine/examplemachines/machineMain.kt b/src/main/kotlin/razorvine/examplemachines/machineMain.kt index 84de2cc..a4f204e 100644 --- a/src/main/kotlin/razorvine/examplemachines/machineMain.kt +++ b/src/main/kotlin/razorvine/examplemachines/machineMain.kt @@ -65,6 +65,11 @@ class VirtualMachine(title: String) : IVirtualMachine { while (cpu.instrCycles > 0) bus.clock() } + override fun reset() { + bus.reset() + hostDisplay.reset() + } + override fun executeMonitorCommand(command: String) = monitor.command(command) fun start() { diff --git a/src/main/kotlin/razorvine/ksim65/IVirtualMachine.kt b/src/main/kotlin/razorvine/ksim65/IVirtualMachine.kt index 10177c0..4f71347 100644 --- a/src/main/kotlin/razorvine/ksim65/IVirtualMachine.kt +++ b/src/main/kotlin/razorvine/ksim65/IVirtualMachine.kt @@ -7,6 +7,7 @@ import java.io.File interface IVirtualMachine { fun step() fun pause(paused: Boolean) + fun reset() fun getZeroAndStackPages(): Array fun loadFileInRam(file: File, loadAddress: Address?) diff --git a/src/main/resources/version.properties b/src/main/resources/version.properties index d01ef93..af5f173 100644 --- a/src/main/resources/version.properties +++ b/src/main/resources/version.properties @@ -1 +1 @@ -version=1.8 +version=1.9-dev