From 2f245294881fb3e8e36c21ec4e222516fe34f008 Mon Sep 17 00:00:00 2001 From: "simon.kagstrom" Date: Tue, 13 Jan 2009 18:46:42 +0000 Subject: [PATCH] Make frodo CVS the current trunk --- 64prgs/3fff | Bin 0 -> 267 bytes 64prgs/colorbars | Bin 0 -> 12 bytes 64prgs/d011h3 | Bin 0 -> 2112 bytes 64prgs/dadb | Bin 0 -> 151 bytes 64prgs/de00all | Bin 0 -> 184 bytes 64prgs/dycp | Bin 0 -> 514 bytes 64prgs/fld | Bin 0 -> 114 bytes 64prgs/lrborder | Bin 0 -> 332 bytes 64prgs/sprsync | Bin 0 -> 226 bytes 64prgs/stretch | Bin 0 -> 482 bytes 64prgs/tech-tech | Bin 0 -> 386 bytes 64prgs/text26 | Bin 0 -> 105 bytes CHANGES | 216 ++++ COPYING | 340 ++++++ Docs/contact.html | 123 ++ Docs/demoprograms.html | 64 + Docs/emulwindow.html | 41 + Docs/files.html | 85 ++ Docs/flavours.html | 58 + Docs/future.html | 24 + Docs/history.html | 242 ++++ Docs/index.html | 56 + Docs/installation.html | 64 + Docs/kernal.html | 55 + Docs/keyboard.html | 140 +++ Docs/legalmush.html | 29 + Docs/overview.html | 49 + Docs/sam.html | 282 +++++ Docs/settings.html | 167 +++ Docs/systemspecific.html | 102 ++ Docs/technicalinfo.html | 61 + Docs/whatsnew.html | 22 + FreeMono.ttf | Bin 0 -> 134512 bytes Frodo Logo | Bin 0 -> 38400 bytes Frodo.spec | 49 + Kernal ROM | Bin 0 -> 8192 bytes Makefile | 160 +++ Src/1541_ROM.h | 2057 ++++++++++++++++++++++++++++++++ Src/1541d64.cpp | 2167 +++++++++++++++++++++++++++++++++ Src/1541d64.h | 152 +++ Src/1541fs.cpp | 604 ++++++++++ Src/1541fs.h | 57 + Src/1541job.cpp | 476 ++++++++ Src/1541job.h | 126 ++ Src/1541t64.cpp | 749 ++++++++++++ Src/1541t64.h | 84 ++ Src/AcornGUI.cc | 1804 ++++++++++++++++++++++++++++ Src/AcornGUI.h | 357 ++++++ Src/AcornGUI_SC.cc | 26 + Src/AmigaGUI.c | 407 +++++++ Src/AmigaGUI.gui | Bin 0 -> 2068 bytes Src/AmigaGUI.h | 153 +++ Src/Basic_ROM.h | 1033 ++++++++++++++++ Src/C64.cpp | 741 ++++++++++++ Src/C64.h | 255 ++++ Src/C64_Acorn.h | 421 +++++++ Src/C64_Amiga.h | 407 +++++++ Src/C64_Be.h | 418 +++++++ Src/C64_Embedded.h | 320 +++++ Src/C64_SC.cpp | 28 + Src/C64_SDL.h | 674 +++++++++++ Src/C64_WIN32.h | 452 +++++++ Src/C64_x.h | 378 ++++++ Src/CIA.cpp | 570 +++++++++ Src/CIA.h | 210 ++++ Src/CIA_SC.cpp | 777 ++++++++++++ Src/CPU1541.cpp | 786 ++++++++++++ Src/CPU1541.h | 295 +++++ Src/CPU1541_SC.cpp | 661 ++++++++++ Src/CPUC64.cpp | 847 +++++++++++++ Src/CPUC64.h | 230 ++++ Src/CPUC64_SC.cpp | 670 +++++++++++ Src/CPU_common.cpp | 97 ++ Src/CPU_common.h | 90 ++ Src/CPU_emulcycle.h | 1114 +++++++++++++++++ Src/CPU_emulline.h | 1418 ++++++++++++++++++++++ Src/Char_ROM.h | 521 ++++++++ Src/CmdPipe.cpp | 181 +++ Src/CmdPipe.h | 98 ++ Src/Display.cpp | 109 ++ Src/Display.h | 247 ++++ Src/Display_Acorn.h | 441 +++++++ Src/Display_Amiga.h | 641 ++++++++++ Src/Display_Be.h | 1181 ++++++++++++++++++ Src/Display_EmbeddedSDL.h | 961 +++++++++++++++ Src/Display_SDL.h | 736 ++++++++++++ Src/Display_WIN32.h | 2380 +++++++++++++++++++++++++++++++++++++ Src/Display_svga.h | 569 +++++++++ Src/Display_x.h | 826 +++++++++++++ Src/FixPoint.h | 433 +++++++ Src/Frodo.ico | Bin 0 -> 1078 bytes Src/Frodo.rc | 291 +++++ Src/Frodo.rsrc | Bin 0 -> 4506 bytes Src/Frodo_GUI.tcl | 1031 ++++++++++++++++ Src/IEC.cpp | 953 +++++++++++++++ Src/IEC.h | 258 ++++ Src/Invisible.cur | Bin 0 -> 326 bytes Src/Kernal_ROM.h | 1033 ++++++++++++++++ Src/Makefile.Amiga | 86 ++ Src/Makefile.BeOS | 117 ++ Src/Makefile.BeOS.SC | 118 ++ Src/Makefile.WIN32 | 111 ++ Src/Makefile.in | 118 ++ Src/MakefileRO | 132 ++ Src/Prefs.cpp | 438 +++++++ Src/Prefs.h | 155 +++ Src/Prefs_Amiga.h | 397 +++++++ Src/Prefs_Be.h | 779 ++++++++++++ Src/Prefs_WIN32.h | 403 +++++++ Src/Prefs_glade.h | 250 ++++ Src/REU.cpp | 276 +++++ Src/REU.h | 53 + Src/ROlib.h | 533 +++++++++ Src/ROlib.s | 1167 ++++++++++++++++++ Src/SAM.cpp | 2192 ++++++++++++++++++++++++++++++++++ Src/SAM.h | 29 + Src/SID.cpp | 1413 ++++++++++++++++++++++ Src/SID.h | 161 +++ Src/SID_Acorn.h | 107 ++ Src/SID_Amiga.h | 298 +++++ Src/SID_Be.h | 102 ++ Src/SID_WIN32.h | 569 +++++++++ Src/SID_hp.h | 191 +++ Src/SID_linux.h | 217 ++++ Src/SID_wii.h | 93 ++ Src/VIC.cpp | 1875 +++++++++++++++++++++++++++++ Src/VIC.h | 288 +++++ Src/VIC_SC.cpp | 1999 +++++++++++++++++++++++++++++++ Src/Version.h | 29 + Src/autogen.sh | 57 + Src/char_to_kc.c | 101 ++ Src/config.guess | 1465 +++++++++++++++++++++++ Src/config.sub | 1569 ++++++++++++++++++++++++ Src/configarm | 2 + Src/configure.ac | 126 ++ Src/debian/changelog | 17 + Src/debian/compat | 1 + Src/debian/control | 40 + Src/debian/copyright | 22 + Src/debian/postinst | 20 + Src/debian/rules | 107 ++ Src/debug.h | 33 + Src/dlgAdvanced.cpp | 107 ++ Src/dlgDrives.cpp | 138 +++ Src/dlgFileSelect.cpp | 414 +++++++ Src/dlgFileSelect.h | 24 + Src/dlgInput.cpp | 74 ++ Src/dlgMain.cpp | 113 ++ Src/dlgMain.h | 29 + Src/dlgOptions.cpp | 116 ++ Src/dlgVideoSound.cpp | 82 ++ Src/el_Acorn.h | 188 +++ Src/file.cpp | 541 +++++++++ Src/file.h | 54 + Src/font8.h | 175 +++ Src/gcaudio.c | 148 +++ Src/gcaudio.h | 27 + Src/glade/Frodo.glade | 1860 +++++++++++++++++++++++++++++ Src/glade/Frodo.gladep | 9 + Src/install-sh | 251 ++++ Src/maemo/Frodo_26_26.png | Bin 0 -> 1205 bytes Src/maemo/Frodo_40_40.png | Bin 0 -> 1361 bytes Src/maemo/Frodo_64_64.png | Bin 0 -> 1495 bytes Src/maemo/frodo.desktop | 8 + Src/maemo/frodo.service | 3 + Src/main.cpp | 121 ++ Src/main.h | 223 ++++ Src/main_Acorn.h | 576 +++++++++ Src/main_Amiga.h | 177 +++ Src/main_Be.h | 401 +++++++ Src/main_WIN32.h | 126 ++ Src/main_wii.h | 114 ++ Src/main_x.h | 160 +++ Src/menu.cpp | 469 ++++++++ Src/menu.h | 59 + Src/mkinstalldirs | 99 ++ Src/ndir.c | 222 ++++ Src/ndir.h | 69 ++ Src/resource.h | 92 ++ Src/sdlgui.cpp | 1476 +++++++++++++++++++++++ Src/sdlgui.h | 83 ++ Src/setarmenv | 6 + Src/sysconfig.h.Acorn | 144 +++ Src/sysconfig.h.Amiga | 143 +++ Src/sysconfig.h.Be | 143 +++ Src/sysconfig.h.Host-SDL | 187 +++ Src/sysconfig.h.WIN32 | 143 +++ Src/sysconfig.h.Wii | 191 +++ Src/sysdeps.h | 208 ++++ 189 files changed, 65679 insertions(+) create mode 100644 64prgs/3fff create mode 100644 64prgs/colorbars create mode 100644 64prgs/d011h3 create mode 100644 64prgs/dadb create mode 100644 64prgs/de00all create mode 100644 64prgs/dycp create mode 100644 64prgs/fld create mode 100644 64prgs/lrborder create mode 100644 64prgs/sprsync create mode 100644 64prgs/stretch create mode 100644 64prgs/tech-tech create mode 100644 64prgs/text26 create mode 100644 CHANGES create mode 100644 COPYING create mode 100644 Docs/contact.html create mode 100644 Docs/demoprograms.html create mode 100644 Docs/emulwindow.html create mode 100644 Docs/files.html create mode 100644 Docs/flavours.html create mode 100644 Docs/future.html create mode 100644 Docs/history.html create mode 100644 Docs/index.html create mode 100644 Docs/installation.html create mode 100644 Docs/kernal.html create mode 100644 Docs/keyboard.html create mode 100644 Docs/legalmush.html create mode 100644 Docs/overview.html create mode 100644 Docs/sam.html create mode 100644 Docs/settings.html create mode 100644 Docs/systemspecific.html create mode 100644 Docs/technicalinfo.html create mode 100644 Docs/whatsnew.html create mode 100644 FreeMono.ttf create mode 100644 Frodo Logo create mode 100644 Frodo.spec create mode 100644 Kernal ROM create mode 100644 Makefile create mode 100644 Src/1541_ROM.h create mode 100644 Src/1541d64.cpp create mode 100644 Src/1541d64.h create mode 100644 Src/1541fs.cpp create mode 100644 Src/1541fs.h create mode 100644 Src/1541job.cpp create mode 100644 Src/1541job.h create mode 100644 Src/1541t64.cpp create mode 100644 Src/1541t64.h create mode 100644 Src/AcornGUI.cc create mode 100644 Src/AcornGUI.h create mode 100644 Src/AcornGUI_SC.cc create mode 100644 Src/AmigaGUI.c create mode 100644 Src/AmigaGUI.gui create mode 100644 Src/AmigaGUI.h create mode 100644 Src/Basic_ROM.h create mode 100644 Src/C64.cpp create mode 100644 Src/C64.h create mode 100644 Src/C64_Acorn.h create mode 100644 Src/C64_Amiga.h create mode 100644 Src/C64_Be.h create mode 100644 Src/C64_Embedded.h create mode 100644 Src/C64_SC.cpp create mode 100644 Src/C64_SDL.h create mode 100644 Src/C64_WIN32.h create mode 100644 Src/C64_x.h create mode 100644 Src/CIA.cpp create mode 100644 Src/CIA.h create mode 100644 Src/CIA_SC.cpp create mode 100644 Src/CPU1541.cpp create mode 100644 Src/CPU1541.h create mode 100644 Src/CPU1541_SC.cpp create mode 100644 Src/CPUC64.cpp create mode 100644 Src/CPUC64.h create mode 100644 Src/CPUC64_SC.cpp create mode 100644 Src/CPU_common.cpp create mode 100644 Src/CPU_common.h create mode 100644 Src/CPU_emulcycle.h create mode 100644 Src/CPU_emulline.h create mode 100644 Src/Char_ROM.h create mode 100644 Src/CmdPipe.cpp create mode 100644 Src/CmdPipe.h create mode 100644 Src/Display.cpp create mode 100644 Src/Display.h create mode 100644 Src/Display_Acorn.h create mode 100644 Src/Display_Amiga.h create mode 100644 Src/Display_Be.h create mode 100644 Src/Display_EmbeddedSDL.h create mode 100644 Src/Display_SDL.h create mode 100644 Src/Display_WIN32.h create mode 100644 Src/Display_svga.h create mode 100644 Src/Display_x.h create mode 100644 Src/FixPoint.h create mode 100644 Src/Frodo.ico create mode 100644 Src/Frodo.rc create mode 100644 Src/Frodo.rsrc create mode 100755 Src/Frodo_GUI.tcl create mode 100644 Src/IEC.cpp create mode 100644 Src/IEC.h create mode 100644 Src/Invisible.cur create mode 100644 Src/Kernal_ROM.h create mode 100644 Src/Makefile.Amiga create mode 100644 Src/Makefile.BeOS create mode 100644 Src/Makefile.BeOS.SC create mode 100644 Src/Makefile.WIN32 create mode 100644 Src/Makefile.in create mode 100644 Src/MakefileRO create mode 100644 Src/Prefs.cpp create mode 100644 Src/Prefs.h create mode 100644 Src/Prefs_Amiga.h create mode 100644 Src/Prefs_Be.h create mode 100644 Src/Prefs_WIN32.h create mode 100644 Src/Prefs_glade.h create mode 100644 Src/REU.cpp create mode 100644 Src/REU.h create mode 100644 Src/ROlib.h create mode 100644 Src/ROlib.s create mode 100644 Src/SAM.cpp create mode 100644 Src/SAM.h create mode 100644 Src/SID.cpp create mode 100644 Src/SID.h create mode 100644 Src/SID_Acorn.h create mode 100644 Src/SID_Amiga.h create mode 100644 Src/SID_Be.h create mode 100644 Src/SID_WIN32.h create mode 100644 Src/SID_hp.h create mode 100644 Src/SID_linux.h create mode 100644 Src/SID_wii.h create mode 100644 Src/VIC.cpp create mode 100644 Src/VIC.h create mode 100644 Src/VIC_SC.cpp create mode 100644 Src/Version.h create mode 100755 Src/autogen.sh create mode 100644 Src/char_to_kc.c create mode 100755 Src/config.guess create mode 100755 Src/config.sub create mode 100755 Src/configarm create mode 100644 Src/configure.ac create mode 100644 Src/debian/changelog create mode 100644 Src/debian/compat create mode 100644 Src/debian/control create mode 100644 Src/debian/copyright create mode 100644 Src/debian/postinst create mode 100755 Src/debian/rules create mode 100644 Src/debug.h create mode 100644 Src/dlgAdvanced.cpp create mode 100644 Src/dlgDrives.cpp create mode 100644 Src/dlgFileSelect.cpp create mode 100644 Src/dlgFileSelect.h create mode 100644 Src/dlgInput.cpp create mode 100644 Src/dlgMain.cpp create mode 100644 Src/dlgMain.h create mode 100644 Src/dlgOptions.cpp create mode 100644 Src/dlgVideoSound.cpp create mode 100644 Src/el_Acorn.h create mode 100644 Src/file.cpp create mode 100644 Src/file.h create mode 100644 Src/font8.h create mode 100644 Src/gcaudio.c create mode 100644 Src/gcaudio.h create mode 100644 Src/glade/Frodo.glade create mode 100644 Src/glade/Frodo.gladep create mode 100755 Src/install-sh create mode 100644 Src/maemo/Frodo_26_26.png create mode 100644 Src/maemo/Frodo_40_40.png create mode 100644 Src/maemo/Frodo_64_64.png create mode 100644 Src/maemo/frodo.desktop create mode 100644 Src/maemo/frodo.service create mode 100644 Src/main.cpp create mode 100644 Src/main.h create mode 100644 Src/main_Acorn.h create mode 100644 Src/main_Amiga.h create mode 100644 Src/main_Be.h create mode 100644 Src/main_WIN32.h create mode 100644 Src/main_wii.h create mode 100644 Src/main_x.h create mode 100644 Src/menu.cpp create mode 100644 Src/menu.h create mode 100755 Src/mkinstalldirs create mode 100644 Src/ndir.c create mode 100644 Src/ndir.h create mode 100644 Src/resource.h create mode 100644 Src/sdlgui.cpp create mode 100644 Src/sdlgui.h create mode 100755 Src/setarmenv create mode 100644 Src/sysconfig.h.Acorn create mode 100644 Src/sysconfig.h.Amiga create mode 100644 Src/sysconfig.h.Be create mode 100644 Src/sysconfig.h.Host-SDL create mode 100644 Src/sysconfig.h.WIN32 create mode 100644 Src/sysconfig.h.Wii create mode 100644 Src/sysdeps.h diff --git a/64prgs/3fff b/64prgs/3fff new file mode 100644 index 0000000000000000000000000000000000000000..795be3ce488bd1317f5dce4741244d44d6f94eaf GIT binary patch literal 267 zcmZQLpuljjm-o&}#$Krly`mRZ7WRrTuRPEz%DnPduh4~+&w3dzEXrTN&?A0frBN&6 zUWN-Z8P3ftIJcMK#!QCuGYih2I3@6ErFkpk0{)$M4$NdYJ5%B8N(qafK(@fynFeP& z1YRs~*a;MNI6Jce$omqJuu`~J@WQJF`S1RpS9!IQ?QE~ug?+*2cFLUZ{cqp*-yRpa zlA%N3B2aQA2T;RGX`mS^^Ut*&5Mb_YJz(*+^+0dOfwlDqBwjLV%J1!gW8~v>H3j zFH|;zq?BDyR{X#ZeT#e2KlRW2wy66*M2aUQSSMvbC8tuXaYu>^{Hf)AjSgHn+^9&a z5v)AiJ1EL{8AvUtRgflNEQB!&$rg+OsadrU=^Z%?c@To+77(vTR%IN>8IIzZCmmQJ zBxZ4$EnMart{P``4?ZCOaEYtflYxL7he_FNK1<3YDpyM*hw`)qwX)Vyn*(0H48D01 z{_;Ec(L{ZaiOQAf!Qna|m*vWGa6g~Vzb0^*+b<7D)Qio*l9h=LWjDV$P#VA(VdL&l zTVx5g$dM=41GO0;{M`S{AFTfkbm+JBXJF=;NH{Qc(~EOt(2{bDRS*;`7)^JxtLBj(LzJ})-UQ`?H zZL-@wh-4p}sJ2M;pYeK?KiL;HJgx7qWLqgyjwx!&Hg&~R+LUXG^~&g((KDlGM$e3% z89nRiU1Q*C1LkT2=4u1xY6IqK1LkT2K;;0a4cxRc0Im!Gl>wkK08|Eu=zxd@h{^yE z4UnS&A{ro~fkJRlX!N$x+h~B_8NF@vc27@@o*F&X`8L)0HZ^*x^KGj00d+ogbUt-- zK6TXmVO-50Q0G%e^#E_@e(e4S{4E`@`iJ79iLr5-N_!KNQ`0lEbMx7S#id;ChOS(0 cX>lPtKQ}uwJvBMur5PO`ON_>c`YjUu1efc=%>V!Z literal 0 HcmV?d00001 diff --git a/64prgs/dadb b/64prgs/dadb new file mode 100644 index 0000000000000000000000000000000000000000..99ab663d3a7f7750b113e10cd70f602f08466cd5 GIT binary patch literal 151 zcmV;I0BHXK2nz_x2c9x8HZcGI0C=fTq6D4z2g(rosbG!$2aW$ZsX)q|06)RR1jTz9n zfe4*oKPeCJ3K)f8ie+G`$k6Pmkd6O8tr5`85zz4apb&&$snCUCKw*-xb#--hax$@# Fl29nNOEmxh literal 0 HcmV?d00001 diff --git a/64prgs/de00all b/64prgs/de00all new file mode 100644 index 0000000000000000000000000000000000000000..a10e3f28e62f75ff7568a69e66017ce5e920fa5b GIT binary patch literal 184 zcmV;p07w4-z<3y`28{sSsXL7k(5V=W7|^Kzh4-moh4`WXpa;E)We5c{R~X$Xb) zkOJoT=-?>O=f?P;_o<#C zjn96mwT=INjW>U(0FA)`sossk0iwW~sUXb~(9ru#0N$El>ZP>+rM0E%rM2p*0HV;E m0IUJr0Ko6?{z$0-jUdnfOaR`gp^f=R0I8vk{6_$y!0G@ts%YW> literal 0 HcmV?d00001 diff --git a/64prgs/dycp b/64prgs/dycp new file mode 100644 index 0000000000000000000000000000000000000000..0423839d5fb72b341402c8bcc56dd0d0e95b11ba GIT binary patch literal 514 zcmaKoL2DCH6ov2WZIUKTI%Cqxw1qk#q^VGhikjlgWN=vov+T0V*GlKMr{mp`xeTCk@lO{-9P*lokKpL^T>B;@e6-J7X?3{CBc{IHNlsu zE;hHuCI*!^Cd#1IobOz2Y3;Z0R2Den5I$vKvZl!blbxH)u~^??Cl;&OGV{nndR>GS z{bLJz+b@T1%BQyvhwcS80`StHUO0Pmn81%9Yr0}nQCskQ4K#W+$rJPOl1d`EVpSj rQ`KibNF$ftdGrL1xl5tUJt_mCA0lEUc^Ab*CZdwS(V3<_kGB5+_C1>; literal 0 HcmV?d00001 diff --git a/64prgs/sprsync b/64prgs/sprsync new file mode 100644 index 0000000000000000000000000000000000000000..ddd13ac85fc4b307bfd47c566806dd0023b536ac GIT binary patch literal 226 zcmZQLP_eSUm-o&}#$Krly`mRZhWCmvuRPEz%Dhs#SMb8hU%f&XRzB-xys*f40Yk6& zg}rMIcxdv^WjHs}A(g1q2Jy-~B&- z?*I9H66bdMpYK(;(5G+#Ke>{jL*OD%$;$L|DF+0Yds7ZryiGaKn||P(X)i%K z9z_qMVNTtH;!Rs!*tssayBEQO*x;#bC0Yb4R9q(tzQfPQd+$Fy@YbC>JdYiZPh#0{ zA9-R0`}9N-Co6su#}KoTPru8u``#0v@kDMu%zk`OAtn_NIw0)8r{8$elvD06Pjmy( z3&iU{M1kn*0Lp~KL?qw%o}jOMZ^n;QkwNTal2%5%eEq3DIe z3q?Ql=`p{m9KvhLvH7Z!@Ycp|7xNoR=J&Qj6P|2^JGYbr719A+ZM9m(4($5T=WDAf zs44o+fpn$0J6Kba>^;?av6B*A{J1l8efmX`=W9x%&V>EnK5knx<;vWAl`SkTE!P{( qp&Iq&rNsqSou8|eXRPT`VS?r|CKLXf6TG8DluLxiTq)D54h0v4V|95DNn%?*`O;?O9*ASnlC;*{FJ+SHIb~jmP74q-`db)eWQb?psjFM(I;A z>fl`2XtG5xi(svSy$;S6WOg982lM*@y&#i$E(=_`yc#J&`t zD>QY0&)SnlyWVhl)+94f5sRO>ypoNOd8&H3-@z|%d)MXGkni%PZ+qL&$a<&Qy?Qnp yGc^#fhl>7499XNB=V-_=Do%wdGdALArTV!!`P-a= 1.2 is + detected by the configure script + - Unix: Increased number of sound buffers from 4 to 16 (emu10k driver doesn't + work with the smaller value) + - Unix: Fixed some small compilation problems + +Changes from V4.1 to V4.1a: + - Fixed bug in IEC::NewPrefs() + - Optimized VIC emulation for speed + - BeOS: Prefs saved in /boot/home/config/settings/Frodo_settings + - BeOS: Directory panel works again + - BeOS: Correct C64/1541 clock frequency ratio (Frodo SC) + - BeOS: Correct audio mixing/clipping + +Changes from V4.0a to V4.1: + - Integrated Win32 and RiscOS ports + - Snapshot support + - Added page-crossing and "borrowed" cycles in line-based CPU emulation (Frodo PC) + - Added precise CIA cycles for line-based emulation (Frodo PC) + - Optional fixed-point arithmetic and precomputed filters in SID.cpp + - Optional dynamic alignment checks in VIC.cpp + - Changed typedefs and constants: + BYTE -> int8 + UBYTE -> uint8 + WORD -> int16 + UWORD -> uint16 + LONG -> int32 + ULONG -> uint32 + FALSE -> false + TRUE -> true + - Unix: Better configure script + - Unix: Sound support for Solaris 2.x + - Unix: Joystick can be toggled between port 1/2 with the NumLock key + - Unix: US keyboard layouts supported + - BeOS: Fixed for BeOS AA:DR9 + - BeOS: Can now switch between window/screen mode while the emulation is running, + speeded up full screen mode + - BeOS: Prefs saved in /system/settings/Frodo_settings by default + +Changes from V4.0 to V4.0a: + - Corrected BRK, ANE, ARR, SBX and DCP instructions + - Frodo SC: Improved the CIA timers + - Frodo SC: MOS6526::EmulateCycle() split into MOS6526::EmulateCycle() + and MOS6526::CheckIRQs() + - Frodo SC: Corrected interrupt behaviour of branch instructions + - BeOS: Sound calculation is now done in stereo + +Changes from V3.1c to V4.0: + - The C64 ROM files are now included + - Unix: Added SVGAlib keyboard patches from Bernd + +Changes from V3.1b to V3.1c: + - Ported to AmigaOS + - Fixed bug in IEC::Reset() + - Fixed bug when writing to SID registers >24 + - The SID noise waveform should now sound the same on all + platforms + - Removed all calls to tolower() in SAM.cpp because of + possible side-effects if tolower() is a macro + - Drive LEDs are only updated once per frame + - .d64/.t64 files are opened with read permissions only + - Fixed bug with read_char buffering in 1541fs.cpp/1541t64.cpp + - Frodo SC: Fixed memory trashing bug in MOS6569::draw_background() + - Unix: Drive LEDs blink on error + - Unix: Added more patches from Bernd + +Changes from V3.1a to V3.1b: + - Corrected SID sustain behaviour + - Reading from write-only SID registers returns the last + byte written to the SID + - No more distortions when playing samples + - Removed the "Ignore SID Volume" prefs item again + - Combined SID waveforms now sampled from a 6581R4 + - Improved 1541 VIA timer operation + - Fixed bug in 1541 head movement + - Raster IRQs can be triggered by writing to $d011/$d012 + - Some changes for the MacOS port + - Included autoconf stuff from Bernd + - Frodo SC: Fixed some CIA timer bugs + +Changes from V3.1 to V3.1a: + - Frodo SC ported + - Processor-level 1541 emulation supports reading GCR data, + removed the faked job loop + - Corrected ISB, RRA, SBX and SHA instructions + - The last line of Y expanded sprites wasn't drawn + - Light pen registers work + - Small fixes to 1541d64/1541t64 + - CIA 2 PRA write: IEC lines respect DDRA + - Better triangle waveform (12 bits) + - SID emulation can play sampled sounds + - New "Ignore SID Volume" prefs item for better sample playing + - '*' on numerical keypad toggles speed limiter + - BeOS: '/' on numerical keypad toggles processor-level 1541 + emulation + - BeOS: Safer quitting + - BeOS: Option to use GameKit (screen) + - BeOS: Replaced srand(system_time()) with srand(real_time_clock()) + - Unix: Sun makefile + +Changes from V3.0h to V3.1: + - BeOS: Joysticks work again + +Changes from V3.0g to V3.0h: + - Implemented SID notch filter, better resonance frequency calculation + - Fixed bug with SID filter option + - CIA timer B one-shot mode stops timer when counting undeflows + of timer A + - Implemented lightpen trigger + - BeOS: Fixed for BeOS DR8, improved the GUI a bit + - Unix: Some fixes to the TkGui (T64, SIDFilters, removed speed + display) + - Unix: Main window no longer resizable + - Unix: SVGALib support works again + +Changes from V3.0f to V3.0g: + - New T64/LYNX mode for 1541 emulation + - 1541fs.cpp/match() used to treat the pattern "foo" as "foo*" + - 1541 DIR mode uses tmpfile() for opening temporary directory files + - 1541 D64 mode allows wildcards for selective directory reading + - Increased compatibility of processor-level 1541 emulation in + various places (C64<->1541 communication, VIA registers, memory + map, disk change flag) + - Inlined MOS6526::EmulateLine() and some small public functions of + MOS6502_1541 + - New prefs option to enable/disable SID filter emulation + - Joystick calibration is reset when joystick options change + - BeOS: Self-calibrating joystick routines + +Changes from V3.0e to V3.0f: + - Improved DIR/D64 drive reset, resetting the C64 resets the drives + - Implemented 'G' command for DIR/D64 drives + - Corrected translation of 0xc1..0xda characters in conv_from_64() + - BeOS: Implemented smart "Insert next disk" menu item + - Unix: Improved the speedometer/LED bar + - Unix: Self-calibrating joystick routines + - Unix: No need to enter path of 'wish' in TkGui.tcl + +Changes from V3.0d to V3.0e: + - Removed the CBOOL data type + - Unix: Sound for HP-UX + - Unix: Keyboard layout matches the picture in the docs more + closely + - Unix: Diagonal directions of keypad joystick emulation work + - Unix: +/- on numerical keypad modifies SkipFrames + - Unix: F9 invokes SAM + - Unix: Drive LEDs and speedometer implemented + - Unix: Some changes to the GUI + - Unix: Random number generator is initialized + - Unix: Name of prefs file can be given as an argument + - Unix: Calls XFlush() and XSync() in C64Display::Update() + +Changes from V3.0c to V3.0d: + - SID filter emulation implemented + - SID master volume setting works again + - Flags are recalculated in MOS6526::SetState() + - Changed CBOOL->bool in some places + - Fixed bug with char_in in MOS6510::new_config() + - BeOS: Emulation thread priority lowered + - Unix: Some changes for DEC Alpha + - Unix: Joystick support for Linux + +Changes from V3.0b to V3.0c: + - 1541 DIR mode can load directory with "$0" + - Rearranged the CPU code (more macros, less inline functions) + - SID envelope generators rewritten, envelopes are now + recalculated for every sample + - SID calc_buffer function now takes pointer to WORD buffer + - Unix: Sound for Linux + - Unix: Prefs window implemented (needs Tcl/Tk) + - Unix: Corrected x64 disk image detection on little-endian systems + - Unix: SVGAlib support works again, accesses frame buffer + directly if possible + - Unix: Added support for SHM under X11 + +Changes from V3.0a to V3.0b: + - Implemented REU emulation + - Formatting disks with ID possible under processor-level + 1541 emulation + - Corrected and optimized SID waveform/envelope calculation + (signed arithmetic) + - Corrected idle state display again (ECM text) + - 1541 D64 mode ignores drive numbers when opening the + directory + - Processor-level 1541 emulation deactivates when idle + - BeOS: Sound output quality is now 16 bits + - Unix: Quits more cleanly, reactivates key repeat + - Unix: Fixed alignment problem with text_chunky_buf in VIC.h + +Changes from V3.0 to V3.0a: + - Implemented SID test bits + - Combined SID waveforms respect pulse width + - Corrected idle state graphics display + - Processor-level 1541 emulation respects .d64 error info + - CPU emulation optimized (6510 and 6502 split) + - VIC emulation optimized (raster counter in local variable + in EmulateLine()) + - BeOS: Now exiting the audio subscriber with ExitStream(TRUE) + - Unix: Fixed missing thread_func() declaration + - Unix: getcwd(AppDirPath) was missing in main_x.i + - Unix: Speed limiter works diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..60549be --- /dev/null +++ b/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/Docs/contact.html b/Docs/contact.html new file mode 100644 index 0000000..0bfdcfe --- /dev/null +++ b/Docs/contact.html @@ -0,0 +1,123 @@ + + +Contact info + + + +

Contact info

+ + +"The Silmaril as lantern light
+And banner bright with living flame
+To gleam thereon by Elbereth
+Herself was set, who thither came." +
+ +
+ +

Authors

+ +Frodo is the combined effort of many people: + +

Main program and project administration

+ +
+Christian Bauer
+Christian.Bauer@uni-mainz.de +
+ +

Initial Unix port

+ +
+Bernd Schmidt
+bernds@btinternet.com

+Lutz Vieweg
+lkv@isg.de +
+ +

Tcl/Tk interface

+ +
+Lutz Vieweg
+lkv@isg.de

+Gerard Decatrel
+deca39720@hotmail.com +
+ +

Mac OS port

+ +
+Richard Bannister
+titan@indigo.ie

+Ernesto Corvi
+macsupport@overnet.com.ar

+Andreas Varga
+e9426444@student.tuwien.ac.at +
+ +

Windows port

+ +
+J. Richard Sladkey
+jrs@foliage.com +
+ +

Risc OS port

+ +
+Andreas Dehmel
+zarquon@t-online.de +
+ +

Frodo logo design

+ +
+Tinic Urou
+tinic@users.sourceforge.net +
+ +

Additional contributions by

+ +
+Marc Chabanas
+Marc.Chabanas@france.sun.com

+Jake Hamby
+jehamby@anobject.com

+Erik Lindberg
+d96shade@dtek.chalmers.se

+Wolfgang Lorenz

+Tinic Urou
+tinic@users.sourceforge.net

+
+ +
+ +

Acknowledgements

+ +Special thanks go to + + + +
+ +

Bug reports

+ +Bug reports and general suggestions are welcome. But be warned that I'm +too lazy to handle most support requests. Questions that are specific to +running Frodo on platforms other than BeOS and Unix should be directed to +one of the maintainers of the respective port, see the list above. Send all +other stuff to: + +
+Christian.Bauer@uni-mainz.de +
+ +But please don't attach binaries of C64 programs that don't work unless I +ask you to. Thanks. + + + diff --git a/Docs/demoprograms.html b/Docs/demoprograms.html new file mode 100644 index 0000000..32a3b25 --- /dev/null +++ b/Docs/demoprograms.html @@ -0,0 +1,64 @@ + + +Sample programs + + + +

Sample programs

+ +
+ +The directory "64prgs" contains some little sample programs to show the +capabilities of Frodo/Frodo SC. Except for "dadb" and "lrborder" they are all loaded with +LOAD"<name>",8,1 and started with SYS49152. +To do so, you have to set "Drive 8" in the settings to "Dir" and set the +path to '64prgs' so Frodo will find the programs.

+ +The programs were written by Pasi Ojala, Marko Mäkelä, Andreas +Boose and me. If you like, you can try them on other emulators or on a real +C64.

+ +A short description of each program:

+ +

3fff

+Opens the top and bottom border and displays swinging letters ("Cycles per +line (CPU)" should be set to 60 for this one) + +

colorbars

+Flickering colors + +

d011h3

+8-way soft scrolling without moving a single byte in memory (cursor keys +to scroll left/right, 't' for text mode, 'g' for bitmap mode), requires Frodo SC + +

dadb

+A program running in the color RAM (press space), requires Frodo SC + +

de00all

+A program running in the address space $de00-$dfff (press space), requires +Frodo SC + +

dycp

+Scrolling with different Y character position + +

fld

+Demonstrates the FLD effect (flexible line distance) + +

lrborder

+Opens the left/right border, requires Frodo SC + +

sprsync

+Stable raster routine by synchronizing with a sprite, requires Frodo SC + +

stretch

+Variably expanded sprites, requires Frodo SC + +

tech-tech

+Horizontal scrolling with large amplitude (use joystick in port 2 to +control) + +

text26

+Displays (nearly) 26 lines of text + + + diff --git a/Docs/emulwindow.html b/Docs/emulwindow.html new file mode 100644 index 0000000..af03f62 --- /dev/null +++ b/Docs/emulwindow.html @@ -0,0 +1,41 @@ + + +Emulation window + + + +

Emulation window

+ +
+ +The emulation window displays the C64 graphics and receives the keyboard +input. When this window is active and the emulation is running, the main +menu (BeOS/AmigaOS only) offers six commands (apart from About/Quit):

+ +

    +
  • "Reset C64" resets the C64 and the 1541 emulation (same as pressing F12) +
  • "Preferences..." re-opens the settings window +
  • "SAM..." halts the emulation and activates SAM (you can exit from SAM with the "x" command) +
  • "Insert next disk" changes the disk image of drive 8 if in "D64" mode and inserts the "next" disk. Frodo tries to be smart about what the "next" disk is. Your disk images should be named "Foo1.d64", "Foo2.d64"... or "BarA.d64", "BarB.d64"... for this feature to work. +
  • "Load snapshot..." restores the emulator state from a snapshot file saved with "Save snapshot...". +
  • "Save snapshot..." saves the current emulator state to disk. +
+ +In the bottom left corner of the window, Frodo displays how much percent of +the speed of a real C64 the emulation achieves.

+ +The four items labeled "Drive 8" to "Drive 11" are the disk activity +indicators of the 1541 emulation.

+ +Under RISC OS Frodo can be controlled via Menus and the emulator pane. Some notes on +the pane entries: + +

    +
  • Drive LEDs 8-11 display the drives' state. You can also set the drive paths by dragging files/directories to the corresponding LEDs. +
  • The Speedometer is clickable, its border type displays the state of the speed limiter. Slab in means off, slab out means on. +
  • The Pause and Reset icons should be self-explanatory. +
  • The Size Toggler lets you choose between two display sizes. It shows the one you'll get if you click it, rather than the current one. +
+ + + diff --git a/Docs/files.html b/Docs/files.html new file mode 100644 index 0000000..9ee1575 --- /dev/null +++ b/Docs/files.html @@ -0,0 +1,85 @@ + + +File access + + + +

File access

+ +
+ +Frodo offers four possibilities for the 1541 emulation: + +

1. Host system directory, setting "Dir"

+ +In this mode, the C64 programs and files are stored in a directory on your +hard disk and can be LOADed and SAVEd as usual from +the emulator. The paths to the directories of the simulated drives are +given in the settings window in the path entry fields of the "Drives" +box.

+ +You can also load the directory with LOAD"$",8. All files are +displayed as "PRG", all subdirectories as "DIR". To get into a +subdirectory, you have to open the settings window and change the path for +the drive, as the operating system of the C64 doesn't know about +subdirectories. However, it is possible to use a "/" in the C64 file name +to access these subdirectories (e.g. LOAD"GAMES/ELITE",8), +unless the '/' translation setting is turned on.

+ +For the opening of files, the file types "P" and "S" and the access modes +"R", "W" and "A" are supported. Wildcards (*,?) can be used, but you have +to remember that files in BeOS/Unix/AmigaOS directories have no determined order +and the result of LOAD"*",8 is rather random. Files are always +overwritten even if they are not opened with "@:". Floppy commands other +than "I" and "UJ", relative files and direct block access are not +implemented. You can however read the error channel.

+ + +

2. .d64/x64 disk image file, setting "D64"

+ +Most C64 programs available on the Internet and on CD-ROMs, expecially +demos and games, are stored in files with the ending ".d64". Such a file +holds all 683 blocks of a complete side of a 1541 disk, so that direct +block accesses are possible from within the emulation. However, Frodo only +supports read accesses. The path name of the disk image file must be given +in the settings window in the path entry fields of the "Drives" box.

+ +Apart from .d64 files, Frodo can also use image files of the "x64" +emulator, automatically detecting the file type.

+ + +

3. .t64/LYNX archive file, setting "T64"

+ +.t64 and LYNX (.lnx) files are archive files like "tar". .t64 files are +also often found on the Internet and on CD-ROMs; LYNX is a native C64 +archiver. Frodo's .t64 support is a bit special in that it doesn't treat +the .t64 file like a tape image (that's what the .t64 format was designed +for), but rather like a disk image file. .t64's are not accessed with +device number 1 (Frodo doesn't have any tape emulation), but with numbers +8..11 like a disk drive.

+ +When loading the directory with LOAD"$",8, Frodo creates a +listing of all files within the archive. You cannot write to .t64 or LYNX +files, they are read-only under Frodo.

+ + +

4. Processor-level 1541 emulation

+ +The 1541 is an "intelligent" disk drive bearing its own CPU and memory that +can even be programmed and execute code concurrently to the C64. Frodo is +able to emulate a 1541 on this level, but as this slows down the whole +emulation notably, the 1541 processor emulation can be turned on and off +from the settings.

+ +If the 1541 processor emulation is turned on, the "Dir"/"D64" drives 8..11 +are no longer available. They are replaced by a single drive with number 8 +that operates in "D64" mode (regardless of the state of the type setting +for drive 8). The path name of the .d64/x64 file to be used must be given +in the path entry field of drive 8.

+ +In contrast to the standard "D64" mode, the 1541 processor emulation is +able to write to the disk. There is no way to "virtually write-protect" the +disk image file, so be careful. + + + diff --git a/Docs/flavours.html b/Docs/flavours.html new file mode 100644 index 0000000..3fc43b4 --- /dev/null +++ b/Docs/flavours.html @@ -0,0 +1,58 @@ + + +Frodo and Frodo SC + + + +

Frodo and Frodo SC

+ +
+ +Frodo comes in two 'flavours' that allow you to decide between speed +and accuracy of the emulation. + +

The line-based emulation 'Frodo'

+ +Frodo is a line-based emulation, i.e. the activities that happen +in parallel during one video line in the real C64 are emulated one after +the other for the different chips. This offers a reasonable degree of +precision of the emulation at a decent speed. There are some things that +cannot be emulated with this technique, but it works fine with about +80% of all C64 programs and it is also the faster of the two Frodo +versions. + +
+ +

The single-cycle emulation 'Frodo SC'

+ +Frodo SC is a special version of Frodo that doesn't work with a +line-based emulation but instead with a cycle-based one. That means that +the emulator switches between 6510 and VIC in every emulated ø2 clock +phase. By doing this, Frodo SC achieves an extreme precision (nearly all +$d011 and $d016 effects can be emulated), but at the expense of speed. + +In the settings options, Frodo SC differs from Frodo in only a few points: + +
    +
  • The "Cycles per line" settings are not available as the timing of Frodo SC is hardcoded +
  • The "Clear CIA IRC on write" hack is not necessary +
+ +Apart from that, Frodo SC is operated in the same way as Frodo and also +uses the same settings. Frodo SC has only a few incompatibilities to a +real C64: + +
    +
  • On the left and right side of the screen, sprites are not clipped but blanked out +
  • Sprite collisions are only detected within the visible screen area (excluding borders) +
  • The sprite data fetch ignores the state of BA +
  • On BA low and AEC high, the VIC always reads $f in D8-D11 +
  • Color register modifications are visible 7 pixels too late +
  • The TOD clock should not be stopped on a read access, but be latched +
  • The SDR interrupt is faked +
  • Some small incompatibilities with the CIA timers +
  • The readable SID registers are not emulated correctly +
+ + + diff --git a/Docs/future.html b/Docs/future.html new file mode 100644 index 0000000..7bb385d --- /dev/null +++ b/Docs/future.html @@ -0,0 +1,24 @@ + + +The future + + + +

The future

+ + +"Ai! Palan ú ná metta eldaloaron!" + + +
+ +Frodo has not been updated for quite a while because I've been working on +other projects (GiNaC, Basilisk II, ...). However, I'm currently doing a +complete rewrite of the code for Frodo V5. It will use an event-based +emulation like UAE or VICE because the current line-based model has already +been stretched beyond its feasibility. Also, the fantastic SDL will be used +as the low-level graphics/sound/input library. + + + + diff --git a/Docs/history.html b/Docs/history.html new file mode 100644 index 0000000..78c0a35 --- /dev/null +++ b/Docs/history.html @@ -0,0 +1,242 @@ + + +History + + + +

Revision history

+ + +"Those days, the Third Age of Middle-earth,
+are now long past, and the shape of all lands
+has been changed." +
+ +
+ +This emulator started as a player routine for C64 music on the Amiga. +Versions V1.x were written in 68K assembly language, versions V2.x in a mix +of 68K assembly and C. V3.x/V4.x are portable versions written in C++.

+ +

V1.x/V2.x

+
    +
  • Amiga 68K versions +
+ +

V3.0

+
    +
  • First release of BeOS/Unix version, features processor-level 1541 +emulation and built-in SID emulation +
+ +

V3.0a

+
    +
  • Implemented SID test bits +
  • Combined SID waveforms respect pulse width +
  • Corrected idle state graphics display +
  • Processor-level 1541 emulation respects .d64 error info +
  • CPU emulation optimized (6510 and 6502 split) +
  • VIC emulation optimized (raster counter in local variable in EmulateLine()) +
  • BeOS: Now exiting the audio subscriber with ExitStream(TRUE) +
  • Unix: Fixed missing thread_func() declaration +
  • Unix: getcwd(AppDirPath) was missing in main_x.i +
  • Unix: Speed limiter works +
+ +

V3.0b

+
    +
  • Implemented REU emulation +
  • Formatting disks with ID possible under processor-level 1541 emulation +
  • Corrected and optimized SID waveform/envelope calculation (signed arithmetic) +
  • Corrected idle state display again (ECM text) +
  • 1541 D64 mode ignores drive numbers when opening the directory +
  • Processor-level 1541 emulation deactivates when idle +
  • BeOS: Sound output quality is now 16 bits +
  • Unix: Quits more cleanly, reactivates key repeat +
  • Unix: Fixed alignment problem with text_chunky_buf in VIC.h +
+ +

V3.0c

+
    +
  • 1541 DIR mode can load directory with "$0" +
  • Rearranged the CPU code (more macros, less inline functions) +
  • SID envelope generators rewritten, envelopes are now recalculated for every sample +
  • SID calc_buffer function now takes pointer to WORD buffer +
  • Unix: Sound for Linux +
  • Unix: Prefs window implemented (needs Tcl/Tk) +
  • Unix: Corrected x64 disk image detection on little-endian systems +
  • Unix: SVGAlib support works again, accesses frame buffer directly if possible +
  • Unix: Added support for SHM under X11 +
+ +

V3.0d

+
    +
  • SID filter emulation implemented +
  • SID master volume setting works again +
  • Flags are recalculated in MOS6526::SetState() +
  • Changed CBOOL->bool in some places +
  • Fixed bug with char_in in MOS6510::new_config() +
  • BeOS: Emulation thread priority lowered +
  • Unix: Some changes for DEC Alpha +
  • Unix: Joystick support for Linux +
+ +

V3.0e

+
    +
  • Removed the CBOOL data type +
  • Unix: Sound for HP-UX +
  • Unix: Keyboard layout matches the picture in the docs more closely +
  • Unix: Diagonal directions of keypad joystick emulation work +
  • Unix: +/- on numerical keypad modifies SkipFrames +
  • Unix: F9 invokes SAM +
  • Unix: Drive LEDs and speedometer implemented +
  • Unix: Some changes to the GUI +
  • Unix: Random number generator is initialized +
  • Unix: Name of prefs file can be given as an argument +
  • Unix: Calls XFlush() and XSync() in C64Display::Update() +
+ +

V3.0f

+
    +
  • Improved DIR/D64 drive reset, resetting the C64 resets the drives +
  • Implemented 'G' command for DIR/D64 drives +
  • Corrected translation of 0xc1..0xda characters in conv_from_64() +
  • BeOS: Implemented smart "Insert next disk" menu item +
  • Unix: Improved the speedometer/LED bar +
  • Unix: Self-calibrating joystick routines +
  • Unix: No need to enter path of 'wish' in TkGui.tcl +
+ +

V3.0g

+
    +
  • New T64/LYNX mode for 1541 emulation +
  • 1541fs.cpp/match() used to treat the pattern "foo" as "foo*" +
  • 1541 DIR mode uses tmpfile() for opening temporary directory files +
  • 1541 D64 mode allows wildcards for selective directory reading +
  • Increased compatibility of processor-level 1541 emulation in various places (C64<->1541 communication, VIA registers, memory map, disk change flag) +
  • Inlined MOS6526::EmulateLine() and some small public functions of MOS6502_1541 +
  • New prefs option to enable/disable SID filter emulation +
  • Joystick calibration is reset when joystick options change +
  • BeOS: Self-calibrating joystick routines +
+ +

V3.0h

+
    +
  • Implemented SID notch filter, better resonance frequency calculation +
  • Fixed bug with SID filter option +
  • CIA timer B one-shot mode stops timer when counting undeflows of timer A +
  • Implemented lightpen trigger +
  • BeOS: Fixed for BeOS DR8, improved the GUI a bit +
  • Unix: Some fixes to the TkGui (T64, SIDFilters, removed speed display) +
  • Unix: Main window no longer resizable +
  • Unix: SVGALib support works again +
+ +

V3.1

+
    +
  • BeOS: Joysticks work again +
+ +

V3.1a

+
    +
  • Frodo SC ported +
  • Processor-level 1541 emulation supports reading GCR data, removed the faked job loop +
  • Corrected ISB, RRA, SBX and SHA instructions +
  • The last line of Y expanded sprites wasn't drawn +
  • Light pen registers work +
  • Small fixes to 1541d64/1541t64 +
  • CIA 2 PRA write: IEC lines respect DDRA +
  • Better triangle waveform (12 bits) +
  • SID emulation can play sampled sounds +
  • New "Ignore SID Volume" prefs item for better sample playing +
  • '*' on numerical keypad toggles speed limiter +
  • BeOS: '/' on numerical keypad toggles processor-level 1541 emulation +
  • BeOS: Safer quitting +
  • BeOS: Option to use GameKit (screen) +
  • BeOS: Replaced srand(system_time()) with srand(real_time_clock()) +
  • Unix: Sun makefile +
+ +

V3.1b

+
    +
  • Corrected SID sustain behaviour +
  • Reading from write-only SID registers returns the last byte written to the SID +
  • No more distortions when playing samples +
  • Removed the "Ignore SID Volume" prefs item again +
  • Combined SID waveforms now sampled from a 6581R4 +
  • Improved 1541 VIA timer operation +
  • Fixed bug in 1541 head movement +
  • Raster IRQs can be triggered by writing to $d011/$d012 +
  • Some changes for the MacOS port +
  • Included autoconf stuff from Bernd +
  • Frodo SC: Fixed some CIA timer bugs +
+ +

V3.1c

+
    +
  • Ported to AmigaOS +
  • Fixed bug in IEC::Reset() +
  • Fixed bug when writing to SID registers >24 +
  • The SID noise waveform should now sound the same on all platforms +
  • Removed all calls to tolower() in SAM.cpp because of possible side-effects if tolower() is a macro +
  • Drive LEDs are only updated once per frame +
  • .d64/.t64 files are opened with read permissions only +
  • Fixed bug with read_char buffering in 1541fs.cpp/1541t64.cpp +
  • Frodo SC: Fixed memory trashing bug in MOS6569::draw_background() +
  • Unix: Drive LEDs blink on error +
  • Unix: Added more patches from Bernd +
+ +

V4.0

+
    +
  • The C64 ROM files are now included +
  • Unix: Added SVGAlib keyboard patches from Bernd +
+ +

V4.0a

+
    +
  • Corrected BRK, ANE, ARR, SBX and DCP instructions +
  • Frodo SC: Improved the CIA timers +
  • Frodo SC: MOS6526::EmulateCycle() split into MOS6526::EmulateCycle() and MOS6526::CheckIRQs() +
  • Frodo SC: Corrected interrupt behaviour of branch instructions +
  • BeOS: Sound calculation is now done in stereo +
+ +

V4.1

+
    +
  • Integrated Win32 and RiscOS ports +
  • Snapshot support +
  • Added page-crossing and "borrowed" cycles in line-based CPU emulation (Frodo PC) +
  • Added precise CIA cycles for line-based emulation (Frodo PC) +
  • Optional fixed-point arithmetic and precomputed filters in SID.cpp +
  • Optional dynamic alignment checks in VIC.cpp +
  • Changed typedefs and constants (BYTE, WORD etc.) +
  • Unix: Better configure script +
  • Unix: Sound support for Solaris 2.x +
  • Unix: Joystick can be toggled between port 1/2 with the NumLock key +
  • Unix: US keyboard layouts supported +
  • BeOS: Fixed for BeOS AA:DR9 +
  • BeOS: Can now switch between window/screen mode while the emulation is running, speeded up full screen mode +
  • BeOS: Prefs saved in /system/settings/Frodo_settings by default +
+ +

V4.1a

+
    +
  • Fixed bug in IEC::NewPrefs() +
  • Optimized VIC emulation for speed +
  • BeOS: Prefs saved in /boot/home/config/settings/Frodo_settings +
  • BeOS: Directory panel works again +
  • BeOS: Correct C64/1541 clock frequency ratio (Frodo SC) +
  • BeOS: Correct audio mixing/clipping +
+ +

V4.1b

+
    +
  • Unix: Updated Tcl/Tk preferences editor [Gerard Decatrel] +
  • Unix: Added SDL display driver which is the default if SDL >= 1.2 is detected by the configure script +
  • Unix: Increased number of sound buffers from 4 to 16 (emu10k driver doesn't work with the smaller value) +
  • Unix: Fixed some small compilation problems +
+ + + diff --git a/Docs/index.html b/Docs/index.html new file mode 100644 index 0000000..21b681d --- /dev/null +++ b/Docs/index.html @@ -0,0 +1,56 @@ + + +Frodo Manual + + + +

Frodo V4.2

+What's new in V4.2? +

The free, portable Commodore 64 emulator

+ +Copyright © 1994-2005 Christian Bauer +

+Permission is granted to make and distribute verbatim copies of this +manual provided the copyright notice and this permission notice are +preserved on all copies. +

+Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided that the entire +resulting derived work is distributed under the terms of a permission +notice identical to this one. + +


+ + + + + + + + + + + + + diff --git a/Docs/installation.html b/Docs/installation.html new file mode 100644 index 0000000..d589243 --- /dev/null +++ b/Docs/installation.html @@ -0,0 +1,64 @@ + + +Installation + + + +

Installation

+ +
+ +There are two kinds of Frodo distributions. The source distributions ("FrodoV?_?.Src.*") +contain source code that has to be compiled on your system. The binary distributions +("FrodoV?_?..*") contain an executable that can be started directly. + +

Compiling under BeOS

+ +There are BeIDE project files for Frodo ("Frodo.proj"), Frodo PC ("FrodoPC.proj") +and Frodo SC ("FrodoSC.proj"). You must rename (or copy) Src/sysconfig.h.Be +to Src/sysconfig.h first. Compilation requires the unlimited Metrowerks +linker. + +

Compiling under Unix

+ +First you have to run the program "configure". This can be done simply with +the following command:

+ +cd Src
+./configure

+ +You might want to give configure optional arguments. To use SVGAlib on Linux +systems, you have to do

+ +cd Src
+./configure --without-x

+ +To select a german keyboard layout in the X11 version instead of the usual +US keyboard, do

+ +cd Src
+./configure --enable-kbd-lang-de

+ +After running configure, you may want to edit the generated Makefile. To use the SHM +extension (highly recommended for speed), add "-DX_USE_SHM" to the definition of +CFLAGS. To get sound under Solaris 2.x, add "-DSUN". Next, type

+ +make all

+ +

Compiling under AmigaOS

+ +The makefile was written for the ADE with GCC. Other compilers have not been tested. +You must rename (or copy) Src/sysconfig.h.Amiga to Src/sysconfig.h first. Then type

+ +cd Src
+make -fMakefile.Amiga all

+ +

The C64 ROM files (all systems)

+ +Frodo looks for four ROM files named "Basic ROM", "Kernal ROM", "Char ROM" +and "1541 ROM" in the same directory Frodo is in. These files are included +in the source and binary distributions of Frodo. "Kernal ROM" is an +extended version of the C64 Kernal. + + + diff --git a/Docs/kernal.html b/Docs/kernal.html new file mode 100644 index 0000000..c22e8d5 --- /dev/null +++ b/Docs/kernal.html @@ -0,0 +1,55 @@ + + +Kernal extensions + + + +

Extensions of the included Kernal ROM

+ +
+ +The included "Kernal ROM" has some improvements/changes as compared to +an original C64 kernal: + +
    +
  • RAM at $fd30-$fd4f is not overwritten during reset +
  • Default device address is 8 +
  • Default secondary address is 1 +
  • Start and end address are displayed when loading +
  • C= key stops scrolling +
  • Faster key repeat +
  • Tape and RS232 routines removed +
  • Key commands: +
      +
    • F1 : <CLS> LIST <CR> +
    • F2 : SYS32768 <CR> +
    • F3 : RUN <CR> +
    • F4 : SYS4096*12 +
    • F5 : LOAD" +
    • F6 : SAVE" +
    • F7 : LOAD"$",8 <CR> +
    • F8 : CLOSE7:OPEN7,8,15," +
    • SHIFT-Run: LOAD":*",8,1:RUN <CR> +
    • CTRL-D : Display directory of drive 8 +
    • CTRL-K : Read error channel of drive 8 +
    • CTRL-L : Load Basic program from RAM disk +
    • CTRL-O : UNNEW +
    • CTRL-U : Modifies the SAVE routine so that the RAM at $a000-$bfff can be SAVEd +
    • CTRL-V : Swap Basic program with RAM disk +
    • CTRL-W : Save Basic program to RAM disk +
    • CTRL-X : Continue LIST command +
    • CTRL-Z : Continue LIST command 50 lines earlier +
    • CTRL-F1 : Swap screen with buffer 1 +
    • CTRL-F3 : Swap screen with buffer 2 +
    • CTRL-F5 : Swap screen with buffer 3 +
    • CTRL-F7 : Swap screen with buffer 4 +
    • CBM-F1 : Write screen to buffer 3 +
    • CBM-F3 : Write screen to buffer 4 +
    • CBM-F5 : Get screen from buffer 3 +
    • CBM-F7 : Get screen from buffer 4 +
    +
  • Startup message shows "BASIC X2" +
+ + + diff --git a/Docs/keyboard.html b/Docs/keyboard.html new file mode 100644 index 0000000..1e3e4ba --- /dev/null +++ b/Docs/keyboard.html @@ -0,0 +1,140 @@ + + +Keyboard layout + + + +

Keyboard layout

+ +
+ +The keyboard layout closely resembles that of a real C64. The individual +rows of the keyboard are mapped as follows (american keyboard): + +
+<- 1 2 3 4 5 6 7 8 9 0 + -
+    Q W E R T Y U I O P @ *
+    A S D F G H J K L : ;
+     Z X C V B N M , . /
+

+ +In addition, the following keys are used: + +

+ Esc        - RUN/STOP
+ Backspace  - INS/DEL
+ Return     - RETURN
+ Enter      - RETURN
+ Shift keys - SHIFT
+ Caps lock  - SHIFT LOCK
+ F1-F8      - F1-F8
+

+ +Special keys under BeOS: + +

+ \          - ^
+ Insert     - Shift-INS/DEL
+ Delete     - INS/DEL
+ Home       - CLR/HOME
+ End        - £
+ Page Up    - £
+ Page Down  - =
+ Menu Keys  - C=
+ Ctrl Left  - CTRL
+ Ctrl Right - C=
+ F11        - RESTORE
+ F12        - C64 Reset
+

+ +Special keys under Unix: + +

+ \          - ^
+ Insert     - Shift-INS/DEL
+ Delete     - INS/DEL
+ Home       - CLR/HOME
+ End        - £
+ Page Up    - ^
+ Page Down  - =
+ Alt Keys   - C=
+ Ctrl Left  - CTRL
+ Ctrl Right - C=
+ F9         - Start SAM
+ F10        - Quit Frodo
+ F11        - RESTORE
+ F12        - C64 Reset
+

+ +Special keys under AmigaOS: + +

+ \          - £
+ Delete     - CLR/HOME
+ ( (keypad) - ^
+ ) (keypad) - =
+ Alt Keys   - C=
+ Ctrl       - CTRL
+ F9         - RESTORE
+ F10        - C64 Reset
+

+ +Special keys under RISC OS: + +

+ F5         - Toggle sound emulation mode
+ F6         - Enter SAM
+ F7         - RESTORE
+ F8         - Reset
+ Copy       - Toggle pause
+ PageUp     - Increase SkipFrames
+ PageDown   - Decrease SkipFrames
+ num/       - Toggle 1541 emulation mode
+ num*       - Toggle speed limiter
+ num+/-     - +/-
+ Alt        - CBM
+ ScrollLock - On: force single tasking, else multitasking
+

+ +Apart from that the function keys are mapped differently under RISC OS. (F1,F2,F3,F4) maps +to the C64's (F1,F3,F5,F7), you get (F2,F4,F6,F8) by pressing shift like on a real C64.

+ +So the famous key combination RUN/STOP-RESTORE must be typed as Esc-F11 (Esc-F9 under AmigaOS). +But you don't have to thrash the F11 key the same way as the RESTORE key on +the original C64. :-)

+ +The cursor (arrow) keys work as expected. I.e. "cursor up" corresponds to +"Shift-cursor down" on the C64. The same applies to the function keys F2, +F4, F6 and F8. + +The numerical keypad emulates a joystick in port 1 or 2, depending on the +state of the Num Lock (Num Lock off: port 2, Num Lock on: port 1):

+ +

+   7    8    9
+        ^
+        |
+   4    5    6
+  <-- Fire  -->
+        |
+        v
+   1    2    3
+
+   0
+ Fire
+

+ +Keyboard joysticks are handled differently under RISC OS: + +

    +
  • NumLock on: only joystick 1 active, mapped to port 2. NumLock off: both joysticks active, 1 mapped to port 1, 2 mapped to port 2. +
  • Joystick keys can be defined freely. Defaults are: joystick 1: (1 2 3 . enter) on the numerical keypad, joystick 2: (z x f c g). +

+ +The '+' and '-' keys on the numerical keypad increase and decrease the +"Draw every n-th frame" setting on the fly. The '*' on the numerical keypad +toggles the "Limit Speed" option. The '/' on the numerical keypad toggles +the processor-level 1541 emulation. + + + diff --git a/Docs/legalmush.html b/Docs/legalmush.html new file mode 100644 index 0000000..384b770 --- /dev/null +++ b/Docs/legalmush.html @@ -0,0 +1,29 @@ + + +Copyright and license + + + +

Copyright and license

+ +
+ +The "Frodo" Commodore 64 emulator is Copyright © 1994-1997,2002-2004 +Christian Bauer +

+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 2 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; see the file COPYING. If not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + + diff --git a/Docs/overview.html b/Docs/overview.html new file mode 100644 index 0000000..f572f68 --- /dev/null +++ b/Docs/overview.html @@ -0,0 +1,49 @@ + + +Overview + + + +

Overview

+ + +"Ú-queta i yéni avánier alye,
+ú-queta i cirya ná vanwa!" +
+ +
+ +Frodo is a freeware C64 emulator for BeOS, Unix, MacOS, AmigaOS, +Win32 and RiscOS systems and the world's first C64 emulator not bearing a +"64" in its name. :-) (No, it has absolutely nothing to do with +frodo.hiof.no, that's a pure coincidence.)

+ +Frodo was developed to reproduce the graphics of games and demos better +than the existing C64 emulators. Therefore Frodo has relatively high system +requirements: It should only be run on systems with at least a +PowerPC/Pentium/68060. But on the other hand, Frodo can display raster +effects correctly that only result in a flickering mess with other +emulators.

+ +Frodo comes in two flavours: The "normal" Frodo +with a line-based emulation and the single-cycle emulation Frodo SC that is slower +but far more compatible.

+ +In addition to a precise 6510/VIC emulation, Frodo features a processor-level +1541 emulation that is even able to handle about 95% of all fast loaders. +There is also a faster 1541 emulation for four drives in .d64/x64 disk +images, .t64/LYNX archives, or directories of the host system.

+ +Frodo runs on these systems:

+ +

    +
  • BeOS-ready system (PPC or x86) with BeOS R3 +
  • Unix systems with X11R6 or Linux/SVGAlib (sound only under Linux, HP-UX and Solaris 2.x) +
  • 68k or PPC Macintosh with System 7.5 +
  • Amiga/DraCo with 68040/68060, AmigaOS 3.0 and a graphics card (AHI V3 required for sound) +
  • Intel x86 system running Windows NT/95 +
  • Acorn computers with RiscOS 3 +
+ + + diff --git a/Docs/sam.html b/Docs/sam.html new file mode 100644 index 0000000..f840e21 --- /dev/null +++ b/Docs/sam.html @@ -0,0 +1,282 @@ + + +SAM + + + +

SAM

+ + +"Frodo! Mr.Frodo, my dear!" cried Sam,
+tears almost blinding him. "It's Sam, I've come!" +
+ +
+ +Frodo has a built-in machine language monitor that can be activated at any +time by pressing F9 (Unix) or selecting the appropriate menu item (BeOS/AmigaOS/MacOS): +SAM (Simple Assembler and Monitor). It provides full access to the memory +and hardware of the emulated C64 and 1541 (under BeOS, you must only start +SAM if you launched Frodo from the Shell).

+ +SAM is controlled by a command-based interface, all numbers are in hex. +With the command "h" you can display a list of all commands. The command +"x" quits SAM and returns to Frodo.

+ +SAM has two modes of operation, indicated by the prompt "C64>" or "1541>". +You can switch between them with the "64" and "1541" commands. In "C64" +mode, all commands that access memory or the CPU operate on the memory/CPU +of the emulated C64. In "1541" mode, they operate on the emulated 1541 +(this is only useful if the processor-level 1541 emulation is turned +on).

+ +All commands that access the C64 memory use the memory configuration set +with the "k" command. On starting up SAM, the configuration is set to the +one the processor is in. Accesses from within SAM have the same effect as +accesses of the emulated processor. This affects especially the I/O +registers: a memory dump of $dc00-$de00 clears pending CIA interrupts as +SAM reads from $dc0d and $dd0d. With the "v" commands, you can examine the +state of the I/O chips without modifying the state of the emulation.

+ +In all places where SAM expects a number (except in the assembler) you can +also enter an expression of hex numbers containing '+', '-', '*', '/' and +parens. Commands that create a longer output can be interrupted with +Ctrl-C.

+ +Here is a description of all commands ('[]' marks a parameter than can be +left out, '{}' marks a parameter that can be repeated many times. If a +[start] parameter is left out, SAM continues at the address where the last +command stopped):

+ + +

+ a [start]           Assemble
+

+ +starts the assembler at the address "start". SAM always prints the address +where the next instruction will be written to. The syntax of the +instructions conforms to the standard except for shift/rotation +instructions in the "accumulator" addressing mode. Those have to be entered +without operand, e.g. "lsr" instead of "lsr a". Entering a blank line quits +the assembler and returns to the command mode of SAM.

+ + +

+ b [start] [end]     Binary dump
+

+ +displays the memory from "start" to "end" byte-wise binary. With this +command, you can view character sets.

+ + +

+ c start end dest    Compare memory
+

+ +compares the memory in the range from "start" to (and including) "end" +with the memory at "dest". The addresses of all different bytes and the +total number of differences (decimal) are printed.

+ + +

+ d [start] [end]     Disassemble
+

+ +disassembles the memory from "start" to "end". Undocumented opcodes are +markes with a star '*'.

+ + +

+ e                   Show interrupt vectors
+

+ +shows the currently active interrupt vectors of the 6510 (C64) or 6502 +(1541) and (in C64 mode, if the Kernal ROM is mapped in) of the Kernal.

+ + +

+ f start end byte    Fill memory
+

+ +fills the memory in the range from "start" to (and including) "end" with +the value "byte".

+ + +

+ i [start] [end]     ASCII/PETSCII dump
+

+ +shows the memory from "start" to "end" as ASCII/PETSCII characters.

+ + +

+ k [config]          Show/set C64 memory configuration
+

+ +"k" without parameters shows the memory configuration that is set for SAM, +"k" with parameter modifies it. On exiting SAM, the configuration set with +the processor port is reactivated. The memory configuration has no effect +in 1541 mode. The 8 possible configurations are: + +

+  #  $a000-$bfff  $d000-$dfff  $e000-$ffff
+  ----------------------------------------
+  0      RAM          RAM          RAM
+  1      RAM       Char ROM        RAM
+  2      RAM       Char ROM    Kernal ROM
+  3   Basic ROM    Char ROM    Kernal ROM
+  4      RAM          RAM          RAM
+  5      RAM          I/O          RAM
+  6      RAM          I/O      Kernal ROM
+  7   Basic ROM       I/O      Kernal ROM
+
+
+ + +
+ l start "file"      Load data
+

+ +loads the contents of the specified file into memory starting from address +"start". The file name must be enclosed in quotation marks even if it +contains no spaces. This command cannot be used to load C64 programs as +it doesn't respect the embedded load address in the programs.

+ + +

+ m [start] [end]     Memory dump
+

+ +displays the memory from "start" to "end" as hexadecimal numbers and ASCII +characters.

+ + +

+ n [start] [end]     Screen code dump
+

+ +displays the memory from "start" to "end" as ASCII characters, interpreting +each byte as a screen code of the standard character set.

+ + +

+ o ["file"]          Redirect output
+

+ +When a file name is specified, all following output is redirected to this +file. The file name must be enclosed in quotation marks even if it contains +no spaces. Entering "o" without parameters closes the file and directs the +output into the window of SAM again.

+ + +

+ p [start] [end]     Sprite dump
+

+ +displays the memory from "start" to "end" binary with three bytes per line. +With this command, you can display sprite data.

+ + +

+ r [reg value]       Show/set CPU registers
+

+ +"r" without parameters shows all 6510 (C64) or 6502 (1541) registers and +flags and the instruction at the address specified by the program counter. +For the 6510, "DR" and "PR" are the data direction register and data +register of the processor port. To modify a register, give its name ("reg") +and the new value ("value") as parameters.

+ + +

+ s start end "file"  Save data
+

+ +writes the memory from "start" to (and including) "end" to the specified +file. The file name must be enclosed in quotation marks even if it contains +no spaces. This command cannot be used to save C64 programs as it doesn't +save a load address in the file.

+ + +

+ t start end dest    Transfer memory
+

+ +transfers the memory from "start" to (and including) "end" to "dest". +Source and destination may overlap.

+ + +

+ vc1                 View CIA 1 state
+

+ +shows the state of CIA 1 ($dc00).

+ + +

+ vc2                 View CIA 2 state
+

+ +shows the state of CIA 2 ($dd00).

+ + +

+ vf                  View floppy state
+

+ +shows the state of the processor-level 1541 emulation.

+ + +

+ vs                  View SID state
+

+ +shows the state of the SID.

+ + +

+ vv                  View VIC state
+

+ +shows the state of the VIC.

+ + +

+ x                   Return to Frodo
+

+ +quits SAM and returns to Frodo.

+ + +

+ : addr {byte}       Modify memory
+

+ +writes the space-separated values "byte" into memory starting at "addr".

+ + +

+ 1541                Switch to 1541 mode
+

+ +switches to 1541 mode. All commands that access memory or the CPU will then +operate on the emulated 1541 (processor-level).

+ + +

+ 64                  Switch to C64 mode
+

+ +switches to C64 mode. All commands that access memory or the CPU will then +operate on the emulated C64.

+ + +

+ ? expression        Calculate expression
+

+ +calculates the value of the given expression and displays it in decimal +and hexadecimal.

+ + + diff --git a/Docs/settings.html b/Docs/settings.html new file mode 100644 index 0000000..061bac4 --- /dev/null +++ b/Docs/settings.html @@ -0,0 +1,167 @@ + + +Settings + + + +

Settings

+ +
+ +Under BeOS, the settings window appears directly after starting Frodo, or +by selecting the "Settings..." menu item in the running emulation. Under +Unix, the settings window is permanently visible.

+ +With "Sprite display", you can switch the display of sprites on and +off. Turning them off speeds up the emulation a little when there are a lot +of sprites on the screen.

+ +"Sprite collisions" determines whether collisions between sprites +and between sprites and graphics should be detected. Turning off collisions +will make you invincible in some games (sadly, your enemies are likely to +become invincible, too :-/.

+ +"Joystick on Port 1/2" specifies on which ports you have real +joysticks connected (as opposed to the joystick +emulation on the numerical keypad). Joysticks are only supported under +BeOS, Linux, RiscOS and AmigaOS (only one joystick). The port numbers relate +to the host machine ports. On the BeBox, port 1 is the upper one and port 2 +the lower one. You should only turn on the ports to which you have actually +joysticks connected, or the keyboard will behave erratically. Frodo has an +automatic joystick calibration. If you plug in a new joystick or change the +joystick settings, you should first move the joystick once in each direction.

+ +With "Swap joysticks" you can swap the assignment of the joystick +ports of the host machine to the C64 ports without having to plug out and +in your joysticks. E.g. if a C64 game is using a joystick on C64 port 1 you +can simply activate "Swap joysticks" and use a joystick in port 2 on your +machine to play the game.

+ +When the field "Limit speed" is active, the emulation is slowed down +when its relative speed exceeds 100%. If you set the value in "Every (n)th +frame" so that the speed is just over 100% and activate the speed limiter, +the emulation always runs at the original C64 speed, with the highest +possible precision.

+ +With the setting "Fast Reset" you can disable the memory test that +is normally performed by the C64 on a reset. Under emulation, the memory +test is not necessary and the reset (F12) becomes much faster when it is +disabled.

+ +The setting "Clear CIA ICR on write" is necessary to make some +programs (such as the games "Gyruss" and "Motos") run that would otherwise +hang in an endless interrupt loop because they use an unusual technique to +acknowledge CIA interrupts (sometimes even without the programmer knowing +it). It should normally be turned off.

+ +The "SID Filters" field enables the emulation of the SID filters. +The sound emulation is slightly faster, but worse, when the filters are +disabled.

+ +"Doublescan lines" is only available under BeOS for the "Screen" +display type. It removes the black lines between scanlines, but makes +the emulation a bit slower.

+ +"Cycles per line (CPU)" and "Cycles per Bad Line (CPU)" set +the number of clock cycles available to the CPU per normal raster line and +per Bad Line. If a program is showing flickering lines or graphical flaws +you should try to slightly alter both values. For "Bruce Lee" you must +enter "62" for the "Cycles per line (CPU)".

+ +With "Cycles per line (CIA)" you can control the speed of the CIA +timers. Entering a higher value increases the frequency of cursor blinking +and key repeat. Some programs don't run correcly with the default value +(e.g. "Ballblazer" which needs a value of 65).

+ +"Cycles per line (1541)" sets the number of cycles available to the +1541 processor emulation per raster line. There is normally no need to +change this value. This setting has no effect if 1541 processor emulation +is turned off.

+ +The settings for the four "cycles" coming closest to an original PAL C64 +are (63, 23, 63, 64).

+ +With "Draw every n-th frame" you can select if Frodo should skip +frames when displaying the C64 graphics. The normal setting is "1", that +is, every frame (every simulated raster beam sweep) is recalculated. If you +change this to "2", for example, then only every second frame is +calculated, immensely speeding up the display, though some raster effects +may look a bit jerky. This setting can also be changed while the emulation +is running with the '+' and '-' keys on the numerical keypad.

+ +"Display type" is only available under BeOS. You can choose between +running the emulation in a window or in full-screen mode (using the +Game Kit).

+ +The "SID emulation type" controls the sound emulation and has two +settings: "None" and "Digital". "None" means no +sound (faster), "Digital" turns on the digital sound emulation +(only available under BeOS, Linux and HP-UX). Future versions of Frodo may +support more emulation types such as the use of a real SID chip on an +expansion card or across a network.

+ +"REU size" sets the size of the REU (RAM Expansion Unit) emulated by +Frodo or turns the REU emulation off ("None"). Only few programs actually +use the REU (operating systems like ACE and GEOS, and some utilities).

+ +In the box "Drives", there are four rows, each corresponding to one +of four emulated 1541 drives with the drive numbers 8, 9, 10 and 11. For +every drive, there is a popup control, a path entry field +and a button:

+ +With the popup control, you select the emulation mode of the +respective disk drive (for more detailed information, see here). There are three choices: "Dir", +"D64" and "T64". "Dir" emulates the drive in a +directory of the BeOS/Unix file system. "D64" accesses a .d64 or x64 +disk image file. "T64" is the setting for accessing a .t64 or C64 +LYNX archive file.

+ +The path entry field holds either the path name of the directory for +the "Dir" mode, the path name of the .d64/x64 image file for the "D64" +mode, or the path name of the .t64/LYNX archive file for the "T64" mode. +Under BeOS, you may also drop Tracker icons to the entry field.

+ +The button labeled "B" opens a file panel/requester for a more +comfortable selection of directories and .d64/x64/.t64/LYNX files.

+ +With "Map '/' <-> '\' in file names" you control whether the +'/' in C64 filenames will be translated to '\' and vice versa for "Dir" +mode drives. The '/' character is used to access subdirectories under +BeOS/Unix, but as the C64 doesn't have subdirectories, it's a valid part of +a C64 file name. This is a problem if a program wants to create a file with +'/' in it as BeOS/Unix would interpret the part before the '/' as a +directory name and, finding no such directory, would return an error and +the operation would fail. Now simply activate this gadget and all '/'s will +transparently be translated into '\', so in directory listings the '/' will +still appear. If you turn off this option, you can of course use the '/' to +access files in subdirectories from the C64.

+ +If "Enable 1541 processor emulation" is turned on, the four emulated +1541s are disabled and replaced by a single 1541 emulation (drive 8) that +only operates on .d64/x64 files, but emulates the 1541 processor and is +compatible with about 50% of all fast loaders. However, it slows down the +emulation considerably. If you have a .d64 with a program that doesn't load +with the normal emulation (see above), you may have better luck with the +1541 processor emulation instead. The path name of the disk image file to +be used must be entered into the path entry field of drive 8.

+ +

BeOS/AmigaOS

+ +Clicking "Start"/"OK" will start the actual emulation (resp. return +to it) and "Quit"/"Cancel" will discard your changes to the settings +and quit Frodo (resp. discard the changes and return to the emulation).

+ +With the menu items "Open...", "Save", "Save As..." +and "Revert" you can load and save the settings from and to +arbitrary files.

+ +

Unix

+ +Clicking "Apply" applies the settings of the "Cycles" controls to +the running emulation (all other settings are applied automatically). +"Defaults" reverts to the default settings, "Quit" quits +Frodo and "Reset" resets the emulation.

+ + + diff --git a/Docs/systemspecific.html b/Docs/systemspecific.html new file mode 100644 index 0000000..34d5c0b --- /dev/null +++ b/Docs/systemspecific.html @@ -0,0 +1,102 @@ + + +System specific notes + + + +

System specific notes

+ +
+ +

BeOS

+ +Frodo may be started from the shell or from the Tracker. When started from +the shell, Frodo accepts a single argument: The name of a preferences file +to load instead of the default "Frodo Prefs".

+ +At first, the window for the emulation settings +appears. The actual emulation is started by a click on "Start". Then the emulation window appears in which the C64 +startup message is displayed.

+ +You can quit from the running emulation by selecting the "Quit" menu +item.

+ +Frodo should be run in a 256-color workspace for maximum speed.

+ +Sampled sounds are only played correctly if "Limit Speed" is on and "Draw every +n-th frame" is set to "1" and if the emulation can sustain 100% speed.

+ +

Unix

+ +Frodo accepts a single argument when started from the shell: The name of a +preferences file to load instead of the default "~/.frodorc".

+ +First the emulation window appears in which +the C64 startup message is displayed. If you have Tcl/Tk 4.1 installed +and the file "TkGui.tcl" is in the current directory, a second window opens +for the emulation settings.

+ +If you have no Tcl/Tk or are using the SVGAlib version under Linux, you +have to copy the included "Frodo Prefs" file to "~/.frodorc" and edit it manually +(read "Prefs.cpp" to find out about the syntax of the settings file). + +You can quit from the running emulation by pressing F10.

+ +Sound is currently only supported under Linux, HP-UX and Solaris 2.x. Sampled +sounds are only played correctly if "Limit Speed" is on and "Draw every n-th frame" +is set to "1" and if the emulation can sustain 100% speed.

+ +

MacOS

+ +Frodo is started by double-clicking its icon.

+ +You can quit from the running emulation by selecting the "Quit" menu +item.

+ +Frodo should be run with 8-bit color depth. The "DIR" 1541 emulation mode +and SAM are currently not implemented.

+ +See the file "MacFrodo Notes" for more information.

+ +

AmigaOS

+ +Frodo should be started from the shell. When started from the shell, Frodo +accepts a single argument: The name of a preferences file to load instead +of the default "Frodo Prefs".

+ +At first, the window for the emulation settings +appears. The actual emulation is started by a click on "OK". Then the emulation window appears in which the C64 +startup message is displayed.

+ +You can quit from the running emulation by selecting the "Quit" menu +item.

+ +Frodo opens its windows on the default public screen. This screen should +be a 256-color screen on a graphics card, otherwise the emulation will +be very slow.

+ +The AmigaOS version is somewhat experimental. You need AHI to get sound, +but the sound emulation is not very good. It might be better to leave the +SID emulation turned of for now. Only one joystick (on port 2) is supported.

+ +

RISC OS

+ +Frodo is started by double-clicking its icon. The shared resources stored in +!FrodoRsrc must have been seen by the filer first, otherwise the program will +abort with an error. Frodo and FrodoPC need 1344kB, FrodoSC 832kB to run.

+ +Frodo can be controlled with the menus and the emulator +pane. You can load snapshots and native C64-files (which usually have the +filetype &64) by dragging them to the emulator window.

+ +Frodo can be run in any colour-depth, although 16bpp and especially 32bpp are +noticably slower than the others. You can toggle between two display sizes +using the pane.

+ +For a lot more information see the file !Help supplied in the RISC OS +distribution. + + + diff --git a/Docs/technicalinfo.html b/Docs/technicalinfo.html new file mode 100644 index 0000000..9c7d985 --- /dev/null +++ b/Docs/technicalinfo.html @@ -0,0 +1,61 @@ + + +Technical info + + + +

Technical info

+ + +"Known?" said Gandalf.
+"I have known much that only the Wise know, Frodo." +
+ +
+ +Frodo tries to exactly imitate C64 hardware features. Now the 64's hardware +(esp. the graphics chip "VIC-II") has a rather simple design resulting in +many of the internal processes coming to the "outside". So there are lots +of "undocumented features" you can do effects with the designers never +dared to dream about.

+ +Frodo uses a line-by-line emulation, i.e. the function of the VIC and the +processor (6510) are emulated for one raster line of the C64 screen at +times. In practice, Frodo runs VIC and 6510 alternately for 63 simulated +cycles each (corresponding to one raster line). At first, it emulates the +processor for 63 cycles, then switches over to the VIC that paints one +pixel row to the screen, then again 63 cycles processor, and so on... If +the 1541 processor emulation is turned on, 6510 and 6502 (in the 1541) +instructions are executed by Frodo in an interleaved fashion.

+ +Even though this is a heavy simplification of the processes in a real C64, +it lets you perfectly emulate many graphical effects possible on the C64, +e.g. FLD, DYCP, hyperscreen and many more. But this method has one big +disadvantage: Changes made to VIC registers by the processor in the middle +of a raster line will only take effect at the start of the next line. E.g. +you can't change the border color in the middle of a line, the color change +takes place in the next line. Therefore, very sophisticated techniques +depending on the exact position of a register change can't be emulated. For +instance, it is no problem to open the top and bottom border, but opening +the left and right border is impossible (and therefore not implemented in +the emulation).

+ +Frodo SC goes one step further by switching between VIC and 6510 in every +cycle and precisely emulating the internal functions. Modifications to +VIC registers become visible immediately in the next clock phase and +therefore it can even emulate effects that depend on the exact position +of a register change within a raster line, e.g. special FLI routines, +opening the left/right border, linecrunch, DMA delay, multiple repeated +sprite lines and executing programs in open address spaces ($de00-$dfff) +and in the color RAM. The 6510 emulation is also more precise and does +the same memory accesses as the real 6510, even the "unnecessary" ones +that come from design weaknesses of the 6510 and are not needed for the +function of single opcodes (e.g. in an instruction sequence like +INX:INX:INX:INX, the 6510 reads every opcode twice).

+ +A detailed technical description of the VIC-II can be found in an +article +I wrote (32k gzipped). + + + diff --git a/Docs/whatsnew.html b/Docs/whatsnew.html new file mode 100644 index 0000000..c6dea67 --- /dev/null +++ b/Docs/whatsnew.html @@ -0,0 +1,22 @@ + + +What's new? + + + +

What's new in V4.2?

+ +
+ +The most important changes from V4.1 are: + +
    +
  • Changed license to GPL +
  • Updated Tcl/Tk preferences editor +
  • Added SDL display driver for Unix +
+ +For the tons of other small changes and bug fixes, please look here.

+ + + diff --git a/FreeMono.ttf b/FreeMono.ttf new file mode 100644 index 0000000000000000000000000000000000000000..517ea95be0786e316e38bd88da112d4d23e45a21 GIT binary patch literal 134512 zcmd?Sd3;l4`Zs*eN!pZdNt&%qv!_Y-ElHd1l#^1rQ(Be+rEFm<1uC0}P!<6zqOwyK z5s^^_k$oEm5s`8HF^-Sp&!0MuBZ`dUIOxMDBEvxPeDC`tZ3^SeJfF||e%?Rc9!^fq zeeT@LwO`kL-3Nwc7)FK6^4WyJ4d=Q8Y_2;AonX&l;I z-8te5+;=ie+_fRiBlt7^Gd|px<9W`|=4{93w?;2x7`7YtBPY+A*!kbHzA0mvn9mp{ z?1RY*=bIQa_Xxu%&fvLt+QfOCem^6^?{UjSOzU`L>VYr5+ru!%GYtFmh3Qi!dcXYd z-&xUKG@j>AM}fGG`3LSRaGx}N*8D}|%Ofd2~31P{7z)@7cgVEmV1BnPvyR&Ed`&FtC*C8 z-NiT;F_W1vhC>-kYn3xuxC0Eh7VaFLFx*=1mw38PxUyH6To$!CN%#Pc3**G%eN3dk zky%7WWWvcbP95yUHZ!wX$uV(I`)>5X~e-oc+sF#lx%xImoQ-%?knKJ)3Xp1~E<7qW*oeW!r zV+8XOT6hUH$>^I=qZ*}mm?Wm#|955~?lC%iTS{7IwlL4)Sqzhlai3sbWENv=pW^r* z^#9k8<2rsP%pwMD3I{{yK>8$yml=TZ3x|q{MQs9wz~TQsFbgu1vcQo><`8mddV5fj zmI1r8;E7)mDD<5Kstis6y8W-DkI;j(OU`EWd*A8*-(ZA%z?8J(!dceuj0U7{Gn(Kj ze8XJRf~Wtw{|EdwzxONyv_sD{JPDqW`2J5$;9bBR!TpHdv(|sxe;!aEI3_b^{QJ=> z!5*lI-hFiV1oG)CmV;(vvVnvMnZ;lD@6f!_U>P=oZ7fg>Nd z-vKJ&m;^@4NbozroWh8-@=wN!K7&}f4xAFGks}857=%g&rBKj1g&L_%Y7W4C;Ft7Q zD zMsG6!DRS&U*+Bd*KXBC3Yc2S!8n8{oDbPERgC0kKo5?|XFTy8}6ZbxwZ!hJf-!Z?# z^#*ek5S_;?CFM91eYSg7aP9Hs_(tQf;W+4PrB|D8H>BfU-0k(HKxS-W{)`jy#(#kM z+&|O*wEvg>_5MZvmzjx}c?@J$6omu9aWf$PSIpryW5t~SA3~F2$dMb29?uD-RO87E zU{QcT8tz+B(u^_}p2uKb3o!pI|5waIxK`q~cNnq%YevCL#IFec6-*N|#D5px2`rQt zgN5P@{O^Cye+m7Eqa`KfRnnpf5;+F#5uB>Hot21xj3~t-`osP%|1*&xATAW++N0`M*L>gvP=d2ToV-slij6;1nZT6=Y5t zYGR)B7=Ul#s9}fHv@zNA4AyI=t&-MAE2x1!1SuBe7YoLSv$uCL0x~Dz?3FB}4(*Zj zN>19A|3_BJY{$`q>-)lid2VN=Y&=tf`#YS3%JLs@SHe6>Key6r2D6^tl`uKrcA<>a zAx91%Py$Hnpe2Yz{u}coGZl2w1}>Na$?gHYPlF~dKND1iZJ;dbvyphYNxoRa&l!ScQ<(R@UA3X&l78kFXJbxF-K zK(kkl2e@6}`4Cx-(Gbe)gE#r!J4QObpO)%D$wc@fct+y;eJAG4&=V4#Ncqqcbyf5v zG!-iM311S4Q%j)cv-*aSEB#*WqmT^V)3SPBWAL6p(>Ko7NUp)tH_X@8`yCP}C~qN) zT7$DEk}3E-c=fgUCI`QhS)opIaCYdUH258L=n-gxz>pr4`~D6(-NgJI95)KbB+x<| z=q>@X+k|l_0-t79!i+~Nw_&r0{r>>(Pe(ANu$b78?VI-od#w(B@Blf3QpH zL@;*%6C8Mahs{7s4D%MQWWK@k!C!`ypl;xgiviEye;j5G(=sx?1RPcz`*94%@oOB9 zJW$ex=146wQ-$;X!^&pS?t=Xs4C#^l%G!G${ACL;Z0;=${!Vz>$#g>UTfWjw~4obG94op9l`Z0W;vw)bf$}ajD4Ej#BO7M&K_aUu)kzqXWwA| zi~W|p!QSE$IV0!fhI6C2R&F9UlbgjY;8t@_a8GmVxgKs8x0gG|{hIq7_eYUjlqj-@ zQbcai0?}eomuRhMo#=VdJEGr-{zvpT@p$ni@mu1z#XpGu9`=i{OJQG!-IV=c*lyTq z_`6AEvY70q9FyBrWGXRLnyO8;Ca452=>7+T%tTtQB$>wzPc=IL;XOUPG z7L`S7F{l@lN+wX1P+J3YTu)FQW_KsWUZ@uvk&VLX7Ch?p0 zuxr@$>=t%A`!ahRGrfSB{+j&_W_pvo%^A2Pu8|wbjpfF3Q!vvx+#}rM+&av3GiJJ* z`z7}#_b&I5h`~(FqGZf;KAq_*%=Br|3!-=FOd0Vy@vp_d5x* z0snsgK7Y6WasL|sD*p=qa{n^_691$AM_{8Z@Xz%>7_rt_@}+s)?HhB z?eS|}*Op#ea_!M;^RLajHuu_?YeTQqU2|T`x|Vs(_QijF@#Yu5{Nl_Pr@naRi}6?g zcJ+T>{qpLcu6};?%GKwtKKuFipNX&Bx$^fbx3Bzo<<^xSu6%Rl`jtOkIeq2Sl_#VH zQdi{dFhn1K(f@z^g>#W)qL}dgb7pBofjlIUg@5%Y{89w%5(ci9Ac{e}+$d;@82GqS zXc9RS2VF)iG&L-Wct|-dyaye0kO6iGA~>+*tgx$Wu;7xJ6ebl~B^|b1CL$TxkUqJP zPEOd1d9XR$)S@baCM$t`RR;g0oEZe$%fnPKgJGc$fmEz!YG7g2G4)IX?AS)wTTPI0 z!{MQfgqCe#Ml)lWv9Qm^LEDapwKfscTf;ogJOP_-6Vt=&hDP1X?1LWt8T9MVnU|RZ z%t7WANa{n(5%@31nUl~frL0ZIJe7nH8`dXN&*A4nt_?2WA$tRQw9; zftAc^W*e(yRpRHwd&Dn_UlQ*Xzrg$=>~-dKwn%(N{Cn|9@u{%Chusms%se9AC*ChU z#H?c0F^@4%F>7G~tYL#7W{w;+5iakVvL5KFk}o zD(uCuPs4M>Nplrb00p|wXj;xEEAGtsB zy(n=MAGI*5C+bMldr@CUM@MU;^P}fR?~nd0Mj6A$EQ~o4^JQ#gtRuEPc5UoivA3l< z=}751={cELRwV0?ZI)eHxLMcpyodEI5*2fC}eZ*_O{!}SyN9r{K3Rr>Y%UHUf- z(S~?Kk|D=XVyH5#GORc3GVC{;FkCRk8mAiP8oP{Zja!X-jfae9jc*#?gPb2}+GyGh zsea0I(e#e#W3%1dZeD0!X@1(g!~CN8nEAZun2 z+*)Z}WnFJQWWAkKmQ<58GHFuM?4-R(hmy`Fy_xi0(x*vZC*87fwn|%7snm<9@1%a5`eo{OslK$xwAQr!X(!Syq`jT?QQEb% z8);V=ZfAcdcWwi zqU%Msi^avVVqI}saY6Ck;=3gUC96u-m+UHex0EZ5E!CFVOC6#E2{G{AoKBl~@{KfLG z21O524;ndW@1Rfkc)p5neyC)|_eDe^4#tnz$Z5m8Z7F|uMpMNh>C6`xgn zU2$`;eelS^lLmJT-aq)$N=s!%CsuBl#9eW3bO^#|2gt9>;QHE}gL zH3c<%&4iliHQhCvYxdOauQ^e3q2}$Hk7};f+^D%*E3S>MRn`{Nme*F*HrDQ_-CKL0 z_M_U*YQL&0s4K7QtXovKyso=$eO*u8p1LpVQ|evy<@I&-E%n~|&iW>?B??&Dkl{U&T zs%_NzQ9Yxsx9}~?TRtAG8$EJ#+vusIXOG@J`o+d~G9zVTk`ocH)iL|UNrms>|1lP=FFaR zV9vLlX`Qn>4|E>uJllD(^K$2V5AhFOnj1d1Xf8i@@!S)0zn*(*9yc#`o_5~!d6(yX zFz@R8wE5HLzqP=yVC}-lg^Lz`{BX|0iyywYD11@tqC<<$E_!p(tw$`6v_EoaaqMF6 z;(d#cEcQJb`RLR~=RSIAiE_!3C0CbnOY4@dTzYZo2TN~tS-Oh4TDv;C_H>=-y4-cO z>&~*+W%gwy%e>1LE<3aA`f}59etFmOp504K}Zr!@KpHeldwGwSN8jUF-L+Ke7J8`nT7AwEo)q8|&|Gh}fXqVA_ze zpVlVzhU`?bsKs%?Avf;!?_J_ZFqmfXB)0>xV=%lQMOUHF=eA`WBJCq zjV&9!8#^~H+1R~t^Ts_J4{kiO@zTb3H-575tBp4iB8}b@zbR=`&Zd%0Rhx!yny{&3 z)1pnQHm%>ZYt#NsCpKN!^!BEYHeK6vW7FNu5u25pO`9_|7i_NB+_<@Q^YqR0H!t73 zZgbD(eVdPLKDYU;&F^piZ1eTax3`G5$hPRVq-=3*Dc@4JrDcnEOXrp)Te`Pw-m+)Q z!7XRDT-x&PmQS{PwdE$9*yyeCTa&itY%SSZwRQN`30ph1F50?k>-w#`w(j40V(W#i zZ*To*>$R;nw%*+qu}!(nv@K&>!M2KRjoVtcP2V(TY3^tgJ;d+K^xdb~ZIJxhAJdp7s%={eYQrsq=6yFH)ueAROkVZZ3@ z@!ONO=WH+8UbTJr_6gfNwlCVgYWw=_ySDG&eq#HD?Qd`YX#2J8H@4s15wSzL!?Yt~ zN5PJY9gRC$cTC?gf5-A2>vr_)*tg@zj&nQS+VTF5&vsnjaeJqDr);NgXUb03&hnjg zJ6m>ocXsYvva@^V=ACzd3Tp`mu{D1SLLoTyC&^gvuppZ z%e&s&b?e!*XUm_R|Lm8~-ra55-L$)Hcl+*zyEpGX_FUw18PBypci_2G&s}`(9UM3J zX!kViS+i%~o==|F_B)cE&w9S#dH(sj=SMz2;raIG=Re=|{F%Mcd&lfO@Iv?t3v zAGq^!?91AhTVC#XdEv`lFZaCs$wAA(l7n*(?m76*!8@;nzoLA_@=DGtWv`5UW%es; zUfKQ1*;hVz<>sNdLzY91L;RtpLz528J+$)B#zT7#9XoXK(0hlj9{TPucR22F(&7BW zHHX^{cOG7KxaaVJ!{-jabNK4vTSp>}=#JzZ;g1YIGX2PsBTpaMbL7O4w~l;#iFFg$`dIk%1(?t(Q#tUiG3%|o_PPn&6Ck5 zlTPv{$DEvd^68TYPhLKG^HlVy_*0fsS*OZQ)tu@$wfI!`sg0+0ojP#p%&Ci~-ahr| zsT-&6o{l)JK5aRjb-Ltq&FPlYQ%}!7z4G+>)4NX}Jbm``o2TD9{psoJXLkSM=D8i` z_Mba-?$WuB&)s@8{?(#a7rol^>b_S$diC0?x6X^t$Dg;KFFId!zU_R+`Q_)=o!@=_ z;Q0&Z-#-88`RnIzzb1Z7_L}asl-FFZmA_W^S__WpuPuM=*lU+x`}TtALjHx83*HNJ zFD$>X@503k?_K!x!kyQXUU$4+_IlOp!(U(Y`nuP*zJB2K6R*Gf`ga$@FUDQWx>$6v z;$q{)B^SFdZoIhr;^m9JHym%&z0vVT_Zx@ac>9eTm*Os!UD|Q!)~_mlHQ`sAe|7d( zU%eUmrv1%zZ(jX%+OH?T{^#I_G3;z^2ckI=ChI86WS1P1h?#F3N5jKDDLE$M@Rd0# zBE?~(_?RS|y<2jOCFM@JS#C4S&1SaV_dXlt`+?hWZ>pKD4|B{X{=czHxi0vEiReKT zZnfBxMfN;bzC*22ig4%3FJ>KTWjOn&J}g6(2!|xAGP6pqOHS6wb#^-!m1 zPd_=V5$=O$^8&obN{mYx%SoK>e5XULXC+RDGM#*?$b}3z&z+xVl_<$KSAL$`YQd$L z?Y)wpDXbDFYhp!Vq9M^-uEH3T!1JaEg@;d!3d>d)B;i%j4z-Sr(()FQS)%Y*O0Lu2setF+lDY05=110}WyTPWbhVf{+J=y(?csrlS@HtO*ke z-8KSsl~VE(^{y2UYbq>iYAQ5jWEhMY>3{ZZjSY{AGqCN^5m9m5@wBK=cxp6cq;oTx ziVK=Z(I8TroPS&>R>Y_x>1X|YPfh7$+#J>*zGLrlcEl=NNFV{V5D){PZUR)NgW!bB zLdx0$7;zJ#3YUaSoYrK_$?0J4@}*I!Rn3zpSjLu`Z7PXEFON^PC#a1&uO^RAh>Mj) ztQ?-#Wb}Ha`jJ)B3X1HdW8@N}LaqX+^(uMFh9;*%$GLQi;$20AHm-vwIw(){a>Vui z9Kf;>{%P!2+#*C|7@MmMq<~Szaw?@pfO!^b6g8XFdTY9MO0-^WYAXJI@YywbrF3BB z(#nnd6jG&r`FZrU0PTzr+R?bmxI9;~FmVHm_CSU91$`|PHk;M@>HS)082pRJ^^~5O zcR|my$$CN@sF?DhN-1Rn;{laY)<)w4S&%n@t&z>U;~g14K%sYxbSVrGLKZzZWNYVg? zaXYD)10DQf)l<>Rm^jfI-_3Y=R74c*>rY%3TTSY*N~_u8a+)1#E{pdq=XrKHWT?jB zRiWPJked%9_8tWW0VI2+WL{iia&lrEB%?GS2#Gv+2JkzLSQ|5drGQ7@h~H4evVZqo zh|yY~#_nQ#CIYuuh-VOio;BwY zvIBd?5>*1ObEw%LZL_!hmpIGeD;mCYHlJ(DRZ8O(O2g7qi`jD-gJv$z=UZk}b}ca2 zUGWx6yk1c>dk)~uWBziq?*vac&?F^gXeJ^h(L5(KFEF%(@=LJ9z_HQKH&(m!niN}p zi7VL^mmpW_yBf#NOUr#IK7VF)>qB}~q)J&dVp2wdSd^~TWt*(23Js_>y~10QTB>(X zNGdN)%C@WYQ87_OuF$#8M&uMwbGVAxJaeAHDTnNksEBTI=SjZj?YZNhg!eVztnkk;uVXPSY z48TzbI2s67g;}yhAjN`=NF`!Dx}FJO%185CN4?Rs|+y@x7tmMMvQ&vvq8lLx#^`jWX`pIKWJn& z;R6L&*sKx-nL%L*TUHM`IgB(TcR3#pk#jm>VoAH&jw>zR(cWG=bWd(wZjL?qYM#yR$gRsGbj>mCfR`Td(joQ@Z4TY66l@OKBXH!V z_<jPG0e^_yXd|^*QzQp&JFabsi7-fCp zMt8s9VFxBB!b&5i80JCcAKDv$Xn%+VjH4Qmux3yewpOI3R-^_cBA1k8G$h#!R1YBA zh%>Q25d6U;1R!DpC`is^^VwVS1e5O{Y?vuQ9xL-Tv4`T=ABV-I{reE9 z9H?$Bw3Ya|n{w5iNo9gmRY9c|K z$FSj(Fa$Qt_YXP*_K>eBF3%`SkgtA1s+9#sK*lhg%F>Zo=>TlvK#4(;CzvI7RHjH& z3UEYFu)poC8lE$5e)ZV!49A3uk{q{VX0Hv*uH}bX<7W&nZA{7M3v9V2TXuY`&lZsX z9P%iqv-h|T$gUEB=OC#my~+W^bT%(QK``?O146PB<}brZ2wagD3ylY=VzXRLIi~1k zlZH*#xU)5p<*hS@7iSF|9&0R1x8y2Sk#fxpv!f&@DY_@Ed_=wd=PUMiH2eM>nUE?H zwU$+m4>vi=mQUDaudU0HDx~pptCMv;AC_w~rUE9&!eFrWIRtAyF#2p{*2yrLi8LeD zv=gAfrI=OF5=F9vZL^p(nr}54o?p#dO*;p|Wd7*a75tNYLc>sKh@v7dY+`m9K74vF z%U!rY_(_CyCx$CVKWg-pEJ#mzplwQ0l&8%KLHMP!P-zg3PDczcmGV~Oy%BQ;ws(y7 z{eIp<`JVjj)vT(a{PDw6z34};Y8z6Xo7Y;^!j4S07%aZiDLGG*K3U{seaE@D5m>i^ z?g+vaxE0V~WC<<+b*m){<_k0lL4lgB$@b{JU-yz?mu&p+e|;HMGzfo^B15+!OOOC+F&TIrYpn1rB*I8V59I^21bY@A$- zC(YAVRMAnMCVOt_uyR>KqEe-g(}=|pmG0qFE(|X4lDWn^?9r>NiDuW3iliY6UND)= z<_42R5g(~FP99eNQo>*m5Sc4%Ol~k>3Wa4NnmssKO8s(T!a(v8h3ypj#~lPAv6Voq zRlM{ADbGKG4#Qorn=pUelP|7t6nmd~%1ex=Wnq~RW=|7S)sY$ITS4SnGx`lfzwp9s zgb`qQ30(@ag!(6RW;K@pCc*vxY3 zdIqdz2opDiT#!__DSY9ww&9O~RR`x3+GP?m(1!qq%CdsrGR*w$Q zHlc?xXdC=Q?Q!By5vW)tY7GSh5GA>vNQzlDnYSJ@Vr8T3$iylW&(F4w%__0-{MG=O zY((6ohm7fk!}z9K_BI(5rkY7=K|QK0e_2 z!Vg)I#e5+M9!5{~7v3%)d-#sf3jIIb3&0{$$E(9-ncCGJYp&QK1w}=J*3-+A~ zc=v#pVxiptDFa-CfO-ja)7cDlNwX*WXLIMjuy0;<`-~c#+=!U8@&z@I9y`9Q>$vxk z;UgYd)I4%AhD`A-r?i=bHkGj=Dw07}eIfuDP}_iVYL0XAXp3`%d(vc{2L`CD`OEml zzM~6W#lQ>$RE80es+?H0eoT5|rOrAlQlVFtCFn}6`h8#S`|ai# z?Hk)!`{Ltle3;UtR~el;tx=^=l*Gp6xm)wPjvs4yWW?~ri<(E#F%epyNpS{ynZyu4 z+d-MoD*(ytK_sAlTTpRvGx?#md8P>=^nGRGqaA}vV|?4|1~!-Fbb7s2Pev^cfMN2` z*5bm6pXSc{|D_IaESurP9)(FLAF~(R#8wx-{ckR*Tn;-Ld za+2z%*IDx@dppu1(5%Y$SN4nWG=tjfyK(g@E29h!_@EC0pUj{&;U=Ci+6Vn<0)iNz z5`r@nAMRx04_oVaqwdAsMf_Ngef&7PXN<{G`0Pu?{2bmrWoka}&?LV33sJViVT_wK zOJP8aF^4<*D=x?3$QCVHga-sSm?ijg2(g-JtROF$2!P(6iDpDk;ptpZ_%F1Y2K}tu zGn%)LZ%eV7T9NUmiQx7P@_4@VK-+Bi*6T3Ky1S(o15~zJ_w5D}Okj~C#Bfsd15brm z=VlcP$qrVB)X#(UammZ}_xS~t6u@rr_34Lk=*&uw{yL!8vh*A@)s=vMNtaS+)e*uoY#L6mNgRw`;PP8 zTVLw{w&-|};n>EVq0#q1t3p88>A1LBqn1W1S8G(VXm0-;MO>^}))^NkB`8L|_6@F> zy@*&2G(0d3S(4`p3SJs5K*WtGMC!TN^6YpkJvsFQ^?` z)6R|^nByFpVQPA~VoIbYSA&>kq@khmvR9Klw8{~cp_e1n$zjLPchK>j$Uy`5ivT+s z6`_(&70`}geS27-H==7I`1u1zJ+!8KcH@xwU>=#wWMOM7hBfnpnwz*X#}&uww^rp^ z!8`1J)y3L^;@aAx0uq*oeRP9^HB4;hfaNdDqR;UnCa54t0RR=Jb0n&wV^aqW8F3`9 zG9e*aX;?k0Y+{i;_L+x977ftnuuk5P5t%Tw{83~%8C2D39zLK4csGlCQ}cP>N8kn^Q(q-KK}SaqXuGD7Snv${H6-eu;x|GUavKG z&6}$oOH_KD>fX%-B{lW#!n#`Eob+>pvtwRrL@Xdd0|6GIKfyg}L6h3dM2~Rx`BReS zZ{9MibCYknWo6#V7^TiPleI@T&K~A5rADrtI{ESLX~`{S{lU~+%KzvA_?bgt2Cj!} z2h0MbK+L_M%^I|f0X??+c%m|FvTZmF{%udft^W{MD$0h(m^Gc7Gf=IM&9!;IA-doj z2n5^UMqv(4u91BMF)GAdG*l`hxJkMb@R3J}4<;?-n<5zTu|H)o%}RIjwNoB{d`eUC ze4dxhYU<=y77iOWw`tf;y(%l&ox6InceNwQ>)ScBoy(}7-B6!j&>-*%(es0WAM~gQ zQeI$cM92xQc~Gte*gA%vUsB&V5vF~o#}jVNv*x6Sdpw;y9}a|p`u3!yaa!KDAHZ7N0Q>vc;_ii1V-hI&4s%g6 zW}uw~ekaxm=0GlES3&?zy;i*d9u_Svuo~y2J1?UxHfvVXT%Ij_n1X|y0t&>j$< zMb*vI*FHHTyV6FO7rNunv#Xk$J%gHm>wURkaqGktT2rRiSLtPoNl$~F#7RKbF=P_^ zy5Lh0C{dv#Ks`hrVGt_V!>^_dhyKox#}BT0(&G_?5iIn_ODfsdU1pUbDZO|T6-M4c znI_Bywg}{@6mxOnhBQv&L$D}`!7V@$g)qWFbVRyhjV)+?gAP^f9fTs{rFoh5(w0?L zv#o35s8Xri>9ohBH{}fOn$b3YOm#t&Dw`c^vZxc(s#v95sf;VJmNx36d9x=YSu0PD zi7GEHsn=?uMlhZjj7QE5C34D5qJn{N7M1f4tcnTf{lMZ%9%MF0DRm>JrH$|s1DR;V zlQ5W1K4sL#%3}>GU8bt0{6mzs;Asmnk!fGw1GXoL#$pNafkUa*@9m7pV-zA9laFu= zJIkQuA1~#{+lo_%PEJfPKxehs9m!U9m{9@N_vTo&qZ?1^)Q~z~S0$#NB7NYGYe65x zTLrsnuxgKfgn`2l58Sa4eAuj+k-!uNEqiJDOR+q^3iSAxgt`wN~Q$Ge8N4ykAxq1Ev)Ps@`_T1r#crd)?3OXNFr z@+8~DC8%Ky=WgGl-VgfRgh~URzqKh4G!YswW@n zBBi$44%<6_!*UnTp3K$Da~*LBPDBc3FN3k|v`3Ye%FIp}-^*u(Wx`*9Z|tP~!ZOkb zxNsBCLYNOM{~**Pq5xCZ0~ucT!CW4ee(~Hz<-!`l2S6+6z@6KUSHqh9>hHYWP`B}sKOg`wQ**Adv z{w1tCABA3mZ%BAVVqi%?HOMd|?26?(7eY{i^GdiD3&?D1v^*|Ks!}H!67uS!mASc! zn0i-&F;T6OvVVHkWL2qc$PviATdXFPpkYU8muyZ4^UxdaFfq%MO zOry;tJ0D?4N@)NLAnkLehgny9;;bfP3GFi=U!qYXn0+;VK-kuwwUA#f4c0ZAO+eK`oct+))vbb548pxIuQ6Au-Xa)eIpZcT!~m_4?GAt)oiwa>GUr{&UNQ#FQEBwZ&7y##DX2d9ppNWaP9#D!C$o z-Qczv)wWV+E}xxM7NwGx4qN7SC!18s#ii+<^szdrbYRP}?+R5Gi_KA#qG01KoZY9Ah>+(Gn$#j#UrOs~SJrlWLDpO5?0jd4xr8&&?aR5f&$U#YiTNMhGyd*=NsCK4UY#HqK zwcV2@c9XGU_tYu&FI+RVEAy!RPGtrqwLHO84G9})^6bGFfyOM*ni<-2+h~`kI^5|P z;cl;;Sg%bgGFFDm)W5OmP1*5^!bkm#S)npAeix_LC%7HU8=}+&DUOP;h(wv@sYHoZ z9vOW=h6EWqoOr^&W=08nrKF7yUGm&VLvf5*N`a)0OwHZ%;PR2H&wJ;m$u^X!vQjkl42 z1o@lB(0n4uW1^hIy*M_(9(R#UI-3fBpllg%U<59P<{`ydY_>qzSR`}@F-=qMi19}v zAZ$!pMnz_Ndgi!-A!eN}At@teK(0!fpfTzb5{z9G&nK1z@O&%Rnv`Tc&snTRS-R|) zgba}>DXAv1QZ0{-`)`WpyCs+)M+(CrheMy@nq$AxZ%^PYo<-#m{!p?xFQFR z8^N0pL+*rag=Ni<3@;*~1XY9`hC-r1AkT}ZX);BP zeMp-ry`W;=;55Bjt0sXy7CZI6WzV8rtXe?kI+2cq(7X_)pmBnJ*+|G-C4zu5X#RO| zMrNx$+pdgFP#P_IjUh@F6{DamC;hQ2A?t&*V!pk$Hpn< z$W*Wchz~K1+V$WP;^!0ZEE(!Q5IP_RJtjrgSAhUISImze+MqLdWdQDcgSB+D6=t~C ztF{klL`vL#=u+PwOuGDEdwqu?7jpeg$kiJHm?se%SxuUtb`{k~!9c7fIhIXlNooTT zVQ#A&wuXCc-q49lwzmH&Ki`s+pFa+!g*@(wp^l=WgBkMhFxjC7HX(AdC#!l|!@ZXp zEUFmx%-2y4ONPa9?cPffN(D?$%SF_uMZ^!nxwn|F_pC?*|uiYW4Z(jH#JpOo1Smc$3nTW*oXQfdyMNM zegTPJ1Xvk_8_1(`vkism=_fNX3Uj1kF>z7_*X28Bwph&HM2l5AX&8~Eiy=!tgDj0g zD|GFkZvg6Eiy{n=6q;u`$aQP=g^C$!>}eA%{T`9Y$ndmRNAfLvy0IWGG6S5*6@JIhQhX;-K1+ z%;aWOY>G}1OXO7OPNYuqD4IR;zRHIlYtxa>A4Y4XB zNXNyv3x}jB`;w9mReK>1n90^0<>rmdLkAUMP2_}Ul!4s>Y zSz*WyLt@x7@U_)nj<12b2DMJ zlE4VzzTWa0I_FYm<9+26rkKzD^=bPkC+&x}(+3Nb;*Z5U7#i`$mCxWaFg6MG5lD0l z^3(_;9a_3~M?o^1F)b0^@U&@KLrUVb-tS74_ID^!De)xqxfTIg1Q3ZX9pFEU_3ULd)B1)%_X{%d0yN>4+DWY{snRZYkZrI(vKd2sQEsDhgcK>$r;!%!J9~Dxv03TiG%xccSs7)nT0dl{{DgTg+xEx{_2794^dzDLvK5yoIPA~N9Z@tuXN+?sD(Pzb$Rn;H+5`k0N9!|S_GzVcsl5uMg&SbVXv*ur z8oSwC&+9WI6B-85<#z#ZbS*r9|4u}nIJ|#`;9n>|P34@&@4v77SHPS4R*X;@QlH}a zM}lXxLd(m*U)nzU!jtbq%6~!I&t%y9%4z!yX&<#i>eKca(mpLGelh4Ji(3u%Xc)E-T4hRc@TQ?U^a|;_QJnt*WRPd4$Xn zktmjF<7JjXy81Y2Oq?cj;%J|5(Acqq%3H_$rEpBXHD-0&gl>gyXu2a>C5wz4pchAR zVFT2$YGX|507a}kCc*Qptg$Gsw#HRFjKEd{m=trASMCvB85WqMFc*OrXH#4>(Yg1P zn>pCv_(f7vw0#k2zkfM}p@_hcmO~$;F*?9d$w&}?!76!vAdaA>(EtQ0 zf*T-QX@OY6Zo2;gTOjc7ORu*w$*51V6|vieD+VFLyI{{kMSqKEg^lE4u>lDeL^A0Vb@r#XwXGz zFtnV)^$vk6N<->X_}wA!3zpM$C+3ghYH6*3yoT_B(DbBr^IvoLcoPU;H; zbifi?-oiu%WqrU$Aoioyf0@~FU;R^%0ovOVlu{i)_DA@0=>CX2qKm_Vt0n?N26r0& z#EuE*K(c#NgiRB7q}fq2&Crw#hc%-DmimJ`D9qOMy!4q3Ls!MCWd>7Dsx6NsUH98f zq0%do^*M=3cFlzg1Q(cN4p#x0oKIE(9vEBS;`=|#;tCF~FF?z_A2P@ylD>6+R+q_= zlAYoXq;!Q06xmz_{S&-YdS$91D?!Od1+%;a`HTG+qS2J5+L`A8W#kC}w=^e<6KJ-D z(rhIyr~5ZVEvQf4ley=<`jnO<2`!^`NPXBzTw)lp#nF05`=A{zaSEaF{`H+G*HGV} zfBk)=Jnni(eZeP5A?5$oiE;#4@F(PW{Tx0y-gOJ9Kmvj)nk`4tF$r6c{FXq{9ErGS z;0V~U{e$X7>{?^TA;4BAS4mYdO>qiI6fz(}Gr*bx*lh4d(td#Md zfg^`?4;w*dz5q^+@3-Fgl%?LOPi8%GxT&qDgUZfJA zwExJ0yBUG6zI;L0@MIh!Gf5j{m-mW?BBgp^|NOzUipCFx48q`e%F?+N(}IbFDnnf2 z5VMl)MnYi_Un0s!4B;a{Kfu4pVdQoa{_=eh0)HY08b$diS4Ve2l0)+Z>LXu}BYdik zzK-&m-f~jEl*oAaLL_#8U!Y%-pVY{`jQU#cE=v2?r}(8leE;?zM*T$pUqb8Se}}-n zCA_sl$~5_Ng!W+{2<`ul<}bp(_>VIf6++B77{mXU35~f;a)neK-9*I2zesLmv%9n^ zU0jz|EsdmeC-&K30frobA!78joY3}Q(Rd zT_DKce{&R)@3?Xg*#HI7?cEt-~br}gIqNFPt6}>qOu1=y7zsJ{sY;qe3~PzVqvgv!R8TCT^)k0!+lRu zRC{67$D&SEvBX-bhf)`!otBc5x5ma7Xo>}_vIAdy+zWb`{xA%=5#M%!2ar>_E#pw z1<61Dmivsz-MExEF6Dy6$chO*yXL=lup`yOm@c z<2rkp&_DVq4%nR%YIlNzFfK|vqWO@0W^M{|4&xmfGG1XGBk6bp^)U}^U>;6d9;lD@ zn*;jbCi}l2L;Koq3e^9e{Tk(c^|2Qk@uNqk#-Uf#?j27vt^WxAM-396LVfUkG#NkW z6x1N>yCi}SYs*ry8x&K$Ba<)$`zT-7qLnIiGR+eo5|*aBB=h6r6na_w z^g^=3-~((eT;h@!Z?aIKmd3XVhw_CI*IzG!$`URHt>UemXyv4D1HL<3H`AbX?YGU z57Y--8!3I0|Mi9V7oqJF|3Y(@_!p>6=TG~E{R9v5|ApLd7CGB_Wc*AHk*;b4W=mNj zlmJ`5xRjx{@!QQd3l}A03;UB4fKB+(7E} zNgHQjFP4GmeSaS7!yF-B@1G+wD&frt5hpS_WLLt4c`(nNcBY`ic`)1E_mR*Q#=<5A z)82toVgIUN!wb68L2)g_i3Hs_mg-JKc!Kpsp}I4)KGmHfqC06ksJH$o@VXJ})5P}- zt$zu)HkOho2dxjk|2k;H3tU(UciJ9Yx&zzwTi#P8$70K^LF8nwIn=_UoznN*;%A>- z%rET#Gd~ch^&KE|oC`(cl@Gqs#_%yj3cxsaZYVySA}$N`pI}Zj)(kHJJfFVkgcN zWGH)^!Vl4Yl;@+oI1neAOL=hwEhoAF`)4W7c__~X>Qi2Pot7(Ud7wURpRD(3u)aoY z_R#hTJ~b*IN>Q7(Pxr~vwG}fm=g8U$MuPFsynPxMBjqHnh)aQiIDXphz&^hi3xjf=@wi_-k(@T4Zh0_IKf_B!H7VPP%yI?J97#4xG=n~ zEh0ReZ{s~Jvkb+vC)Ac^Ct*!hQm9VKi;T{9D`7h8Vii%GIHD=Hpgyowx3vLVb)^Oc z`%ApDEsku}Ew|$})!7X-trER7kefmlSjS5AbnC7}9Txk*-d&4{&aWEtBl{cRN z$jqkk2yAd5s^#BI5Hv?<@t^H_P{yW~(OnOf?W2Y4nc%$eq6V4MksO^lENAet>DU5M zRS+#~fsn=PRk4aVr81?upk9&~I+vDkRd7c{N_22XL@z#Fgf3#12-pw7{|lsiz+Z#- zMEx|(7x_uhxr@VzW`%!7>&N(`$i50v{`C!%j|!B-2jsFu6@(s9vJNegUkGbmaftQc zPC>m7$itxHCWYHBtmV^Pkw39TW1n8tBaUv0Q<8WO8^&Qb#y{Dp!3{-T{QyY}Vm&I^ z0M$}fB8s*;E4#|jAtg3b^Ufcx9!@ZV_GB5=&!#eQrkW%5TiX%B)# z5_W2{75un&ppol|sa%iqoxq0eUKo(QD2RyCcUT2f)W4W?aOFFY)rZ}A{*qcJj+3$bwOr`;ibS}?XP>@^S7HKFwd zUDHH$4SvzveyFYqtxt8$9inUcwJ-95c65N(Q+N_u{{zsb&PP_C`qvNDO`-LvZgLUb zMC$|Jk?_OFUU1lB_j_Sb(8x*rcYEhy=RsMqj}os;-JF!+&{{Rpcu!NoKiN;8q9VyLOB6^9e?ITAH?L(h&67DQ$g}j-SL;1Ln`hET`YKPQc z2pUS{NGuz*L+U3Jxkn=p{p-`18cSkov_9~IC=c}Z9J+swZ0A+o=Qs<_JaI074KA8& zQ2J}#x7oL?4@f&!hY^3K_Urz#EEHQBas_w&QjNbn3KtH z_<|m_o%IB^aQo)*A0iw)`GGy&$)1P~(p-;;u`6oGX=*wGKXP>O6z zgdq)Cg?R&;5}HfD8|l>;D%fvdW@89<6a`R zCEm9|*^#bI^sVNBC}_?a$=QHuKoWL?i})_#-B`7Q#coG>nO?0hiU&C2m7FF%Js&T~ z8k1d-NZz0|EAWmiYj&kmt1{{{r|@b+YD`F~m#`oIX{_AA^ZK{@@%xR9?#x%l2s`rw zxdOdA^K0Frg5Ht+)DC@3c9k%!Apav@3l_jeim&}Z8p+>^BKv1)S{4`ti7Phh01+|< zKmy?);+Y8q#a;})zRTxKaE;CXD|w~Y+jzZ~97NRj60eSdX@xGDyhY#gB@ht04WSzk zx1GGH`2tC}z)&c@rXcO2Cu&~a=9u6!T`R0=M2>9Md#`gNa zy3+3ht3|1Au68`?#m;JLF8WQwZVT)?mPK(hkY{=90vQWk_W(?ym=yf4U;^C)-%o>K zDU@o!fjXxF7I!1H2gU#;n?N=THrLS*g<$&z2#UVX=9kswX*^#%u%WKN(S-l_SS7{o zETyu#d0JO}f^o^X1{bw+l)bYx(wmmS#)vS^Em=i`$nG_*LRgVN}0X+mCN z1DLV??RXzE!6ED-;r(Snj>j0O|4Kzqz>n+y7PAl^mcBBr?@eY$`~27EVTFXe(2VrS z6r#^0M0EAzYtNp!l_jSE62?I8)Ahqp@{QqI^P2eN1s(kUfze-ZsYTx%Qsmh*8 zEvcll|B_D9={@OvU#h#Ice?=CF&VSW?69dcN=khARG;=10QAwZJ2nvp^R#HwA2A7NhHx5)vv=Kqeo^ z%F@6FX2&{2LcA4Y z)pSh1sOG@Cf2d_)V9{=o8LzXZGGvE%i}6S&xly8;dOSk{9e$LUqdXetWZ=D}!8$~) zj(fb>aWCcWI5Zt{eJa@k2;(ROHBQk-rlxzt3BOOY9@QI%Gk;#F9N*G4j!ZFGUPA|m+1-5%U zuy|iL&lygy-k$A?ig$r1CDfvQLArtc0&w6)XNR5xiXy~ocQ6}=18(aA5@Z3x)lMSz zgtMoDcB>FezBxKBN#i3Uc%8UGuBxf8Z?fgC-C0qw^V+u-s`6bmCbzfA+q;C4qp&S( zVxWH#i8cHC78O5GV3MuYF}%8B%XQansi@d`tqYPtyBcGuWY@foWzj*j3+6iM7AXGqT)ua3_B=lc3+LR= z<;KRc-n1ncw4|94EP7azi$%&RVt~Z=^C0k1!?jcbA2{&!+>8LKQ3Mao8C6xv;Hl?X z4qe}x9!gY(Le27(NXcS@Ug$C$P*q1Ztk#dV`s-zRZKd5Fwp~((#P}=MSo|ilsrc`d z{1SWxT)kuUz_f4Ww+c=BcdmD2oFq#sa-(Hb*Ikxt(XQlKLHn9=JI(`sU&b78E;Nut z8O`OBIYr~{Ho)w~o}@f*cizeVN8#V^c-MdYmXg4I{`1=(l;!a!K0g@&BY+Mf$8)A| z9R#?vl1xe?d4W8GjD&d}f>FBQ2r-GX2@khpEF93}PqDiE8kHUAxnx?gYcr`JbVt?I zZ4zu3eEl^L48=%-&x-SVE_IS1ih~B=i5(L=M(Q|0nCpAky+G^%nN)Q^GU$=BBf3a@ z9!rBfir5N#&XgyH=ga4E4Hv#KJrZiTjBx5~m7)-Y79&!`XoQ5v=T22gBg5Vjbh=7n zf?YFJkxhof(SkFK=1Osmd`g7e(mo*hYK|UKBWLkck)(huZY_&i@7L-dNq_fV=~Lp2 zrnzzRJPB<=f)vY%gIQx9SJ=L^!ke8=SDz+zB=9VKYN(|hG>+##NViBy zl2>>vt-^s-KBFkKz!kZWpp4FCm#VmvVr%Co%c-R#Iw}_`oRTvKsasS%LvQvZPz-wd z66lHYG6Dv08ZJEJIwsOV6946>LC6=s7AltwN{m45R&RDge$(j^G*a$Hz`@QV+M1uI z5!a1df2+j<`Q_d#@L;C_)s!^ilvO4S(o@pY;BX+JiRZA%d`!tuvsX-eI<2pkM5Y=- z22Wzc9#>M;sFUyRVLW3^DV2?eS3o8ueKc_gcwlp$!MzFcDy<L_NKZQllclWEe3`+w@;Et4r>3+f;AyrF zgahrfvM(C($09prxr(ylSR4J0T00KQb7cv~VDbA~)6TdlZ4a|6Lx?Pc^uGsp`88pm zG3O9Dwe>o$;_u_fF3tf$?Z}E#hdMGMkOA6hJ$_nc?;aBApTyu+D_(AH;66XQ@%Aaj z=Z9ZEHsLx}N{am&LQ3bm{oq=z^8uzN+{OH$c-;30KiIW-j4qsv)K7rL4(~4VZziLm zfz)B@cK}+v&9EpR5p8C*F#kYeUar!R8R@R!mL6dU=y}|O1M(^q5zy{ z?y|=9yjlw}#EiP)b{>?Ok%soqa>V(({p$LEn2{d zTmbjKFjsT9+5d4ZZYc?3DiD3(<7psRjj<56P{d5!h`j^}2K`8(iqa2_zr>|RM?R;2pmyc?1AMMe>Hcbw*i+@_rT+zc)p;+UgVwJLoGCHw|3d9k z8v*$VrJctzN(-pn`Bk(-Pmcdc*8V;Dz>!r8_5=i+5?eiUEeDeX85`a*YB>kE`eo7* zYEFg6R(B5<`jh5+E+5y_bTEvyb}wtPy5n6f?p|Km;j3Z!{XyqYgD;+&lw)&BJ9r}v z3wndY&4Y25!AlCJ0>MZ+bdg`SMrdc1l0gZQ87M7@kdV|{0vDXiSu0)4EHcV6gWTZW z*n1coZeH411Te6zN^;ra-gHMdM%E^ueb9;Z5x(px+B2q$fIpnEd>w@E`1`2+gWKKKaj0qi7Yos(R@ zPwjt3ySkUe^L(9kSZ7AoDnUnhUaYeo>&)&_+PTai;L!mdh8@M9C!f$)xG%mt1Yi7; zPpFtL;_R`P{JNVkGG@&2K)(2of{u>;7St%_3p#>67XGgj#4$LJ!`U)dH4Fnsyks*J zLNB}^*)}110;M92B^5TnF8&Qvg%5stKAjVxm z`2esdf6d{9R1G)@o<8Xjz&ZNDD}ZyQn6KDR6|mXsj?KQx?!hYwi?jLYNM^)tMm8r^&F4OW2Qp%_HzNqXLZ z#QnNYYvIw84?!pUCHK$L^5`e29rx?g#ldyK`Zhlfj$*O8w9hN~7dJFQoTB>Io?{;@Mk&ujbFJ}>&;$l>SD^Smtk z6#W0wDgJSy{}!!(9Ok=E;D`U&P=iYXX40SdiC+p0isl-x(VonQj-0v_e=e>` z`HB`0(yAP%6Omo1C{&7rbH_DeT=MN|BHbbP?kZ`;GvpMD<-OO(n?lvH~rXg0^?a=D35D=IzG4PYF~nG4-RBgAsG zKinL-zA>6Z=3I5mZuZ@@#za$m1X*;Iq`G&>yGzy?6VmCDf`Bsv%u$>*(jRfA`=8o_ zT@237=Pu;$9n}8v(`Z-aZ{uv7?A|ydF0s)W0Zz@IkIvdD{*T&qFQXltm$wVv$Yn{a zImyR&miq4yvLqfP`S_EX=ii01={!{0ZAAOKG|yM__E&Xxp#2l&=LP&af*+i;{Jg~B z*Ax8Uq~+)JTvkN`qyzRH>GV)t`gbe^s9c4U9@98K&fOC1S?8tg3?y!z0*a!?Xo)-nqSnUi za_5S!Rf}0V7AgL#5~3?(%;@ZIG1j*%b?gVywe0gWrc(Ff3XIuKdt7m%fTD-T&Yoi3 z#mGF9CAmy#Jf-H^b<4g%tmG?I{MR{#-MsLc0`dTtQV#?A_i<@oNe4vu$VuOa4xo-C zPAxw8&3c}-Toh^GsDS}IQO3?RBAYd;T}@q!5~ALRQWjU26un(afeQd9*5td;W!Dl9 zEWwcOR(xJ)Jg6OLRfwIJ7ipgNi$C%S5o^A1BZ7Cd-O@dIX^Ri6If0umgTe?>P+#YsN0~K z{ORIH3mRXE(K!*Md~C$gyoRw!Km0t3QEfaNBwuF&?VI(jXXY&Ew8($JbiLtAZx z1IV3%X#Cmu|oq$g_xRH~K?I~K#@l`jjf;ereyZNOoLtvGf! z!c>;a@^W5}WI5Z%k$`mdjTDWNk3EABy?#vQ!sY%)Gu98nnpth8qz3Nuln_qM* zIohk&lDzd^56{$2xXHoq=NP|nhD>-3?5xy%F#Ql1{E9`Mp;DB22S*12g@oQ>rl;YRa+9()5 zCuJaKFF0*omeYify@3)f8qz;wsg~lSWy)Wu!d0%>GJ6-`qFN|8MGVTW<5nnC1_8Ct zE89}qpxs;|g!Xi9@s^TM$hMN2R8zr44Q<0-<8gG-7cnMorKgFbqpi<38fOM)@U79% z!0F=XN_(XoM~88Tu+hVyfe3&g9q}A(H(z$B@fB@1`w^7eAJHhcAA)k5Jx+@3hgsEq zq{u$W_1Lf83lhe(z-Re=88W1#2RFgz?%9{f&mVNSS~`#DP;Ha0hOG0lXaii`Sc7W- z7o>Iw$z-sdgM$HR;Iw>^#{$4HTIxgDC=DX|#~Z1Xi>1RNO1ZFRnat6}_m{A-yggE? z=)%V(-zHushUc*sYhIEXcm-G0G0%DnFA2j(m{%+YWkygLM(MbCFTX-T^}=*ZIcYB}7lVfAP7^1&l&ss7wp1Jo z#CtO;8KH@ae7G}7I?1vf(4k5Od6{df-4l?P%l=3h7LyyvXT%z+s{c3Za3fqwa)rXN zXa-ILX-9}t*V=&I`s}OpKXI4!>m6f8oHN}^6l zZw!=(qX0*~lfC;H{pv*etK2zb84{n%7NJr8UYvVp3=G^g>Nk;V#=7N&8S*av#0>lp zK2g9%BD%$YRMQCaUr&EIFD$ohD7Ar=;=T(0sM{pkK;NWC_y*vPk-nl#ECXaXF9*W1^%buxf;bgW=6!Bo5(-u%>BnL?$tSQ~d=qv^g<@X;_BEX2D z{UivI-|u0~;A#f;Lpe*n90huLtn`jd8l@H-*6o!i;nIPB%a)11CnM~gc2Il{RLPj_lcNt~gB8z(Tpfwv1Ye?hOC=6nzO{c}6ny)rrWTxrHDM!u z^q^Av4eCp^DBNg?LB94a9B5_w22F99OS#hIN+eB+xKpV9!?I;Yjz(t$dCwvGLMk0_ zr4$0k6)5gGch$g(CZc=V?`3+oY3aS#$X=MOLJYcIH~QKGgr3>i;(cg)3vW9n+5qq6 z>_zrN)N!x^c&;x8a)w0wbu7u+&qEm(XSL*6rK)?RB67BT)jP<#^6I z6iWd?+Hu$;v>Dk&!5!kizS^fBukuul>wTp+>~ULV^{r;}t<{yb;$mBsf!-UcY=G_C zx<4uX&g-?@kM`F9hUW8U)U|k+a`_-VC*tREQyCO)Y{aGC9x_!{FPL6XU1a_N*U*Ah;)V+XOJm>IQJmtNOnnQ7v+)-=qU@S@ZG4=~~zw|xmC@`8j zhEiaKQ!i`FOfD)cT9gj7wDhzzOW&JJrzR$MOyruno0@!qCXDsUY>Isgy&C< zynVODhqq`zowS1%cffVf3_@|1--3SMAWh@B7iX_I6HOPtdv=<}IDV;a_9p4=7QGZI z?VUy`hP`tH`{_J&@4WUCw0%wD^=T+KN)+dJ(UHrstCR{gwC5VxpYgkg(U-5(*PY07 zW|3}2o4?eSLfb{urrWFbUtG_=IJ+G5fwPjS39G@*gr6848U~Y|)cqjL;-^Cmp_V|f z+2^0gc1^XIymggkmzn)M60Y`u@!PU%y)0ryKJ znlARt;|q6)-9Dc?tPrQQhAu{lE-LQY7|HD59B)x zA5|+wb4s6vvv3b9sKFYuv##2AJ5**zKZqGu|~l|43%87 zkS&ff#^a)jMGA*v{nzpEr)w$jiOUAJvBgki9I{9i4~x&U``A|9Efzg}E=B+~(>({- z)FR;5@;L!Nd-oY8fFIzDylr*|+X|UjB3eOTKV)BL8>A13Gf%nm52=STTh~$8=_43^GaPH zjDInIZXcdYz~)5H-7@<*izF@P?Kh)+A!Jl)e`NOkXkW_PKZACXU8(&iynQ)ue>?o0 zMkUH8(_egS_E9{)nzvts=SL;V`QKl>d-g+U-@w~HjP^dI{ad_!3$^RE!~Yw1sZ@A7(KG|GAcE?2;Vs8YZ<0~l$|RG$!<6Xm57-cwA)Ew4vGKDBjF z1LBUzbsHs&@fNlPxtedj*)7X&1?JwvG53`*`34?>v84zVSqXoTKryzcO$|Df$m)R4 zFJo-VNyc-FLGz9~hm^xQnL)jX(QO3t6u#u*FZAVv9#tC0d81-!Kx(A;5MtZnZ;pK% z0{&~i#Y@2HXgSdAv$QlHuzH(WtoThdvRCj@7^SC(QOG7WnLcq-VOPspN&pJ` zx$1X7w-fG<1%zQdS{uY}VZ03gT%tF&n}3EZPks>iKF>dkyEAv*f4&b3!ceD2C!GU)imN}M0Z4I$Kr)XFFFC$1HFYSnWH^I`{i~~E za%3WPVzR^aSA5JI3tTa|&vswu_%XdOuAQqm{YeXI*L zptYr#AKDFifktUJTZJOceBQ!8jqJJ|7@GSC@K{rD0ujVdRdERja0dA3<9~j^r65oz zQ^HO^35TWN@zzY^!6+{szqGG^WoPP!27|A&6oCp5o2vaTPf{<+$?qCXj-?m+{k36e z3b4;qd-kZ-&JA2_0xl|H&%!>f6sk0F^#>qB#NWZcqE3TwZ6K+c=)IM(MVPUhdnvwOvjmo2LyN0^Un-WtrsC;FOLz)3Z+)p%v- zHBPmy@YoH+#a1acRTy&1Hk_@eF{E5xU_TR)$`Fi^W0!PQhrpkm-0sLd;JBOuae0vG zGvo;8M)ekoR^EF^iKy*(o(=6C`W1hb4&8Ub3LZjBuf^Xl+qCJCC7U(@hkOr^ECi1~ z!YNKG%72Po_ytZ^g0~$eUm~)bN?;CdZJaw#wSh&pBU~j!LGBG5Hsg=f_;w0{f2gA% zOM|vZq|H-Yrv$a9c-;FkzM;uao9rQv)9Iwdt;NXTiKU^yT^`UbE}mp6#0JXeR23<& z2-(;--B<+zK1>8$rK$DK6{I|D&9qk#G5d76;yacURU&E5MAW1sm8f2sSXU)$YMON_H^vTEb`SPN|u%>u<=?HWEtD^o-~tp zUQ4?&^)|vRyRK*H(!)!a5Golwuzm<%i*Hc{2xu`#u!6IuqI(0XXv@Lc7E5vrLiD2j z=gPS0km*1g5Il=>9C;N6CH9Qdo|3HfJ;Ot}H5Xcz_T_u*xh{Kkb90r&x-d}N**dk) z>1<5wT2;3=r?03ovVRWRR(I=b>{e&CB^Zm@Y8sXfH8q4y>B+82r#0l08qI!}vA!nI z-#D^1qSsee63p;z1kc|9+|+=Z>&iSWqjE8Vh&-KC{`HP_9y9+JI>t&2YhY4D%j57P zJdC4HJLno88t2@xK+QmXRFDkj&h9SVSB~%wH__|0t*aEh`3KP<{(TS{bLy9$Pv{r& z5BgOE1-@N?NFbHV4xmz!hR}Iat89lg4@+n{Z^xGXVuQ4=YRTUXHE#OD8#ZlP_YuA= zUTvfPrP)_zyCoU8sODK}XoM_09rC0n<~*H7oI3^vqW#KlYl<)PePi3wFralW-5zv@ z!~V;=9D%EsZ7a0+&X-@w4{yAa=62`0kuL{bZeQ^i4H33wBPa#)ssMdXVO}|W07FJ` za|P*e_U&W_F8y+LkOz?v3*dATW_c2nkl1}vw!ZVcs)hg!5RP|7>({Lu;`6rMwynC} zZRTGDHs>a$hp8E6o1cEG4v^~W{I2(3M#%WdKbbA{XxV!8F@j4GT28@@OFqA;tqPr`TFxij@1CX)Rm;z{@S*>lZezb8NQ;ifUXW4x z_O<&2YWA}R?o3SA}~FQ z-EES-+B%l-R5t~?4%ov%XCmc}2WqV?K}&5Y?(7PiQwe)8bubfd+O;CzYP2^twGJ%l zan6iYM_L_jb8u{?b0TVvHC*pPx`*orN z<#!}5H)Cs%y1P3!sOL+CMH$rRx<$q^NB+ z8*99Nw{=f#0A=6tIUckoA}LQDlHyvHS%Q9h!n=7rdtl33_TIhbZU6J~Ny%fYHB}la z^sVmW#>V{C1?KMVC5;Y$YqKNZLP{E|Ps$LgXgONgHuz$gDqyRFmJ#zI`M`)+iAGzx4BW&elDnBB3cP=Uj6279a z^+NIP!mR}?6Y*Eg*^?(MaP|k+0EEbE&Y{xbb91zfnfU(r{mO0XIiwsvk);V2#$G?c zs>CVuD{=gQcDSxUp9c+&wq1!69U22pQ$mOE9MR^<^=doT&@sDE8kfdNlc0;i07hFW zjQwoV8<0DNr1kCr3BCp}K8g!@GM)gk@|nCf4(k~|!lF+0L}9q2e|n3tvNn?0*e^-7 z`krxSnXDg+z)-Y1TF-1?Sk;3yR!^Ee$nG1yInmJ_f8N`fZwjyiYmWx|E=|TN5;qlU zEFRWW!`PLPdgdJ7y@PNU!Ap-m58VK`P@`bIYAN41o+zS@Ok2NaN2g9dEWt!ufjcefIK~TH4k^j<%V+IiAja1G&qSY5KqLhiXd$> z4xl8CK?pFephPkth{c*nLJCJCo=lK?;JEw>U;Wh8oi#z{Wt(oWx~iDZVP19^H0Acf z;Y6yfv-{GBTI+7MID9DZ>5!ahB7@Yey*1&c7@OSYvR5NA5&DXI4q5cBaJa2T4#uRK z+L{{Xw6S%SEyZa+2J_TZ8*ap0zJ$44CQa&==pbNg=3vs~v3yZDDh*esrC~w;vgxyyFo>G{pgd6* zpD5`47Ims!EPWHZiKd@+23VS1S3k+#))uVWpl1IDC0aR> zF!==4-CLUE>9_EGw1tl@PgTgfX%=Gha)8UzR5$Pkuz{EDTH>ipR;3AI+PVOasngMH zU+iD#-=%5n-?R?*R%QQo;38Vm@Ij;y8yPqU+?om#aq?vnp)BQ`&JuY)9E`uMzd`2O zAF;LXkmZI)LUJJz8~msC^|8J4w)nvZ9uYw{w9N~*zq>w~!Nz~L=i=4#Hal`x0p3x- zo6-&IxQ30>2q9DuzYr}2q$S-Hgbm^Ct|tK>+g~jN$;RjE-*$=0JE->}^l;a`)$KCZ zJKpMcTIvh`@u_A3B`>)1vrn~vGYQR{K6%J)Gr25>Z8kHBN5Ctz%F>uV32k%_d3O>c z%M@2{yX-Dt@!07>1Tsn=>=96;RKlTs)aAR;&G-`2e*^(IBATRiSw|vJu8xC9# z&u4X#587QPibvEw^pHK2Cdb6%`p&dIIp6S${Bg7Ih2DLFV(U@oT5pI$WOGADf_qL9 zS0a@RvG(FE?Kq-aD<$tNH3?d_y4+G{wbkgUxV5Uv*6pgOxUJe%SAlaSRZ$hKV-6-? z46;Q{qgXBf5DQ6iRa#1$?KKq=^IKVaO^w;(_b|Pr$L?yWM!FuWn-TdIk1Ovzpy1;1 z$`{h25?IbjPFbymy1N~yu?}Gr>rK3% ze5%9MrV4@~klLM3@|z8)*UfW55LvVLjT{Th-Ez;-&H7qdUMKbSMWXU{IU4Eflh(;{ zt$y>-9(*4@HWG_$k4ABryyFgR_QJcn9xoibnKCwEPu+X-vBKkB?=EDb^>@63bix2l zJyV4|i?K*+23V-pt8&Q@dEAf+Te(+o@rMq6a48ZA=oerU56CTpi{&jN23A$=Fn2kE zW=nN;+2+f_-lbRUnOWp;WLyDR2xIpc1zvpx=74qkM3I1 zGGflIS+*_73kNt{Ra7@1+76TgR+S#1Ga?(no8nNf$C1v$g)p|tACusZxS74_(ND*N zUJs&|=dq%*KzJ%6;`ric5NHq@+`r)jdP64;ZMid_GFUT`!Knrz^s5otluNiz-W3ShUpP?CFFUUtw-weAi534?`fVlK zdz0Q6@wr2F`VYg1P*vxNWn+yC8vXSKMg}cnVa!nPM-#sA)KvjoG<{WH7jpX|Mm@)E zEpV%`!|AGU7EA-T(1upf34jUVR>xVcdI}3g3jgGkrVGV`B@%qbjH}YcGD5O$k{3fn zp$hbLkOCO|g<_e}ib`qpk5hqj3Az*M2go@E%#|>T9l(PMh_9FACzh2h?E{gneB7Ot zcf-fkHoY)Rx5!A0?3R}l_9rSW!F2o)(xpF~NIZ;v*IudQ+M9(IANne8&m%WSxFA8A z;Fm-(2+~WDASC8Gw>@KhN*a<$1to!sVj`omK(pWk^E`s73d_u05sziJ_P^`kOYe~`xm@y1uP+6C5-eMQI38bTOd=q*YPEx0CpV6 z;tXrWjd-anhnI|P?{Zzd^=Yobdum&zuP?KWjm09LFBBRF24gd1SfIf5pLVsjbV1Le z$W1t(y2#fS=e@{QixIF6e8;3+WaYr-Dvgd4)QY<$(FM}a2mqu5{r&R##^As}JiH#8 z;cD3zS-2zZyky&5;dm_O@;T(IeA#S&e>RIF9?K7V2G4craHya;`r$}X>2{@yK@J_z zz_PW{JZbJa@X5J_rQ^Q)dbug7Ot%_MLibeWQ(kFdLd!N z?z>1na{G!_lo10R{0hAGV(2guv?IBSA_0w0=@ynzQ#JC{0SI|WxQUq4k{5p;M z8eTR6=kXFJh$C&CBI40p#nvRjUxDW_g_sqb=c%pW2-J$DF96WrzIoY*(-B`Wjs?n9x z*Gx}K$K}P9_U2gn+MR`Kj$c?gwRfu3Q*8j<{+Oy`O!f6btwLwas@Yko0y_RWoNT0r zgy735zC(hZ2&%Sh+yK`}g#Q62v_H6UmeWc6PnJ|oDrsx#Z)qA|@W7U_!NIWyeC?C1 z-SJ>Uf7V}N(zBJltj=0rU&&6^yCl~7Sm9)$(veLra@BQak+QXh0S2bqftNo42KWt8 z4*o2pZ*FSj<8oaZ`%?Orf!><+qXPq@Y^C!h*~d<%vgqwf5-VsQ#b-Y#<$3=M1R1t%p^~YdF^#4PPhZH$U@n&!s+(-Hu~KbKSNc^g^^gm zed0u&-8(iRo0b)%fFqr@g`|NgvkPw`S01E{f@zz5D6J1!?cs33S;Nwz?4=`g5QjuM z?J`dh{wQ|p1s*$vn|mG_G$kgAE)Q84nTZ9)W4uy1)(2k;`ZPEd6+4*@bBmOysO6n4 z149+z&Y^+CU~{E4l_m4qdWA6>6ap1m={-@Y+vRxjdjnFwW`jH+ zNalMAuaN894p2d$2#DILT*!zb!;zjRDf`SepskAyDTk@^Syf0 zb=f6ppWEhhh&?ut&0>R{EOa#I2K+G}Tz>0+Lc0v~$9=G9KjDT>S^yGnm5l^J))1%Q z!t!TqFD!IkYLR>NeS=%&VcVW1du+q<*18ku9~w$B5fDuJ*Z=nkA0l{Rn#>s z`%DO?h*`7u@P%7Pr`8YgvTh)UCG&v`UI|Vs`+mdw<~dWif5s;Ct)KlN#=cBC0b5oF z_o1URoHz!Ra>83;aa_I2qtgab1sDnxI;Hn?N=Sxu@kStB#+ma8{<_7v zNrqj3_(m5aI!;L`sX#{r;TOldltllyFRJ(jPs9PF&<~JJ2P-}k?b3A{=$4Wk-I1Pk z=YV6b8y)Cw2=!yliFIMGG=~OOtR~L1+U*QjR;99xHVd2mBgQIVEch#;>L^_u5Gy9c zRrJeoEijBT`!t^^#!M>6MW^hxTHT}VlP(9uFk3(#kpniobGRnkN9n6hWNR7>)(gCHSDZHvS4N6rzy_>jvG2@4vB&wa z{7gO*U|4HK9|Zga$PYSCt4%AMfliF*P419W)c&{~3dA!#GatX3U?e3V_5- z7ogw4r6`!6^l_>Rh0rbG*(`QGmlHDY&Ye5cIMC*OfF!7Oa(}YFsov5OX=JH@n~jX? zhcX#?;ix+RX=-qB=a9D=$5r8Eol9@D)>N>&;jGJ8THkrHQ=d+Iy_o+hz(q2hho~JA z9hK5h=S}3Oh3ZcW*UC|Iv~3`1SI37mXLIKo5*W{5iF7n1dIMHH9}_|x9~1Q626>?k zJ}FTDiMs@@5|9)iC6IcOq*jOsikQH~MG)zhI4TLNe%b`<8E7I6O2?{{z43xy$blJj$NC>T(AI+SVih5(tVc zh`SYC9{L#WDY5as_Gs&1qw@h7*U5EbgMlfFd`xC5GIB08NoteP+TQ+zptVAwciieo zvz0_};Av6JJ0bE+c2ge6NkT`d9r-+Ud;k0uwH<3@oSk4r_+2g|Z&)llxKBQt%#l!M zVrIJIag!=Oa>E>a5V!{qaHkqOh@y$1xamp+@!vonV#%lFhL)^k3AQjle8pK6?28UI zdKmVODweG`nd&nZVRAhGy+)JC_+GYP8B0$g3E2Lb|CvN zeGY94!#Qlna5$ii)5^fbmDqG}Iqn;$(z#@F2K~I++>zK5`l0?WgicV+xq3(F#NMTc zd%U-tx3;$?=ykJ9BnFeQcXMG8NIm3i9c&q&Ozt}o3WafZ!FsBGw57VlK!A_pZ(u>D z?_pb|{EM{SI$G~PD0D#iRe#B!TS_>a{n>0$x|ux&9W(skNu@=0jH-B&mRnqr#7@Nt zNG=wb*$~Uomzhez$``OfDWA*6*{3er%#O3#*7}1o>o)AaF2AT#-&`fx>fJT)Rv%?e zav*971l<WXEa;uegYQ7avrC2QeSDTscIt_0Vw68 zSwOmb1x8L|97Jp;H+Hn7Kukw8N%-6kfP^>YD8;E$0`;zoHv!a0^bkS%NKYkh0_*85 z9k4PRcx0f@ut5%oRA@c9Re-jObyTca(NYcTTSo=SSB#}tS5Lk@&$3>JUImx-6yRNm zJ%xK;=8bI|$j3MY=5DpqZNvEbv+keeJNQ1cN%G-g{b&`lgqm5P%jH*hAMA#wY@zi_ z*a^O)@a3hn;UJ5P4afHs=VRDY35v=g9T*Urr_6Cct13Wf?y2Mfn3=!Fq=vC$vSr!% z5Yr%_$>-x+ffQ?OMlYE=@mft zNT22LZ8RuH65$9(0^v7`Q-H4aQR{qr{t*Z%NP~#}@J4v4WuOv%SMfOYE6!;v*X1d? z0?tlo$%0(9$A%R}9`S-d&^_m5f!{D0?l!szhl^|js$G<>5gNGE+r=qx2~WB?a4Ft% z1Q+-<*D;dxpuojv3Qh-E4f;SV3N|HOU4_h;R^WuDqdCd7j=}UT*=CnLOCT?`x|>#z z_7R%M6)5Y31DEN-vxgv`bqflBQ#P$5G(Tl+|95stD7AxV3X<4G0wk z1^ZLi%k2w79bFNJzRue;+Sb~Yj@tuKtG~e?h`%iyt@j#DM&mI3VPCPc!eIJius`{` zVoy1IjUIR1wrKF4NKjwnw#|4`9;e^ts%GqHivHXd26t802)ycV*SErl0`?^Gp;(9x zal7KK6PPJ*7=Kgl6Lb%z2o7YIQ(Pzt-LL0DK@Jx%bijckeK`zHXLh0p3Req&IQwhd zQ%S$HhSL=w}PfJHWNLCP#Heh0x=gK%hNTfmX(+IwlpJ~_hasS13UZ1Mj${ZrI?HuTPKP&z$} zG#!a)|!C8AnL$zfJ;J>-W_;U?IdNgm>kvNzw1_nfv!?@Tr< z>?5&vx$f~4lNIESdA0}%I_Ro&esn-PR%Pc|*cg8^vNe0?*xu^&;u@#^+L9&Oq35 z%{AtT-)z8`&e?-x;~~2w?-z<98nm>hF{C)6h2MF1Mq&}<>Zq1JI5Il=3uHJM`Oxs_ zdZoT4McKOozrB^xb=>;fLaC@Fjf1(7-#GTL$f?TNLm!*hy_g&U!C_D!Ifh{h7^|Qe zYm)*-VRW>>LBW{q_+K+D2d20f#P~d`fnZN>V*ITbf9q++-#R+Fm5;yetmD@} zHtNUP=2Y2;OU-35Im7R{m_FTIOL9>a^iAA2Y^aiqA=}d7+>RZYp?xD^|NHOC53Q52 z$PL45hl9R5@AHQY<`dz_qEv_e{PXo4sg&FI`A78aU0p%j-o4gPSC`An9{XpX`{G!Q zgJ=!%P#5H(jLK7qNuYwgdW}Ufc(iC~jl%|bcqEj>(u>1Ur)|+hVr2ht?#9c~LmL_c zzP>*1@cQBGRk!6F5RRNdi?6b;&m2husLRf+Y)b`g0ZUhx)!UV7tK563zGE^R11v&r z%qh7I%1(;{0HS{$J;1>cpJ0pOVq)=w6j6KUOF3{uBbqeYy>@IpwX-sH1HXE`R-!|Om zj|{IK9r%3T@S2fuU_+&AeJt!rNuAGnU5MvRb^V+@+tncf`OjYL^k731?Ipm2$Y&C2 zc(HfMCraf)z;OvSnjlo|Fz@{o+g3h;s_56V<7?vduWP)ZZRRni%x zd)%pxlo-DYyo&6qIJ1bp=JP6yekL0$dlvjzu&z@KtlU39;bNy)S^2zSIh1gk5REmG zV8G`oCCg@3%#=Lr$zPN+V?sAc7@Xy?mRQ)_z98A#8y5u$Z-JXKfkKn!C?|Cn zY?;Ee3{nubWh{JJa`hV4jP%7at9Jz={Z*?+`qu>)^mSLSAC3h25hv4?4E8OX_PJ9` z-=3Oo@tdcy_E=-@v$XD-`QvQBWR;`4bX5jQ$ZJq=dD7U^9^{$~Y&PZ>^Iy;xIpg%D zN-_VQ=F`ld9X)maxYtUY1ON(1P-v}ary~;*mm|1T#S8=qc0}h{rq3=o>-=BuY<>gZ z&tTn3`K6#A9<^TKn z_^9!I`XskAU4$DPJURHCj{6Tw-2P-B`xC6D)UNosm*cDFKF@8%Rb(sX&tt#BlS|r- zbG?UbDkLVu`cHflLO;I}WCW8XFTW&RP8DkaHx7XHE4~f(oWUU*j+$`oV4o;lVb>$O zc-rk__S5xt3_m2xBRAz^k!5nocZ(Z8yENc&1Qsl)YR)nJ z(@)c6^rGG+Y$KVEp7@1N#ZiK>8otpRDmn8J!^0cIk7sK{dhT z5{>xEz#g62{a0gS*tfRc;cMwn+j5;Ix6_>ITec;=XwYqK^*bHYumLp>w3+2}_Mf^2 z6Oq<#WGprHAxLhJV2f1X5^8WqjC6k1T@)GXftXv{cFJ9lENb>l+ ziHW$M{aAzLsPMi0zeFOLM`3@X*Z$+0wfmc8k;mpuA2cZJLC=ByCaE6rf6gX!Dq+6q zh+@5Fo^K|)nEmml;4ujB0Az{ z(Xub*V4TfU=|&C)_Px+mJ_o$?LV9}hn88(RK4Y5~Q(ZEDDEUHuq}>W<&KDv*H*XK< zh29(Pg&wP!|aQH(!g>WRIq=U2gm=DMav@ef3+;CP0}vT#1< zoM+9ops;Yh*E86=?eMEUPxl$z&4h2KUdj3J9N;2}{*6I)Dt|g7jF)PN^6Ze#XQ3sS z`Td^zg3p0jp99PpvcJADY#QhWT`B-Gw;aqF!PlR@Ow8G5afzbCn2g=BiXYiZI-*DM6D?XpUH3sX{6?mgjS;ha*%;k-RXq~z?5;M;_*dk;KG*Gl{ z1x4Lx)EBudi+HSviV^=F;eW2P)*G!>W4$=P-;Lk@GylCExg?s&Zsk&XNpg{n>&Cia zktO3kG$Y8S2|-V^#uTA1mwnb_vw5r`-m%sCa=ldoEb;u{loyEWJ*J z$p}dWkuBsThaq7qg4XT~kbLyon)>=0dUTogQv6 zF<}EH^Ocw?CViUe|6513(O7*Qb)TY9zA|s;j70* z=+dh7msU#BJ&otC!8-ApVpt*NNmX=FSKvCrv`zh?x(b-*)0ijyp6CSfrAjHP)&+0Q!Nt(;dw9RlTh3jlUtLF&z77~FtpMhW=Ns5t0SBDPkzR-vbUv<` zdmdWnq7nW9I6TzYKfzsMBlZqt9Jiz@h=3Iw4b3~KdOf11X^_F`4`~C^S z6ukgH{p>O+z#}{(bgEb|Gx@b%-?OR3M1JQAH7;3Pr%9J!kVL zlYUzukqDIDnUmegL{EYZCH;Y9vInO-#{Q#pAM28S4BDo3;2e-u`K(r-Rr)brtUf%0 zbCu(jeAr;FpvZ~=%34E1BfhN^{b6E3*agrDigUp?B+(x?JIv3@M#BDi(uSpG`uvNA zplD$07n|8feIE1A!x8;btjWjL<(3*8Kg#^Yd9(lEjtlD=Uf+4^5ouYz*h(FeOsz+3z{ zwG&Qwr5(O5MzVAuW>7I7(E%fHL|qNQ*;Ip@twAIu3n2tpAc=YgZmi+gsN2`l578fb zVPE7}63z2)bO&I*1USOdDBw~A3x;Qxpuc6*1^a1nn*W_b3mz-s74ubNUbXXmWhp^b zq!(KF`Livre=9oU>#xjBpNt#e&d5&~o>mMQnN>1463ll;_zXSP@Hu%aO`X-&7M>vVgG3R2TB3bxNbk;Iyg>$s zJ}$>oe%g!`E?Fj!4RfX@;ueM2D*(b!nDX*eZ80OcdF^$#b_7OJxoCGjz-h7gZ4Lug zqs|fh`aVa4$x(Y-@!Qo)I|5X<^XyUEpjJB}!6;}f>prC*`3cYaLDwmo6S-hro& zWC3nWwA0BSn_DtDEoJlqSy3wton>lzND8KE8mcSm^((9|j|cO)XmQwKvn8nC*H~vq zL}Dwfbq}%2EDZ}&)x~c!3-h!&ZG1Ht>Wv5<3R+UuLZQ+eJh|ea7ncEOLK_v{=E!vB zGi+zivIg@PX@sqeD9C%f)*wl=eXZ1Z!9kE^q&pWj6shLe*Et1Uj#T&+d>=E~dOgMo_IRC!&YFVnlH{Gpz(k8pvpW_vg;z*jUBh*MMzR)cR@i2yks)YCzOfcnZ+ zG|(p9h0D%H*d3}z3l3`~w($3hhl*7WzcDK3`w6z%@w8UvE1!x-pOFzrtffM4|>ybdZqmpV%G-ksdW?O_rKi+l~CxzxA8M>X(c6%(c~k{_nj}9Gz_S z-BEnJ2>q%&0HbL8bvrqkXQGYeju-6|on3sfa;{~HuPEIorwG6A->~~i{P|%&Ud{uZ zr?Iv+;MGl(q;f0p3{E)2tKh0zl~kv|k8ZXs;5HwtmlQZZ8Z#d@-K-whBsyJKZ@L5B zzoHKe)EqVaxOgKWo1Kr>GVRjdQu#f?8BRI>*^xQyUaX12?oU1JMee3v^a%-ryBi`q zBb{wga!DnIWzE=Cu@fH+dO8o;SCc{K?nKSHiJIcKH#>uF$HImHL?ma>VU9=Jy#{t! z8hJ9Gnpq2R-02Dwf60Qz2u;S~uPpvT3Uh@&qYATpf2cfj?tBP`B`Peh-@FO#Uoun` zA?6eb8yR0QI`~Nd)Xw(TRYOaFSymtUHU3~&Iu#@j=FRJG&y{kb$}!3bF~h3y3nBA- zq4Z8G{b&Ut)Brhizmy(_)=Ts&5#!+fZowPnfYLE21r1A7KXR6Le1-n6pHVu>?pbJ1am15Y=wJ_(=jjJf=j`t+SQ ztu&bN^e^ZBYL@?&>2~YtAQyav$3zf_oY_DF3Km1LlpXp`mgh;e+3zp7?}GYzbGOB` z{P6NfI-9Dk9;gjB5_ z9sSkzA-qlGJG_hh?q%KirT$nOdw-}c9P3G}+_Y(B+eBlJ-LSgeW@uVs4liC&Z|hFn zgM8mj{(MhQKG5#NW+EEfQhb%Y!VW-B!=ntqN-5Vk60jO*ONk8zJ|;kJq~dEFwsiNF z_MzeSw!t=guV-aX?@9^RxxmE_Ud*=m(B_R3Q!693U?O+(`YYRdjT>JLwG3|=Of@BL?rli+ zcPDb4?7+GS`{aRL<3s6smTL^$&=oqe)ff2U(D?Si*qT7#{kis$rHO8`@iN^m;OHg5 z)J*p=kpzZsKROzvd`6)at8hjRNkH8#md3@|sXgaCcz(6XQMYJ#`9+J5v@T{fwWfvs z?|8b>k*TTT_t$ROyhidT+^)Lp!hr<~23XVTrljNuTJ3e<*BG~;j7w4q7Y9*Cs)P@W zEXb6*fYTK*$KMeYbjJV`Nr)+*7Bs)>EneKeIT@V?znB?4b^RB4X z<{TOyAL?ErF&2GK|A|8dd#$$)!O_`xyhW{Z7lw$s zSAn-51<67wKp&J#Kr-na-LP!m zK1>z;?gXv;kzK+o#Zl24FfDMI)6t7aNFd0CeZuLfYfdJTu|*e7&m1!t?R6_WZPQ1l zrgG0SdS$Pm{7&LxQ@kJ?udC|R$ zIGQ2ZP0WouX?A0a^5TM&Z5M1#Y!p9dXj5}c@pC?^%X4l( zc>+1J6g(Z2C(%L>PZcY0sl(;UBpSyj`Wu@UAjNQF#^rJ>_eHA*lY!-*^)G0M#`80o z$#dJ<&YjGx>W)Vn7Wj`i?bWy6>BME2XrO?(z9ij7J_HIc0Cl^|XBlVO;#5uM!3ZF{ z8C>k8V;?+bvRJbQQ*G7C-k#}^&Y|_*cys??SLI;ss?4Icg`b<*x@E@G9`MHMoOX|~ zcR_zo`_NF#ANJR|mdKSyCo>BgGZ=pp_Q0#`HXiqjmBpqe0#w)}#}jp%C}4c$1t`q*}7h11o$av~S53I`KyVW+Rs+UN0l1AIM-(8#`>yp~XdhBm%H zKdfL>#~GYRFm0YWrV{y!Y4=u90x6;tDd6#YGbm%c+zIbvMk|638C!3nf6-KbVrc7R zf0H!O(7a%B{Il`Nj>dSnu`h6>-e#(L=bcXPkw9NVIPP7(dbH4k{DS?h;qa0Rtj0#H zBwsfnpWF%yin4A9UE_?J#1BvnG_2xKk=XAbhYUg7SQbe}Qt@!Ox;~PKd}ig=Z7bL_ z#g^{=zI?3H-RzI2hU%+p1A$@mUjg3m7sOOiz3@0_1D@!>fFyFL5s%=O3mg$imHe?T zhSK)K*^X2!w3q|fK0uO$i?9?4^lZCoTTftOonv6#fMf0CSG_i?&u6uHC7;dW@y5J2 zBDY~%>p)w~nq0hlanhO|+`l20+pvG|$UEZ!eD}Y}dk^rqimQM4&dj~)ZMEuk)n)Z< zS`}NinwwnYZfr1?E!mbWOU9N=H=R&}sUeh*00|@zY6y^!ggAjXBtQxY2@nXOhZ=ee zUj2V(X727vE^*$!zwdjW=M#AE+_`5?n=@z5%$=D#t)?`)`09^Tq3koAIeEp|*?Sfa zx~%H9wra~YxUhcl{My?2a)A#+T=FIS+sU-H%1Ir**W+)40jd7PHnv7sJ7${j5XL4Y zh(_hA?nHMpGQPYkkeOP>yrq>nu9}8!wr%;c(b~GotjzRQff3*7D#mMtHRG24@RTc`Hi=3VRYto7DZ=S^p2#k2FL z4i_E$W0nv7;8&l>>N|PaKowy-BGb%barZ8njZ}W_* zsu|6__4T!tWi^E*m2IpR{$x&I(^x|wxoYlkx8Q7=#kx1nXj*|p$L#fGxKXSU73YuIsVSU=QilqZCTOJSw18C8`l zr=Re>F>P@t_2kVgm^PH3m(LcmGIvI~cYe*h;CdOH+Ezx<=^t|o7thRZEtdM>q1dll zu6u;ch{+>5m{5yQJ*(1??C_Poq-C9*mRFrwyR>zBRavWNHruwa_ksnUcs4T&BbDsTQMEPoGZ*JT|gROWFKD)4b_@oq>fLJInF{ z^Luwp*(WWR@%-N zu$@!n87k`2uk>vxXa`0M4@QLOjfe`tpp<&tNRN*IsDSap*4B%Uw@%*G(czxjIyAp~ zh;`RHdNW)(Y1IXlZCQD7Re^`QiUM<&oG0!$dHu~xD(76ba&-HWX~Nw-y)Yv!&x(z5 zy9x`Fk_C^M5gGF&$fz^i>>Xa&v=v9}!_iwu#}=)upFSON&cdeV1?YB@v;2sMZK7|$;ZlD1<*+ntQNeMAl zX-;+-Yb(yrDXocSDYrd8$2YaOw8b~aU0-_F3icE}y&9k3h|4K0%39cyQBj)Hb$0sx zVX@Uax29&Uw?2?{L+}p8M-Tj4BHxI$?sHJj)J0Hdk_mPW7*ZqP3ZON@O0j!abtf27 zhN{EwqdI^PR5@(kjC$XkIh$Iu9PW(NxWeYd!nV{zPxZ8ltZAh^V^u5br=GrK`FU5Q z)cgCUHXk#TlrSwTr>rce=z8y5uWwG{{i#{8^&Q#yUGoxC+p2GEUz@yp)ll7{bt#Lz zi#gzD-*qqlKU%HNWGWj>nb3B{!;G)s@@iG)EnjNpar6y2eA!5{8e(z|o| z_3kw>jzwcsQ#~j3{AB5x6$|mU>!CdrW2|p6BGF-trg44{^ivKWwghi!<(L50rDVrf zoMn>UDHuAsWQy}q<1zVWK4#MsT7i#_?b zcE)5TIaen;1aGsV{3R7lrFpkBCAC-h=jOZWYRhwT%3~L=zs;4OmXsbB9TmuIsH$(D z;=1x=Gd?z9y1Qgb=J^kym!tNk;+HF_Zw^qjsUy4ck~DO_^bPe;OM|>LHjQG(3X*$lwRd6v*NNdl3OaP^NVT&uF7U_S9W(#RU^HB zD8snf%WuRUr-g_n3PN9ap#i*7GxgIHe9`0+&d|V6fw+`?HW;PZqDFLvFbq_uJ(7+3 zrtow&I-A-{iyTc^uDtZNPM(sJQ&N|d>WPkb#`CJyLRTPf-LiqpV^Um6S!HHUO*fqO z84Cg{9SOkFHgm8cV-6itoSB`L*T%9_(o%U^T6S4%YU0$Tg~in~3M;&`hVS2GMNKmk zDp#*;ZY-N|YC-^ za`9AGCX2_^Ff&F#|1DldaXnKl2ZUu^QG_5pQYcg*(L<)F42~8uuy46tnWY8!&BwKO zZM$w;bxlP^hI8(wp6NB-t`cv+73iGfF09XCzso4e%5@zxZ9%PfY59UR$1QB=EX4^a z6RIc@1n=HT(v>fo0<@EEZ^?NJTBA-`c+f;A?_eumYZaCun6S`~a&e!Bqq z4SJJk|kc?_U{$FB;F~KGBW2=AAf; z^it$sDCssLf4)4Id*^mKm;1YlPsx9|;{QG3ze4BJxjzGnfAqw}|Ev)I<(mJCB>yM@ zxr)XY^8v;G_&bt+1s^?r0nVF*yx{*L$-fPMih@V}JCc7Je~E-IHcq)TW&%Iy5+3>Q z6aRpgbfd=|3O~;PewQjC`4O6*4B*F9n9V;Lf4TS<_~{(~h&3bs8O=Y=Ef?!lzVSWe z|4GRkg5PN*9uyw=k?>)Dj3mhq=29d-4E@_4)NjW4m-|zIf40IuzC`n10QlL+x8XZA ze8Kqs{Y??@(2wu~PYy<_=i^I;zt#Aiz;iS0#s2Ig2kgHYTWdrQ( zSmP`_nWOQ1$b0#dw7z_JHFEL(Mdy2w-%9x}{~Gyob-p^kOWHpt{8(?I)WIxM+|8fP7{2Z~1^2fh49!LJYN)O0irQ2_mY`<7VM)rgJR`8lh9^Hm`oY^+nXllBBpy{?B@#d0zDs%6LSBX6 zkokxYkY6a_rTyFt`w@TB_Rxm>9R4QR&tG>U|3#f&1pgrYA?TJ!x;Fj`6n^9zGGE~@ zLiV+2MGM zK^7eezmEKT{xgyAkZXj`CU|Yn8lLiHd$;pJf&4u9>5b?w+*%LRj#|0Izn}0g()@f5 zJiWSpEvaXefS#!x(YJJn&;2qVC1gKxfr5wLEU7me9(pEt_}l$5--dT+{27KR^=89E z&k{fRE0^L3bmiVL6Mh5s+{#uU|7(&Txfe>Dsq&4z$bV4gOL*}!KmvWH5t2eey> zU&5>Ux|XZ~&=K&mzH+I)BH_0v_$vtBukkd7>o-Xof ze*Z4>Q3Cl2Ube3*(7sG&;|Kf@Z-E|8FfJd5{!OmoFHrcApCaqs#!vPD{IG|=%Y21j z?a7w@d@1n{z9k;DpIOSgfcRgo^V`tx({~=o{$pjoj}qW>&Ui0i8dal#;rTiXBfNmn>b?}7}eq73H z!(X7_Lw?MLKS{wu|Kt0qU$EgF8h-}zsomM|OykcO56bvK!N)=W3y2@IFGK1d(Y@qn z3F`L&${&Ai|1{uPDf8pTyA{0bzo=ep_?Zfx7XZFB0$ynN4B)3Y%7!;Je8%{z`<)T+ zAMyp_RnXmO_*f0<5gL~Ke1!gMvCOYAu0y`+m*Ws;E)d^R{4?NTsy|H}C-_vAZ=8$# zTEe6D2MG>8TwrVmRhmU3{P^Q5(f?BZOUOS)<>O54wK|^9h2ALMMhSvP{z}ph>UUh` zqXh7O3Owtie?WXF{R6(56vBTW`mfH9$Um&o_`~qzXDHsD48Jq+0C=)D)EBicKu6$T zC-bR&O~4;vp9jFB-A#apT4PWAnk`ksk@K@PykJ-nmM;Y7ZUc)%+i;^AUx@4|JhkU~k4Hpp3N^;0N9b^LY0H zK7;)mxyRY?kSlqDyc!<)(W-v6yeI)Ya}aMx_)vSa;V)3|$XD@?f=>jziW}{G6$cu& ze?a?+^bcSQcKAH_%ri{zsq0bi84Ty!@&en$IGnW;pOJQpc1L!r$D!~ucE5k0^fPiC z8ie=bTgBzj+m!Jqj33$X!A0P+l?TDkl+P=W3uuL(;LQNF$MOB_3_Lkk>WBK-R&l#* zr^5IE`L|Aj?;L;SpzxjJZ-wE5RF7NDb0j|>usd!1!G#k4$1?v@mOl}{p!|aI4_Lnq zFZ=ygF(C6lHU1QaNBwS9b^>`}CnjdpP`_2AA7v+>!%oc3NO`H>fxNG?df@qGguE+c z{#(XP$bT#>ulOhOI}Hc(OoE>-^D~XF*zm#mQr>ha*T<~Qh7a~idA}<2KQkV*;k6%o zhvdR+$FVlNmiJhdZ~Qh4FXi{G%A5zX>)ZEv4Tdz1KsYoPyf zX;)J~m+UP9zqYr}rM*db%wrI|v^VVVCwtTI(0{r1OG4V4@}EVpw{m4~pOU?4KN&=C z!Y!>&L+Z1JL(rjKF)B^`7~dI zHF4Vh>06!(p6VAbg^4bm`7nVW2_HNb^42MSa1xmM0x028alZ5q7`IY=U8C^>Ue{Nm z)c+4PKftf+>!X9hQ-4bJCE*o6s=i?Vsc9>IHU4yWkc6;CjL@Eb`BBbzyj^|6@b};0y3A`mfsXQr^}4 zJir%x`N;(M#rzn4ZP*?{{P%+Yn6N#N1gPIo+$ime?8lDlblixzC?al@^|fGv{Yd!W z5{lE5{p=(AS*i6yaovq#p48{N##P{Si=<0&-DUh&q|aB9kvlX2zEk?uT;t+N@DGwc z$H&9)!Q&+U^QAsb<8B*1I8EYzkM#K}KJ_*UztksU9@1wdKi7~xgHaRU7xNqW!x8$l z;g^8_3d&ddAqv!9(B3&}-1>6{$BlmU^9KClZ$gz(7 zAL3|5p5!V9C{AL80wJdj4?eyr>HM1=%O2yaB%RR4Np^a-a3(mCFXgMAPB!BmS+7gj zuLE{K!D2---Z-Cd2E0PUJ%8?{32BL` z;{4#pfQyMo-D%q35cXaRT#ku}_l>aj}GB`~vp2lmWa1LM0D_<9t=tMgjS>Jdp1@Qh#g4Z)N+iUpEtX zbSOA&*wwFI^x2wTwy*P;{#CBxV%I9v*)i;a{@~T9@Ga>r=_jH(Pn>`UGyQ}oT^{pI z``hW%PO20-oKwBhiNt$MXNgN9?^rc~!fu>8e_}_+i8v>F_XIlE7}v3%v&Yo_VtTzu zI9c`d6)R6$v64NuV#YK|feE}{${zn0pKgr74%I$?O`jGvdJmD_8rlhb=Rs0>cd*(c ztbPr-E12T{%~;(}t2XN~+U%m4T4rf8E2CHB>1k#1b*LnqAzx(CJ5=YvGDBw|_!`5I z>`%cf6Qbi&^VwFq1kZMNcGu%2*X)Mk;;hV)%89qnOuS_$?=;gA)8ocWXJYyrU2ZH7 zUE9@CjG~#@!RIF4A3O1ubcy!Edf4%Ng!Jw_p#AU>-GB%d^J0K~Ny1W_2|-tcQf#$u zc&F-X2*D>|M8a^g9zI>;$&bK<#b0&{E(@htyvFs3z6M`A6!H)evEC`;u-KSf3wTh4AG&l$*sfzb!`u!cguYQ6hwDQSuE zj?X{GmVIZ+bC)J(r6xZ7P-1FAJfN`_DB4KF_)eBBtB@|m4!cVg{_HbZ_^%TS zo5u0j>%Ncg0^K~UcELOcld7o@YPA#0&fcL%W*^T8?I9kr&-1OC^jz#Dti##)vmLZ! z!hzlKzdLv~PYgcm&dFp`vSLeW>x!fIKcQL#&pUWNM1`KxjSakueN3l`#7bsp*^yjr zjJC`RgNt%foCV1l?Cxv_uI#J9w;Zmt6fA(qbQGqgP=~>c&vC}Uy|4?+GL>O*P-xW% z-d{IjwK`p-;-j!1N3M6n@&tTr;MnB!jNF23|NQt=N5Xs`w!>tkCnq}H?zDujeJ3p$ zvybVSxeNTaR+MF=m$+{AFUZZr7I2o5c4Jx!w^l{*WYE41v@y$1v}vshUg*<%bSRbN zP|NL5g?7qbpu*N3NYmzqy>l3f>0!noI_%PfPg}!xAvP+$!AKDpx4kU;&@YfbsPg+| z{&=B;cVo;zbC;dQN&NVby>jDr<2`l{KVAAp183%_)c{Z(z72|{l(ZC}IO?4MWUBg8%vH{j-{9_0vyBXPw_)M?YYS8w4w({eet> zG#e``E2~*pUG2Q(3dcge16$Z!JcSn)7Z;@jK79SGvw#Zvy~=o--OabrJ9YZ5f;!+I z$h3;_ogS=e#aaL?0f5r6Dvj+dtQ;$I1m@0M(li%sB(=0?MNvV{)TqSdhxoSQqG@pp zTm8KyIf>aRDeZ+*axzlBPdwiQ`R{=IxpFN?v0O*2j0R|dTrO(@*^1C&onlr~*_xj? zy2w@GOfYk!YUjmOOlj!qI4!C;FRQRHqvpNRmX^X}&d5$pOpobktm^SKbzfNSD$33( zCA%>`g}!%?e{iBpEK0XMn6o76@j@2c7nhNkVXi!v^W@5@(}FJ+q$b40v*;q~$LzXt zqK%hAFClfv)yb0U3bi7_SDvO5Y^-(8)RsA|jdT6Jo-=ZaanwSIr?RZ6jQyyz#XW7B zyQQ?Gp}xJn&V5{NWkG&r-nU(qdAT&A=f*A2&7IHT{VgAf8g8ee@JL9X_ea1gv?WczFu14PX2@b1zY`7ay_D3>rER=T{;I1>C z1-?f^c~^}uWOri*RhT~y<=up`cSyQ>LV351uL3Vr_7|bNFn^d!()`_mvUkd|m`9>K z)HltUVlKwOn@W$0eIkElL@Z;YB1B3mUJCstb|g3F<~Gy6WW-nO+5U0kioColuFTE7 zvcIF_+5W+%s;1KrihZr4!+RRigG3TEVJx7zoX&Qpb!2j#99C)pafZ5Jy}tI0GAhTO zf=28h2V>Y>BrmF#VfO4=XHH2$;gpn-yu7t$)Qa5Pr7Yt&QPr7wO}^mII?CO6%RZEw zJ1An-rRKA%03n_H5T8E8t%Yijh?739>^d9yPdj)H2+@np_9uDv|H0QQvu zK06rgZ^wAD3|soJ?iC#+&V;~bRj@$bNT@CC2=8bqs%q^edQ+*FeIq+BmSbn;vf0re z;8NMxTS*)8d1ZToJKjn$k$$3|MJ1%f!Kq`*J9tb zy_Hz$(8L%M@3v?L8fTEfFUa+VkzYAZ_XPBq8F*uwUMtUqMXmtMJE~ zW>jje<}S(#?zFNpQVY_`X2;dJN=jUHN!wR|#pBP8&vX@c6vU>1)8KM;dHhV@lHQn% z(n7M$0P37<6CQ=m{3FwY7Z2GRG_CBFI**W%o=k@3td`c5rVO$sQ(K`lt+GJLB}K}v z$oA52tZr?VhKI^TO@d}W+F-d+r@rHklX-Abf!dahu}%r?8kM~W=}5UT8vV$RALHY( z!j8?sW;hN{DR%8T=c%Hij?L@OZEW@6_z#a69V2o&*ppdVBUxF^#q|wEMfLT*vfSKM zXD&~N`Q6K~F~$?jXS4RsYMRy@<0@`<#Kk+<`ru=|y=+SBl*-oDDN|aii)xFC($R_X z@t2Gj@NRb&Miev9W60rDs6WM*bz~PpW2;Q;&p`u+N(1U!N0|WM~2pN?GyTN9*gS*OeC5&GGx@))l6t)lILjZ*sEWUrWo%O8@Gvt2^bCx;pp5 zi*Q>cp0f%|&pNBL(0UGMhv4}IyQEJ>JdQb_St*>Wxh4C)lkWMf8HkJ}9L*WiS{05u zpt1hjxZf%^;{GcD5aTb7zr#;P{OZP9EezW*MxyRrZ|l*kerdsE$W)TJz04G~BJ2+v z$v{KK%m~9Q9a!3NB|F`$sXey5zC0}{JFT!dKeHe{B|b52z9%p{F*%*jn7ypE+xb>= zb@c-^HO&plR$^+hlRX>!G8to~IA*5c(v|MZ%M}i1k~7!g%uGm3NK4AAnpU4$kmTS+ zQ|o5sXD6lcmf#~r2xs0+Fw^srt$D#|IcbS;DeMgKu6uvn(&SFvgLbl*Vp30!3^Iet{2+ zr^PWV2_r_HQPSPzE1$K-Ro&FJqq{0UBPYWdoO%T6V85NH0IKUZ_}QrIGQ4r@fm5L7 z^&bN~e5cH|a7-Z_s*3rHbZ0mWbYf&xT4INR!Qf%5mJKDQhoeL+j@D5kOFe9L%KB9D z5l(cy^}?bI|2*kE00uuHuDG!wfxBRVUE8f^`XW&(+s@*a~@)>^FOQY$0iJr5|32I zgAApLj!YuzBVQc<81D*p$#KwBI(gMPva!$+wMzEF;YUotKODAYCLD4=#eRSIwrP&P zAmyTQWILq7jJH#I5xJ{M_4+dQIAAn+MAA=ow?B;-f=={353!2FtDtORdyO2hhF#JA za}TYF^^=+{&eMHy{Cz%*I#uJv7FxH9DGfb@KHQk`h$@wP4>7U~u0CvS{`hd?P9A&M z3bo*iNG9?{-RM1+V?WY=>yNA>QGuA`fse=?^&CPWizn3W5&5Ev52qw{{egA;g47Gy zIqjmIOKVS#(9-`W>NuRC?>ZtaOqh}hTX?YNzY^RsqNUt4pF*df`)Q}e0rM(HG*L3S z#>*ooW7xlrOsI)-Ho-gp8)5kPi-woIiuRgLvtBej3D2D!D&9H&uuYBcI&5pA@mPwn zlegfERE))C9F~lsD}pioTC1X#HL}%?Y**kfKHpygu53r5Blt&Fo5~(2OwMs+FE7BZ zvclM}Uxm1q;oL=oJ;kf!8F<8SMG2zyKrX32x&-(2m?_0q+!#m6_*kA5bJcY*Sv(;o z+fh(c#S)raQE@p*DfB6bL`P0sl&c9u32y;-oUM2a&;Wprm%}LA-pHHCqV}g|xf-AK z`TpPriwUW$Huy)!PU5g&dA8*6s;`5?$vn~vsC+p)pXntY;~DWwqs~mmt7-}y*)a*= z^17?QWo&%5gB6e#B%6-J;2&6BN)oXNyxiD;clil?0p>DFQFny7z*Mf5#*~@Z%~xEG z4ccmO=_^0S*S&3)FDHI<{p!)}tEzHyHcZdUe#kvzhR5AHji;qnR?k|zVn)``87Dq8 znpoG!a{fH6-rd%^t_|+aSTcSY){8u7#9%Ci@wD8}g?ZadC&u}57BHF7JZ=)yRDlU^ zIcQS5!sWz-z>tC_cG>!zeE*ENryTJasc8iXMYXj9v+7HV;xp;4rU`e=P1icp{4;o5 z%9;gRYl_%?W!cVrXJXdOd4tFIoVqAwCfz1w9k*zVZeI%iGzM@O0YV>Gq0Q%D+$xMH zjP#veCx(#8pZ9zgT+LE0;IsBW+>ga>nCH`Qc#rZ4O6c(->b!RRCZ5f2k~5D*WR20H zC_wwb3dxA%SA_#kLA&D*VTN!PEV5-@GZ_&(8Fj ze2O_m@YeUyGp7bWTIltiE<82a>8Y%QnW-7sHE!#4FBZVl`|a29K76ix$A#J}!W2MJ zaTR9Z&g`If+&HE%y`Ol;+&{0wK8Sc@2j2T_dzA8Y-C$gsD(x*{{FfN(JWPA7oS8Tr z5p64;%-4Hw(QVK8{g-{buEOtUi%w=QFW=wC0?SEvUgI?G!@H3<8Y`!!p|CVAW+ADw$cDmY0|2n%~+wf8MfX za~DjVx}X3#?5cg(c4Ld~E z^4UCr%QD)Pta-&O`P2n-mo1wYqRP^i+5A-0277BOY(|%{>sD=84$_kPBgcZBtjv1( zheRfHfI7j{CeBE@m-HAYBI!$t)NJ8imMdrW&ca+@KmM^Z=vJ1gGO#LvC(_J*D*T20 z8ctS%n#q%HHRMnNB~=}NEqCNP3o=?dI=0o`*wOLFI!C@UKQ?`ON5@#*Ev%z|7B>1O zPU&CTzq}yVk&;r`zZ9gPsVbumv^Z==o&ip0KP$VT+EFRm)j!#mLPixzNbCA1EG)Q( z%#7?o;HQi)#JjR=sDkPsI#Ks&#-4Y^1y6)^UFhQr zbJB8C&c7%n*O?ph!k=UI-@6=^Mz~AxGt<^ z#)Al}5B|Qmja}sP1$X*+u`?&;&o9Ifg^SKl3HB~uF5AWq_Ivgdyl)z2UxeN_G0@zH zwbyG%CPz_v5ns9gcD6>(TN}n+Jlj3t+3vl>C!HtW#MTHGJ;Q{Z17(Ek%PjFGJ==?C zyCa_M-fKStbckNGT!-RK$JDad!d_eUaesgR-o3$R`ul+me}E-;n}@@}@7{|)2_NQ5 z!GkwCBEf+(1|(MYac~>k4LbX8M+p(Z>w)lubt1euiGO&@vJ!tQPlqq?d!tE;-=BT< z%=Y%+hi9L?x4nHI$pJyAT%;WTsT_$z0+|tdCjL+kyK((a{B!str0VbItxrE4yzl9! z*>5*){POjUm=-W6$#$@^Qg3??EGz3*+Jyx_S(IMlDA~u>?4ybaqRUhDFKuJe^WA%c z+vIcDz{K)<8Hk0;tLMA1{!Zgp&qL)6vM<;ro|Apa+5-#wpJRKVPuQ(|F3U$e-wo>} z3sui|Pbe?RPbj||wjIV#&%@>Sk)6VZ@jQU@6ujyC`uFYQ<-s;km-176PI``<;5ITt zY6FwYqdp_c@10zpDmR$5>ngpYv*7Soa z*kZetn!~s#SeASdI+*$1y`Fv`4Q`Vb9)^pMlWL1}5t38dqtb=4M=$xref!As!DLZu z+GY<0vzuy8+pW@rqsDVh{rDAn63DKl_2Ja!hNMDorA{bdm zk|5QQRD)_0x=oXe8qP_ol4SRhS{a&BaGPp;VHpleGc2d{4}d|xLzPs*SE9b9hp=s2 z`jFkk7rdUNR+O%x6x{B;9;FLeQ-E`L{TiF}SNQQzmG4n)LgPDtCe^jwZY0ba(pQ)- z=<5)CsXD^TVrhji8rUIzJ+_as`3j(Z6la7B6+mi}YK`Fk0Gg3}X@3G3)!rRm>Z_?u z!nmjrLGRk9Ko4al>C$Tu)IRAr(B@D_li|=eQcVIFmE@CQq|$X zWs|x(6fDK`A)QHo4PQV`QLDl;Nj|`BhbxK93S;3c*P1$x1KSN_LX?4o!mcaJN zHNy(XsywI`7{^3-?VqU?!Ca+}4tZ%fMUU+}52Hh68>-PsA7#TTUX*`_4vUaL&*38_@wVwc2bm%dWUr*EbQ+O z-pnuRhm8Oq=&q4{7ceR3Pfg$&__H=%PR6tcvM9JchK@>e z^zlv^7zHpAtR%Vj8j`GE;*}v0c-Tieg|5h-!$r#`19((9nAq3PFG7NNxJLV6;uWof z+_}RGo45vMsr@iwC)%Z>@JSv5R7aQyLi_eYEPxUV8Y>tj9T?Zh7!!E$=>q9Q(NBOf zbrit7e;q(-0uFD59jfDjFu}HKB>nx*k;&7z0eS#Vs(;BtnS`eJTE_PTPeC^D z!r_A>(m~jN$-YCG0(k&w_R?~KH`BON*D-JlS;wSXs$r<(8gN?!eW;)(6e-g9Qrd+q z6HB8j=&wZTq{JzUqU7Fv#Fbr`3=e0=1aBs}QTG&sON&B5G7;zrx&d!w6$t%kBrD|( z**&0%FIoG<;~pp;WYiE@s_M%us0q9ge$`eaZ?cr6qP(+|{hEDX&n^pdBYH4L{BbI; z+azdGd$eR}ilO8vpGN+m4*NqXC68=NfR?^XLn}HIN5Gn;MN_pa{xp8cpQIq`N638P?uEDJZ=VaD7=^IQ>snYsicDy@@V&fg!z;pyi;u(9sq4b{eU91 zUC@>awDk>pklz!~J!selsgk7?lE)*8<=MH2A^RyC!GzQVU9@JGh36=X`R5a zckEGPhR#=G(Uf?~=PMs2SUo-_SeV*MwXO(zjQ%uUz|RpaWF65smb;p-T)zMIyTqG5 zGHR?d($7(DEKDP!9C(do(Xp%|mIY#gU?a<09vlyjFK2ndSC=#VEoU{sav!@hxYWm< zW6$}5OWB=xEkX7RK5JkwpiQ<2xMv_EvPl>81p37@YDdUNAC`d0)bVkgavO10o}oM)*$a>tQ}2|IMBZWOK3h!;-}dL!Y`jh;HNpSFn-xD1J`LG{20d~+#&p@ zN8(3&+AVu;^bpjGN*XovOED7Bp>m*O$J>yFo`A29g9${C>QTi3(D9jJ9V1_tMgCcl zbq4lpq|HmcIpiE5F|TAMA2R*`=`9`nP@C>Kopeq4*yoCT;*;_{`$GA5t9*s)?%;(= zN6H`7&@%_(1L+@XBI&U2PI&g+{g;J#Ko1sC4xNOW&zCs)Efb#I^5xaxa;i>jdgLiX z&k`e^CGP(y@|pI#l26%EBg(N8!{ykC!3!sptBH7qT8n&UysV$W>%WXNIFWiZ&@V~; zk$;WsqjY{xIKKyJf@d|lKZGtAbclMlsT9aMDd+D2mzpbdV%>i(zEDzvljN$g;@OM0 zQ>|E^+KDxNJvilW3Dys-HjX#eV-?XBW5n2DoG2rWk_vbW`iH0^l(r($k@@6V4hA0y zb0ldD6g+$7|Lo&XswR{=DtR9Hca8kr7m>y+0sW~5hx#QO#?DRB|No_qPc_ak&cQm% zi?KuFN}SnrJ$60Zf}IF=7Z-2+j(~VMMu<+{VcLR>^vJ@{Fm^NUM_HD-QRb)8k@?yb4}1^_bR=oxBxS48hy2g|!T*h|eq?-Rd|`~^n_PTp_A~Yi_AB-q_B8t=dzL-VUOHqOsgCqjw*8Uq zMgNyJe<=9>ZEeSvG4gu2wiXFzr~fy?AE8f(bO=b3=RnJe%%!Zzdkuxw7nysAg8x5- zQm*N+zfj&m|M0!(@Lwd*|K>FH`-s!{E#Vkh#k<0V&>hcsVnj@a7>+0EXpPEYDjo9$ zA5(mXnGpw!kOFloCr^pd&k)TA5X}idT*SJ9kE~=Luf(G5>Guc zZsu}2-%-^?ZeHkU0@yKCS8XZH+?fap29k+aNWjWPCFHA_HZtc#9a|h*aH=q zOY&FX10)sE({ao~H1WT8Zq8i%p95MsbKB<5o%;ve5Y6qBhjj1}{h$puV1OYiG%zyS zCv6DzemCYHkk2FX$$p@*XguA&X3c_q3)b8zVQl-8V=mkNFknFOkp0m!X@CBrXz1m9 zHaqyxQrO>Vm>D2QnqM?9nHXOfRVQ8XYqkb|Nd&a=r$1dO0U7q~8rVPj4}~MZ?g@T=?mtMDaK0*^SrlNia&vFO z^Qm=pQvt(_r2s7CsZ=(WIj}L_&tik0Ap<$#XDH7q(#x2G4gZ{NCzz2JM-&)v{0dL+ zWO0O}EZV{DeC@pRt2r6iZ#e(X3He%Hz<(dO{}F-@=ifCUU&{yhyRl)?>$8R zec^n_hG|Rol9DY@=2*kdI`6#Tr^uK5L&d@{iehmnW$-f=tNsgqMtToFkA_Ck*qj7~ zR~nZn$kRh9f)_YfEXV^q4fE=0^!-k`cn>SAutQBQWRolS)Y?$F(T&wrG`HaUa4pqa zufK74w?>GQM$ z2%8ajl8r3-3T*@~;lO&=%nVP!QI1b`WI7ss%tjg0d3|bHYD#Q;+V0K}-XJF5nwG9S zp5t4hoL2^4ZzOxberBydSY!nD5Ux=9iE^QdL_lXc)F#4EsiApWw#*3?T^hVXX_EY} zrUyHEEbM@s-9{o{W#3}xMube$%7veyN(62JV;4n9#bW#pzC zMuu;^o>lyK&gL!GZsuv%jcmIS`VM~jL$;*{yiTYy^eGXt2`})31nYTl9*XO#3V!|r zHV9y{S{+rx+w|>FMp1f3rQs(SRC`L z7JloTEwJ+;!pr?y{-r|@erSHrX2@Xof8YuMfer#FwwcgpK0Tq%bQ#PgBcFZz=p15LAX}2+hI~+Jv%y)@2Wf z*<|E}w@OaT>{BH0+|HdVcT&&KZm^>-swQFkYeY?nbS{P5u%|u=lBqhiT6}%3G$-dQ zyKj3u*O8i&lj_VK)}5U8iL#$meTnJ|S?q+a5PVDD$sUKDJr}Mg@L<=ifR}`&5$}SQ zZaA=W5T82w_^GE@*Hb$+`%mp;T{|b&`wQW^oS>f<4pQ&P3)y(sKgS_fLj)AjvZASF z;cOQyYo<@Mq=y<=_j&HwiAn3*=WXe24K=nG&Q6$lL0j*Zc{BQxo(B)MPw2G%4zoyA zb{3HzZ~Yx@BvsmE1Z)IvjLy_!cku?frvkkNmVd)ZtIwgJ3&HYt+*Dwb#@Wv7#3#?r zN#&00yPc?;oJUz)0&mDEP5do#vvX4K$xh`m7urQzT7o^uZa!7|g+pM)`Z@V!7)KG# zY;$K!#QvKPVG-=U)Eu6Yo-vWcZ-PFu4qlprdew5lw$crk)GfT9Z1{)h{A$zkv5GrC zYhvHSrWT}OU3W%W{$CF0gdk6({mAhOEq*G~|H$@fxPX6n_a^^Gk%Nsh7~HV3UogQ&5E}gkCgd z`|1EDO}$2;u<2vDGO>Zn!DR@5e+$@XtYzWfKD?{htK?SB2on#)U6jl!;!XE@pP`hTiP>ILpFU#w{yu-6YFB z)3#ju`mg+|&d#d*gw&kE!kp9ucHV&&{onBIp@XmR;V9bMqubVz@f~J4orQ7%{&7N8LY0-@_DVas=g2O$0p#R-|oLBiS{#CRsE6qzUNXd6r z95fTM2r0`JoN+cqz9Y5I%LJ$YNgAnj><@*A5x`D({=;&=vDkG|DURm3i^NLn`0h>| z;dwBQsh;k}IW;5;D;%Wp!}$Bq`ylk*49*XyOWKD9&O&_Q7#+D=tfUyVz)Gb0?tx)c zmw}101%Vm0$;D@vb#{`z%Zr_kwduC%79sVO~p zE!%__KflaraC!1d3bJYnn~DSNSJZndf={%cO3#8{hp_ltYn~6QSASk(lTI^?6YkP! zj%ecNIxUO_R;kmbk;cx^Y0JoAPw6z~1-a1aL}MoZhE69LrJ`P^okoqg3z9NZpgkvv z=XKgNipASHon*{5lV#chOq_X)PMb!#IiS-7bE!;60Vc|PR;NuP$9zer3FZr#js{GO z)veQ}k!LN`X~0-pWjY2h3D$!;Z5l3&hl$S^wC35?8!{aWb#1^u%8!LaDaH*t4g7fL ztn!7i)_7c}O{0=2{$tT^``EQQKg!5rZ|QWRaS2b+Y0%=Y>9o^WDVEAK%~T~txpf-4 zifY$sz(*;a!~-TZiq?!UGafZp9`y^ICK#pL1kyvaU*;zOCNFxrP6HQvNlNNiuPD7uu_v$pkDErAEJaJ_*A76bkisO`w8AK~i=|7wB z#L*fTW@ZDXGET{rEn!}f`S`Mx5f`s`$OTM6e6!96o_K|az9AYFukhrPT=92En0&$$ z|0|g;Bs>WYod%u+C1WAMBq&{#0wy)#D-xy@qr>wPl#HbWlUSnjoyO8cWuH}mk4gN6 z%&!97yu>GUn&4Ia))L($C3`LLpQP-hmT)HZ5qylar6kES($H1Xk99u5$oh?u`b?Jf zi!@-8r5=z5O!9FApCIuhOFKcDU}XIwO)#=v5~RJQ$a+BA;)r-pvL^4IU0xqV>3wPa+&=!t#X2V65p#)kX*Mh8cRQOKq< zUt$?S24Lrp3?tQntRdRG+c7ePU!pn;dUV~6RY1g>2Xq^7ZJEHajcW&_?!$_q0U&XO z&?#s)&JCn9A8NHO0w6FlJUVk^`^Ev6r>?=(qPQkjc5LfgKhW;Td7KuOVJOJ_QpVmu2jD+Xm*pN?82SWv`gP_1ebkM_ezsHP;wo< zLo@*0k~NJ2-i38q3-Glax()!AB+96k6Xgwr=yl`cc>Dh-=n@Wv*ER!vovxKr`yXFn9 z9~j;-06d$(#waSh1&hb(VXX9D9n54HC<<`Iw`Mw)bZ60V!^d@D%o zKzamqOL|edCe05)N2j1=x?rIrU`6ZWgt5W(TU_h5kDNFReVpXlJhpY)4%f)ef$h-I zfNQAll#^ZkBO3v|00^mi1|WMMx=h;wTsT^aDv>H?GR#ra6ljjIL{NF_FqBUO2a#|` zkNO5k4ZmA-d8RqCw?}d)Ze^VE! z0x7&SKd8afhn+$uLKax>;zpnXP80*=RPIGt6mbk4cjS2kFfY0eRFo zLC1dhJ}vV3(W9#RkAq4KEhx)8denNz=9wEk_rWPfK0kU?x52K+zfrWZO)la$@R1V= ziYTAL$zkwCA(IMD37&$;buxD+p6oy=3Yq%RUR(&AD1f5ii%L>t_dgCok6!f1)`FYu z;EF=(b&{u3H2-~&jCiDB3Du|z(u~QTj>2RL0Jj=_h^HVu+Xb&cAvei}FpoKme~1!1 zKWKeLJUx1~H4pssLyo?K(?stZetz_*MtYb-QJxI*{|xp2*L~ps$5EHv_aD^4FsP%J zNYT*I(>9P{H3t2S-+&P=y&ahJXMHJi=@csy^LkNO`xk?E{Bc-Snt-{SB)pAD!4AY! zoST(~Hwqbe`;mneBRP2Eorf<^6kx@a3tu!S#+&L=oQqhFy+xInF|Wc7qZ*9->o8Dk z!1&r@c=5K$k8f57Fe}iEA!I9d&rCDgFtKG2SzN zggp@^!+Ge~84+XbH-cEs7Kc3&3D_-RutY{51HczVFeI&RFtwZR*sbim23*DV%1nJ`YhIH*2?bwV7A}K{8*U~ zU`?!X*(++*C!=3-~YJT{*#!2XP0 zwumieOR%ToOXF4JHMW#3!*0wKY$ZE}t-{WYW7%=oGkH8)%lgk*IFp^l z&c^PKbJ=<9eCz?afL(|MJ{Pk~*rn{N*blOsUCyq+u8^zP)$AJV4Y`(mon40=A~&!b z**CCH}Kp1xs`pJeFu9+ZfD|ypJ>?nDJ{e=A#`$`_g7tQuycgZi=W9)J4G1<$0&7Qzcli#u@*;CkW@;jWU_6O`b zd4~Oo{TX{t{=)u>9jVW=7ues}i&)9{clI*-2YZFR%3foyvp4XL?k)CD_BMNmz02NX z@3Rlszu1TDBdlfkgni0BW1q8sV{PXb>`S(v1=%<^IOCiPd_LadQTXC=43EWo&v?9( zOyo)Ua&ih*7^ZS3PvbZao@eqboY0uVb9o*<6;*&$sxDr{i+Kqz#c6frynKZ{e+cDxb#N@P)4#yxsVV@iOn=o!Eo+g7G)w z@5Wz^mv|Sx!aS4D;l`8CG%{A>JL{&jvGzn^r%0J_u^MCVw{0sgi-_L`5 zTo{50E`%_JC89*Mh!L?OPQ;4@ktmWxvPcmQkt&=bO{9wqktwo7w#X5=B2VOt0#PVj zqDT~r5>YD3M7gLCm12sh64jzc)QUP$FB*hfc!XE@gkLm@fM^oUqD8cdsbZRF6Vt^E z(Jnefr|1&hVy2iSW{WvuuILf-#C)+pEEK(BkytF2h^1ngST0tGmEst&N~{*gisQr@ zalBY7`ouc1Ui6Cru|aGUo5Y~lEVhUtu~iI<5wT62AhwGgVpNQYo#I4sk~mqMB2E>j ziPOa{afUckoF&c{=ZJH~dE$KW6-?M%C@vBgi%Z0%;;Z5^v0Gd&t`Jv>tHjmf8u2x8 zt@ygQPFyc;5I2f%h?~SW#ka)G;udkM__p|txJ}$HzAL^b?htp1?~A*{-Qpf`ueeX# zFMc2%5I+nObMtV#9W@nsH{lnP4WGNoKN{Vmi!J(`lxe>1KwRX=a((W{#O_=9&3s0fu@mv&bwqOUzQU z%q%x6%t~{LS!Gt6HD;|@XV#kyrrY$GUejm#F?0-I@Yrm&U?4daL&!FBI);+%W(S6q zU1qmA)0}0_Hs_dgF}R#(&NmmB3(a10k-6AhVlFk8naj-;=1TJzbCtQ;Jk~tUTw@+@ zt~LA2b>@1r-yASEm>bPa=AgOR++q%yTg_o}#N1|{U~V^en4{*Hxzjw+Jjp!SJjFcK zJk31a+-06&o@t(Co^76Eo@<_Go^O7|yuiHByvV%Ryu`fJ{Hl4Gx!b(lyu!TFyvn@V zyvF>Rd9C?%^E&f-^9J)q^Bd+(<~Pl6nKzran75kWHos%uX5Mao*ZiJ&hk2*@ee*8! zZu1`VUh_Wle)9+B1LhCS2hE4fhs__EKQIcRH z#$)Cl<7{IOcJ$t1JZ}EN{H6Jr`MCKjbFcAJ<7eitjYrHU%-@*5HJ>z}GM_eoXa3&& zgZW4E8S_u(pUr2@znFhDpEI8~Uoih>zG%K={@r}p{D=99`KtMv`MU9$`G)zX`Ih-l z^KJ7T;}Y{-^F8x@^8@o==7;7-=EvqI=BMUo=I7?W&3)z<=9lJvGiZ)mhQ*9)E$nEp zgz zEwz?e%dHjGO6wSFm9^SB);i8wV;yg;wfd}e)_SYo8n8B48?8;&ptafBVhveatzm1# z+Gd?#ZMSwl>Q$vW9O#X8kG%{txMWu0N2X`N-AZJlGCYn^AEZ+*qOz`D@7 z$hz3N#Jbe_s&$#Q+q&Gk!n)GB%DURR#`>Cdt@U;5I_rAt2J1%a8`e$MH?413H(R$@ zw_4w}zGK~H-EMu?`kr-%b*J@x>n`hV>mKV~>pts#>j%~Y)(@=*t%t0KtshxGwjQy5 zV*S+mnf0jkb8CICzqOvUp0b{{erNsO`h)dH>ly1$ z)}O6st-n}*wVtz{w_dRRX1!>=Wc}TG+4_g|iuJ1Xn)SN%hV`cPmi15TZR;KDUF$vT zed`13U)G1#N7l#IC)TIdXV&M|zpZ`N7uJ{7ek>pVxfP7tfwzU5PTsa@U^u#cYv20q zBg4^s>KfI)Zu`K_fv7%tiEba+I5Ir2CAv>t<2%<6ZeKsPb;HoWN%8B$>A24RkRR6i)KUHF(giwwqtV@(;D9FBtq2atOI&xTblgBF8Qrbf7*N-!ZiR6` zUgBqk8HwK*PRGp(A&A=;N+!%&KeBaepJFy)W5iAT>~PumP2sdTdtKjla}$1|=8O&w z^$$c1%1iVdEyas(h$0EY5jRl_ zRQX}0;NiY)BRfX7kHBC=_wYtBFuXB(p%#8b3%^haKO!%Q3pb4oZ|vJXwsoj)Y&3Bs z@-Awz;$geuVQ~c6gzXVGQHvE1+tp=>Dz!si;+KR~7r!H%jvgBxY;ZSv^wk@+R23eT z7jr39R@{^VF$%>ePDZoT+5&(s;Wx z-Y%_+E{(rS^Vy~O?9zO8X}VpSZkMLprR$+f)9=#syEOeSO}|Ui@7DCYHT`Z)zgyGq z*7UpmQL|L*+9)rI`)*CWTT}1W)VnqHZcV*gQ}5Q)XKJc5HTIcWx|v$KnHulR=D2oh ziK>zGg_6Z$Mt-7w#D9+p$TdN5W~zCDxbdtCG>vpk(wkC>cEsiVIJJ;=1j|>dK#3Jo(3hQr$I^SX;3=$xHWyZrtjAD-I~5z(|2q7 zZcX1ENLUi#A`^B*-Ykv08I8D6vbr@7Zq0*7)9`2-9!*qKMu(c93JxM^f$OW!)I;E5=uwC&X` z(yLpfSGP#7ZjoNyBE7mrdUcERx>YTCJxVICM`_=yTZdP-4zE{f-K%Tet83k>Yug*> zwnk}@TVnsH?rO?@&M+c!KiIxsXa*q6|~ZO0%wc$pp3J*uD2L1Znn z5*BV9q)=VmEQ|HlwULCe`Ui$a`=T{2 z^AtP~^bjk#AQB<7wn&kNq)5uhIy(9A7(Zz+dPK=3wr?7V+Cf6OEqM{6kWJGU+aT9^ z`~y4I2q`*&(AsPzPhvAc<5abzAIh6JSy(0&8(j@il1;Y>657sYs;jS|LAD+@7JGW_ zl;0gnc|xi1Q(q|0A4)ZbQh`vaDU@oqQ;i{N+FSY>xUx2$3hiEj1Xf%gtG>2$3hiK>?$>$GY3xu)Rw9sO-<+>Z}r*8Br)E6PA!KUSIuxYs) zY+CLHo0hx5mdD*-%j4G3p3iT$4YxZ)OOJ|ty0`V|-qxpkTVK;myFG=u*FCGRS@kVG z8;9%cpy4pB`!XbdT-RJ+@Ex*goB3`*e@()1xh) z9&P#BRb=7Qqb;8vZTa+Q%cn%%?|UK0Oli>5-UEkHma>BLq$Bk4o$y9)7K+6pB}mS zIyHSgg7bB%h{LBxay~th^XUoR=F=lJpB}0C^hnL8M`}JjQuB4$ zmeu4_F_%w|)qHxa=F?*}pB}6E^jOWO$7()3R`cnxnop0_e0r?r(_=MXx30-S!JJqCHHJ)mJ z?+-L;?~Od|?Jf;))8?eHF`Tk_YSf+`6trijl+9ho}E(4 zUs1}Y->7{&<%MWyA5VF#tG!c<9@0a~05W0_CYugkM{RUs;B`LA4=Vr7yr$S`e<%f^d~>5m#x!xJnDg zRa!8v5-+aO7vL&=0j@HB$5pmfT%|9-Rr&&4r3JeiR13mY(^oAB_nN+HLAclSRSUws zrmtEM?lpbYf^e_ts}_WNO<%Pj+-v%(1-Tnk3&K^)uk?g_Ex&3(?rt@6fve)TTg_bH zUh&(lW-f5A`0ZAsCEP21yVcAE?iIh?YUTp>ir;QEPQtz7w_A;qaIg67R^udhw;Ctm z`hR*m8{n#r>j1AF($mw=lY|4ft)aKJ2VYzJuVfPX;5KUjbyK;rh> zJ?A~-Hf`hSG%!bd&%I~&{Os91=kC?6L|$K3F2F@zUsf)_rN8``@)tkgzVeSrfBEvh z#eL~7Kc@U;)q?xVKki~j%$N5vO64C{{bf~y`>MaJYJe+$d2a(({<5k8uKLTW2DtK< zRSj_EFRL0cUsg3JmA|ZNfUEuq<)2Xf)`C*k$3C|Z(z9W7d)y%KxOQ~t6#6!T?uh*JB1 zp7Nik{V(||&(;2tzk+N3%c@b#m#+bo+W(Tff~)-{cLi7elDmSd{_>RoT=_~)8uKM5 zrPOvMCk5AbB_{>fb|ohT*LEc*1y{W!Ck0o#N=^!{c9ono=1WdWsr@QBDY*8l?J7AbxY|{6(wHwfDW&Q!IVrg6e~t3LM)j8*mFKFz zd~E<%{Uv7wSN-Ly1GwrhUmaq;#$jLMu&;60*EsBJ9QHL1`x=LRjl;giVPE60uW{Jd zIP7a2_B9Us8i#$2!@kC0U*oW^aoE>5>}wqMH4gh4hkcF1zQ$o+Q zq?-5^;a&h)&tbvxrY_d}?05D|N2*ClYs2eWTJ6W(4wf-n^{#{yzDu}nr*z#;Syhy} zWhHA`O)Jt;UnYFMTquQ1^x!IzOz?ckNS5B3%CvQKwWl&IZJE;Mwyw-Yq~+#R7Afgy zxg~oY?LgMn9F}TXwYoElmA1CD<|>ISGO6*k#5q6ZHPhSKo7`&mFK4S=;_hUowp5hk z;6!X`ZFVA-@EZuZ>(2FU+3RTMYQBtTF{j8y1VyrlRU~^YisUe-NEUO7WRd)IOJ=$2 zF_PRwWxn5)X%ppS28hRElA~9~IE~gliAk;zk4dgU>Bb=C)e%%+|81tJ2>IPbhg6X; z8IMVrq;w8N>4r0<^j^&SlOmdM;;t@rddqc3Ni&-2b*bwmMYMgE9Wv~6wU#e*S6%JJ zu5^}{hfVB+jilDKcYe7e)fvI<2_dbWu7SAZb#WvqZRZyN+A?-$`SNrz7pv`e?&ax{ z6dP)9UY<_1u1rPo>2%wwmX*uXtZGNDt7SEYf37L-4`cR*yQ4SU+#2^JXQdP;_atWp z7h`ymvx18;Jjq$Xuckr9+C>g-a^-=W+$d`;LE<8TWUX;evR2B3CE#;lRz^u%c_5?6 zi&a?yR%3GnDud*$5IPxnWz7+&43fA);3oLECy6Vi3ow-8G+x|^P<^oh$y^~&5!9PJ z$y~Xw5~z21co!m8PH8|Tkkpk5DuJY~aZgfLN|hktB&fRBfKE1^PBt7CCjBM3D-~4& z$z8#n1m2VeR02s}Ay5e3yQ**i`@OKfl0JIO3@!gar; z$z3>W+B>daxJcwKvX7Ek7O`wQ?uO%)6*ZY7&bFM)%EKzCiji&Esb#BsIR$g=Oz~jJ zY~!9}wv^gAlG(;R$!sarJtVUQcNqw!c8+AW;OZTc*@CNgXb|u;2zZj&@?1MdGFx!? zx35UItz0W!Cf_^bo_z15OcXY?PJOQYC9~yuRl(G!%3bnWo?cy;Zl3yFxl3Nl^V-5p zr#w%0^7{f!sRH-2uxYK7HjaCe)lzyz%`NG4s%b^rE#W$xBx@W=Q{$ebsgx?Dq^aO) zBuP`jtHX7%c{si+JewxiW;2|{AXbn(6+)fLBu|ZdlBZI}3Oh3`%U5-^3%%s2+}B>x zDCJ3>%6%0;@>K9@sh{bTvdR7C4foYPlAnUBcSwF3_ar~1RPP`^b#kPeMGnbNd9JgU z6DVyIK}0yIqpgJNvRWqWS`(VF-Z0au4a|&6TGUZrS;|&UD&SEUQJ~o z8D!j(43bhU<9o$z?WtC&B+^I{35m`xl0^O8B^*{k_Rhv+M5K+S(eo zsVtAPQ$uctnLYHEEtOyeW?JYI{7nS*q;NGW^hvHi72^D-P`uzgwE2$8R<}FTirF)E zg$dQvF1X&5rI&ZMve(Q~*i(V+iJ;jldm;2mc7aW$pPi$!``+xiU;IrRo|nH5&1`nUeK_A6l+g3XK$Hdx0qpnm|nw!zu6&TL^{Su6XiUSRLf&Fr~3#IBE>)BcTp z0)6@W`JL*=!gb+8g{JVEh2JjRS@?M2vxPs4ToJiG^5>D&kw+qfk>?_B6@`jQi)Ixa zD0;nkcJXf&f2H`H5>v9KWOHe_G+DZ$^u_4a(Yom3XgYdpbW?O|v@iNd^vBT`qVGn} zl|{;CmCY@Smwm46+Oo#7HD&GWJuDAiG8MslW_mDWF6ZRF-wj4hJowNggOjEzc-zbi zhD|l+8D1HjGFO4uu&>c)e`0Mg!H&ax_8f*xeK2XR2VWArYZ`*nW@&KHEQ7w0a$WFK zb60S}Y^KfoXzSa+{lEjjwqQKJJGeW)H`tLs0Nr$ULUQ_SFb2o>;dl;?2ibv`&kn_W z_9*7FKQW)(0r~X#G;u}>H+!)O^s%L1%47yUN?8-Y!< zF$1X&(96f^<@b>In5o2WRamNqI(68)p5AMqw;F>Nu+lN4JH}WF^EcJP{8fifBGqZ6 zI&Jm@C-P>{R!7x$&eG~}T74ehd6S;}5mr5cw9nJ>C@sH%#1%+dfut2k zT7iF@p*PRM@hrW0*4zth1|FdPp5P2Vaz8#Y$llp<{(|E4pc~C5;dmB~=g{LkmOq31 zlgK{~mvJmTiRP1NK8fa&a5zWn=V|pk);@!^&tUB{=yVCzoquTtYF zYCJ`ar>OA^{P)3sAN=>x+i%1F5d2?-|EutSmEJyuk5yp#i{sJC@)q_yi9E-VXB=7H zqSrV>O~+pY)@a0c?&3tttbaeibvHCUKrhh84&&|651{2B@Hp+DEu*F&Zzepg)}wHG z4KAZ_ISrR1jOq{2VH6!k(P0!FMw!3MgTF?`htN80<^{Z&fvxi3VtwQfC>k4r|`V{o8>% zfIEQ=z(!yba1VW8_5BubFJQ;@7N8I5cLzt%EzI1qi}6z)dNf8^+h)W@mOWdOe3uFQLeG{>^x+5 z@FF@4IElWCG|g z2ojAU(HIhqA<-BTjUmw(5{)6z1thwFL>G|g0uo&yzS_U;G9AlKAkh)Veg!kd<@7_G z{!9XO=u(gM8o1ZUc-hQ7YstTb@=@fz1-aKC_g^8m_3)RG`(D_HU^IyOM4Ax$=}Xv6ALjoO#@|vbvkZ&g2;LaH3-uH1 z@ULN(uY=;Qo=?cB?b-4NV|q5lCAR$--u!AKVS($+rx=NsV2xSiixG6LB1Tr664qowHbpL?);eGR`jLtf&^m%MPA3lFZZm|H1F2n<_#Tyo3xi8>ZUu5jrsC6AD zD%c$42DDjBZ!RI)H2_P|a2aqT5;ih+zQnVw@aP5}1bTp8pbwq4F;*XTJ#*IVpzNnz z8}S|kY}DHwoMxW91dU#E8rjJ7H2Kh+;A_bKk9hFEBmYU{K90QSk?SaYPQv3PJWj%+ z2x}jw&7-t;l-gT3iL^WTG1e_-WK=NTrw2{6_bBcCfVL*F1wBYlEuuf_X>&37R@;lf zgTO;T56}zPXx0zS05Ayb25ijiLGNj_{P+K<<#R52%;(u6#$G++`+sizQqFw{xd^g@ zvLE^ZU=Y|19>(Jekaqi_5Lm;Sw*Ip?Pz zQ?_5VCjUzg@3hF~>9c=jcFS_iW2CcTpE*V{GMkAN5J5hgquIM3rTHip{x3DMSsm@; zulDDA`0;<2DadpFD*cp9!Y2ownoUe&{>#M@r1=;a@5alRZyOoKww}ZpV$3hVWQMhQ zg{8cRLmT`_*srCj4nmnaSrJ?hnv4#`!WuoGH7LgB_eg)6Y)s8s4=Bc`M8d z+33l6>eTui=f8B)PCxs)U*zh6qw_HiTT*ZudJNGsK8v&bn+d6ks=P#)9RfqMPOUW62gJ@*4z5AI5zHUAd zJPh5CnaR8`i_GHEV4L|h-rasZcmT={b6N11nN8HSt?l9I4_$qWeIM+3uqSiRhQPWV zwo9K$z30I8@oc{v<169&1?vA0o=?Jakf-;-^AUJQ@feks~nFZcpQhv33%9b zfPGL6LB(EuU=vZ$_E|qv2cfd>(e`a=FI4?d^+VMU)d2oB7jKoG-3-+$Q0?T&*iGT9Z22GHvDPdA literal 0 HcmV?d00001 diff --git a/Frodo Logo b/Frodo Logo new file mode 100644 index 0000000000000000000000000000000000000000..f17abd36dc02740988a9916fd9b69a87a9f3d2a3 GIT binary patch literal 38400 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 literal 0 HcmV?d00001 diff --git a/Frodo.spec b/Frodo.spec new file mode 100644 index 0000000..fb44bfe --- /dev/null +++ b/Frodo.spec @@ -0,0 +1,49 @@ +%define name Frodo +%define version 4.2 +%define release 1 + +Summary: Commodore 64 emulator +Name: %{name} +Version: %{version} +Release: %{release} +Copyright: GPL +Group: Applications/Emulators +Source: %{name}-%{version}.tar.gz +URL: http://www.uni-mainz.de/~bauec002/FRMain.html +BuildRoot: %{_tmppath}/%{name}-root +Prefix: %{_prefix} + +%description +Frodo is a free, portable Commodore 64 emulator that runs on a variety +of platforms, with a focus on the exact reproduction of special graphical +effects possible on the C64. + +Frodo comes in two flavours: The "normal" Frodo with a line-based +emulation, and the single-cycle emulation "Frodo SC" that is slower +but far more compatible. + +%prep +%setup -q + +%build +cd Src +CFLAGS=${RPM_OPT_FLAGS} CXXFLAGS=${RPM_OPT_FLAGS} ./configure --prefix=%{_prefix} +make + +%install +rm -rf ${RPM_BUILD_ROOT} +cd Src +make DESTDIR=${RPM_BUILD_ROOT} install + +%clean +rm -rf ${RPM_BUILD_ROOT} + +%files +%defattr(-,root,root) +%doc COPYING CHANGES +%doc Docs/*.html +%{_bindir}/Frodo +%{_bindir}/FrodoSC +%{_bindir}/Frodo_GUI.tcl +"%{_datadir}/frodo/Kernal ROM" +%{_datadir}/frodo/Frodo.glade diff --git a/Kernal ROM b/Kernal ROM new file mode 100644 index 0000000000000000000000000000000000000000..0fb99af59c5f8b325ef0a0bef20eafeda9b13d27 GIT binary patch literal 8192 zcmeG>dt4J&){{FB2oea08eJcEl~y8Hrsx#!-?ynv%mavOw9D~Yd`;Y%mEFm3t{sVlifVX%iDOFz0{?}k0;6`F4D z1?F|UsstbXnm@;Wjn~xk1oJ;eTv|pmZ!H25PIQ6@f*w* z#~Z#}5&7R_-f+ALI2?Sm>7iDIFNm|HgnyYO%D0G(bncUj-zAI*#Y+jCcv3${#JDS9 zo@koHh7?a^UhzTkDjN>vM!C9)`mvt>h$XQ71aD#MfiV9Ge72N0H*v2po6N5`H*&8s z8_ll*qno)6%a1NwgK#dBTw>8Y=iUnIP3(}!PjIL0-xerp?_rwx7nak;}rPnRa|UJfQglzP43%L zV$_gwql0ar7S+qnMnLa>H`rGITRlOSliffzbS$oI-~miUpDO}Zf1)ngE^KqTT*G(Byx>a#;CMKG*SXrhJ%b>#^dvRcLH*HzS2g0V zf5a0T?fv`3M*q98cHVQ-t-@SSgn*@v_ zPhFgvwgk_f^$2tIj9`~q4WMUn+JfbH)tXh$Em@8+vBQ9cfXirAWYM6+fA@2nk*$kP zM-H1$3^{*L?4?IPzIgPQm}d3skxFGHnxiNp(g{1k*3$L_b~Nn?Ggq6ca7L4}+EGPg zwuvV!HpE=v4VI4)f7@b3e1zpgWXVQ+l*Nqr5X(o1k7O=0m)x01wY`8RTzhJ?=?M_m zn(N1iU3_3~ja`caEftyvEMclLRXeM>s-rdfa6n?JDm3@7)WT1028a?M&6*zO8WrE# zC`csL=BHbM18Q>J!v8}055M#Ltrs}-U8sSOIHDnn$QQGaODz0R%>*gu1 z$6wcg0<0mAMvI}z~# zEiFtG!?~*|3g#+5kyg!jUXXyC^M-`VZxor4%>9>szz4w6t1wc#n#Nx=`>VKX=c_#P zgj8vU8)PP_EiKOK`q9=f+}d0Yx*l2cLQV6r8rAl_H5J@7S?AY|)#%9z@jfNBlvm^T zn}NM5+F30s%~eLDm|tP82J3@5s%QgqJ+!Z>l14+Jz-WuCicRRuYf{f8>>3o+t2ZEku(fxr6v zHR~OzwY<6|!5YF;iG3grAmEG5)Z#n3ZNfIt>^fi-IQBnc9IP`BIGp3S7PNNAxhgU2 zRH_t@H?=sf0iT)0l|EZ#W^tF#)|CmJ|K!tu14x66p{n_^lriE+XSFn^9K1PsXnM^Q zP@MRf5G#g&$}WHevAL3JNCPrgpb%hwIH+cE6Vz1OWj<5yEESy9&Gn-}>Q*q6=AOBl z%4o6^<{Rb<=BwtaA(8BeC(c(%kd-7I=o~Ym zt)XN8`q}si2X^fJ%f8&a{hxA$2F`8ZiVWOggFR4QT`qjXp=MZv^;%D)xAP8Dc%LZ*mw>qylN<}mG*?l6@gToC zY?WCML%B+(i~BZ6;2w=z1KcH1SAJQ1jE`e3JF1pmfiP2C>51`QVZMQ3#a)(N2-Yq4 zP0DsWwv|)pG*SdO@F`BA!m!X!iAsP?U=(;)t6N=bEPy3FA7ZTqvGYwAL90cI+WLUB zp@84Pbn~~F9)1vj_HYAe`?8`|>|5p;X(;X%LOqcJDTS*o2!?8q>7vnhYJq21h4_84 zaSDtOFBvmFMvjZc-L7-zr7%{LosGoVx@}2WNI&xR6k(gB6XK*u*iplUaZ(7l^^=9! zNDBQ1&=SA-2#JrgWcEkMQJ$$8>$qq-UuZ$uIS4q5%5s+9`4D-Oa{M)y2hC5rEl426 z5tnn2)b6$+{W!N)OhFc{NWFDCLI{V=L+SFqbDQb*z6&Bz^{qQ@pSexm*?K2VJuZ_m z?~0lE;(}SfUA08k{24Df8kdt%k;~iOxG$TsQv>G+#f=M|5Ad+zbG%ZpE*^oA=B0Aro0txl<?p-w#|#e*d=?uro>azZ zCyWj|fWk+Nj8#Q^q8=6<70^Tm0>2;vV*}#?feGUSR%IYG5Qqr`b_TLfNnV_UWLRW ztd<{biLQm!0e>A5WvZTgt7(3F5>Mi>gR8aHWz1eYyGkvl4*7jxx;0cjh z@hPZsYn|(v_2#wQIw#H0=5=__Lq6K_Hu9I#pxyOcqBDtEYhGs$G<#hgaPAa)@xo52 zU(j1=VV37*fVg-yl-{xq=4J0!9SlvYpNiFg@-&{Ie-DDU1;pK+Qg*oMs1#*GrH9-uIc- zX}R^A)&Y-nI>FHAlO$z@_#I$#i2zFit#ko;pm=?_pH2{4X(0sCqWceAMq?HYX?@nJ znpyf^&7LxSMm~|x?4|@lZH9F>mr!RfqOQ!Dn<~ypN=};oWYWZmMBFZUS<01df8Lm5K2alb~vFA4F0N6gpD8&uc8|I@m4_A|tI%nU;!|q@|^% z;X~?GOBSRpGNi0{hFzMP#;RfT+$k_TY5LqpCk4ovZ%Cli=K4sn+pgS7w?Yt-gTh?3 zT4p3=#RWC+AyZJlup}*I=^7cbH1+uvi&=H*vzGXLjk?q;h3mEf5nqC)OG|Tku%VSp z(OapbO>j)tn)YF{m-?06n?eO6UD=Uz;?uEa&iZ3N3`Bd ziFkCN!qHujkScodgf6a38VUPrtS1fxBdO(*v$URu&WK(nPfNS7s*NkvDDkB>%5WE7 zXv>PXYW!4K@6RZ}ICU=&iT1<i+A3`lLxszN#WxjKmoexV;)c++~Z1h(b6O=_VPqBuImJ0 zK?xuiS1P_mFt7tuYTi9c|{|0gZN+fVdH|0^mE? z1s zDC+nSHBkrg9!{YL5p6zeI+vj*ncYK@fMSQs{&e5k>o^V#O|P>QRK$Z~L6IP-?3Cb) zcz8Rcyw+vQJS*ku;Vf9CX904KIgG#U<_8|LGFJRU04U7t0R&eNwK>bNTh>Ztkrd8r zU?WSi75R(c3gj?sRTsg|;5^oMzy`~LR{F_WvYm`Wc2a?zU_0j#Et_eTr;4-cG|A$! zdW95IPl|P{qWB1N)(6EoPY72kcCcje5mwQ8#03|eM_AN(q(Gas2^h`Y1e^lLr-9=$ zz%gFd7f)^KbsVO-e9d?^5>M*{3yxq%K?UqQWJ-uhvMFgch1)G5b`7<^-&|xm>^ep; zr;jO^GZ`~TuIyhl$ZuduA88Skhd=IlrmChZ~$uQe`v{Sk$v?sn7~egYb)TgRgndL>xso%`aEg? zAxRlrpz+|j`I>(AHy{>uy4z0p?V{3u5KO=(bM_Vmcd%s)$F`(em1fp_&}{$dK;Y63 zw|jGf2ZuZI0RfW#W;jsW=&hXHdCsnVB0hg*1aM+BsPqu1H>Ob8Q4+8=s_2Y}Qp_6G5idYwg%!w~S6 zrll^&|L#)jKmGsFz<)IG|Dgdso;k)V?8hM11s_Xj`R(HWY$C8Xz{Ybln2EQ_TM&y( zyu72he%d`8R&g2t;FI9sGsPdAE_vb0kpQ4hs~GE&l=$6#YE6HT2#dt8AbiSiLp0#P z#b+OP6ka-xiV{B;q6K$`adW5P#)u&3Q)?htCw8(aII$A~$e2d~B-Z}G`*F{|1dwNX zY5ZI&#z&i+gnpXyhL{L9eUqK}(j)4=#sx0xsKMkn>&kHQFX zBEOt{XqiD~&m?JnHr(p8b<4*rIvDtd3SpS2t@PjB5k_{`w4F5BIxyDRU`?0T`Y zqO+~%>CS0gn+LgjAUjczhlo4^lZJ{N! zmim2wIc`2-I!QAn<}y<$&6PnCs)n-!+<9B}!O@Dz=d7WSCeaoKu2PjnJ5iwt+O& za_JOOf*Vf#{h`NzX*k-!$Shr~q?VHbTs=tT58|)@KG9BnIhgemOs;Aq5;}$k3|$?b zp8WKf^D%#nYKvT=gckxu3GoI2DX31teC}4(wTyi&4_TCT?y$Po_9IJJ9UlXl8ye3C zpaVeEnhr={Wh6*uA7SvZ%Ymtb@IEnwQb9$5z>-u>J4wA1pbiaC zD+j0%1JsxSN;^oEwow<_sK*AWS%cKBR_d37aNdLS9M#cA4YpB{ZIr5w3LB(|LF)cM z#uzeVESWK$%)n&EL^30u%y^un4&0;ax~awdevkitPPC") +endif + +include $(DEVKITPPC)/wii_rules + +#--------------------------------------------------------------------------------- +# TARGET is the name of the output +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# INCLUDES is a list of directories containing extra header files +#--------------------------------------------------------------------------------- +TARGET := frodo +BUILD := build +SOURCES := Src +DATA := data +INCLUDES := + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- + +PCFLAGS = -DPRECISE_CPU_CYCLES=1 -DPRECISE_CIA_CYCLES=1 -DPC_IS_POINTER=0 +CFLAGS = -O3 -g -Wall $(MACHDEP) $(INCLUDE) -I$(DEVKITPRO)/SDL/include -U__unix -DHAVE_SDL +CXXFLAGS = $(CFLAGS) + +LDFLAGS = -L$(DEVKITPRO)/SDL/lib -g $(MACHDEP) -Wl,-Map,$(notdir $@).map + +#--------------------------------------------------------------------------------- +# any extra libraries we wish to link with the project +#--------------------------------------------------------------------------------- +LIBS := -lSDL_image -lSDL_ttf -ljpeg -lpng -lz -lSDL -lfreetype -lfat -lwiiuse -lbte -logc -lm + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export OUTPUT := $(CURDIR)/$(TARGET) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +export DEPSDIR := $(CURDIR)/$(BUILD) + +#--------------------------------------------------------------------------------- +# automatically build a list of object files for our project +#--------------------------------------------------------------------------------- +CFILES := char_to_kc.c gcaudio.c +CPPFILES := Display.cpp main.cpp Prefs.cpp SID.cpp REU.cpp IEC.cpp 1541fs.cpp \ + 1541d64.cpp 1541t64.cpp 1541job.cpp SAM.cpp C64.cpp CPUC64.cpp VIC.cpp \ + CIA.cpp CPU1541.cpp menu.cpp +sFILES := +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.S))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) + export LD := $(CC) +else + export LD := $(CXX) +endif + +export OFILES := $(addsuffix .o,$(BINFILES)) \ + $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) \ + $(sFILES:.s=.o) $(SFILES:.S=.o) + +#--------------------------------------------------------------------------------- +# build a list of include paths +#--------------------------------------------------------------------------------- +export INCLUDE := $(foreach dir,$(INCLUDES), -iquote $(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) \ + -I$(LIBOGC_INC) + +#--------------------------------------------------------------------------------- +# build a list of library paths +#--------------------------------------------------------------------------------- +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) \ + -L$(LIBOGC_LIB) + +export OUTPUT := $(CURDIR)/$(TARGET) +.PHONY: $(BUILD) clean + +#--------------------------------------------------------------------------------- +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @make --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) $(OUTPUT).elf $(OUTPUT).dol Src/sysconfig.h Src/*.o \ + FrodoSC FrodoPC dist + +dist: $(BUILD) + install -d $@/apps/frodo + install -d $@/apps/frodo/images + install -d $@/apps/frodo/saves + install -d $@/apps/frodo/tmp + cp $(TARGET).dol $@/apps/frodo/boot.dol + cp meta.xml $@/apps/frodo/ + cp icon.png $@/apps/frodo/ + cp FreeMono.ttf $@/apps/frodo/ + cp "Kernal ROM" $@/apps/frodo/Kernal_ROM + cp "Char ROM" $@/apps/frodo/Char_ROM + cp "1541 ROM" $@/apps/frodo/1541_ROM + cp "Basic ROM" $@/apps/frodo/Basic_ROM + cp frodorc $@/apps/frodo/ + cd $@ && tar -czf ../frodo-wii-bin.tar.gz * + +#--------------------------------------------------------------------------------- +run: + wiiload $(TARGET).dol + + +#--------------------------------------------------------------------------------- +else + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +$(OUTPUT).dol: $(OUTPUT).elf +$(OUTPUT).elf: sysconfig.h $(OFILES) + +#--------------------------------------------------------------------------------- +# This rule links in binary data with the .jpg extension +#--------------------------------------------------------------------------------- +%.jpg.o : %.jpg +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + $(bin2o) + +sysconfig.h: sysconfig.h.Wii + cp $< $@ + +Display.cpp: Display_SDL.h + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- diff --git a/Src/1541_ROM.h b/Src/1541_ROM.h new file mode 100644 index 0000000..d55af33 --- /dev/null +++ b/Src/1541_ROM.h @@ -0,0 +1,2057 @@ +/* + * 1541_ROM.h - 1541 ROM + * + * Frodo (C) 1994-1997,2002-2005 Christian Bauer + * C64/1541 ROMs (C) Commodore Business Machines + */ + +static const uint8 builtin_drive_rom[DRIVE_ROM_SIZE] = { + 0x97, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0x78, 0xa9, 0xf7, 0x2d, 0x00, 0x1c, 0x48, 0xa5, + 0x7f, 0xf0, 0x05, 0x68, 0x09, 0x00, 0xd0, 0x03, + 0x68, 0x09, 0x08, 0x8d, 0x00, 0x1c, 0x58, 0x60, + 0x78, 0xa9, 0x08, 0x0d, 0x00, 0x1c, 0x8d, 0x00, + 0x1c, 0x58, 0x60, 0xa9, 0x00, 0x8d, 0x6c, 0x02, + 0x8d, 0x6d, 0x02, 0x60, 0x78, 0x8a, 0x48, 0xa9, + 0x50, 0x8d, 0x6c, 0x02, 0xa2, 0x00, 0xbd, 0xca, + 0xfe, 0x8d, 0x6d, 0x02, 0x0d, 0x00, 0x1c, 0x8d, + 0x00, 0x1c, 0x68, 0xaa, 0x58, 0x60, 0xa9, 0x00, + 0x8d, 0xf9, 0x02, 0xad, 0x8e, 0x02, 0x85, 0x7f, + 0x20, 0xbc, 0xe6, 0xa5, 0x84, 0x10, 0x09, 0x29, + 0x0f, 0xc9, 0x0f, 0xf0, 0x03, 0x4c, 0xb4, 0xd7, + 0x20, 0xb3, 0xc2, 0xb1, 0xa3, 0x8d, 0x75, 0x02, + 0xa2, 0x0b, 0xbd, 0x89, 0xfe, 0xcd, 0x75, 0x02, + 0xf0, 0x08, 0xca, 0x10, 0xf5, 0xa9, 0x31, 0x4c, + 0xc8, 0xc1, 0x8e, 0x2a, 0x02, 0xe0, 0x09, 0x90, + 0x03, 0x20, 0xee, 0xc1, 0xae, 0x2a, 0x02, 0xbd, + 0x95, 0xfe, 0x85, 0x6f, 0xbd, 0xa1, 0xfe, 0x85, + 0x70, 0x6c, 0x6f, 0x00, 0xa9, 0x00, 0x8d, 0xf9, + 0x02, 0xad, 0x6c, 0x02, 0xd0, 0x2a, 0xa0, 0x00, + 0x98, 0x84, 0x80, 0x84, 0x81, 0x84, 0xa3, 0x20, + 0xc7, 0xe6, 0x20, 0x23, 0xc1, 0xa5, 0x7f, 0x8d, + 0x8e, 0x02, 0xaa, 0xa9, 0x00, 0x95, 0xff, 0x20, + 0xbd, 0xc1, 0x4c, 0xda, 0xd4, 0xa0, 0x28, 0xa9, + 0x00, 0x99, 0x00, 0x02, 0x88, 0x10, 0xfa, 0x60, + 0xa0, 0x00, 0x84, 0x80, 0x84, 0x81, 0x4c, 0x45, + 0xe6, 0xa2, 0x00, 0x8e, 0x7a, 0x02, 0xa9, 0x3a, + 0x20, 0x68, 0xc2, 0xf0, 0x05, 0x88, 0x88, 0x8c, + 0x7a, 0x02, 0x4c, 0x68, 0xc3, 0xa0, 0x00, 0xa2, + 0x00, 0xa9, 0x3a, 0x4c, 0x68, 0xc2, 0x20, 0xe5, + 0xc1, 0xd0, 0x05, 0xa9, 0x34, 0x4c, 0xc8, 0xc1, + 0x88, 0x88, 0x8c, 0x7a, 0x02, 0x8a, 0xd0, 0xf3, + 0xa9, 0x3d, 0x20, 0x68, 0xc2, 0x8a, 0xf0, 0x02, + 0xa9, 0x40, 0x09, 0x21, 0x8d, 0x8b, 0x02, 0xe8, + 0x8e, 0x77, 0x02, 0x8e, 0x78, 0x02, 0xad, 0x8a, + 0x02, 0xf0, 0x0d, 0xa9, 0x80, 0x0d, 0x8b, 0x02, + 0x8d, 0x8b, 0x02, 0xa9, 0x00, 0x8d, 0x8a, 0x02, + 0x98, 0xf0, 0x29, 0x9d, 0x7a, 0x02, 0xad, 0x77, + 0x02, 0x8d, 0x79, 0x02, 0xa9, 0x8d, 0x20, 0x68, + 0xc2, 0xe8, 0x8e, 0x78, 0x02, 0xca, 0xad, 0x8a, + 0x02, 0xf0, 0x02, 0xa9, 0x08, 0xec, 0x77, 0x02, + 0xf0, 0x02, 0x09, 0x04, 0x09, 0x03, 0x4d, 0x8b, + 0x02, 0x8d, 0x8b, 0x02, 0xad, 0x8b, 0x02, 0xae, + 0x2a, 0x02, 0x3d, 0xa5, 0xfe, 0xd0, 0x01, 0x60, + 0x8d, 0x6c, 0x02, 0xa9, 0x30, 0x4c, 0xc8, 0xc1, + 0x8d, 0x75, 0x02, 0xcc, 0x74, 0x02, 0xb0, 0x2e, + 0xb1, 0xa3, 0xc8, 0xcd, 0x75, 0x02, 0xf0, 0x28, + 0xc9, 0x2a, 0xf0, 0x04, 0xc9, 0x3f, 0xd0, 0x03, + 0xee, 0x8a, 0x02, 0xc9, 0x2c, 0xd0, 0xe4, 0x98, + 0x9d, 0x7b, 0x02, 0xad, 0x8a, 0x02, 0x29, 0x7f, + 0xf0, 0x07, 0xa9, 0x80, 0x95, 0xe7, 0x8d, 0x8a, + 0x02, 0xe8, 0xe0, 0x04, 0x90, 0xcd, 0xa0, 0x00, + 0xad, 0x74, 0x02, 0x9d, 0x7b, 0x02, 0xad, 0x8a, + 0x02, 0x29, 0x7f, 0xf0, 0x04, 0xa9, 0x80, 0x95, + 0xe7, 0x98, 0x60, 0xa4, 0xa3, 0xf0, 0x14, 0x88, + 0xf0, 0x10, 0xb9, 0x00, 0x02, 0xc9, 0x0d, 0xf0, + 0x0a, 0x88, 0xb9, 0x00, 0x02, 0xc9, 0x0d, 0xf0, + 0x02, 0xc8, 0xc8, 0x8c, 0x74, 0x02, 0xc0, 0x2a, + 0xa0, 0xff, 0x90, 0x08, 0x8c, 0x2a, 0x02, 0xa9, + 0x32, 0x4c, 0xc8, 0xc1, 0xa0, 0x00, 0x98, 0x85, + 0xa3, 0x8d, 0x58, 0x02, 0x8d, 0x4a, 0x02, 0x8d, + 0x96, 0x02, 0x85, 0xd3, 0x8d, 0x79, 0x02, 0x8d, + 0x77, 0x02, 0x8d, 0x78, 0x02, 0x8d, 0x8a, 0x02, + 0x8d, 0x6c, 0x02, 0xa2, 0x05, 0x9d, 0x79, 0x02, + 0x95, 0xd7, 0x95, 0xdc, 0x95, 0xe1, 0x95, 0xe6, + 0x9d, 0x7f, 0x02, 0x9d, 0x84, 0x02, 0xca, 0xd0, + 0xec, 0x60, 0xad, 0x78, 0x02, 0x8d, 0x77, 0x02, + 0xa9, 0x01, 0x8d, 0x78, 0x02, 0x8d, 0x79, 0x02, + 0xac, 0x8e, 0x02, 0xa2, 0x00, 0x86, 0xd3, 0xbd, + 0x7a, 0x02, 0x20, 0x3c, 0xc3, 0xa6, 0xd3, 0x9d, + 0x7a, 0x02, 0x98, 0x95, 0xe2, 0xe8, 0xec, 0x78, + 0x02, 0x90, 0xea, 0x60, 0xaa, 0xa0, 0x00, 0xa9, + 0x3a, 0xdd, 0x01, 0x02, 0xf0, 0x0c, 0xdd, 0x00, + 0x02, 0xd0, 0x16, 0xe8, 0x98, 0x29, 0x01, 0xa8, + 0x8a, 0x60, 0xbd, 0x00, 0x02, 0xe8, 0xe8, 0xc9, + 0x30, 0xf0, 0xf2, 0xc9, 0x31, 0xf0, 0xee, 0xd0, + 0xeb, 0x98, 0x09, 0x80, 0x29, 0x81, 0xd0, 0xe7, + 0xa9, 0x00, 0x8d, 0x8b, 0x02, 0xac, 0x7a, 0x02, + 0xb1, 0xa3, 0x20, 0xbd, 0xc3, 0x10, 0x11, 0xc8, + 0xcc, 0x74, 0x02, 0xb0, 0x06, 0xac, 0x74, 0x02, + 0x88, 0xd0, 0xed, 0xce, 0x8b, 0x02, 0xa9, 0x00, + 0x29, 0x01, 0x85, 0x7f, 0x4c, 0x00, 0xc1, 0xa5, + 0x7f, 0x49, 0x01, 0x29, 0x01, 0x85, 0x7f, 0x60, + 0xa0, 0x00, 0xad, 0x77, 0x02, 0xcd, 0x78, 0x02, + 0xf0, 0x16, 0xce, 0x78, 0x02, 0xac, 0x78, 0x02, + 0xb9, 0x7a, 0x02, 0xa8, 0xb1, 0xa3, 0xa0, 0x04, + 0xd9, 0xbb, 0xfe, 0xf0, 0x03, 0x88, 0xd0, 0xf8, + 0x98, 0x8d, 0x96, 0x02, 0x60, 0xc9, 0x30, 0xf0, + 0x06, 0xc9, 0x31, 0xf0, 0x02, 0x09, 0x80, 0x29, + 0x81, 0x60, 0xa9, 0x00, 0x85, 0x6f, 0x8d, 0x8d, + 0x02, 0x48, 0xae, 0x78, 0x02, 0x68, 0x05, 0x6f, + 0x48, 0xa9, 0x01, 0x85, 0x6f, 0xca, 0x30, 0x0f, + 0xb5, 0xe2, 0x10, 0x04, 0x06, 0x6f, 0x06, 0x6f, + 0x4a, 0x90, 0xea, 0x06, 0x6f, 0xd0, 0xe6, 0x68, + 0xaa, 0xbd, 0x3f, 0xc4, 0x48, 0x29, 0x03, 0x8d, + 0x8c, 0x02, 0x68, 0x0a, 0x10, 0x3e, 0xa5, 0xe2, + 0x29, 0x01, 0x85, 0x7f, 0xad, 0x8c, 0x02, 0xf0, + 0x2b, 0x20, 0x3d, 0xc6, 0xf0, 0x12, 0x20, 0x8f, + 0xc3, 0xa9, 0x00, 0x8d, 0x8c, 0x02, 0x20, 0x3d, + 0xc6, 0xf0, 0x1e, 0xa9, 0x74, 0x20, 0xc8, 0xc1, + 0x20, 0x8f, 0xc3, 0x20, 0x3d, 0xc6, 0x08, 0x20, + 0x8f, 0xc3, 0x28, 0xf0, 0x0c, 0xa9, 0x00, 0x8d, + 0x8c, 0x02, 0xf0, 0x05, 0x20, 0x3d, 0xc6, 0xd0, + 0xe2, 0x4c, 0x00, 0xc1, 0x2a, 0x4c, 0x00, 0xc4, + 0x00, 0x80, 0x41, 0x01, 0x01, 0x01, 0x01, 0x81, + 0x81, 0x81, 0x81, 0x42, 0x42, 0x42, 0x42, 0x20, + 0xca, 0xc3, 0xa9, 0x00, 0x8d, 0x92, 0x02, 0x20, + 0xac, 0xc5, 0xd0, 0x19, 0xce, 0x8c, 0x02, 0x10, + 0x01, 0x60, 0xa9, 0x01, 0x8d, 0x8d, 0x02, 0x20, + 0x8f, 0xc3, 0x20, 0x00, 0xc1, 0x4c, 0x52, 0xc4, + 0x20, 0x17, 0xc6, 0xf0, 0x10, 0x20, 0xd8, 0xc4, + 0xad, 0x8f, 0x02, 0xf0, 0x01, 0x60, 0xad, 0x53, + 0x02, 0x30, 0xed, 0x10, 0xf0, 0xad, 0x8f, 0x02, + 0xf0, 0xd2, 0x60, 0x20, 0x04, 0xc6, 0xf0, 0x1a, + 0xd0, 0x28, 0xa9, 0x01, 0x8d, 0x8d, 0x02, 0x20, + 0x8f, 0xc3, 0x20, 0x00, 0xc1, 0xa9, 0x00, 0x8d, + 0x92, 0x02, 0x20, 0xac, 0xc5, 0xd0, 0x13, 0x8d, + 0x8f, 0x02, 0xad, 0x8f, 0x02, 0xd0, 0x28, 0xce, + 0x8c, 0x02, 0x10, 0xde, 0x60, 0x20, 0x17, 0xc6, + 0xf0, 0xf0, 0x20, 0xd8, 0xc4, 0xae, 0x53, 0x02, + 0x10, 0x07, 0xad, 0x8f, 0x02, 0xf0, 0xee, 0xd0, + 0x0e, 0xad, 0x96, 0x02, 0xf0, 0x09, 0xb5, 0xe7, + 0x29, 0x07, 0xcd, 0x96, 0x02, 0xd0, 0xde, 0x60, + 0xa2, 0xff, 0x8e, 0x53, 0x02, 0xe8, 0x8e, 0x8a, + 0x02, 0x20, 0x89, 0xc5, 0xf0, 0x06, 0x60, 0x20, + 0x94, 0xc5, 0xd0, 0xfa, 0xa5, 0x7f, 0x55, 0xe2, + 0x4a, 0x90, 0x0b, 0x29, 0x40, 0xf0, 0xf0, 0xa9, + 0x02, 0xcd, 0x8c, 0x02, 0xf0, 0xe9, 0xbd, 0x7a, + 0x02, 0xaa, 0x20, 0xa6, 0xc6, 0xa0, 0x03, 0x4c, + 0x1d, 0xc5, 0xbd, 0x00, 0x02, 0xd1, 0x94, 0xf0, + 0x0a, 0xc9, 0x3f, 0xd0, 0xd2, 0xb1, 0x94, 0xc9, + 0xa0, 0xf0, 0xcc, 0xe8, 0xc8, 0xec, 0x76, 0x02, + 0xb0, 0x09, 0xbd, 0x00, 0x02, 0xc9, 0x2a, 0xf0, + 0x0c, 0xd0, 0xdf, 0xc0, 0x13, 0xb0, 0x06, 0xb1, + 0x94, 0xc9, 0xa0, 0xd0, 0xb2, 0xae, 0x79, 0x02, + 0x8e, 0x53, 0x02, 0xb5, 0xe7, 0x29, 0x80, 0x8d, + 0x8a, 0x02, 0xad, 0x94, 0x02, 0x95, 0xdd, 0xa5, + 0x81, 0x95, 0xd8, 0xa0, 0x00, 0xb1, 0x94, 0xc8, + 0x48, 0x29, 0x40, 0x85, 0x6f, 0x68, 0x29, 0xdf, + 0x30, 0x02, 0x09, 0x20, 0x29, 0x27, 0x05, 0x6f, + 0x85, 0x6f, 0xa9, 0x80, 0x35, 0xe7, 0x05, 0x6f, + 0x95, 0xe7, 0xb5, 0xe2, 0x29, 0x80, 0x05, 0x7f, + 0x95, 0xe2, 0xb1, 0x94, 0x9d, 0x80, 0x02, 0xc8, + 0xb1, 0x94, 0x9d, 0x85, 0x02, 0xad, 0x58, 0x02, + 0xd0, 0x07, 0xa0, 0x15, 0xb1, 0x94, 0x8d, 0x58, + 0x02, 0xa9, 0xff, 0x8d, 0x8f, 0x02, 0xad, 0x78, + 0x02, 0x8d, 0x79, 0x02, 0xce, 0x79, 0x02, 0x10, + 0x01, 0x60, 0xae, 0x79, 0x02, 0xb5, 0xe7, 0x30, + 0x05, 0xbd, 0x80, 0x02, 0xd0, 0xee, 0xa9, 0x00, + 0x8d, 0x8f, 0x02, 0x60, 0xa0, 0x00, 0x8c, 0x91, + 0x02, 0x88, 0x8c, 0x53, 0x02, 0xad, 0x85, 0xfe, + 0x85, 0x80, 0xa9, 0x01, 0x85, 0x81, 0x8d, 0x93, + 0x02, 0x20, 0x75, 0xd4, 0xad, 0x93, 0x02, 0xd0, + 0x01, 0x60, 0xa9, 0x07, 0x8d, 0x95, 0x02, 0xa9, + 0x00, 0x20, 0xf6, 0xd4, 0x8d, 0x93, 0x02, 0x20, + 0xe8, 0xd4, 0xce, 0x95, 0x02, 0xa0, 0x00, 0xb1, + 0x94, 0xd0, 0x18, 0xad, 0x91, 0x02, 0xd0, 0x2f, + 0x20, 0x3b, 0xde, 0xa5, 0x81, 0x8d, 0x91, 0x02, + 0xa5, 0x94, 0xae, 0x92, 0x02, 0x8d, 0x92, 0x02, + 0xf0, 0x1d, 0x60, 0xa2, 0x01, 0xec, 0x92, 0x02, + 0xd0, 0x2d, 0xf0, 0x13, 0xad, 0x85, 0xfe, 0x85, + 0x80, 0xad, 0x90, 0x02, 0x85, 0x81, 0x20, 0x75, + 0xd4, 0xad, 0x94, 0x02, 0x20, 0xc8, 0xd4, 0xa9, + 0xff, 0x8d, 0x53, 0x02, 0xad, 0x95, 0x02, 0x30, + 0x08, 0xa9, 0x20, 0x20, 0xc6, 0xd1, 0x4c, 0xd7, + 0xc5, 0x20, 0x4d, 0xd4, 0x4c, 0xc4, 0xc5, 0xa5, + 0x94, 0x8d, 0x94, 0x02, 0x20, 0x3b, 0xde, 0xa5, + 0x81, 0x8d, 0x90, 0x02, 0x60, 0xa5, 0x68, 0xd0, + 0x28, 0xa6, 0x7f, 0x56, 0x1c, 0x90, 0x22, 0xa9, + 0xff, 0x8d, 0x98, 0x02, 0x20, 0x0e, 0xd0, 0xa0, + 0xff, 0xc9, 0x02, 0xf0, 0x0a, 0xc9, 0x03, 0xf0, + 0x06, 0xc9, 0x0f, 0xf0, 0x02, 0xa0, 0x00, 0xa6, + 0x7f, 0x98, 0x95, 0xff, 0xd0, 0x03, 0x20, 0x42, + 0xd0, 0xa6, 0x7f, 0xb5, 0xff, 0x60, 0x48, 0x20, + 0xa6, 0xc6, 0x20, 0x88, 0xc6, 0x68, 0x38, 0xed, + 0x4b, 0x02, 0xaa, 0xf0, 0x0a, 0x90, 0x08, 0xa9, + 0xa0, 0x91, 0x94, 0xc8, 0xca, 0xd0, 0xfa, 0x60, + 0x98, 0x0a, 0xa8, 0xb9, 0x99, 0x00, 0x85, 0x94, + 0xb9, 0x9a, 0x00, 0x85, 0x95, 0xa0, 0x00, 0xbd, + 0x00, 0x02, 0x91, 0x94, 0xc8, 0xf0, 0x06, 0xe8, + 0xec, 0x76, 0x02, 0x90, 0xf2, 0x60, 0xa9, 0x00, + 0x8d, 0x4b, 0x02, 0x8a, 0x48, 0xbd, 0x00, 0x02, + 0xc9, 0x2c, 0xf0, 0x14, 0xc9, 0x3d, 0xf0, 0x10, + 0xee, 0x4b, 0x02, 0xe8, 0xa9, 0x0f, 0xcd, 0x4b, + 0x02, 0x90, 0x05, 0xec, 0x74, 0x02, 0x90, 0xe5, + 0x8e, 0x76, 0x02, 0x68, 0xaa, 0x60, 0xa5, 0x83, + 0x48, 0xa5, 0x82, 0x48, 0x20, 0xde, 0xc6, 0x68, + 0x85, 0x82, 0x68, 0x85, 0x83, 0x60, 0xa9, 0x11, + 0x85, 0x83, 0x20, 0xeb, 0xd0, 0x20, 0xe8, 0xd4, + 0xad, 0x53, 0x02, 0x10, 0x0a, 0xad, 0x8d, 0x02, + 0xd0, 0x0a, 0x20, 0x06, 0xc8, 0x18, 0x60, 0xad, + 0x8d, 0x02, 0xf0, 0x1f, 0xce, 0x8d, 0x02, 0xd0, + 0x0d, 0xce, 0x8d, 0x02, 0x20, 0x8f, 0xc3, 0x20, + 0x06, 0xc8, 0x38, 0x4c, 0x8f, 0xc3, 0xa9, 0x00, + 0x8d, 0x73, 0x02, 0x8d, 0x8d, 0x02, 0x20, 0xb7, + 0xc7, 0x38, 0x60, 0xa2, 0x18, 0xa0, 0x1d, 0xb1, + 0x94, 0x8d, 0x73, 0x02, 0xf0, 0x02, 0xa2, 0x16, + 0x88, 0xb1, 0x94, 0x8d, 0x72, 0x02, 0xe0, 0x16, + 0xf0, 0x0a, 0xc9, 0x0a, 0x90, 0x06, 0xca, 0xc9, + 0x64, 0x90, 0x01, 0xca, 0x20, 0xac, 0xc7, 0xb1, + 0x94, 0x48, 0x0a, 0x10, 0x05, 0xa9, 0x3c, 0x9d, + 0xb2, 0x02, 0x68, 0x29, 0x0f, 0xa8, 0xb9, 0xc5, + 0xfe, 0x9d, 0xb1, 0x02, 0xca, 0xb9, 0xc0, 0xfe, + 0x9d, 0xb1, 0x02, 0xca, 0xb9, 0xbb, 0xfe, 0x9d, + 0xb1, 0x02, 0xca, 0xca, 0xb0, 0x05, 0xa9, 0x2a, + 0x9d, 0xb2, 0x02, 0xa9, 0xa0, 0x9d, 0xb1, 0x02, + 0xca, 0xa0, 0x12, 0xb1, 0x94, 0x9d, 0xb1, 0x02, + 0xca, 0x88, 0xc0, 0x03, 0xb0, 0xf5, 0xa9, 0x22, + 0x9d, 0xb1, 0x02, 0xe8, 0xe0, 0x20, 0xb0, 0x0b, + 0xbd, 0xb1, 0x02, 0xc9, 0x22, 0xf0, 0x04, 0xc9, + 0xa0, 0xd0, 0xf0, 0xa9, 0x22, 0x9d, 0xb1, 0x02, + 0xe8, 0xe0, 0x20, 0xb0, 0x0a, 0xa9, 0x7f, 0x3d, + 0xb1, 0x02, 0x9d, 0xb1, 0x02, 0x10, 0xf1, 0x20, + 0xb5, 0xc4, 0x38, 0x60, 0xa0, 0x1b, 0xa9, 0x20, + 0x99, 0xb0, 0x02, 0x88, 0xd0, 0xfa, 0x60, 0x20, + 0x19, 0xf1, 0x20, 0xdf, 0xf0, 0x20, 0xac, 0xc7, + 0xa9, 0xff, 0x85, 0x6f, 0xa6, 0x7f, 0x8e, 0x72, + 0x02, 0xa9, 0x00, 0x8d, 0x73, 0x02, 0xa6, 0xf9, + 0xbd, 0xe0, 0xfe, 0x85, 0x95, 0xad, 0x88, 0xfe, + 0x85, 0x94, 0xa0, 0x16, 0xb1, 0x94, 0xc9, 0xa0, + 0xd0, 0x0b, 0xa9, 0x31, 0x2c, 0xb1, 0x94, 0xc9, + 0xa0, 0xd0, 0x02, 0xa9, 0x20, 0x99, 0xb3, 0x02, + 0x88, 0x10, 0xf2, 0xa9, 0x12, 0x8d, 0xb1, 0x02, + 0xa9, 0x22, 0x8d, 0xb2, 0x02, 0x8d, 0xc3, 0x02, + 0xa9, 0x20, 0x8d, 0xc4, 0x02, 0x60, 0x20, 0xac, + 0xc7, 0xa0, 0x0b, 0xb9, 0x17, 0xc8, 0x99, 0xb1, + 0x02, 0x88, 0x10, 0xf7, 0x4c, 0x4d, 0xef, 0x42, + 0x4c, 0x4f, 0x43, 0x4b, 0x53, 0x20, 0x46, 0x52, + 0x45, 0x45, 0x2e, 0x20, 0x98, 0xc3, 0x20, 0x20, + 0xc3, 0x20, 0xca, 0xc3, 0xa9, 0x00, 0x85, 0x86, + 0x20, 0x9d, 0xc4, 0x30, 0x3d, 0x20, 0xb7, 0xdd, + 0x90, 0x33, 0xa0, 0x00, 0xb1, 0x94, 0x29, 0x40, + 0xd0, 0x2b, 0x20, 0xb6, 0xc8, 0xa0, 0x13, 0xb1, + 0x94, 0xf0, 0x0a, 0x85, 0x80, 0xc8, 0xb1, 0x94, + 0x85, 0x81, 0x20, 0x7d, 0xc8, 0xae, 0x53, 0x02, + 0xa9, 0x20, 0x35, 0xe7, 0xd0, 0x0d, 0xbd, 0x80, + 0x02, 0x85, 0x80, 0xbd, 0x85, 0x02, 0x85, 0x81, + 0x20, 0x7d, 0xc8, 0xe6, 0x86, 0x20, 0x8b, 0xc4, + 0x10, 0xc3, 0xa5, 0x86, 0x85, 0x80, 0xa9, 0x01, + 0xa0, 0x00, 0x4c, 0xa3, 0xc1, 0x20, 0x5f, 0xef, + 0x20, 0x75, 0xd4, 0x20, 0x19, 0xf1, 0xb5, 0xa7, + 0xc9, 0xff, 0xf0, 0x08, 0xad, 0xf9, 0x02, 0x09, + 0x40, 0x8d, 0xf9, 0x02, 0xa9, 0x00, 0x20, 0xc8, + 0xd4, 0x20, 0x56, 0xd1, 0x85, 0x80, 0x20, 0x56, + 0xd1, 0x85, 0x81, 0xa5, 0x80, 0xd0, 0x06, 0x20, + 0xf4, 0xee, 0x4c, 0x27, 0xd2, 0x20, 0x5f, 0xef, + 0x20, 0x4d, 0xd4, 0x4c, 0x94, 0xc8, 0xa0, 0x00, + 0x98, 0x91, 0x94, 0x20, 0x5e, 0xde, 0x4c, 0x99, + 0xd5, 0xa9, 0x31, 0x4c, 0xc8, 0xc1, 0xa9, 0x4c, + 0x8d, 0x00, 0x06, 0xa9, 0xc7, 0x8d, 0x01, 0x06, + 0xa9, 0xfa, 0x8d, 0x02, 0x06, 0xa9, 0x03, 0x20, + 0xd3, 0xd6, 0xa5, 0x7f, 0x09, 0xe0, 0x85, 0x03, + 0xa5, 0x03, 0x30, 0xfc, 0xc9, 0x02, 0x90, 0x07, + 0xa9, 0x03, 0xa2, 0x00, 0x4c, 0x0a, 0xe6, 0x60, + 0xa9, 0xe0, 0x8d, 0x4f, 0x02, 0x20, 0xd1, 0xf0, + 0x20, 0x19, 0xf1, 0xa9, 0xff, 0x95, 0xa7, 0xa9, + 0x0f, 0x8d, 0x56, 0x02, 0x20, 0xe5, 0xc1, 0xd0, + 0x03, 0x4c, 0xc1, 0xc8, 0x20, 0xf8, 0xc1, 0x20, + 0x20, 0xc3, 0xad, 0x8b, 0x02, 0x29, 0x55, 0xd0, + 0x0f, 0xae, 0x7a, 0x02, 0xbd, 0x00, 0x02, 0xc9, + 0x2a, 0xd0, 0x05, 0xa9, 0x30, 0x4c, 0xc8, 0xc1, + 0xad, 0x8b, 0x02, 0x29, 0xd9, 0xd0, 0xf4, 0x4c, + 0x52, 0xc9, 0xa9, 0x00, 0x8d, 0x58, 0x02, 0x8d, + 0x8c, 0x02, 0x8d, 0x80, 0x02, 0x8d, 0x81, 0x02, + 0xa5, 0xe3, 0x29, 0x01, 0x85, 0x7f, 0x09, 0x01, + 0x8d, 0x91, 0x02, 0xad, 0x7b, 0x02, 0x8d, 0x7a, + 0x02, 0x60, 0x20, 0x4f, 0xc4, 0xad, 0x78, 0x02, + 0xc9, 0x03, 0x90, 0x45, 0xa5, 0xe2, 0xc5, 0xe3, + 0xd0, 0x3f, 0xa5, 0xdd, 0xc5, 0xde, 0xd0, 0x39, + 0xa5, 0xd8, 0xc5, 0xd9, 0xd0, 0x33, 0x20, 0xcc, + 0xca, 0xa9, 0x01, 0x8d, 0x79, 0x02, 0x20, 0xfa, + 0xc9, 0x20, 0x25, 0xd1, 0xf0, 0x04, 0xc9, 0x02, + 0xd0, 0x05, 0xa9, 0x64, 0x20, 0xc8, 0xc1, 0xa9, + 0x12, 0x85, 0x83, 0xad, 0x3c, 0x02, 0x8d, 0x3d, + 0x02, 0xa9, 0xff, 0x8d, 0x3c, 0x02, 0x20, 0x2a, + 0xda, 0xa2, 0x02, 0x20, 0xb9, 0xc9, 0x4c, 0x94, + 0xc1, 0x20, 0xa7, 0xc9, 0x4c, 0x94, 0xc1, 0x20, + 0xe7, 0xca, 0xa5, 0xe2, 0x29, 0x01, 0x85, 0x7f, + 0x20, 0x86, 0xd4, 0x20, 0xe4, 0xd6, 0xae, 0x77, + 0x02, 0x8e, 0x79, 0x02, 0x20, 0xfa, 0xc9, 0xa9, + 0x11, 0x85, 0x83, 0x20, 0xeb, 0xd0, 0x20, 0x25, + 0xd1, 0xd0, 0x03, 0x20, 0x53, 0xca, 0xa9, 0x08, + 0x85, 0xf8, 0x4c, 0xd8, 0xc9, 0x20, 0x9b, 0xcf, + 0x20, 0x35, 0xca, 0xa9, 0x80, 0x20, 0xa6, 0xdd, + 0xf0, 0xf3, 0x20, 0x25, 0xd1, 0xf0, 0x03, 0x20, + 0x9b, 0xcf, 0xae, 0x79, 0x02, 0xe8, 0xec, 0x78, + 0x02, 0x90, 0xc6, 0xa9, 0x12, 0x85, 0x83, 0x4c, + 0x02, 0xdb, 0xae, 0x79, 0x02, 0xb5, 0xe2, 0x29, + 0x01, 0x85, 0x7f, 0xad, 0x85, 0xfe, 0x85, 0x80, + 0xb5, 0xd8, 0x85, 0x81, 0x20, 0x75, 0xd4, 0xae, + 0x79, 0x02, 0xb5, 0xdd, 0x20, 0xc8, 0xd4, 0xae, + 0x79, 0x02, 0xb5, 0xe7, 0x29, 0x07, 0x8d, 0x4a, + 0x02, 0xa9, 0x00, 0x8d, 0x58, 0x02, 0x20, 0xa0, + 0xd9, 0xa0, 0x01, 0x20, 0x25, 0xd1, 0xf0, 0x01, + 0xc8, 0x98, 0x4c, 0xc8, 0xd4, 0xa9, 0x11, 0x85, + 0x83, 0x20, 0x9b, 0xd3, 0x85, 0x85, 0xa6, 0x82, + 0xb5, 0xf2, 0x29, 0x08, 0x85, 0xf8, 0xd0, 0x0a, + 0x20, 0x25, 0xd1, 0xf0, 0x05, 0xa9, 0x80, 0x20, + 0x97, 0xdd, 0x60, 0x20, 0xd3, 0xd1, 0x20, 0xcb, + 0xe1, 0xa5, 0xd6, 0x48, 0xa5, 0xd5, 0x48, 0xa9, + 0x12, 0x85, 0x83, 0x20, 0x07, 0xd1, 0x20, 0xd3, + 0xd1, 0x20, 0xcb, 0xe1, 0x20, 0x9c, 0xe2, 0xa5, + 0xd6, 0x85, 0x87, 0xa5, 0xd5, 0x85, 0x86, 0xa9, + 0x00, 0x85, 0x88, 0x85, 0xd4, 0x85, 0xd7, 0x68, + 0x85, 0xd5, 0x68, 0x85, 0xd6, 0x4c, 0x3b, 0xe3, + 0x20, 0x20, 0xc3, 0xa5, 0xe3, 0x29, 0x01, 0x85, + 0xe3, 0xc5, 0xe2, 0xf0, 0x02, 0x09, 0x80, 0x85, + 0xe2, 0x20, 0x4f, 0xc4, 0x20, 0xe7, 0xca, 0xa5, + 0xe3, 0x29, 0x01, 0x85, 0x7f, 0xa5, 0xd9, 0x85, + 0x81, 0x20, 0x57, 0xde, 0x20, 0x99, 0xd5, 0xa5, + 0xde, 0x18, 0x69, 0x03, 0x20, 0xc8, 0xd4, 0x20, + 0x93, 0xdf, 0xa8, 0xae, 0x7a, 0x02, 0xa9, 0x10, + 0x20, 0x6e, 0xc6, 0x20, 0x5e, 0xde, 0x20, 0x99, + 0xd5, 0x4c, 0x94, 0xc1, 0xa5, 0xe8, 0x29, 0x07, + 0x8d, 0x4a, 0x02, 0xae, 0x78, 0x02, 0xca, 0xec, + 0x77, 0x02, 0x90, 0x0a, 0xbd, 0x80, 0x02, 0xd0, + 0xf5, 0xa9, 0x62, 0x4c, 0xc8, 0xc1, 0x60, 0x20, + 0xcc, 0xca, 0xbd, 0x80, 0x02, 0xf0, 0x05, 0xa9, + 0x63, 0x4c, 0xc8, 0xc1, 0xca, 0x10, 0xf3, 0x60, + 0xad, 0x01, 0x02, 0xc9, 0x2d, 0xd0, 0x4c, 0xad, + 0x03, 0x02, 0x85, 0x6f, 0xad, 0x04, 0x02, 0x85, + 0x70, 0xa0, 0x00, 0xad, 0x02, 0x02, 0xc9, 0x52, + 0xf0, 0x0e, 0x20, 0x58, 0xf2, 0xc9, 0x57, 0xf0, + 0x37, 0xc9, 0x45, 0xd0, 0x2e, 0x6c, 0x6f, 0x00, + 0xb1, 0x6f, 0x85, 0x85, 0xad, 0x74, 0x02, 0xc9, + 0x06, 0x90, 0x1a, 0xae, 0x05, 0x02, 0xca, 0xf0, + 0x14, 0x8a, 0x18, 0x65, 0x6f, 0xe6, 0x6f, 0x8d, + 0x49, 0x02, 0xa5, 0x6f, 0x85, 0xa5, 0xa5, 0x70, + 0x85, 0xa6, 0x4c, 0x43, 0xd4, 0x20, 0xeb, 0xd0, + 0x4c, 0x3a, 0xd4, 0xa9, 0x31, 0x4c, 0xc8, 0xc1, + 0xb9, 0x06, 0x02, 0x91, 0x6f, 0xc8, 0xcc, 0x05, + 0x02, 0x90, 0xf5, 0x60, 0xac, 0x01, 0x02, 0xc0, + 0x30, 0xd0, 0x09, 0xa9, 0xea, 0x85, 0x6b, 0xa9, + 0xff, 0x85, 0x6c, 0x60, 0x20, 0x72, 0xcb, 0x4c, + 0x94, 0xc1, 0x88, 0x98, 0x29, 0x0f, 0x0a, 0xa8, + 0xb1, 0x6b, 0x85, 0x75, 0xc8, 0xb1, 0x6b, 0x85, + 0x76, 0x6c, 0x75, 0x00, 0xad, 0x8e, 0x02, 0x85, + 0x7f, 0xa5, 0x83, 0x48, 0x20, 0x3d, 0xc6, 0x68, + 0x85, 0x83, 0xae, 0x74, 0x02, 0xca, 0xd0, 0x0d, + 0xa9, 0x01, 0x20, 0xe2, 0xd1, 0x4c, 0xf1, 0xcb, + 0xa9, 0x70, 0x4c, 0xc8, 0xc1, 0xa0, 0x01, 0x20, + 0x7c, 0xcc, 0xae, 0x85, 0x02, 0xe0, 0x05, 0xb0, + 0xef, 0xa9, 0x00, 0x85, 0x6f, 0x85, 0x70, 0x38, + 0x26, 0x6f, 0x26, 0x70, 0xca, 0x10, 0xf9, 0xa5, + 0x6f, 0x2d, 0x4f, 0x02, 0xd0, 0xda, 0xa5, 0x70, + 0x2d, 0x50, 0x02, 0xd0, 0xd3, 0xa5, 0x6f, 0x0d, + 0x4f, 0x02, 0x8d, 0x4f, 0x02, 0xa5, 0x70, 0x0d, + 0x50, 0x02, 0x8d, 0x50, 0x02, 0xa9, 0x00, 0x20, + 0xe2, 0xd1, 0xa6, 0x82, 0xad, 0x85, 0x02, 0x95, + 0xa7, 0xaa, 0xa5, 0x7f, 0x95, 0x00, 0x9d, 0x5b, + 0x02, 0xa6, 0x83, 0xbd, 0x2b, 0x02, 0x09, 0x40, + 0x9d, 0x2b, 0x02, 0xa4, 0x82, 0xa9, 0xff, 0x99, + 0x44, 0x02, 0xa9, 0x89, 0x99, 0xf2, 0x00, 0xb9, + 0xa7, 0x00, 0x99, 0x3e, 0x02, 0x0a, 0xaa, 0xa9, + 0x01, 0x95, 0x99, 0xa9, 0x0e, 0x99, 0xec, 0x00, + 0x4c, 0x94, 0xc1, 0xa0, 0x00, 0xa2, 0x00, 0xa9, + 0x2d, 0x20, 0x68, 0xc2, 0xd0, 0x0a, 0xa9, 0x31, + 0x4c, 0xc8, 0xc1, 0xa9, 0x30, 0x4c, 0xc8, 0xc1, + 0x8a, 0xd0, 0xf8, 0xa2, 0x05, 0xb9, 0x00, 0x02, + 0xdd, 0x5d, 0xcc, 0xf0, 0x05, 0xca, 0x10, 0xf8, + 0x30, 0xe4, 0x8a, 0x09, 0x80, 0x8d, 0x2a, 0x02, + 0x20, 0x6f, 0xcc, 0xad, 0x2a, 0x02, 0x0a, 0xaa, + 0xbd, 0x64, 0xcc, 0x85, 0x70, 0xbd, 0x63, 0xcc, + 0x85, 0x6f, 0x6c, 0x6f, 0x00, 0x41, 0x46, 0x52, + 0x57, 0x45, 0x50, 0x03, 0xcd, 0xf5, 0xcc, 0x56, + 0xcd, 0x73, 0xcd, 0xa3, 0xcd, 0xbd, 0xcd, 0xa0, + 0x00, 0xa2, 0x00, 0xa9, 0x3a, 0x20, 0x68, 0xc2, + 0xd0, 0x02, 0xa0, 0x03, 0xb9, 0x00, 0x02, 0xc9, + 0x20, 0xf0, 0x08, 0xc9, 0x1d, 0xf0, 0x04, 0xc9, + 0x2c, 0xd0, 0x07, 0xc8, 0xcc, 0x74, 0x02, 0x90, + 0xeb, 0x60, 0x20, 0xa1, 0xcc, 0xee, 0x77, 0x02, + 0xac, 0x79, 0x02, 0xe0, 0x04, 0x90, 0xec, 0xb0, + 0x8a, 0xa9, 0x00, 0x85, 0x6f, 0x85, 0x70, 0x85, + 0x72, 0xa2, 0xff, 0xb9, 0x00, 0x02, 0xc9, 0x40, + 0xb0, 0x18, 0xc9, 0x30, 0x90, 0x14, 0x29, 0x0f, + 0x48, 0xa5, 0x70, 0x85, 0x71, 0xa5, 0x6f, 0x85, + 0x70, 0x68, 0x85, 0x6f, 0xc8, 0xcc, 0x74, 0x02, + 0x90, 0xe1, 0x8c, 0x79, 0x02, 0x18, 0xa9, 0x00, + 0xe8, 0xe0, 0x03, 0xb0, 0x0f, 0xb4, 0x6f, 0x88, + 0x30, 0xf6, 0x7d, 0xf2, 0xcc, 0x90, 0xf8, 0x18, + 0xe6, 0x72, 0xd0, 0xf3, 0x48, 0xae, 0x77, 0x02, + 0xa5, 0x72, 0x9d, 0x80, 0x02, 0x68, 0x9d, 0x85, + 0x02, 0x60, 0x01, 0x0a, 0x64, 0x20, 0xf5, 0xcd, + 0x20, 0x5f, 0xef, 0x4c, 0x94, 0xc1, 0xa9, 0x01, + 0x8d, 0xf9, 0x02, 0x20, 0xf5, 0xcd, 0xa5, 0x81, + 0x48, 0x20, 0xfa, 0xf1, 0xf0, 0x0b, 0x68, 0xc5, + 0x81, 0xd0, 0x19, 0x20, 0x90, 0xef, 0x4c, 0x94, + 0xc1, 0x68, 0xa9, 0x00, 0x85, 0x81, 0xe6, 0x80, + 0xa5, 0x80, 0xcd, 0xd7, 0xfe, 0xb0, 0x0a, 0x20, + 0xfa, 0xf1, 0xf0, 0xee, 0xa9, 0x65, 0x20, 0x45, + 0xe6, 0xa9, 0x65, 0x20, 0xc8, 0xc1, 0x20, 0xf2, + 0xcd, 0x4c, 0x60, 0xd4, 0x20, 0x2f, 0xd1, 0xa1, + 0x99, 0x60, 0x20, 0x36, 0xcd, 0xa9, 0x00, 0x20, + 0xc8, 0xd4, 0x20, 0x3c, 0xcd, 0x99, 0x44, 0x02, + 0xa9, 0x89, 0x99, 0xf2, 0x00, 0x60, 0x20, 0x42, + 0xcd, 0x20, 0xec, 0xd3, 0x4c, 0x94, 0xc1, 0x20, + 0x6f, 0xcc, 0x20, 0x42, 0xcd, 0xb9, 0x44, 0x02, + 0x99, 0x3e, 0x02, 0xa9, 0xff, 0x99, 0x44, 0x02, + 0x4c, 0x94, 0xc1, 0x20, 0xf2, 0xcd, 0x20, 0xe8, + 0xd4, 0xa8, 0x88, 0xc9, 0x02, 0xb0, 0x02, 0xa0, + 0x01, 0xa9, 0x00, 0x20, 0xc8, 0xd4, 0x98, 0x20, + 0xf1, 0xcf, 0x8a, 0x48, 0x20, 0x64, 0xd4, 0x68, + 0xaa, 0x20, 0xee, 0xd3, 0x4c, 0x94, 0xc1, 0x20, + 0x6f, 0xcc, 0x20, 0xf2, 0xcd, 0x20, 0x64, 0xd4, + 0x4c, 0x94, 0xc1, 0x20, 0x58, 0xf2, 0x20, 0x36, + 0xcd, 0xa9, 0x00, 0x85, 0x6f, 0xa6, 0xf9, 0xbd, + 0xe0, 0xfe, 0x85, 0x70, 0x20, 0xba, 0xcd, 0x4c, + 0x94, 0xc1, 0x6c, 0x6f, 0x00, 0x20, 0xd2, 0xcd, + 0xa5, 0xf9, 0x0a, 0xaa, 0xad, 0x86, 0x02, 0x95, + 0x99, 0x20, 0x2f, 0xd1, 0x20, 0xee, 0xd3, 0x4c, + 0x94, 0xc1, 0xa6, 0xd3, 0xe6, 0xd3, 0xbd, 0x85, + 0x02, 0xa8, 0x88, 0x88, 0xc0, 0x0c, 0x90, 0x05, + 0xa9, 0x70, 0x4c, 0xc8, 0xc1, 0x85, 0x83, 0x20, + 0xeb, 0xd0, 0xb0, 0xf4, 0x20, 0x93, 0xdf, 0x85, + 0xf9, 0x60, 0x20, 0xd2, 0xcd, 0xa6, 0xd3, 0xbd, + 0x85, 0x02, 0x29, 0x01, 0x85, 0x7f, 0xbd, 0x87, + 0x02, 0x85, 0x81, 0xbd, 0x86, 0x02, 0x85, 0x80, + 0x20, 0x5f, 0xd5, 0x4c, 0x00, 0xc1, 0x20, 0x2c, + 0xce, 0x20, 0x6e, 0xce, 0xa5, 0x90, 0x85, 0xd7, + 0x20, 0x71, 0xce, 0xe6, 0xd7, 0xe6, 0xd7, 0xa5, + 0x8b, 0x85, 0xd5, 0xa5, 0x90, 0x0a, 0x18, 0x69, + 0x10, 0x85, 0xd6, 0x60, 0x20, 0xd9, 0xce, 0x85, + 0x92, 0xa6, 0x82, 0xb5, 0xb5, 0x85, 0x90, 0xb5, + 0xbb, 0x85, 0x91, 0xd0, 0x04, 0xa5, 0x90, 0xf0, + 0x0b, 0xa5, 0x90, 0x38, 0xe9, 0x01, 0x85, 0x90, + 0xb0, 0x02, 0xc6, 0x91, 0xb5, 0xc7, 0x85, 0x6f, + 0x46, 0x6f, 0x90, 0x03, 0x20, 0xed, 0xce, 0x20, + 0xe5, 0xce, 0xa5, 0x6f, 0xd0, 0xf2, 0xa5, 0xd4, + 0x18, 0x65, 0x8b, 0x85, 0x8b, 0x90, 0x06, 0xe6, + 0x8c, 0xd0, 0x02, 0xe6, 0x8d, 0x60, 0xa9, 0xfe, + 0x2c, 0xa9, 0x78, 0x85, 0x6f, 0xa2, 0x03, 0xb5, + 0x8f, 0x48, 0xb5, 0x8a, 0x95, 0x8f, 0x68, 0x95, + 0x8a, 0xca, 0xd0, 0xf3, 0x20, 0xd9, 0xce, 0xa2, + 0x00, 0xb5, 0x90, 0x95, 0x8f, 0xe8, 0xe0, 0x04, + 0x90, 0xf7, 0xa9, 0x00, 0x85, 0x92, 0x24, 0x6f, + 0x30, 0x09, 0x06, 0x8f, 0x08, 0x46, 0x8f, 0x28, + 0x20, 0xe6, 0xce, 0x20, 0xed, 0xce, 0x20, 0xe5, + 0xce, 0x24, 0x6f, 0x30, 0x03, 0x20, 0xe2, 0xce, + 0xa5, 0x8f, 0x18, 0x65, 0x90, 0x85, 0x90, 0x90, + 0x06, 0xe6, 0x91, 0xd0, 0x02, 0xe6, 0x92, 0xa5, + 0x92, 0x05, 0x91, 0xd0, 0xc2, 0xa5, 0x90, 0x38, + 0xe5, 0x6f, 0x90, 0x0c, 0xe6, 0x8b, 0xd0, 0x06, + 0xe6, 0x8c, 0xd0, 0x02, 0xe6, 0x8d, 0x85, 0x90, + 0x60, 0xa9, 0x00, 0x85, 0x8b, 0x85, 0x8c, 0x85, + 0x8d, 0x60, 0x20, 0xe5, 0xce, 0x18, 0x26, 0x90, + 0x26, 0x91, 0x26, 0x92, 0x60, 0x18, 0xa2, 0xfd, + 0xb5, 0x8e, 0x75, 0x93, 0x95, 0x8e, 0xe8, 0xd0, + 0xf7, 0x60, 0xa2, 0x00, 0x8a, 0x95, 0xfa, 0xe8, + 0xe0, 0x04, 0xd0, 0xf8, 0xa9, 0x06, 0x95, 0xfa, + 0x60, 0xa0, 0x04, 0xa6, 0x82, 0xb9, 0xfa, 0x00, + 0x96, 0xfa, 0xc5, 0x82, 0xf0, 0x07, 0x88, 0x30, + 0xe1, 0xaa, 0x4c, 0x0d, 0xcf, 0x60, 0x20, 0x09, + 0xcf, 0x20, 0xb7, 0xdf, 0xd0, 0x46, 0x20, 0xd3, + 0xd1, 0x20, 0x8e, 0xd2, 0x30, 0x48, 0x20, 0xc2, + 0xdf, 0xa5, 0x80, 0x48, 0xa5, 0x81, 0x48, 0xa9, + 0x01, 0x20, 0xf6, 0xd4, 0x85, 0x81, 0xa9, 0x00, + 0x20, 0xf6, 0xd4, 0x85, 0x80, 0xf0, 0x1f, 0x20, + 0x25, 0xd1, 0xf0, 0x0b, 0x20, 0xab, 0xdd, 0xd0, + 0x06, 0x20, 0x8c, 0xcf, 0x4c, 0x5d, 0xcf, 0x20, + 0x8c, 0xcf, 0x20, 0x57, 0xde, 0x68, 0x85, 0x81, + 0x68, 0x85, 0x80, 0x4c, 0x6f, 0xcf, 0x68, 0x85, + 0x81, 0x68, 0x85, 0x80, 0x20, 0x8c, 0xcf, 0x20, + 0x93, 0xdf, 0xaa, 0x4c, 0x99, 0xd5, 0xa9, 0x70, + 0x4c, 0xc8, 0xc1, 0x20, 0x09, 0xcf, 0x20, 0xb7, + 0xdf, 0xd0, 0x08, 0x20, 0x8e, 0xd2, 0x30, 0xee, + 0x20, 0xc2, 0xdf, 0x60, 0xa6, 0x82, 0xb5, 0xa7, + 0x49, 0x80, 0x95, 0xa7, 0xb5, 0xae, 0x49, 0x80, + 0x95, 0xae, 0x60, 0xa2, 0x12, 0x86, 0x83, 0x20, + 0x07, 0xd1, 0x20, 0x00, 0xc1, 0x20, 0x25, 0xd1, + 0x90, 0x05, 0xa9, 0x20, 0x20, 0x9d, 0xdd, 0xa5, + 0x83, 0xc9, 0x0f, 0xf0, 0x23, 0xd0, 0x08, 0xa5, + 0x84, 0x29, 0x8f, 0xc9, 0x0f, 0xb0, 0x19, 0x20, + 0x25, 0xd1, 0xb0, 0x05, 0xa5, 0x85, 0x4c, 0x9d, + 0xd1, 0xd0, 0x03, 0x4c, 0xab, 0xe0, 0xa5, 0x85, + 0x20, 0xf1, 0xcf, 0xa4, 0x82, 0x4c, 0xee, 0xd3, + 0xa9, 0x04, 0x85, 0x82, 0x20, 0xe8, 0xd4, 0xc9, + 0x2a, 0xf0, 0x05, 0xa5, 0x85, 0x20, 0xf1, 0xcf, + 0xa5, 0xf8, 0xf0, 0x01, 0x60, 0xee, 0x55, 0x02, + 0x60, 0x48, 0x20, 0x93, 0xdf, 0x10, 0x06, 0x68, + 0xa9, 0x61, 0x4c, 0xc8, 0xc1, 0x0a, 0xaa, 0x68, + 0x81, 0x99, 0xf6, 0x99, 0x60, 0x20, 0xd1, 0xc1, + 0x20, 0x42, 0xd0, 0x4c, 0x94, 0xc1, 0x20, 0x0f, + 0xf1, 0xa8, 0xb6, 0xa7, 0xe0, 0xff, 0xd0, 0x14, + 0x48, 0x20, 0x8e, 0xd2, 0xaa, 0x10, 0x05, 0xa9, + 0x70, 0x20, 0x48, 0xe6, 0x68, 0xa8, 0x8a, 0x09, + 0x80, 0x99, 0xa7, 0x00, 0x8a, 0x29, 0x0f, 0x85, + 0xf9, 0xa2, 0x00, 0x86, 0x81, 0xae, 0x85, 0xfe, + 0x86, 0x80, 0x20, 0xd3, 0xd6, 0xa9, 0xb0, 0x4c, + 0x8c, 0xd5, 0x20, 0xd1, 0xf0, 0x20, 0x13, 0xd3, + 0x20, 0x0e, 0xd0, 0xa6, 0x7f, 0xa9, 0x00, 0x9d, + 0x51, 0x02, 0x8a, 0x0a, 0xaa, 0xa5, 0x16, 0x95, + 0x12, 0xa5, 0x17, 0x95, 0x13, 0x20, 0x86, 0xd5, + 0xa5, 0xf9, 0x0a, 0xaa, 0xa9, 0x02, 0x95, 0x99, + 0xa1, 0x99, 0xa6, 0x7f, 0x9d, 0x01, 0x01, 0xa9, + 0x00, 0x95, 0x1c, 0x95, 0xff, 0x20, 0x3a, 0xef, + 0xa0, 0x04, 0xa9, 0x00, 0xaa, 0x18, 0x71, 0x6d, + 0x90, 0x01, 0xe8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc0, + 0x48, 0xf0, 0xf8, 0xc0, 0x90, 0xd0, 0xee, 0x48, + 0x8a, 0xa6, 0x7f, 0x9d, 0xfc, 0x02, 0x68, 0x9d, + 0xfa, 0x02, 0x60, 0x20, 0xd0, 0xd6, 0x20, 0xc3, + 0xd0, 0x20, 0x99, 0xd5, 0x20, 0x37, 0xd1, 0x85, + 0x80, 0x20, 0x37, 0xd1, 0x85, 0x81, 0x60, 0x20, + 0x9b, 0xd0, 0xa5, 0x80, 0xd0, 0x01, 0x60, 0x20, + 0x1e, 0xcf, 0x20, 0xd0, 0xd6, 0x20, 0xc3, 0xd0, + 0x4c, 0x1e, 0xcf, 0xa9, 0x80, 0xd0, 0x02, 0xa9, + 0x90, 0x8d, 0x4d, 0x02, 0x20, 0x93, 0xdf, 0xaa, + 0x20, 0x06, 0xd5, 0x8a, 0x48, 0x0a, 0xaa, 0xa9, + 0x00, 0x95, 0x99, 0x20, 0x25, 0xd1, 0xc9, 0x04, + 0xb0, 0x06, 0xf6, 0xb5, 0xd0, 0x02, 0xf6, 0xbb, + 0x68, 0xaa, 0x60, 0xa5, 0x83, 0xc9, 0x13, 0x90, + 0x02, 0x29, 0x0f, 0xc9, 0x0f, 0xd0, 0x02, 0xa9, + 0x10, 0xaa, 0x38, 0xbd, 0x2b, 0x02, 0x30, 0x06, + 0x29, 0x0f, 0x85, 0x82, 0xaa, 0x18, 0x60, 0xa5, + 0x83, 0xc9, 0x13, 0x90, 0x02, 0x29, 0x0f, 0xaa, + 0xbd, 0x2b, 0x02, 0xa8, 0x0a, 0x90, 0x0a, 0x30, + 0x0a, 0x98, 0x29, 0x0f, 0x85, 0x82, 0xaa, 0x18, + 0x60, 0x30, 0xf6, 0x38, 0x60, 0xa6, 0x82, 0xb5, + 0xec, 0x4a, 0x29, 0x07, 0xc9, 0x04, 0x60, 0x20, + 0x93, 0xdf, 0x0a, 0xaa, 0xa4, 0x82, 0x60, 0x20, + 0x2f, 0xd1, 0xb9, 0x44, 0x02, 0xf0, 0x12, 0xa1, + 0x99, 0x48, 0xb5, 0x99, 0xd9, 0x44, 0x02, 0xd0, + 0x04, 0xa9, 0xff, 0x95, 0x99, 0x68, 0xf6, 0x99, + 0x60, 0xa1, 0x99, 0xf6, 0x99, 0x60, 0x20, 0x37, + 0xd1, 0xd0, 0x36, 0x85, 0x85, 0xb9, 0x44, 0x02, + 0xf0, 0x08, 0xa9, 0x80, 0x99, 0xf2, 0x00, 0xa5, + 0x85, 0x60, 0x20, 0x1e, 0xcf, 0xa9, 0x00, 0x20, + 0xc8, 0xd4, 0x20, 0x37, 0xd1, 0xc9, 0x00, 0xf0, + 0x19, 0x85, 0x80, 0x20, 0x37, 0xd1, 0x85, 0x81, + 0x20, 0x1e, 0xcf, 0x20, 0xd3, 0xd1, 0x20, 0xd0, + 0xd6, 0x20, 0xc3, 0xd0, 0x20, 0x1e, 0xcf, 0xa5, + 0x85, 0x60, 0x20, 0x37, 0xd1, 0xa4, 0x82, 0x99, + 0x44, 0x02, 0xa5, 0x85, 0x60, 0x20, 0xf1, 0xcf, + 0xf0, 0x01, 0x60, 0x20, 0xd3, 0xd1, 0x20, 0x1e, + 0xf1, 0xa9, 0x00, 0x20, 0xc8, 0xd4, 0xa5, 0x80, + 0x20, 0xf1, 0xcf, 0xa5, 0x81, 0x20, 0xf1, 0xcf, + 0x20, 0xc7, 0xd0, 0x20, 0x1e, 0xcf, 0x20, 0xd0, + 0xd6, 0xa9, 0x02, 0x4c, 0xc8, 0xd4, 0x85, 0x6f, + 0x20, 0xe8, 0xd4, 0x18, 0x65, 0x6f, 0x95, 0x99, + 0x85, 0x94, 0x60, 0x20, 0x93, 0xdf, 0xaa, 0xbd, + 0x5b, 0x02, 0x29, 0x01, 0x85, 0x7f, 0x60, 0x38, + 0xb0, 0x01, 0x18, 0x08, 0x85, 0x6f, 0x20, 0x27, + 0xd2, 0x20, 0x7f, 0xd3, 0x85, 0x82, 0xa6, 0x83, + 0x28, 0x90, 0x02, 0x09, 0x80, 0x9d, 0x2b, 0x02, + 0x29, 0x3f, 0xa8, 0xa9, 0xff, 0x99, 0xa7, 0x00, + 0x99, 0xae, 0x00, 0x99, 0xcd, 0x00, 0xc6, 0x6f, + 0x30, 0x1c, 0x20, 0x8e, 0xd2, 0x10, 0x08, 0x20, + 0x5a, 0xd2, 0xa9, 0x70, 0x4c, 0xc8, 0xc1, 0x99, + 0xa7, 0x00, 0xc6, 0x6f, 0x30, 0x08, 0x20, 0x8e, + 0xd2, 0x30, 0xec, 0x99, 0xae, 0x00, 0x60, 0xa5, + 0x83, 0xc9, 0x0f, 0xd0, 0x01, 0x60, 0xa6, 0x83, + 0xbd, 0x2b, 0x02, 0xc9, 0xff, 0xf0, 0x22, 0x29, + 0x3f, 0x85, 0x82, 0xa9, 0xff, 0x9d, 0x2b, 0x02, + 0xa6, 0x82, 0xa9, 0x00, 0x95, 0xf2, 0x20, 0x5a, + 0xd2, 0xa6, 0x82, 0xa9, 0x01, 0xca, 0x30, 0x03, + 0x0a, 0xd0, 0xfa, 0x0d, 0x56, 0x02, 0x8d, 0x56, + 0x02, 0x60, 0xa6, 0x82, 0xb5, 0xa7, 0xc9, 0xff, + 0xf0, 0x09, 0x48, 0xa9, 0xff, 0x95, 0xa7, 0x68, + 0x20, 0xf3, 0xd2, 0xa6, 0x82, 0xb5, 0xae, 0xc9, + 0xff, 0xf0, 0x09, 0x48, 0xa9, 0xff, 0x95, 0xae, + 0x68, 0x20, 0xf3, 0xd2, 0xa6, 0x82, 0xb5, 0xcd, + 0xc9, 0xff, 0xf0, 0x09, 0x48, 0xa9, 0xff, 0x95, + 0xcd, 0x68, 0x20, 0xf3, 0xd2, 0x60, 0x98, 0x48, + 0xa0, 0x01, 0x20, 0xba, 0xd2, 0x10, 0x0c, 0x88, + 0x20, 0xba, 0xd2, 0x10, 0x06, 0x20, 0x39, 0xd3, + 0xaa, 0x30, 0x13, 0xb5, 0x00, 0x30, 0xfc, 0xa5, + 0x7f, 0x95, 0x00, 0x9d, 0x5b, 0x02, 0x8a, 0x0a, + 0xa8, 0xa9, 0x02, 0x99, 0x99, 0x00, 0x68, 0xa8, + 0x8a, 0x60, 0xa2, 0x07, 0xb9, 0x4f, 0x02, 0x3d, + 0xe9, 0xef, 0xf0, 0x04, 0xca, 0x10, 0xf5, 0x60, + 0xb9, 0x4f, 0x02, 0x5d, 0xe9, 0xef, 0x99, 0x4f, + 0x02, 0x8a, 0x88, 0x30, 0x03, 0x18, 0x69, 0x08, + 0xaa, 0x60, 0xa6, 0x82, 0xb5, 0xa7, 0x30, 0x09, + 0x8a, 0x18, 0x69, 0x07, 0xaa, 0xb5, 0xa7, 0x10, + 0xf0, 0xc9, 0xff, 0xf0, 0xec, 0x48, 0xa9, 0xff, + 0x95, 0xa7, 0x68, 0x29, 0x0f, 0xa8, 0xc8, 0xa2, + 0x10, 0x6e, 0x50, 0x02, 0x6e, 0x4f, 0x02, 0x88, + 0xd0, 0x01, 0x18, 0xca, 0x10, 0xf3, 0x60, 0xa9, + 0x0e, 0x85, 0x83, 0x20, 0x27, 0xd2, 0xc6, 0x83, + 0xd0, 0xf9, 0x60, 0xa9, 0x0e, 0x85, 0x83, 0xa6, + 0x83, 0xbd, 0x2b, 0x02, 0xc9, 0xff, 0xf0, 0x14, + 0x29, 0x3f, 0x85, 0x82, 0x20, 0x93, 0xdf, 0xaa, + 0xbd, 0x5b, 0x02, 0x29, 0x01, 0xc5, 0x7f, 0xd0, + 0x03, 0x20, 0x27, 0xd2, 0xc6, 0x83, 0x10, 0xdf, + 0x60, 0xa5, 0x6f, 0x48, 0xa0, 0x00, 0xb6, 0xfa, + 0xb5, 0xa7, 0x10, 0x04, 0xc9, 0xff, 0xd0, 0x16, + 0x8a, 0x18, 0x69, 0x07, 0xaa, 0xb5, 0xa7, 0x10, + 0x04, 0xc9, 0xff, 0xd0, 0x09, 0xc8, 0xc0, 0x05, + 0x90, 0xe4, 0xa2, 0xff, 0xd0, 0x1c, 0x86, 0x6f, + 0x29, 0x3f, 0xaa, 0xb5, 0x00, 0x30, 0xfc, 0xc9, + 0x02, 0x90, 0x08, 0xa6, 0x6f, 0xe0, 0x07, 0x90, + 0xd7, 0xb0, 0xe2, 0xa4, 0x6f, 0xa9, 0xff, 0x99, + 0xa7, 0x00, 0x68, 0x85, 0x6f, 0x8a, 0x60, 0xa0, + 0x00, 0xa9, 0x01, 0x2c, 0x56, 0x02, 0xd0, 0x09, + 0xc8, 0x0a, 0xd0, 0xf7, 0xa9, 0x70, 0x4c, 0xc8, + 0xc1, 0x49, 0xff, 0x2d, 0x56, 0x02, 0x8d, 0x56, + 0x02, 0x98, 0x60, 0x20, 0xeb, 0xd0, 0x20, 0x00, + 0xc1, 0x20, 0xaa, 0xd3, 0xa6, 0x82, 0xbd, 0x3e, + 0x02, 0x60, 0xa6, 0x82, 0x20, 0x25, 0xd1, 0xd0, + 0x03, 0x4c, 0x20, 0xe1, 0xa5, 0x83, 0xc9, 0x0f, + 0xf0, 0x5a, 0xb5, 0xf2, 0x29, 0x08, 0xd0, 0x13, + 0x20, 0x25, 0xd1, 0xc9, 0x07, 0xd0, 0x07, 0xa9, + 0x89, 0x95, 0xf2, 0x4c, 0xde, 0xd3, 0xa9, 0x00, + 0x95, 0xf2, 0x60, 0xa5, 0x83, 0xf0, 0x32, 0x20, + 0x25, 0xd1, 0xc9, 0x04, 0x90, 0x22, 0x20, 0x2f, + 0xd1, 0xb5, 0x99, 0xd9, 0x44, 0x02, 0xd0, 0x04, + 0xa9, 0x00, 0x95, 0x99, 0xf6, 0x99, 0xa1, 0x99, + 0x99, 0x3e, 0x02, 0xb5, 0x99, 0xd9, 0x44, 0x02, + 0xd0, 0x05, 0xa9, 0x81, 0x99, 0xf2, 0x00, 0x60, + 0x20, 0x56, 0xd1, 0xa6, 0x82, 0x9d, 0x3e, 0x02, + 0x60, 0xad, 0x54, 0x02, 0xf0, 0xf2, 0x20, 0x67, + 0xed, 0x4c, 0x03, 0xd4, 0x20, 0xe8, 0xd4, 0xc9, + 0xd4, 0xd0, 0x18, 0xa5, 0x95, 0xc9, 0x02, 0xd0, + 0x12, 0xa9, 0x0d, 0x85, 0x85, 0x20, 0x23, 0xc1, + 0xa9, 0x00, 0x20, 0xc1, 0xe6, 0xc6, 0xa5, 0xa9, + 0x80, 0xd0, 0x12, 0x20, 0x37, 0xd1, 0x85, 0x85, + 0xd0, 0x09, 0xa9, 0xd4, 0x20, 0xc8, 0xd4, 0xa9, + 0x02, 0x95, 0x9a, 0xa9, 0x88, 0x85, 0xf7, 0xa5, + 0x85, 0x8d, 0x43, 0x02, 0x60, 0x20, 0x93, 0xdf, + 0x0a, 0xaa, 0xa9, 0x00, 0x95, 0x99, 0xa1, 0x99, + 0xf0, 0x05, 0xd6, 0x99, 0x4c, 0x56, 0xd1, 0x60, + 0xa9, 0x80, 0xd0, 0x02, 0xa9, 0x90, 0x05, 0x7f, + 0x8d, 0x4d, 0x02, 0xa5, 0xf9, 0x20, 0xd3, 0xd6, + 0xa6, 0xf9, 0x4c, 0x93, 0xd5, 0xa9, 0x01, 0x8d, + 0x4a, 0x02, 0xa9, 0x11, 0x85, 0x83, 0x20, 0x46, + 0xdc, 0xa9, 0x02, 0x4c, 0xc8, 0xd4, 0xa9, 0x12, + 0x85, 0x83, 0x4c, 0xda, 0xdc, 0x20, 0x3b, 0xde, + 0xa9, 0x01, 0x85, 0x6f, 0xa5, 0x69, 0x48, 0xa9, + 0x03, 0x85, 0x69, 0x20, 0x2d, 0xf1, 0x68, 0x85, + 0x69, 0xa9, 0x00, 0x20, 0xc8, 0xd4, 0xa5, 0x80, + 0x20, 0xf1, 0xcf, 0xa5, 0x81, 0x20, 0xf1, 0xcf, + 0x20, 0xc7, 0xd0, 0x20, 0x99, 0xd5, 0xa9, 0x00, + 0x20, 0xc8, 0xd4, 0x20, 0xf1, 0xcf, 0xd0, 0xfb, + 0x20, 0xf1, 0xcf, 0xa9, 0xff, 0x4c, 0xf1, 0xcf, + 0x85, 0x6f, 0x20, 0x93, 0xdf, 0x0a, 0xaa, 0xb5, + 0x9a, 0x85, 0x95, 0xa5, 0x6f, 0x95, 0x99, 0x85, + 0x94, 0x60, 0xa9, 0x11, 0x85, 0x83, 0x20, 0x27, + 0xd2, 0xa9, 0x12, 0x85, 0x83, 0x4c, 0x27, 0xd2, + 0x20, 0x93, 0xdf, 0x0a, 0xaa, 0xb5, 0x9a, 0x85, + 0x95, 0xb5, 0x99, 0x85, 0x94, 0x60, 0x85, 0x71, + 0x20, 0x93, 0xdf, 0xaa, 0xbd, 0xe0, 0xfe, 0x85, + 0x72, 0xa0, 0x00, 0xb1, 0x71, 0x60, 0xbd, 0x5b, + 0x02, 0x29, 0x01, 0x0d, 0x4d, 0x02, 0x48, 0x86, + 0xf9, 0x8a, 0x0a, 0xaa, 0xb5, 0x07, 0x8d, 0x4d, + 0x02, 0xb5, 0x06, 0xf0, 0x2d, 0xcd, 0xd7, 0xfe, + 0xb0, 0x28, 0xaa, 0x68, 0x48, 0x29, 0xf0, 0xc9, + 0x90, 0xd0, 0x4f, 0x68, 0x48, 0x4a, 0xb0, 0x05, + 0xad, 0x01, 0x01, 0x90, 0x03, 0xad, 0x02, 0x01, + 0xf0, 0x05, 0xcd, 0xd5, 0xfe, 0xd0, 0x33, 0x8a, + 0x20, 0x4b, 0xf2, 0xcd, 0x4d, 0x02, 0xf0, 0x02, + 0xb0, 0x30, 0x20, 0x52, 0xd5, 0xa9, 0x66, 0x4c, + 0x45, 0xe6, 0xa5, 0xf9, 0x0a, 0xaa, 0xb5, 0x06, + 0x85, 0x80, 0xb5, 0x07, 0x85, 0x81, 0x60, 0xa5, + 0x80, 0xf0, 0xea, 0xcd, 0xd7, 0xfe, 0xb0, 0xe5, + 0x20, 0x4b, 0xf2, 0xc5, 0x81, 0xf0, 0xde, 0x90, + 0xdc, 0x60, 0x20, 0x52, 0xd5, 0xa9, 0x73, 0x4c, + 0x45, 0xe6, 0xa6, 0xf9, 0x68, 0x8d, 0x4d, 0x02, + 0x95, 0x00, 0x9d, 0x5b, 0x02, 0x60, 0xa9, 0x80, + 0xd0, 0x02, 0xa9, 0x90, 0x05, 0x7f, 0xa6, 0xf9, + 0x8d, 0x4d, 0x02, 0xad, 0x4d, 0x02, 0x20, 0x0e, + 0xd5, 0x20, 0xa6, 0xd5, 0xb0, 0xfb, 0x48, 0xa9, + 0x00, 0x8d, 0x98, 0x02, 0x68, 0x60, 0xb5, 0x00, + 0x30, 0x1a, 0xc9, 0x02, 0x90, 0x14, 0xc9, 0x08, + 0xf0, 0x08, 0xc9, 0x0b, 0xf0, 0x04, 0xc9, 0x0f, + 0xd0, 0x0c, 0x2c, 0x98, 0x02, 0x30, 0x03, 0x4c, + 0x3f, 0xd6, 0x18, 0x60, 0x38, 0x60, 0x98, 0x48, + 0xa5, 0x7f, 0x48, 0xbd, 0x5b, 0x02, 0x29, 0x01, + 0x85, 0x7f, 0xa8, 0xb9, 0xca, 0xfe, 0x8d, 0x6d, + 0x02, 0x20, 0xa6, 0xd6, 0xc9, 0x02, 0xb0, 0x03, + 0x4c, 0x6d, 0xd6, 0xbd, 0x5b, 0x02, 0x29, 0xf0, + 0x48, 0xc9, 0x90, 0xd0, 0x07, 0xa5, 0x7f, 0x09, + 0xb8, 0x9d, 0x5b, 0x02, 0x24, 0x6a, 0x70, 0x39, + 0xa9, 0x00, 0x8d, 0x99, 0x02, 0x8d, 0x9a, 0x02, + 0xac, 0x99, 0x02, 0xad, 0x9a, 0x02, 0x38, 0xf9, + 0xdb, 0xfe, 0x8d, 0x9a, 0x02, 0xb9, 0xdb, 0xfe, + 0x20, 0x76, 0xd6, 0xee, 0x99, 0x02, 0x20, 0xa6, + 0xd6, 0xc9, 0x02, 0x90, 0x08, 0xac, 0x99, 0x02, + 0xb9, 0xdb, 0xfe, 0xd0, 0xdb, 0xad, 0x9a, 0x02, + 0x20, 0x76, 0xd6, 0xb5, 0x00, 0xc9, 0x02, 0x90, + 0x2b, 0x24, 0x6a, 0x10, 0x0f, 0x68, 0xc9, 0x90, + 0xd0, 0x05, 0x05, 0x7f, 0x9d, 0x5b, 0x02, 0xb5, + 0x00, 0x20, 0x0a, 0xe6, 0x68, 0x2c, 0x98, 0x02, + 0x30, 0x23, 0x48, 0xa9, 0xc0, 0x05, 0x7f, 0x95, + 0x00, 0xb5, 0x00, 0x30, 0xfc, 0x20, 0xa6, 0xd6, + 0xc9, 0x02, 0xb0, 0xd9, 0x68, 0xc9, 0x90, 0xd0, + 0x0c, 0x05, 0x7f, 0x9d, 0x5b, 0x02, 0x20, 0xa6, + 0xd6, 0xc9, 0x02, 0xb0, 0xd2, 0x68, 0x85, 0x7f, + 0x68, 0xa8, 0xb5, 0x00, 0x18, 0x60, 0xc9, 0x00, + 0xf0, 0x18, 0x30, 0x0c, 0xa0, 0x01, 0x20, 0x93, + 0xd6, 0x38, 0xe9, 0x01, 0xd0, 0xf6, 0xf0, 0x0a, + 0xa0, 0xff, 0x20, 0x93, 0xd6, 0x18, 0x69, 0x01, + 0xd0, 0xf6, 0x60, 0x48, 0x98, 0xa4, 0x7f, 0x99, + 0xfe, 0x02, 0xd9, 0xfe, 0x02, 0xf0, 0xfb, 0xa9, + 0x00, 0x99, 0xfe, 0x02, 0x68, 0x60, 0xa5, 0x6a, + 0x29, 0x3f, 0xa8, 0xad, 0x6d, 0x02, 0x4d, 0x00, + 0x1c, 0x8d, 0x00, 0x1c, 0xbd, 0x5b, 0x02, 0x95, + 0x00, 0xb5, 0x00, 0x30, 0xfc, 0xc9, 0x02, 0x90, + 0x03, 0x88, 0xd0, 0xe7, 0x48, 0xad, 0x6d, 0x02, + 0x0d, 0x00, 0x1c, 0x8d, 0x00, 0x1c, 0x68, 0x60, + 0x20, 0x93, 0xdf, 0x0a, 0xa8, 0xa5, 0x80, 0x99, + 0x06, 0x00, 0xa5, 0x81, 0x99, 0x07, 0x00, 0xa5, + 0x7f, 0x0a, 0xaa, 0x60, 0xa5, 0x83, 0x48, 0xa5, + 0x82, 0x48, 0xa5, 0x81, 0x48, 0xa5, 0x80, 0x48, + 0xa9, 0x11, 0x85, 0x83, 0x20, 0x3b, 0xde, 0xad, + 0x4a, 0x02, 0x48, 0xa5, 0xe2, 0x29, 0x01, 0x85, + 0x7f, 0xa6, 0xf9, 0x5d, 0x5b, 0x02, 0x4a, 0x90, + 0x0c, 0xa2, 0x01, 0x8e, 0x92, 0x02, 0x20, 0xac, + 0xc5, 0xf0, 0x1d, 0xd0, 0x28, 0xad, 0x91, 0x02, + 0xf0, 0x0c, 0xc5, 0x81, 0xf0, 0x1f, 0x85, 0x81, + 0x20, 0x60, 0xd4, 0x4c, 0x3d, 0xd7, 0xa9, 0x01, + 0x8d, 0x92, 0x02, 0x20, 0x17, 0xc6, 0xd0, 0x0d, + 0x20, 0x8d, 0xd4, 0xa5, 0x81, 0x8d, 0x91, 0x02, + 0xa9, 0x02, 0x8d, 0x92, 0x02, 0xad, 0x92, 0x02, + 0x20, 0xc8, 0xd4, 0x68, 0x8d, 0x4a, 0x02, 0xc9, + 0x04, 0xd0, 0x02, 0x09, 0x80, 0x20, 0xf1, 0xcf, + 0x68, 0x8d, 0x80, 0x02, 0x20, 0xf1, 0xcf, 0x68, + 0x8d, 0x85, 0x02, 0x20, 0xf1, 0xcf, 0x20, 0x93, + 0xdf, 0xa8, 0xad, 0x7a, 0x02, 0xaa, 0xa9, 0x10, + 0x20, 0x6e, 0xc6, 0xa0, 0x10, 0xa9, 0x00, 0x91, + 0x94, 0xc8, 0xc0, 0x1b, 0x90, 0xf9, 0xad, 0x4a, + 0x02, 0xc9, 0x04, 0xd0, 0x13, 0xa0, 0x10, 0xad, + 0x59, 0x02, 0x91, 0x94, 0xc8, 0xad, 0x5a, 0x02, + 0x91, 0x94, 0xc8, 0xad, 0x58, 0x02, 0x91, 0x94, + 0x20, 0x64, 0xd4, 0x68, 0x85, 0x82, 0xaa, 0x68, + 0x85, 0x83, 0xad, 0x91, 0x02, 0x85, 0xd8, 0x9d, + 0x60, 0x02, 0xad, 0x92, 0x02, 0x85, 0xdd, 0x9d, + 0x66, 0x02, 0xad, 0x4a, 0x02, 0x85, 0xe7, 0xa5, + 0x7f, 0x85, 0xe2, 0x60, 0xa5, 0x83, 0x8d, 0x4c, + 0x02, 0x20, 0xb3, 0xc2, 0x8e, 0x2a, 0x02, 0xae, + 0x00, 0x02, 0xad, 0x4c, 0x02, 0xd0, 0x2c, 0xe0, + 0x2a, 0xd0, 0x28, 0xa5, 0x7e, 0xf0, 0x4d, 0x85, + 0x80, 0xad, 0x6e, 0x02, 0x85, 0x7f, 0x85, 0xe2, + 0xa9, 0x02, 0x85, 0xe7, 0xad, 0x6f, 0x02, 0x85, + 0x81, 0x20, 0x00, 0xc1, 0x20, 0x46, 0xdc, 0xa9, + 0x04, 0x05, 0x7f, 0xa6, 0x82, 0x99, 0xec, 0x00, + 0x4c, 0x94, 0xc1, 0xe0, 0x24, 0xd0, 0x1e, 0xad, + 0x4c, 0x02, 0xd0, 0x03, 0x4c, 0x55, 0xda, 0x20, + 0xd1, 0xc1, 0xad, 0x85, 0xfe, 0x85, 0x80, 0xa9, + 0x00, 0x85, 0x81, 0x20, 0x46, 0xdc, 0xa5, 0x7f, + 0x09, 0x02, 0x4c, 0xeb, 0xd7, 0xe0, 0x23, 0xd0, + 0x12, 0x4c, 0x84, 0xcb, 0xa9, 0x02, 0x8d, 0x96, + 0x02, 0xa9, 0x00, 0x85, 0x7f, 0x8d, 0x8e, 0x02, + 0x20, 0x42, 0xd0, 0x20, 0xe5, 0xc1, 0xd0, 0x04, + 0xa2, 0x00, 0xf0, 0x0c, 0x8a, 0xf0, 0x05, 0xa9, + 0x30, 0x4c, 0xc8, 0xc1, 0x88, 0xf0, 0x01, 0x88, + 0x8c, 0x7a, 0x02, 0xa9, 0x8d, 0x20, 0x68, 0xc2, + 0xe8, 0x8e, 0x78, 0x02, 0x20, 0x12, 0xc3, 0x20, + 0xca, 0xc3, 0x20, 0x9d, 0xc4, 0xa2, 0x00, 0x8e, + 0x58, 0x02, 0x8e, 0x97, 0x02, 0x8e, 0x4a, 0x02, + 0xe8, 0xec, 0x77, 0x02, 0xb0, 0x10, 0x20, 0x09, + 0xda, 0xe8, 0xec, 0x77, 0x02, 0xb0, 0x07, 0xc0, + 0x04, 0xf0, 0x3e, 0x20, 0x09, 0xda, 0xae, 0x4c, + 0x02, 0x86, 0x83, 0xe0, 0x02, 0xb0, 0x12, 0x8e, + 0x97, 0x02, 0xa9, 0x40, 0x8d, 0xf9, 0x02, 0xad, + 0x4a, 0x02, 0xd0, 0x1b, 0xa9, 0x02, 0x8d, 0x4a, + 0x02, 0xad, 0x4a, 0x02, 0xd0, 0x11, 0xa5, 0xe7, + 0x29, 0x07, 0x8d, 0x4a, 0x02, 0xad, 0x80, 0x02, + 0xd0, 0x05, 0xa9, 0x01, 0x8d, 0x4a, 0x02, 0xad, + 0x97, 0x02, 0xc9, 0x01, 0xf0, 0x18, 0x4c, 0x40, + 0xd9, 0xbc, 0x7a, 0x02, 0xb9, 0x00, 0x02, 0x8d, + 0x58, 0x02, 0xad, 0x80, 0x02, 0xd0, 0xb7, 0xa9, + 0x01, 0x8d, 0x97, 0x02, 0xd0, 0xb0, 0xa5, 0xe7, + 0x29, 0x80, 0xaa, 0xd0, 0x14, 0xa9, 0x20, 0x24, + 0xe7, 0xf0, 0x06, 0x20, 0xb6, 0xc8, 0x4c, 0xe3, + 0xd9, 0xad, 0x80, 0x02, 0xd0, 0x03, 0x4c, 0xe3, + 0xd9, 0xad, 0x00, 0x02, 0xc9, 0x40, 0xf0, 0x0d, + 0x8a, 0xd0, 0x05, 0xa9, 0x63, 0x4c, 0xc8, 0xc1, + 0xa9, 0x33, 0x4c, 0xc8, 0xc1, 0xa5, 0xe7, 0x29, + 0x07, 0xcd, 0x4a, 0x02, 0xd0, 0x67, 0xc9, 0x04, + 0xf0, 0x63, 0x20, 0xda, 0xdc, 0xa5, 0x82, 0x8d, + 0x70, 0x02, 0xa9, 0x11, 0x85, 0x83, 0x20, 0xeb, + 0xd0, 0xad, 0x94, 0x02, 0x20, 0xc8, 0xd4, 0xa0, + 0x00, 0xb1, 0x94, 0x09, 0x20, 0x91, 0x94, 0xa0, + 0x1a, 0xa5, 0x80, 0x91, 0x94, 0xc8, 0xa5, 0x81, + 0x91, 0x94, 0xae, 0x70, 0x02, 0xa5, 0xd8, 0x9d, + 0x60, 0x02, 0xa5, 0xdd, 0x9d, 0x66, 0x02, 0x20, + 0x3b, 0xde, 0x20, 0x64, 0xd4, 0x4c, 0xef, 0xd9, + 0xad, 0x80, 0x02, 0xd0, 0x05, 0xa9, 0x62, 0x4c, + 0xc8, 0xc1, 0xad, 0x97, 0x02, 0xc9, 0x03, 0xf0, + 0x0b, 0xa9, 0x20, 0x24, 0xe7, 0xf0, 0x05, 0xa9, + 0x60, 0x4c, 0xc8, 0xc1, 0xa5, 0xe7, 0x29, 0x07, + 0xcd, 0x4a, 0x02, 0xf0, 0x05, 0xa9, 0x64, 0x4c, + 0xc8, 0xc1, 0xa0, 0x00, 0x8c, 0x79, 0x02, 0xae, + 0x97, 0x02, 0xe0, 0x02, 0xd0, 0x1a, 0xc9, 0x04, + 0xf0, 0xeb, 0xb1, 0x94, 0x29, 0x4f, 0x91, 0x94, + 0xa5, 0x83, 0x48, 0xa9, 0x11, 0x85, 0x83, 0x20, + 0x3b, 0xde, 0x20, 0x64, 0xd4, 0x68, 0x85, 0x83, + 0x20, 0xa0, 0xd9, 0xad, 0x97, 0x02, 0xc9, 0x02, + 0xd0, 0x55, 0x20, 0x2a, 0xda, 0x4c, 0x94, 0xc1, + 0xa0, 0x13, 0xb1, 0x94, 0x8d, 0x59, 0x02, 0xc8, + 0xb1, 0x94, 0x8d, 0x5a, 0x02, 0xc8, 0xb1, 0x94, + 0xae, 0x58, 0x02, 0x8d, 0x58, 0x02, 0x8a, 0xf0, + 0x0a, 0xcd, 0x58, 0x02, 0xf0, 0x05, 0xa9, 0x50, + 0x20, 0xc8, 0xc1, 0xae, 0x79, 0x02, 0xbd, 0x80, + 0x02, 0x85, 0x80, 0xbd, 0x85, 0x02, 0x85, 0x81, + 0x20, 0x46, 0xdc, 0xa4, 0x82, 0xae, 0x79, 0x02, + 0xb5, 0xd8, 0x99, 0x60, 0x02, 0xb5, 0xdd, 0x99, + 0x66, 0x02, 0x60, 0xa5, 0xe2, 0x29, 0x01, 0x85, + 0x7f, 0x20, 0xda, 0xdc, 0x20, 0xe4, 0xd6, 0xa5, + 0x83, 0xc9, 0x02, 0xb0, 0x11, 0x20, 0x3e, 0xde, + 0xa5, 0x80, 0x85, 0x7e, 0xa5, 0x7f, 0x8d, 0x6e, + 0x02, 0xa5, 0x81, 0x8d, 0x6f, 0x02, 0x4c, 0x99, + 0xc1, 0xbc, 0x7a, 0x02, 0xb9, 0x00, 0x02, 0xa0, + 0x04, 0x88, 0x30, 0x08, 0xd9, 0xb2, 0xfe, 0xd0, + 0xf8, 0x8c, 0x97, 0x02, 0xa0, 0x05, 0x88, 0x30, + 0x08, 0xd9, 0xb6, 0xfe, 0xd0, 0xf8, 0x8c, 0x4a, + 0x02, 0x60, 0x20, 0x39, 0xca, 0xa9, 0x80, 0x20, + 0xa6, 0xdd, 0xf0, 0xf6, 0x20, 0x95, 0xde, 0xa6, + 0x81, 0xe8, 0x8a, 0xd0, 0x05, 0x20, 0xa3, 0xd1, + 0xa9, 0x02, 0x20, 0xc8, 0xd4, 0xa6, 0x82, 0xa9, + 0x01, 0x95, 0xf2, 0xa9, 0x80, 0x05, 0x82, 0xa6, + 0x83, 0x9d, 0x2b, 0x02, 0x60, 0xa9, 0x0c, 0x8d, + 0x2a, 0x02, 0xa9, 0x00, 0xae, 0x74, 0x02, 0xca, + 0xf0, 0x0b, 0xca, 0xd0, 0x21, 0xad, 0x01, 0x02, + 0x20, 0xbd, 0xc3, 0x30, 0x19, 0x85, 0xe2, 0xee, + 0x77, 0x02, 0xee, 0x78, 0x02, 0xee, 0x7a, 0x02, + 0xa9, 0x80, 0x85, 0xe7, 0xa9, 0x2a, 0x8d, 0x00, + 0x02, 0x8d, 0x01, 0x02, 0xd0, 0x18, 0x20, 0xe5, + 0xc1, 0xd0, 0x05, 0x20, 0xdc, 0xc2, 0xa0, 0x03, + 0x88, 0x88, 0x8c, 0x7a, 0x02, 0x20, 0x00, 0xc2, + 0x20, 0x98, 0xc3, 0x20, 0x20, 0xc3, 0x20, 0xca, + 0xc3, 0x20, 0xb7, 0xc7, 0x20, 0x9d, 0xc4, 0x20, + 0x9e, 0xec, 0x20, 0x37, 0xd1, 0xa6, 0x82, 0x9d, + 0x3e, 0x02, 0xa5, 0x7f, 0x8d, 0x8e, 0x02, 0x09, + 0x04, 0x95, 0xec, 0xa9, 0x00, 0x85, 0xa3, 0x60, + 0xa9, 0x00, 0x8d, 0xf9, 0x02, 0xa5, 0x83, 0xd0, + 0x0b, 0xa9, 0x00, 0x8d, 0x54, 0x02, 0x20, 0x27, + 0xd2, 0x4c, 0xda, 0xd4, 0xc9, 0x0f, 0xf0, 0x14, + 0x20, 0x02, 0xdb, 0xa5, 0x83, 0xc9, 0x02, 0x90, + 0xf0, 0xad, 0x6c, 0x02, 0xd0, 0x03, 0x4c, 0x94, + 0xc1, 0x4c, 0xad, 0xc1, 0xa9, 0x0e, 0x85, 0x83, + 0x20, 0x02, 0xdb, 0xc6, 0x83, 0x10, 0xf9, 0xad, + 0x6c, 0x02, 0xd0, 0x03, 0x4c, 0x94, 0xc1, 0x4c, + 0xad, 0xc1, 0xa6, 0x83, 0xbd, 0x2b, 0x02, 0xc9, + 0xff, 0xd0, 0x01, 0x60, 0x29, 0x0f, 0x85, 0x82, + 0x20, 0x25, 0xd1, 0xc9, 0x07, 0xf0, 0x0f, 0xc9, + 0x04, 0xf0, 0x11, 0x20, 0x07, 0xd1, 0xb0, 0x09, + 0x20, 0x62, 0xdb, 0x20, 0xa5, 0xdb, 0x20, 0xf4, + 0xee, 0x4c, 0x27, 0xd2, 0x20, 0xf1, 0xdd, 0x20, + 0x1e, 0xcf, 0x20, 0xcb, 0xe1, 0xa6, 0xd5, 0x86, + 0x73, 0xe6, 0x73, 0xa9, 0x00, 0x85, 0x70, 0x85, + 0x71, 0xa5, 0xd6, 0x38, 0xe9, 0x0e, 0x85, 0x72, + 0x20, 0x51, 0xdf, 0xa6, 0x82, 0xa5, 0x70, 0x95, + 0xb5, 0xa5, 0x71, 0x95, 0xbb, 0xa9, 0x40, 0x20, + 0xa6, 0xdd, 0xf0, 0x03, 0x20, 0xa5, 0xdb, 0x4c, + 0x27, 0xd2, 0xa6, 0x82, 0xb5, 0xb5, 0x15, 0xbb, + 0xd0, 0x0c, 0x20, 0xe8, 0xd4, 0xc9, 0x02, 0xd0, + 0x05, 0xa9, 0x0d, 0x20, 0xf1, 0xcf, 0x20, 0xe8, + 0xd4, 0xc9, 0x02, 0xd0, 0x0f, 0x20, 0x1e, 0xcf, + 0xa6, 0x82, 0xb5, 0xb5, 0xd0, 0x02, 0xd6, 0xbb, + 0xd6, 0xb5, 0xa9, 0x00, 0x38, 0xe9, 0x01, 0x48, + 0xa9, 0x00, 0x20, 0xc8, 0xd4, 0x20, 0xf1, 0xcf, + 0x68, 0x20, 0xf1, 0xcf, 0x20, 0xc7, 0xd0, 0x20, + 0x99, 0xd5, 0x4c, 0x1e, 0xcf, 0xa6, 0x82, 0x8e, + 0x70, 0x02, 0xa5, 0x83, 0x48, 0xbd, 0x60, 0x02, + 0x85, 0x81, 0xbd, 0x66, 0x02, 0x8d, 0x94, 0x02, + 0xb5, 0xec, 0x29, 0x01, 0x85, 0x7f, 0xad, 0x85, + 0xfe, 0x85, 0x80, 0x20, 0x93, 0xdf, 0x48, 0x85, + 0xf9, 0x20, 0x60, 0xd4, 0xa0, 0x00, 0xbd, 0xe0, + 0xfe, 0x85, 0x87, 0xad, 0x94, 0x02, 0x85, 0x86, + 0xb1, 0x86, 0x29, 0x20, 0xf0, 0x43, 0x20, 0x25, + 0xd1, 0xc9, 0x04, 0xf0, 0x44, 0xb1, 0x86, 0x29, + 0x8f, 0x91, 0x86, 0xc8, 0xb1, 0x86, 0x85, 0x80, + 0x84, 0x71, 0xa0, 0x1b, 0xb1, 0x86, 0x48, 0x88, + 0xb1, 0x86, 0xd0, 0x0a, 0x85, 0x80, 0x68, 0x85, + 0x81, 0xa9, 0x67, 0x20, 0x45, 0xe6, 0x48, 0xa9, + 0x00, 0x91, 0x86, 0xc8, 0x91, 0x86, 0x68, 0xa4, + 0x71, 0x91, 0x86, 0xc8, 0xb1, 0x86, 0x85, 0x81, + 0x68, 0x91, 0x86, 0x20, 0x7d, 0xc8, 0x4c, 0x29, + 0xdc, 0xb1, 0x86, 0x29, 0x0f, 0x09, 0x80, 0x91, + 0x86, 0xae, 0x70, 0x02, 0xa0, 0x1c, 0xb5, 0xb5, + 0x91, 0x86, 0xc8, 0xb5, 0xbb, 0x91, 0x86, 0x68, + 0xaa, 0xa9, 0x90, 0x05, 0x7f, 0x20, 0x90, 0xd5, + 0x68, 0x85, 0x83, 0x4c, 0x07, 0xd1, 0xa9, 0x01, + 0x20, 0xe2, 0xd1, 0x20, 0xb6, 0xdc, 0xad, 0x4a, + 0x02, 0x48, 0x0a, 0x05, 0x7f, 0x95, 0xec, 0x20, + 0x9b, 0xd0, 0xa6, 0x82, 0xa5, 0x80, 0xd0, 0x05, + 0xa5, 0x81, 0x9d, 0x44, 0x02, 0x68, 0xc9, 0x04, + 0xd0, 0x3f, 0xa4, 0x83, 0xb9, 0x2b, 0x02, 0x09, + 0x40, 0x99, 0x2b, 0x02, 0xad, 0x58, 0x02, 0x95, + 0xc7, 0x20, 0x8e, 0xd2, 0x10, 0x03, 0x4c, 0x0f, + 0xd2, 0xa6, 0x82, 0x95, 0xcd, 0xac, 0x59, 0x02, + 0x84, 0x80, 0xac, 0x5a, 0x02, 0x84, 0x81, 0x20, + 0xd3, 0xd6, 0x20, 0x73, 0xde, 0x20, 0x99, 0xd5, + 0xa6, 0x82, 0xa9, 0x02, 0x95, 0xc1, 0xa9, 0x00, + 0x20, 0xc8, 0xd4, 0x20, 0x53, 0xe1, 0x4c, 0x3e, + 0xde, 0x20, 0x56, 0xd1, 0xa6, 0x82, 0x9d, 0x3e, + 0x02, 0xa9, 0x88, 0x95, 0xf2, 0x60, 0xa6, 0x82, + 0xb5, 0xa7, 0x0a, 0xa8, 0xa9, 0x02, 0x99, 0x99, + 0x00, 0xb5, 0xae, 0x09, 0x80, 0x95, 0xae, 0x0a, + 0xa8, 0xa9, 0x02, 0x99, 0x99, 0x00, 0xa9, 0x00, + 0x95, 0xb5, 0x95, 0xbb, 0xa9, 0x00, 0x9d, 0x44, + 0x02, 0x60, 0x20, 0xa9, 0xf1, 0xa9, 0x01, 0x20, + 0xdf, 0xd1, 0x20, 0xd0, 0xd6, 0x20, 0xb6, 0xdc, + 0xa6, 0x82, 0xad, 0x4a, 0x02, 0x48, 0x0a, 0x05, + 0x7f, 0x95, 0xec, 0x68, 0xc9, 0x04, 0xf0, 0x05, + 0xa9, 0x01, 0x95, 0xf2, 0x60, 0xa4, 0x83, 0xb9, + 0x2b, 0x02, 0x29, 0x3f, 0x09, 0x40, 0x99, 0x2b, + 0x02, 0xad, 0x58, 0x02, 0x95, 0xc7, 0x20, 0x8e, + 0xd2, 0x10, 0x03, 0x4c, 0x0f, 0xd2, 0xa6, 0x82, + 0x95, 0xcd, 0x20, 0xc1, 0xde, 0x20, 0x1e, 0xf1, + 0xa5, 0x80, 0x8d, 0x59, 0x02, 0xa5, 0x81, 0x8d, + 0x5a, 0x02, 0xa6, 0x82, 0xb5, 0xcd, 0x20, 0xd3, + 0xd6, 0xa9, 0x00, 0x20, 0xe9, 0xde, 0xa9, 0x00, + 0x20, 0x8d, 0xdd, 0xa9, 0x11, 0x20, 0x8d, 0xdd, + 0xa9, 0x00, 0x20, 0x8d, 0xdd, 0xad, 0x58, 0x02, + 0x20, 0x8d, 0xdd, 0xa5, 0x80, 0x20, 0x8d, 0xdd, + 0xa5, 0x81, 0x20, 0x8d, 0xdd, 0xa9, 0x10, 0x20, + 0xe9, 0xde, 0x20, 0x3e, 0xde, 0xa5, 0x80, 0x20, + 0x8d, 0xdd, 0xa5, 0x81, 0x20, 0x8d, 0xdd, 0x20, + 0x6c, 0xde, 0x20, 0x99, 0xd5, 0xa9, 0x02, 0x20, + 0xc8, 0xd4, 0xa6, 0x82, 0x38, 0xa9, 0x00, 0xf5, + 0xc7, 0x95, 0xc1, 0x20, 0xe2, 0xe2, 0x20, 0x19, + 0xde, 0x20, 0x5e, 0xde, 0x20, 0x99, 0xd5, 0x20, + 0xf4, 0xee, 0x4c, 0x98, 0xdc, 0x48, 0xa6, 0x82, + 0xb5, 0xcd, 0x4c, 0xfd, 0xcf, 0x90, 0x06, 0xa6, + 0x82, 0x15, 0xec, 0xd0, 0x06, 0xa6, 0x82, 0x49, + 0xff, 0x35, 0xec, 0x95, 0xec, 0x60, 0xa6, 0x82, + 0x35, 0xec, 0x60, 0x20, 0x93, 0xdf, 0xaa, 0xbd, + 0x5b, 0x02, 0x29, 0xf0, 0xc9, 0x90, 0x60, 0xa2, + 0x00, 0x86, 0x71, 0xbd, 0x2b, 0x02, 0xc9, 0xff, + 0xd0, 0x08, 0xa6, 0x71, 0xe8, 0xe0, 0x10, 0x90, + 0xf0, 0x60, 0x86, 0x71, 0x29, 0x3f, 0xa8, 0xb9, + 0xec, 0x00, 0x29, 0x01, 0x85, 0x70, 0xae, 0x53, + 0x02, 0xb5, 0xe2, 0x29, 0x01, 0xc5, 0x70, 0xd0, + 0xe1, 0xb9, 0x60, 0x02, 0xd5, 0xd8, 0xd0, 0xda, + 0xb9, 0x66, 0x02, 0xd5, 0xdd, 0xd0, 0xd3, 0x18, + 0x60, 0x20, 0x9e, 0xdf, 0x50, 0x06, 0x20, 0x5e, + 0xde, 0x20, 0x99, 0xd5, 0x60, 0x20, 0x2b, 0xde, + 0xa5, 0x80, 0x91, 0x94, 0xc8, 0xa5, 0x81, 0x91, + 0x94, 0x4c, 0x05, 0xe1, 0x20, 0x2b, 0xde, 0xb1, + 0x94, 0x85, 0x80, 0xc8, 0xb1, 0x94, 0x85, 0x81, + 0x60, 0x20, 0x2b, 0xde, 0xa9, 0x00, 0x91, 0x94, + 0xc8, 0xa6, 0x82, 0xb5, 0xc1, 0xaa, 0xca, 0x8a, + 0x91, 0x94, 0x60, 0x20, 0x93, 0xdf, 0x0a, 0xaa, + 0xb5, 0x9a, 0x85, 0x95, 0xa9, 0x00, 0x85, 0x94, + 0xa0, 0x00, 0x60, 0x20, 0xeb, 0xd0, 0x20, 0x93, + 0xdf, 0x85, 0xf9, 0x0a, 0xa8, 0xb9, 0x06, 0x00, + 0x85, 0x80, 0xb9, 0x07, 0x00, 0x85, 0x81, 0x60, + 0xa9, 0x90, 0x8d, 0x4d, 0x02, 0xd0, 0x28, 0xa9, + 0x80, 0x8d, 0x4d, 0x02, 0xd0, 0x21, 0xa9, 0x90, + 0x8d, 0x4d, 0x02, 0xd0, 0x26, 0xa9, 0x80, 0x8d, + 0x4d, 0x02, 0xd0, 0x1f, 0xa9, 0x90, 0x8d, 0x4d, + 0x02, 0xd0, 0x02, 0xa9, 0x80, 0x8d, 0x4d, 0x02, + 0xa6, 0x82, 0xb5, 0xcd, 0xaa, 0x10, 0x13, 0x20, + 0xd0, 0xd6, 0x20, 0x93, 0xdf, 0xaa, 0xa5, 0x7f, + 0x9d, 0x5b, 0x02, 0x20, 0x15, 0xe1, 0x20, 0x93, + 0xdf, 0xaa, 0x4c, 0x06, 0xd5, 0xa9, 0x00, 0x20, + 0xc8, 0xd4, 0x20, 0x37, 0xd1, 0x85, 0x80, 0x20, + 0x37, 0xd1, 0x85, 0x81, 0x60, 0x48, 0xa9, 0x00, + 0x85, 0x6f, 0x85, 0x71, 0xb9, 0xe0, 0xfe, 0x85, + 0x70, 0xbd, 0xe0, 0xfe, 0x85, 0x72, 0x68, 0xa8, + 0x88, 0xb1, 0x6f, 0x91, 0x71, 0x88, 0x10, 0xf9, + 0x60, 0xa8, 0xb9, 0xe0, 0xfe, 0x85, 0x70, 0xa9, + 0x00, 0x85, 0x6f, 0xa8, 0x91, 0x6f, 0xc8, 0xd0, + 0xfb, 0x60, 0xa9, 0x00, 0x20, 0xdc, 0xde, 0xa0, + 0x02, 0xb1, 0x94, 0x60, 0x85, 0x94, 0xa6, 0x82, + 0xb5, 0xcd, 0xaa, 0xbd, 0xe0, 0xfe, 0x85, 0x95, + 0x60, 0x48, 0x20, 0xdc, 0xde, 0x48, 0x8a, 0x0a, + 0xaa, 0x68, 0x95, 0x9a, 0x68, 0x95, 0x99, 0x60, + 0x20, 0x66, 0xdf, 0x30, 0x0e, 0x50, 0x13, 0xa6, + 0x82, 0xb5, 0xcd, 0x20, 0x1b, 0xdf, 0x20, 0x66, + 0xdf, 0x10, 0x07, 0x20, 0xcb, 0xe1, 0x2c, 0xce, + 0xfe, 0x60, 0xa5, 0xd6, 0x20, 0xe9, 0xde, 0x2c, + 0xcd, 0xfe, 0x60, 0x85, 0xf9, 0xa9, 0x80, 0xd0, + 0x04, 0x85, 0xf9, 0xa9, 0x90, 0x48, 0xb5, 0xec, + 0x29, 0x01, 0x85, 0x7f, 0x68, 0x05, 0x7f, 0x8d, + 0x4d, 0x02, 0xb1, 0x94, 0x85, 0x80, 0xc8, 0xb1, + 0x94, 0x85, 0x81, 0xa5, 0xf9, 0x20, 0xd3, 0xd6, + 0xa6, 0xf9, 0x4c, 0x93, 0xd5, 0xa6, 0x82, 0xb5, + 0xcd, 0x4c, 0xeb, 0xd4, 0xa9, 0x78, 0x20, 0x5c, + 0xdf, 0xca, 0x10, 0xf8, 0xa5, 0x72, 0x4a, 0x20, + 0x5c, 0xdf, 0xa5, 0x73, 0x18, 0x65, 0x70, 0x85, + 0x70, 0x90, 0x02, 0xe6, 0x71, 0x60, 0x20, 0xd2, + 0xde, 0xc5, 0xd5, 0xd0, 0x0e, 0xa4, 0xd6, 0xb1, + 0x94, 0xf0, 0x04, 0x2c, 0xcd, 0xfe, 0x60, 0x2c, + 0xcf, 0xfe, 0x60, 0xa5, 0xd5, 0xc9, 0x06, 0xb0, + 0x0a, 0x0a, 0xa8, 0xa9, 0x04, 0x85, 0x94, 0xb1, + 0x94, 0xd0, 0x04, 0x2c, 0xd0, 0xfe, 0x60, 0x2c, + 0xce, 0xfe, 0x60, 0xa6, 0x82, 0xb5, 0xa7, 0x10, + 0x02, 0xb5, 0xae, 0x29, 0xbf, 0x60, 0xa6, 0x82, + 0x8e, 0x57, 0x02, 0xb5, 0xa7, 0x10, 0x09, 0x8a, + 0x18, 0x69, 0x07, 0x8d, 0x57, 0x02, 0xb5, 0xae, + 0x85, 0x70, 0x29, 0x1f, 0x24, 0x70, 0x60, 0xa6, + 0x82, 0xb5, 0xa7, 0x30, 0x02, 0xb5, 0xae, 0xc9, + 0xff, 0x60, 0xa6, 0x82, 0x09, 0x80, 0xb4, 0xa7, + 0x10, 0x03, 0x95, 0xa7, 0x60, 0x95, 0xae, 0x60, + 0xa9, 0x20, 0x20, 0x9d, 0xdd, 0xa9, 0x80, 0x20, + 0xa6, 0xdd, 0xd0, 0x41, 0xa6, 0x82, 0xf6, 0xb5, + 0xd0, 0x02, 0xf6, 0xbb, 0xa6, 0x82, 0xb5, 0xc1, + 0xf0, 0x2e, 0x20, 0xe8, 0xd4, 0xa6, 0x82, 0xd5, + 0xc1, 0x90, 0x03, 0x20, 0x3c, 0xe0, 0xa6, 0x82, + 0xb5, 0xc1, 0x20, 0xc8, 0xd4, 0xa1, 0x99, 0x85, + 0x85, 0xa9, 0x20, 0x20, 0x9d, 0xdd, 0x20, 0x04, + 0xe3, 0x48, 0x90, 0x28, 0xa9, 0x00, 0x20, 0xf6, + 0xd4, 0xd0, 0x21, 0x68, 0xc9, 0x02, 0xf0, 0x12, + 0xa9, 0x80, 0x20, 0x97, 0xdd, 0x20, 0x2f, 0xd1, + 0xb5, 0x99, 0x99, 0x44, 0x02, 0xa9, 0x0d, 0x85, + 0x85, 0x60, 0x20, 0x35, 0xe0, 0xa6, 0x82, 0xa9, + 0x00, 0x95, 0xc1, 0x60, 0x68, 0xa6, 0x82, 0x95, + 0xc1, 0x4c, 0x6e, 0xe1, 0x20, 0xd3, 0xd1, 0x20, + 0x95, 0xde, 0x20, 0x9e, 0xdf, 0x50, 0x16, 0x20, + 0x5e, 0xde, 0x20, 0x1e, 0xcf, 0xa9, 0x02, 0x20, + 0xc8, 0xd4, 0x20, 0xab, 0xdd, 0xd0, 0x24, 0x20, + 0x57, 0xde, 0x4c, 0x99, 0xd5, 0x20, 0x1e, 0xcf, + 0x20, 0xab, 0xdd, 0xd0, 0x06, 0x20, 0x57, 0xde, + 0x20, 0x99, 0xd5, 0x20, 0x95, 0xde, 0xa5, 0x80, + 0xf0, 0x09, 0x20, 0x1e, 0xcf, 0x20, 0x57, 0xde, + 0x20, 0x1e, 0xcf, 0x60, 0x20, 0x05, 0xe1, 0x20, + 0x93, 0xdf, 0x0a, 0xaa, 0xa5, 0x85, 0x81, 0x99, + 0xb4, 0x99, 0xc8, 0xd0, 0x09, 0xa4, 0x82, 0xb9, + 0xc1, 0x00, 0xf0, 0x0a, 0xa0, 0x02, 0x98, 0xa4, + 0x82, 0xd9, 0xc1, 0x00, 0xd0, 0x05, 0xa9, 0x20, + 0x4c, 0x97, 0xdd, 0xf6, 0x99, 0xd0, 0x03, 0x20, + 0x3c, 0xe0, 0x60, 0xa9, 0xa0, 0x20, 0xa6, 0xdd, + 0xd0, 0x27, 0xa5, 0x85, 0x20, 0x7c, 0xe0, 0xa5, + 0xf8, 0xf0, 0x0d, 0x60, 0xa9, 0x20, 0x20, 0xa6, + 0xdd, 0xf0, 0x05, 0xa9, 0x51, 0x8d, 0x6c, 0x02, + 0x20, 0xf3, 0xe0, 0x20, 0x53, 0xe1, 0xad, 0x6c, + 0x02, 0xf0, 0x03, 0x4c, 0xc8, 0xc1, 0x4c, 0xbc, + 0xe6, 0x29, 0x80, 0xd0, 0x05, 0xa5, 0xf8, 0xf0, + 0xdb, 0x60, 0xa5, 0x85, 0x48, 0x20, 0x1c, 0xe3, + 0x68, 0x85, 0x85, 0xa9, 0x80, 0x20, 0x9d, 0xdd, + 0x4c, 0xb2, 0xe0, 0xa9, 0x20, 0x20, 0xa6, 0xdd, + 0xd0, 0x0a, 0xa9, 0x00, 0x85, 0x85, 0x20, 0x7c, + 0xe0, 0x4c, 0xf3, 0xe0, 0x60, 0xa9, 0x40, 0x20, + 0x97, 0xdd, 0x20, 0x9e, 0xdf, 0x09, 0x40, 0xae, + 0x57, 0x02, 0x95, 0xa7, 0x60, 0x20, 0x9e, 0xdf, + 0x29, 0xbf, 0xae, 0x57, 0x02, 0x95, 0xa7, 0x60, + 0xa9, 0x80, 0x20, 0xa6, 0xdd, 0xd0, 0x37, 0x20, + 0x2f, 0xd1, 0xb5, 0x99, 0xd9, 0x44, 0x02, 0xf0, + 0x22, 0xf6, 0x99, 0xd0, 0x06, 0x20, 0x3c, 0xe0, + 0x20, 0x2f, 0xd1, 0xa1, 0x99, 0x99, 0x3e, 0x02, + 0xa9, 0x89, 0x99, 0xf2, 0x00, 0xb5, 0x99, 0xd9, + 0x44, 0x02, 0xf0, 0x01, 0x60, 0xa9, 0x81, 0x99, + 0xf2, 0x00, 0x60, 0x20, 0xd0, 0xdf, 0x20, 0x2f, + 0xd1, 0xa5, 0x85, 0x4c, 0x3d, 0xe1, 0xa6, 0x82, + 0xa9, 0x0d, 0x9d, 0x3e, 0x02, 0xa9, 0x81, 0x95, + 0xf2, 0xa9, 0x50, 0x20, 0xc8, 0xc1, 0xa6, 0x82, + 0xb5, 0xc1, 0x85, 0x87, 0xc6, 0x87, 0xc9, 0x02, + 0xd0, 0x04, 0xa9, 0xff, 0x85, 0x87, 0xb5, 0xc7, + 0x85, 0x88, 0x20, 0xe8, 0xd4, 0xa6, 0x82, 0xc5, + 0x87, 0x90, 0x19, 0xf0, 0x17, 0x20, 0x1e, 0xcf, + 0x20, 0xb2, 0xe1, 0x90, 0x08, 0xa6, 0x82, 0x9d, + 0x44, 0x02, 0x4c, 0x1e, 0xcf, 0x20, 0x1e, 0xcf, + 0xa9, 0xff, 0x85, 0x87, 0x20, 0xb2, 0xe1, 0xb0, + 0x03, 0x20, 0xe8, 0xd4, 0xa6, 0x82, 0x9d, 0x44, + 0x02, 0x60, 0x20, 0x2b, 0xde, 0xa4, 0x87, 0xb1, + 0x94, 0xd0, 0x0d, 0x88, 0xc0, 0x02, 0x90, 0x04, + 0xc6, 0x88, 0xd0, 0xf3, 0xc6, 0x88, 0x18, 0x60, + 0x98, 0x38, 0x60, 0x20, 0xd2, 0xde, 0x85, 0xd5, + 0xa9, 0x04, 0x85, 0x94, 0xa0, 0x0a, 0xd0, 0x04, + 0x88, 0x88, 0x30, 0x26, 0xb1, 0x94, 0xf0, 0xf8, + 0x98, 0x4a, 0xc5, 0xd5, 0xf0, 0x09, 0x85, 0xd5, + 0xa6, 0x82, 0xb5, 0xcd, 0x20, 0x1b, 0xdf, 0xa0, + 0x00, 0x84, 0x94, 0xb1, 0x94, 0xd0, 0x0b, 0xc8, + 0xb1, 0x94, 0xa8, 0x88, 0x84, 0xd6, 0x98, 0x4c, + 0xe9, 0xde, 0xa9, 0x67, 0x20, 0x45, 0xe6, 0x20, + 0xb3, 0xc2, 0xad, 0x01, 0x02, 0x85, 0x83, 0x20, + 0xeb, 0xd0, 0x90, 0x05, 0xa9, 0x70, 0x20, 0xc8, + 0xc1, 0xa9, 0xa0, 0x20, 0x9d, 0xdd, 0x20, 0x25, + 0xd1, 0xf0, 0x05, 0xa9, 0x64, 0x20, 0xc8, 0xc1, + 0xb5, 0xec, 0x29, 0x01, 0x85, 0x7f, 0xad, 0x02, + 0x02, 0x95, 0xb5, 0xad, 0x03, 0x02, 0x95, 0xbb, + 0xa6, 0x82, 0xa9, 0x89, 0x95, 0xf2, 0xad, 0x04, + 0x02, 0xf0, 0x10, 0x38, 0xe9, 0x01, 0xf0, 0x0b, + 0xd5, 0xc7, 0x90, 0x07, 0xa9, 0x51, 0x8d, 0x6c, + 0x02, 0xa9, 0x00, 0x85, 0xd4, 0x20, 0x0e, 0xce, + 0x20, 0xf8, 0xde, 0x50, 0x08, 0xa9, 0x80, 0x20, + 0x97, 0xdd, 0x4c, 0x5e, 0xe1, 0x20, 0x75, 0xe2, + 0xa9, 0x80, 0x20, 0xa6, 0xdd, 0xf0, 0x03, 0x4c, + 0x5e, 0xe1, 0x4c, 0x94, 0xc1, 0x20, 0x9c, 0xe2, + 0xa5, 0xd7, 0x20, 0xc8, 0xd4, 0xa6, 0x82, 0xb5, + 0xc7, 0x38, 0xe5, 0xd4, 0xb0, 0x03, 0x4c, 0x02, + 0xe2, 0x18, 0x65, 0xd7, 0x90, 0x03, 0x69, 0x01, + 0x38, 0x20, 0x09, 0xe0, 0x4c, 0x38, 0xe1, 0xa9, + 0x51, 0x20, 0xc8, 0xc1, 0xa5, 0x94, 0x85, 0x89, + 0xa5, 0x95, 0x85, 0x8a, 0x20, 0xd0, 0xe2, 0xd0, + 0x01, 0x60, 0x20, 0xf1, 0xdd, 0x20, 0x0c, 0xde, + 0xa5, 0x80, 0xf0, 0x0e, 0x20, 0xd3, 0xe2, 0xd0, + 0x06, 0x20, 0x1e, 0xcf, 0x4c, 0xda, 0xd2, 0x20, + 0xda, 0xd2, 0xa0, 0x00, 0xb1, 0x89, 0x85, 0x80, + 0xc8, 0xb1, 0x89, 0x85, 0x81, 0x4c, 0xaf, 0xd0, + 0x20, 0x3e, 0xde, 0xa0, 0x00, 0xb1, 0x89, 0xc5, + 0x80, 0xf0, 0x01, 0x60, 0xc8, 0xb1, 0x89, 0xc5, + 0x81, 0x60, 0x20, 0x2b, 0xde, 0xa0, 0x02, 0xa9, + 0x00, 0x91, 0x94, 0xc8, 0xd0, 0xfb, 0x20, 0x04, + 0xe3, 0x95, 0xc1, 0xa8, 0xa9, 0xff, 0x91, 0x94, + 0x20, 0x04, 0xe3, 0x90, 0xf4, 0xd0, 0x04, 0xa9, + 0x00, 0x95, 0xc1, 0x60, 0xa6, 0x82, 0xb5, 0xc1, + 0x38, 0xf0, 0x0d, 0x18, 0x75, 0xc7, 0x90, 0x0b, + 0xd0, 0x06, 0xa9, 0x02, 0x2c, 0xcc, 0xfe, 0x60, + 0x69, 0x01, 0x38, 0x60, 0x20, 0xd3, 0xd1, 0x20, + 0xcb, 0xe1, 0x20, 0x9c, 0xe2, 0x20, 0x7b, 0xcf, + 0xa5, 0xd6, 0x85, 0x87, 0xa5, 0xd5, 0x85, 0x86, + 0xa9, 0x00, 0x85, 0x88, 0xa9, 0x00, 0x85, 0xd4, + 0x20, 0x0e, 0xce, 0x20, 0x4d, 0xef, 0xa4, 0x82, + 0xb6, 0xc7, 0xca, 0x8a, 0x18, 0x65, 0xd7, 0x90, + 0x0c, 0xe6, 0xd6, 0xe6, 0xd6, 0xd0, 0x06, 0xe6, + 0xd5, 0xa9, 0x10, 0x85, 0xd6, 0xa5, 0x87, 0x18, + 0x69, 0x02, 0x20, 0xe9, 0xde, 0xa5, 0xd5, 0xc9, + 0x06, 0x90, 0x05, 0xa9, 0x52, 0x20, 0xc8, 0xc1, + 0xa5, 0xd6, 0x38, 0xe5, 0x87, 0xb0, 0x03, 0xe9, + 0x0f, 0x18, 0x85, 0x72, 0xa5, 0xd5, 0xe5, 0x86, + 0x85, 0x73, 0xa2, 0x00, 0x86, 0x70, 0x86, 0x71, + 0xaa, 0x20, 0x51, 0xdf, 0xa5, 0x71, 0xd0, 0x07, + 0xa6, 0x70, 0xca, 0xd0, 0x02, 0xe6, 0x88, 0xcd, + 0x73, 0x02, 0x90, 0x09, 0xd0, 0xcd, 0xad, 0x72, + 0x02, 0xc5, 0x70, 0x90, 0xc6, 0xa9, 0x01, 0x20, + 0xf6, 0xd4, 0x18, 0x69, 0x01, 0xa6, 0x82, 0x95, + 0xc1, 0x20, 0x1e, 0xf1, 0x20, 0xfd, 0xdd, 0xa5, + 0x88, 0xd0, 0x15, 0x20, 0x5e, 0xde, 0x20, 0x1e, + 0xcf, 0x20, 0xd0, 0xd6, 0x20, 0x1e, 0xf1, 0x20, + 0xfd, 0xdd, 0x20, 0xe2, 0xe2, 0x4c, 0xd4, 0xe3, + 0x20, 0x1e, 0xcf, 0x20, 0xd0, 0xd6, 0x20, 0xe2, + 0xe2, 0x20, 0x19, 0xde, 0x20, 0x5e, 0xde, 0x20, + 0x0c, 0xde, 0xa5, 0x80, 0x48, 0xa5, 0x81, 0x48, + 0x20, 0x3e, 0xde, 0xa5, 0x81, 0x48, 0xa5, 0x80, + 0x48, 0x20, 0x45, 0xdf, 0xaa, 0xd0, 0x0a, 0x20, + 0x4e, 0xe4, 0xa9, 0x10, 0x20, 0xe9, 0xde, 0xe6, + 0x86, 0x68, 0x20, 0x8d, 0xdd, 0x68, 0x20, 0x8d, + 0xdd, 0x68, 0x85, 0x81, 0x68, 0x85, 0x80, 0xf0, + 0x0f, 0xa5, 0x86, 0xc5, 0xd5, 0xd0, 0xa7, 0x20, + 0x45, 0xdf, 0xc5, 0xd6, 0x90, 0xa0, 0xf0, 0xb0, + 0x20, 0x45, 0xdf, 0x48, 0xa9, 0x00, 0x20, 0xdc, + 0xde, 0xa9, 0x00, 0xa8, 0x91, 0x94, 0xc8, 0x68, + 0x38, 0xe9, 0x01, 0x91, 0x94, 0x20, 0x6c, 0xde, + 0x20, 0x99, 0xd5, 0x20, 0xf4, 0xee, 0x20, 0x0e, + 0xce, 0x20, 0x1e, 0xcf, 0x20, 0xf8, 0xde, 0x70, + 0x03, 0x4c, 0x75, 0xe2, 0xa9, 0x80, 0x20, 0x97, + 0xdd, 0xa9, 0x50, 0x20, 0xc8, 0xc1, 0x20, 0x1e, + 0xf1, 0x20, 0x1e, 0xcf, 0x20, 0xf1, 0xdd, 0x20, + 0x93, 0xdf, 0x48, 0x20, 0xc1, 0xde, 0xa6, 0x82, + 0xb5, 0xcd, 0xa8, 0x68, 0xaa, 0xa9, 0x10, 0x20, + 0xa5, 0xde, 0xa9, 0x00, 0x20, 0xdc, 0xde, 0xa0, + 0x02, 0xb1, 0x94, 0x48, 0xa9, 0x00, 0x20, 0xc8, + 0xd4, 0x68, 0x18, 0x69, 0x01, 0x91, 0x94, 0x0a, + 0x69, 0x04, 0x85, 0x89, 0xa8, 0x38, 0xe9, 0x02, + 0x85, 0x8a, 0xa5, 0x80, 0x85, 0x87, 0x91, 0x94, + 0xc8, 0xa5, 0x81, 0x85, 0x88, 0x91, 0x94, 0xa0, + 0x00, 0x98, 0x91, 0x94, 0xc8, 0xa9, 0x11, 0x91, + 0x94, 0xa9, 0x10, 0x20, 0xc8, 0xd4, 0x20, 0x50, + 0xde, 0x20, 0x99, 0xd5, 0xa6, 0x82, 0xb5, 0xcd, + 0x48, 0x20, 0x9e, 0xdf, 0xa6, 0x82, 0x95, 0xcd, + 0x68, 0xae, 0x57, 0x02, 0x95, 0xa7, 0xa9, 0x00, + 0x20, 0xc8, 0xd4, 0xa0, 0x00, 0xa5, 0x80, 0x91, + 0x94, 0xc8, 0xa5, 0x81, 0x91, 0x94, 0x4c, 0xde, + 0xe4, 0x20, 0x93, 0xdf, 0xa6, 0x82, 0x20, 0x1b, + 0xdf, 0xa9, 0x00, 0x20, 0xc8, 0xd4, 0xc6, 0x8a, + 0xc6, 0x8a, 0xa4, 0x89, 0xa5, 0x87, 0x91, 0x94, + 0xc8, 0xa5, 0x88, 0x91, 0x94, 0x20, 0x5e, 0xde, + 0x20, 0x99, 0xd5, 0xa4, 0x8a, 0xc0, 0x03, 0xb0, + 0xd8, 0x4c, 0x1e, 0xcf, 0x00, 0xa0, 0x4f, 0xcb, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x27, 0xd2, 0x45, + 0x41, 0x44, 0x89, 0x52, 0x83, 0x20, 0x54, 0x4f, + 0x4f, 0x20, 0x4c, 0x41, 0x52, 0x47, 0xc5, 0x50, + 0x8b, 0x06, 0x20, 0x50, 0x52, 0x45, 0x53, 0x45, + 0x4e, 0xd4, 0x51, 0xcf, 0x56, 0x45, 0x52, 0x46, + 0x4c, 0x4f, 0x57, 0x20, 0x49, 0x4e, 0x8b, 0x25, + 0x28, 0x8a, 0x89, 0x26, 0x8a, 0x20, 0x50, 0x52, + 0x4f, 0x54, 0x45, 0x43, 0x54, 0x20, 0x4f, 0xce, + 0x29, 0x88, 0x20, 0x49, 0x44, 0x85, 0x30, 0x31, + 0x32, 0x33, 0x34, 0xd3, 0x59, 0x4e, 0x54, 0x41, + 0x58, 0x89, 0x60, 0x8a, 0x03, 0x84, 0x63, 0x83, + 0x20, 0x45, 0x58, 0x49, 0x53, 0x54, 0xd3, 0x64, + 0x83, 0x20, 0x54, 0x59, 0x50, 0x45, 0x85, 0x65, + 0xce, 0x4f, 0x20, 0x42, 0x4c, 0x4f, 0x43, 0xcb, + 0x66, 0x67, 0xc9, 0x4c, 0x4c, 0x45, 0x47, 0x41, + 0x4c, 0x20, 0x54, 0x52, 0x41, 0x43, 0x4b, 0x20, + 0x4f, 0x52, 0x20, 0x53, 0x45, 0x43, 0x54, 0x4f, + 0xd2, 0x61, 0x83, 0x06, 0x84, 0x39, 0x62, 0x83, + 0x06, 0x87, 0x01, 0x83, 0x53, 0x20, 0x53, 0x43, + 0x52, 0x41, 0x54, 0x43, 0x48, 0x45, 0xc4, 0x70, + 0xce, 0x4f, 0x20, 0x43, 0x48, 0x41, 0x4e, 0x4e, + 0x45, 0xcc, 0x71, 0xc4, 0x49, 0x52, 0x89, 0x72, + 0x88, 0x20, 0x46, 0x55, 0x4c, 0xcc, 0x73, 0xc3, + 0x42, 0x4d, 0x20, 0x44, 0x4f, 0x53, 0x20, 0x56, + 0x32, 0x2e, 0x36, 0x20, 0x31, 0x35, 0x34, 0xb1, + 0x74, 0xc4, 0x52, 0x49, 0x56, 0x45, 0x06, 0x20, + 0x52, 0x45, 0x41, 0x44, 0xd9, 0x09, 0xc5, 0x52, + 0x52, 0x4f, 0xd2, 0x0a, 0xd7, 0x52, 0x49, 0x54, + 0xc5, 0x03, 0xc6, 0x49, 0x4c, 0xc5, 0x04, 0xcf, + 0x50, 0x45, 0xce, 0x05, 0xcd, 0x49, 0x53, 0x4d, + 0x41, 0x54, 0x43, 0xc8, 0x06, 0xce, 0x4f, 0xd4, + 0x07, 0xc6, 0x4f, 0x55, 0x4e, 0xc4, 0x08, 0xc4, + 0x49, 0x53, 0xcb, 0x0b, 0xd2, 0x45, 0x43, 0x4f, + 0x52, 0xc4, 0x48, 0x86, 0xf9, 0x8a, 0x0a, 0xaa, + 0xb5, 0x06, 0x85, 0x80, 0xb5, 0x07, 0x85, 0x81, + 0x68, 0x29, 0x0f, 0xf0, 0x08, 0xc9, 0x0f, 0xd0, + 0x06, 0xa9, 0x74, 0xd0, 0x08, 0xa9, 0x06, 0x09, + 0x20, 0xaa, 0xca, 0xca, 0x8a, 0x48, 0xad, 0x2a, + 0x02, 0xc9, 0x00, 0xd0, 0x0f, 0xa9, 0xff, 0x8d, + 0x2a, 0x02, 0x68, 0x20, 0xc7, 0xe6, 0x20, 0x42, + 0xd0, 0x4c, 0x48, 0xe6, 0x68, 0x20, 0xc7, 0xe6, + 0x20, 0xbd, 0xc1, 0xa9, 0x00, 0x8d, 0xf9, 0x02, + 0x20, 0x2c, 0xc1, 0x20, 0xda, 0xd4, 0xa9, 0x00, + 0x85, 0xa3, 0xa2, 0x45, 0x9a, 0xa5, 0x84, 0x29, + 0x0f, 0x85, 0x83, 0xc9, 0x0f, 0xf0, 0x31, 0x78, + 0xa5, 0x79, 0xd0, 0x1c, 0xa5, 0x7a, 0xd0, 0x10, + 0xa6, 0x83, 0xbd, 0x2b, 0x02, 0xc9, 0xff, 0xf0, + 0x1f, 0x29, 0x0f, 0x85, 0x82, 0x4c, 0x8e, 0xe6, + 0x20, 0xeb, 0xd0, 0xea, 0xea, 0xea, 0xd0, 0x06, + 0x20, 0x07, 0xd1, 0xea, 0xea, 0xea, 0x20, 0x25, + 0xd1, 0xc9, 0x04, 0xb0, 0x03, 0x20, 0x27, 0xd2, + 0x4c, 0xe7, 0xeb, 0xaa, 0xa9, 0x00, 0xf8, 0xe0, + 0x00, 0xf0, 0x07, 0x18, 0x69, 0x01, 0xca, 0x4c, + 0x9f, 0xe6, 0xd8, 0xaa, 0x4a, 0x4a, 0x4a, 0x4a, + 0x20, 0xb4, 0xe6, 0x8a, 0x29, 0x0f, 0x09, 0x30, + 0x91, 0xa5, 0xc8, 0x60, 0x20, 0x23, 0xc1, 0xa9, + 0x00, 0xa0, 0x00, 0x84, 0x80, 0x84, 0x81, 0xa0, + 0x00, 0xa2, 0xd5, 0x86, 0xa5, 0xa2, 0x02, 0x86, + 0xa6, 0x20, 0xab, 0xe6, 0xa9, 0x2c, 0x91, 0xa5, + 0xc8, 0xad, 0xd5, 0x02, 0x8d, 0x43, 0x02, 0x8a, + 0x20, 0x06, 0xe7, 0xa9, 0x2c, 0x91, 0xa5, 0xc8, + 0xa5, 0x80, 0x20, 0x9b, 0xe6, 0xa9, 0x2c, 0x91, + 0xa5, 0xc8, 0xa5, 0x81, 0x20, 0x9b, 0xe6, 0x88, + 0x98, 0x18, 0x69, 0xd5, 0x8d, 0x49, 0x02, 0xe6, + 0xa5, 0xa9, 0x88, 0x85, 0xf7, 0x60, 0xaa, 0xa5, + 0x86, 0x48, 0xa5, 0x87, 0x48, 0xa9, 0xfc, 0x85, + 0x86, 0xa9, 0xe4, 0x85, 0x87, 0x8a, 0xa2, 0x00, + 0xc1, 0x86, 0xf0, 0x21, 0x48, 0x20, 0x75, 0xe7, + 0x90, 0x05, 0x20, 0x75, 0xe7, 0x90, 0xfb, 0xa5, + 0x87, 0xc9, 0xe6, 0x90, 0x08, 0xd0, 0x0a, 0xa9, + 0x0a, 0xc5, 0x86, 0x90, 0x04, 0x68, 0x4c, 0x18, + 0xe7, 0x68, 0x4c, 0x4d, 0xe7, 0x20, 0x67, 0xe7, + 0x90, 0xfb, 0x20, 0x54, 0xe7, 0x20, 0x67, 0xe7, + 0x90, 0xf8, 0x20, 0x54, 0xe7, 0x68, 0x85, 0x87, + 0x68, 0x85, 0x86, 0x60, 0xc9, 0x20, 0xb0, 0x0b, + 0xaa, 0xa9, 0x20, 0x91, 0xa5, 0xc8, 0x8a, 0x20, + 0x06, 0xe7, 0x60, 0x91, 0xa5, 0xc8, 0x60, 0xe6, + 0x86, 0xd0, 0x02, 0xe6, 0x87, 0xa1, 0x86, 0x0a, + 0xa1, 0x86, 0x29, 0x7f, 0x60, 0x20, 0x6d, 0xe7, + 0xe6, 0x86, 0xd0, 0x02, 0xe6, 0x87, 0x60, 0x60, + 0x60, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, + 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, + 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, + 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, + 0xea, 0xea, 0x60, 0xa9, 0x8d, 0x20, 0x68, 0xc2, + 0x20, 0x58, 0xf2, 0xad, 0x78, 0x02, 0x48, 0xa9, + 0x01, 0x8d, 0x78, 0x02, 0xa9, 0xff, 0x85, 0x86, + 0x20, 0x4f, 0xc4, 0xad, 0x80, 0x02, 0xd0, 0x05, + 0xa9, 0x39, 0x20, 0xc8, 0xc1, 0x68, 0x8d, 0x78, + 0x02, 0xad, 0x80, 0x02, 0x85, 0x80, 0xad, 0x85, + 0x02, 0x85, 0x81, 0xa9, 0x03, 0x20, 0x77, 0xd4, + 0xa9, 0x00, 0x85, 0x87, 0x20, 0x39, 0xe8, 0x85, + 0x88, 0x20, 0x4b, 0xe8, 0x20, 0x39, 0xe8, 0x85, + 0x89, 0x20, 0x4b, 0xe8, 0xa5, 0x86, 0xf0, 0x0a, + 0xa5, 0x88, 0x48, 0xa5, 0x89, 0x48, 0xa9, 0x00, + 0x85, 0x86, 0x20, 0x39, 0xe8, 0x85, 0x8a, 0x20, + 0x4b, 0xe8, 0x20, 0x39, 0xe8, 0xa0, 0x00, 0x91, + 0x88, 0x20, 0x4b, 0xe8, 0xa5, 0x88, 0x18, 0x69, + 0x01, 0x85, 0x88, 0x90, 0x02, 0xe6, 0x89, 0xc6, + 0x8a, 0xd0, 0xe7, 0x20, 0x35, 0xca, 0xa5, 0x85, + 0xc5, 0x87, 0xf0, 0x08, 0x20, 0x3e, 0xde, 0xa9, + 0x50, 0x20, 0x45, 0xe6, 0xa5, 0xf8, 0xd0, 0xa8, + 0x68, 0x85, 0x89, 0x68, 0x85, 0x88, 0x6c, 0x88, + 0x00, 0x20, 0x35, 0xca, 0xa5, 0xf8, 0xd0, 0x08, + 0x20, 0x3e, 0xde, 0xa9, 0x51, 0x20, 0x45, 0xe6, + 0xa5, 0x85, 0x60, 0x18, 0x65, 0x87, 0x69, 0x00, + 0x85, 0x87, 0x60, 0xad, 0x01, 0x18, 0xa9, 0x01, + 0x85, 0x7c, 0x60, 0x78, 0xa9, 0x00, 0x85, 0x7c, + 0x85, 0x79, 0x85, 0x7a, 0xa2, 0x45, 0x9a, 0xa9, + 0x80, 0x85, 0xf8, 0x85, 0x7d, 0x20, 0xb7, 0xe9, + 0x20, 0xa5, 0xe9, 0xad, 0x00, 0x18, 0x09, 0x10, + 0x8d, 0x00, 0x18, 0xad, 0x00, 0x18, 0x10, 0x57, + 0x29, 0x04, 0xd0, 0xf7, 0x20, 0xc9, 0xe9, 0xc9, + 0x3f, 0xd0, 0x06, 0xa9, 0x00, 0x85, 0x79, 0xf0, + 0x71, 0xc9, 0x5f, 0xd0, 0x06, 0xa9, 0x00, 0x85, + 0x7a, 0xf0, 0x67, 0xc5, 0x78, 0xd0, 0x0a, 0xa9, + 0x01, 0x85, 0x7a, 0xa9, 0x00, 0x85, 0x79, 0xf0, + 0x29, 0xc5, 0x77, 0xd0, 0x0a, 0xa9, 0x01, 0x85, + 0x79, 0xa9, 0x00, 0x85, 0x7a, 0xf0, 0x1b, 0xaa, + 0x29, 0x60, 0xc9, 0x60, 0xd0, 0x3f, 0x8a, 0x85, + 0x84, 0x29, 0x0f, 0x85, 0x83, 0xa5, 0x84, 0x29, + 0xf0, 0xc9, 0xe0, 0xd0, 0x35, 0x58, 0x20, 0xc0, + 0xda, 0x78, 0x2c, 0x00, 0x18, 0x30, 0xad, 0xa9, + 0x00, 0x85, 0x7d, 0xad, 0x00, 0x18, 0x29, 0xef, + 0x8d, 0x00, 0x18, 0xa5, 0x79, 0xf0, 0x06, 0x20, + 0x2e, 0xea, 0x4c, 0xe7, 0xeb, 0xa5, 0x7a, 0xf0, + 0x09, 0x20, 0x9c, 0xe9, 0x20, 0xae, 0xe9, 0x20, + 0x09, 0xe9, 0x4c, 0x4e, 0xea, 0xa9, 0x10, 0x8d, + 0x00, 0x18, 0x2c, 0x00, 0x18, 0x10, 0xd0, 0x30, + 0xf9, 0x78, 0x20, 0xeb, 0xd0, 0xb0, 0x06, 0xa6, + 0x82, 0xb5, 0xf2, 0x30, 0x01, 0x60, 0x20, 0x59, + 0xea, 0x20, 0xc0, 0xe9, 0x29, 0x01, 0x08, 0x20, + 0xb7, 0xe9, 0x28, 0xf0, 0x12, 0x20, 0x59, 0xea, + 0x20, 0xc0, 0xe9, 0x29, 0x01, 0xd0, 0xf6, 0xa6, + 0x82, 0xb5, 0xf2, 0x29, 0x08, 0xd0, 0x14, 0x20, + 0x59, 0xea, 0x20, 0xc0, 0xe9, 0x29, 0x01, 0xd0, + 0xf6, 0x20, 0x59, 0xea, 0x20, 0xc0, 0xe9, 0x29, + 0x01, 0xf0, 0xf6, 0x20, 0xae, 0xe9, 0x20, 0x59, + 0xea, 0x20, 0xc0, 0xe9, 0x29, 0x01, 0xd0, 0xf3, + 0xa9, 0x08, 0x85, 0x98, 0x20, 0xc0, 0xe9, 0x29, + 0x01, 0xd0, 0x36, 0xa6, 0x82, 0xbd, 0x3e, 0x02, + 0x6a, 0x9d, 0x3e, 0x02, 0xb0, 0x05, 0x20, 0xa5, + 0xe9, 0xd0, 0x03, 0x20, 0x9c, 0xe9, 0x20, 0xb7, + 0xe9, 0xa5, 0x23, 0xd0, 0x03, 0x20, 0xf3, 0xfe, + 0x20, 0xfb, 0xfe, 0xc6, 0x98, 0xd0, 0xd5, 0x20, + 0x59, 0xea, 0x20, 0xc0, 0xe9, 0x29, 0x01, 0xf0, + 0xf6, 0x58, 0x20, 0xaa, 0xd3, 0x78, 0x4c, 0x0f, + 0xe9, 0x4c, 0x4e, 0xea, 0xad, 0x00, 0x18, 0x29, + 0xfd, 0x8d, 0x00, 0x18, 0x60, 0xad, 0x00, 0x18, + 0x09, 0x02, 0x8d, 0x00, 0x18, 0x60, 0xad, 0x00, + 0x18, 0x09, 0x08, 0x8d, 0x00, 0x18, 0x60, 0xad, + 0x00, 0x18, 0x29, 0xf7, 0x8d, 0x00, 0x18, 0x60, + 0xad, 0x00, 0x18, 0xcd, 0x00, 0x18, 0xd0, 0xf8, + 0x60, 0xa9, 0x08, 0x85, 0x98, 0x20, 0x59, 0xea, + 0x20, 0xc0, 0xe9, 0x29, 0x04, 0xd0, 0xf6, 0x20, + 0x9c, 0xe9, 0xa9, 0x01, 0x4c, 0x20, 0xff, 0x20, + 0x59, 0xea, 0xad, 0x0d, 0x18, 0x29, 0x40, 0xd0, + 0x09, 0x20, 0xc0, 0xe9, 0x29, 0x04, 0xf0, 0xef, + 0xd0, 0x19, 0x20, 0xa5, 0xe9, 0xa2, 0x0a, 0xca, + 0xd0, 0xfd, 0x20, 0x9c, 0xe9, 0x20, 0x59, 0xea, + 0x20, 0xc0, 0xe9, 0x29, 0x04, 0xf0, 0xf6, 0xa9, + 0x00, 0x85, 0xf8, 0xad, 0x00, 0x18, 0x49, 0x01, + 0x4a, 0x29, 0x02, 0xd0, 0xf6, 0xea, 0xea, 0xea, + 0x66, 0x85, 0x20, 0x59, 0xea, 0x20, 0xc0, 0xe9, + 0x29, 0x04, 0xf0, 0xf6, 0xc6, 0x98, 0xd0, 0xe3, + 0x20, 0xa5, 0xe9, 0xa5, 0x85, 0x60, 0x78, 0x20, + 0x07, 0xd1, 0xb0, 0x05, 0xb5, 0xf2, 0x6a, 0xb0, + 0x0b, 0xa5, 0x84, 0x29, 0xf0, 0xc9, 0xf0, 0xf0, + 0x03, 0x4c, 0x4e, 0xea, 0x20, 0xc9, 0xe9, 0x58, + 0x20, 0xb7, 0xcf, 0x4c, 0x2e, 0xea, 0xa9, 0x00, + 0x8d, 0x00, 0x18, 0x4c, 0xe7, 0xeb, 0x4c, 0x5b, + 0xe8, 0xa5, 0x7d, 0xf0, 0x06, 0xad, 0x00, 0x18, + 0x10, 0x09, 0x60, 0xad, 0x00, 0x18, 0x10, 0xfa, + 0x4c, 0x5b, 0xe8, 0x4c, 0xd7, 0xe8, 0xa2, 0x00, + 0x2c, 0xa6, 0x6f, 0x9a, 0xba, 0xa9, 0x08, 0x0d, + 0x00, 0x1c, 0x4c, 0xea, 0xfe, 0x98, 0x18, 0x69, + 0x01, 0xd0, 0xfc, 0x88, 0xd0, 0xf8, 0xad, 0x00, + 0x1c, 0x29, 0xf7, 0x8d, 0x00, 0x1c, 0x98, 0x18, + 0x69, 0x01, 0xd0, 0xfc, 0x88, 0xd0, 0xf8, 0xca, + 0x10, 0xdb, 0xe0, 0xfc, 0xd0, 0xf0, 0xf0, 0xd4, + 0x78, 0xd8, 0xa2, 0xff, 0x4c, 0x10, 0xff, 0xe8, + 0xa0, 0x00, 0xa2, 0x00, 0x8a, 0x95, 0x00, 0xe8, + 0xd0, 0xfa, 0x8a, 0xd5, 0x00, 0xd0, 0xb7, 0xf6, + 0x00, 0xc8, 0xd0, 0xfb, 0xd5, 0x00, 0xd0, 0xae, + 0x94, 0x00, 0xb5, 0x00, 0xd0, 0xa8, 0xe8, 0xd0, + 0xe9, 0xe6, 0x6f, 0x86, 0x76, 0xa9, 0x00, 0x85, + 0x75, 0xa8, 0xa2, 0x20, 0x18, 0xc6, 0x76, 0x71, + 0x75, 0xc8, 0xd0, 0xfb, 0xca, 0xd0, 0xf6, 0x69, + 0x00, 0xaa, 0xc5, 0x76, 0xd0, 0x39, 0xe0, 0xc0, + 0xd0, 0xdf, 0xa9, 0x01, 0x85, 0x76, 0xe6, 0x6f, + 0xa2, 0x07, 0x98, 0x18, 0x65, 0x76, 0x91, 0x75, + 0xc8, 0xd0, 0xf7, 0xe6, 0x76, 0xca, 0xd0, 0xf2, + 0xa2, 0x07, 0xc6, 0x76, 0x88, 0x98, 0x18, 0x65, + 0x76, 0xd1, 0x75, 0xd0, 0x12, 0x49, 0xff, 0x91, + 0x75, 0x51, 0x75, 0x91, 0x75, 0xd0, 0x08, 0x98, + 0xd0, 0xea, 0xca, 0xd0, 0xe5, 0xf0, 0x03, 0x4c, + 0x71, 0xea, 0xa2, 0x45, 0x9a, 0xad, 0x00, 0x1c, + 0x29, 0xf7, 0x8d, 0x00, 0x1c, 0xa9, 0x01, 0x8d, + 0x0c, 0x18, 0xa9, 0x82, 0x8d, 0x0d, 0x18, 0x8d, + 0x0e, 0x18, 0xad, 0x00, 0x18, 0x29, 0x60, 0x0a, + 0x2a, 0x2a, 0x2a, 0x09, 0x48, 0x85, 0x78, 0x49, + 0x60, 0x85, 0x77, 0xa2, 0x00, 0xa0, 0x00, 0xa9, + 0x00, 0x95, 0x99, 0xe8, 0xb9, 0xe0, 0xfe, 0x95, + 0x99, 0xe8, 0xc8, 0xc0, 0x05, 0xd0, 0xf0, 0xa9, + 0x00, 0x95, 0x99, 0xe8, 0xa9, 0x02, 0x95, 0x99, + 0xe8, 0xa9, 0xd5, 0x95, 0x99, 0xe8, 0xa9, 0x02, + 0x95, 0x99, 0xa9, 0xff, 0xa2, 0x12, 0x9d, 0x2b, + 0x02, 0xca, 0x10, 0xfa, 0xa2, 0x05, 0x95, 0xa7, + 0x95, 0xae, 0x95, 0xcd, 0xca, 0x10, 0xf7, 0xa9, + 0x05, 0x85, 0xab, 0xa9, 0x06, 0x85, 0xac, 0xa9, + 0xff, 0x85, 0xad, 0x85, 0xb4, 0xa9, 0x05, 0x8d, + 0x3b, 0x02, 0xa9, 0x84, 0x8d, 0x3a, 0x02, 0xa9, + 0x0f, 0x8d, 0x56, 0x02, 0xa9, 0x01, 0x85, 0xf6, + 0xa9, 0x88, 0x85, 0xf7, 0xa9, 0xe0, 0x8d, 0x4f, + 0x02, 0xa9, 0xff, 0x8d, 0x50, 0x02, 0xa9, 0x01, + 0x85, 0x1c, 0x85, 0x1d, 0x20, 0x63, 0xcb, 0x20, + 0xfa, 0xce, 0x20, 0x59, 0xf2, 0xa9, 0x22, 0x85, + 0x65, 0xa9, 0xeb, 0x85, 0x66, 0xa9, 0x0a, 0x85, + 0x69, 0xa9, 0x05, 0x85, 0x6a, 0xa9, 0x73, 0x20, + 0xc1, 0xe6, 0xa9, 0x00, 0x8d, 0x00, 0x18, 0xa9, + 0x1a, 0x8d, 0x02, 0x18, 0x20, 0x80, 0xe7, 0x58, + 0xad, 0x00, 0x18, 0x29, 0xe5, 0x8d, 0x00, 0x18, + 0xad, 0x55, 0x02, 0xf0, 0x0a, 0xa9, 0x00, 0x8d, + 0x55, 0x02, 0x85, 0x67, 0x20, 0x46, 0xc1, 0x58, + 0xa5, 0x7c, 0xf0, 0x03, 0x4c, 0x5b, 0xe8, 0x58, + 0xa9, 0x0e, 0x85, 0x72, 0xa9, 0x00, 0x85, 0x6f, + 0x85, 0x70, 0xa6, 0x72, 0xbd, 0x2b, 0x02, 0xc9, + 0xff, 0xf0, 0x10, 0x29, 0x3f, 0x85, 0x82, 0x20, + 0x93, 0xdf, 0xaa, 0xbd, 0x5b, 0x02, 0x29, 0x01, + 0xaa, 0xf6, 0x6f, 0xc6, 0x72, 0x10, 0xe3, 0xa0, + 0x04, 0xb9, 0x00, 0x00, 0x10, 0x05, 0x29, 0x01, + 0xaa, 0xf6, 0x6f, 0x88, 0x10, 0xf3, 0x78, 0xad, + 0x00, 0x1c, 0x29, 0xf7, 0x48, 0xa5, 0x7f, 0x85, + 0x86, 0xa9, 0x00, 0x85, 0x7f, 0xa5, 0x6f, 0xf0, + 0x0b, 0xa5, 0x1c, 0xf0, 0x03, 0x20, 0x13, 0xd3, + 0x68, 0x09, 0x08, 0x48, 0xe6, 0x7f, 0xa5, 0x70, + 0xf0, 0x0b, 0xa5, 0x1d, 0xf0, 0x03, 0x20, 0x13, + 0xd3, 0x68, 0x09, 0x00, 0x48, 0xa5, 0x86, 0x85, + 0x7f, 0x68, 0xae, 0x6c, 0x02, 0xf0, 0x21, 0xad, + 0x00, 0x1c, 0xe0, 0x80, 0xd0, 0x03, 0x4c, 0x8b, + 0xec, 0xae, 0x05, 0x18, 0x30, 0x12, 0xa2, 0xa0, + 0x8e, 0x05, 0x18, 0xce, 0x6c, 0x02, 0xd0, 0x08, + 0x4d, 0x6d, 0x02, 0xa2, 0x10, 0x8e, 0x6c, 0x02, + 0x8d, 0x00, 0x1c, 0x4c, 0xff, 0xeb, 0xa9, 0x00, + 0x85, 0x83, 0xa9, 0x01, 0x20, 0xe2, 0xd1, 0xa9, + 0x00, 0x20, 0xc8, 0xd4, 0xa6, 0x82, 0xa9, 0x00, + 0x9d, 0x44, 0x02, 0x20, 0x93, 0xdf, 0xaa, 0xa5, + 0x7f, 0x9d, 0x5b, 0x02, 0xa9, 0x01, 0x20, 0xf1, + 0xcf, 0xa9, 0x04, 0x20, 0xf1, 0xcf, 0xa9, 0x01, + 0x20, 0xf1, 0xcf, 0x20, 0xf1, 0xcf, 0xad, 0x72, + 0x02, 0x20, 0xf1, 0xcf, 0xa9, 0x00, 0x20, 0xf1, + 0xcf, 0x20, 0x59, 0xed, 0x20, 0x93, 0xdf, 0x0a, + 0xaa, 0xd6, 0x99, 0xd6, 0x99, 0xa9, 0x00, 0x20, + 0xf1, 0xcf, 0xa9, 0x01, 0x20, 0xf1, 0xcf, 0x20, + 0xf1, 0xcf, 0x20, 0xce, 0xc6, 0x90, 0x2c, 0xad, + 0x72, 0x02, 0x20, 0xf1, 0xcf, 0xad, 0x73, 0x02, + 0x20, 0xf1, 0xcf, 0x20, 0x59, 0xed, 0xa9, 0x00, + 0x20, 0xf1, 0xcf, 0xd0, 0xdd, 0x20, 0x93, 0xdf, + 0x0a, 0xaa, 0xa9, 0x00, 0x95, 0x99, 0xa9, 0x88, + 0xa4, 0x82, 0x8d, 0x54, 0x02, 0x99, 0xf2, 0x00, + 0xa5, 0x85, 0x60, 0xad, 0x72, 0x02, 0x20, 0xf1, + 0xcf, 0xad, 0x73, 0x02, 0x20, 0xf1, 0xcf, 0x20, + 0x59, 0xed, 0x20, 0x93, 0xdf, 0x0a, 0xaa, 0xd6, + 0x99, 0xd6, 0x99, 0xa9, 0x00, 0x20, 0xf1, 0xcf, + 0x20, 0xf1, 0xcf, 0x20, 0xf1, 0xcf, 0x20, 0x93, + 0xdf, 0x0a, 0xa8, 0xb9, 0x99, 0x00, 0xa6, 0x82, + 0x9d, 0x44, 0x02, 0xde, 0x44, 0x02, 0x4c, 0x0d, + 0xed, 0xa0, 0x00, 0xb9, 0xb1, 0x02, 0x20, 0xf1, + 0xcf, 0xc8, 0xc0, 0x1b, 0xd0, 0xf5, 0x60, 0x20, + 0x37, 0xd1, 0xf0, 0x01, 0x60, 0x85, 0x85, 0xa4, + 0x82, 0xb9, 0x44, 0x02, 0xf0, 0x08, 0xa9, 0x80, + 0x99, 0xf2, 0x00, 0xa5, 0x85, 0x60, 0x48, 0x20, + 0xea, 0xec, 0x68, 0x60, 0x20, 0xd1, 0xc1, 0x20, + 0x42, 0xd0, 0xa9, 0x40, 0x8d, 0xf9, 0x02, 0x20, + 0xb7, 0xee, 0xa9, 0x00, 0x8d, 0x92, 0x02, 0x20, + 0xac, 0xc5, 0xd0, 0x3d, 0xa9, 0x00, 0x85, 0x81, + 0xad, 0x85, 0xfe, 0x85, 0x80, 0x20, 0xe5, 0xed, + 0xa9, 0x00, 0x8d, 0xf9, 0x02, 0x20, 0xff, 0xee, + 0x4c, 0x94, 0xc1, 0xc8, 0xb1, 0x94, 0x48, 0xc8, + 0xb1, 0x94, 0x48, 0xa0, 0x13, 0xb1, 0x94, 0xf0, + 0x0a, 0x85, 0x80, 0xc8, 0xb1, 0x94, 0x85, 0x81, + 0x20, 0xe5, 0xed, 0x68, 0x85, 0x81, 0x68, 0x85, + 0x80, 0x20, 0xe5, 0xed, 0x20, 0x04, 0xc6, 0xf0, + 0xc3, 0xa0, 0x00, 0xb1, 0x94, 0x30, 0xd4, 0x20, + 0xb6, 0xc8, 0x4c, 0xd4, 0xed, 0x20, 0x5f, 0xd5, + 0x20, 0x90, 0xef, 0x20, 0x75, 0xd4, 0xa9, 0x00, + 0x20, 0xc8, 0xd4, 0x20, 0x37, 0xd1, 0x85, 0x80, + 0x20, 0x37, 0xd1, 0x85, 0x81, 0xa5, 0x80, 0xd0, + 0x03, 0x4c, 0x27, 0xd2, 0x20, 0x90, 0xef, 0x20, + 0x4d, 0xd4, 0x4c, 0xee, 0xed, 0x20, 0x12, 0xc3, + 0xa5, 0xe2, 0x10, 0x05, 0xa9, 0x33, 0x4c, 0xc8, + 0xc1, 0x29, 0x01, 0x85, 0x7f, 0x20, 0x00, 0xc1, + 0xa5, 0x7f, 0x0a, 0xaa, 0xac, 0x7b, 0x02, 0xcc, + 0x74, 0x02, 0xf0, 0x1a, 0xb9, 0x00, 0x02, 0x95, + 0x12, 0xb9, 0x01, 0x02, 0x95, 0x13, 0x20, 0x07, + 0xd3, 0xa9, 0x01, 0x85, 0x80, 0x20, 0xc6, 0xc8, + 0x20, 0x05, 0xf0, 0x4c, 0x56, 0xee, 0x20, 0x42, + 0xd0, 0xa6, 0x7f, 0xbd, 0x01, 0x01, 0xcd, 0xd5, + 0xfe, 0xf0, 0x03, 0x4c, 0x72, 0xd5, 0x20, 0xb7, + 0xee, 0xa5, 0xf9, 0xa8, 0x0a, 0xaa, 0xad, 0x88, + 0xfe, 0x95, 0x99, 0xae, 0x7a, 0x02, 0xa9, 0x1b, + 0x20, 0x6e, 0xc6, 0xa0, 0x12, 0xa6, 0x7f, 0xad, + 0xd5, 0xfe, 0x9d, 0x01, 0x01, 0x8a, 0x0a, 0xaa, + 0xb5, 0x12, 0x91, 0x94, 0xc8, 0xb5, 0x13, 0x91, + 0x94, 0xc8, 0xc8, 0xa9, 0x32, 0x91, 0x94, 0xc8, + 0xad, 0xd5, 0xfe, 0x91, 0x94, 0xa0, 0x02, 0x91, + 0x6d, 0xad, 0x85, 0xfe, 0x85, 0x80, 0x20, 0x93, + 0xef, 0xa9, 0x01, 0x85, 0x81, 0x20, 0x93, 0xef, + 0x20, 0xff, 0xee, 0x20, 0x05, 0xf0, 0xa0, 0x01, + 0xa9, 0xff, 0x91, 0x6d, 0x20, 0x64, 0xd4, 0xc6, + 0x81, 0x20, 0x60, 0xd4, 0x4c, 0x94, 0xc1, 0x20, + 0xd1, 0xf0, 0xa0, 0x00, 0xa9, 0x12, 0x91, 0x6d, + 0xc8, 0x98, 0x91, 0x6d, 0xc8, 0xc8, 0xc8, 0xa9, + 0x00, 0x85, 0x6f, 0x85, 0x70, 0x85, 0x71, 0x98, + 0x4a, 0x4a, 0x20, 0x4b, 0xf2, 0x91, 0x6d, 0xc8, + 0xaa, 0x38, 0x26, 0x6f, 0x26, 0x70, 0x26, 0x71, + 0xca, 0xd0, 0xf6, 0xb5, 0x6f, 0x91, 0x6d, 0xc8, + 0xe8, 0xe0, 0x03, 0x90, 0xf6, 0xc0, 0x90, 0x90, + 0xd6, 0x4c, 0x75, 0xd0, 0x20, 0x93, 0xdf, 0xaa, + 0xbd, 0x5b, 0x02, 0x29, 0x01, 0x85, 0x7f, 0xa4, + 0x7f, 0xb9, 0x51, 0x02, 0xd0, 0x01, 0x60, 0xa9, + 0x00, 0x99, 0x51, 0x02, 0x20, 0x3a, 0xef, 0xa5, + 0x7f, 0x0a, 0x48, 0x20, 0xa5, 0xf0, 0x68, 0x18, + 0x69, 0x01, 0x20, 0xa5, 0xf0, 0xa5, 0x80, 0x48, + 0xa9, 0x01, 0x85, 0x80, 0x0a, 0x0a, 0x85, 0x6d, + 0x20, 0x20, 0xf2, 0xe6, 0x80, 0xa5, 0x80, 0xcd, + 0xd7, 0xfe, 0x90, 0xf0, 0x68, 0x85, 0x80, 0x4c, + 0x8a, 0xd5, 0x20, 0x0f, 0xf1, 0xaa, 0x20, 0xdf, + 0xf0, 0xa6, 0xf9, 0xbd, 0xe0, 0xfe, 0x85, 0x6e, + 0xa9, 0x00, 0x85, 0x6d, 0x60, 0xa6, 0x7f, 0xbd, + 0xfa, 0x02, 0x8d, 0x72, 0x02, 0xbd, 0xfc, 0x02, + 0x8d, 0x73, 0x02, 0x60, 0x20, 0xf1, 0xef, 0x20, + 0xcf, 0xef, 0x38, 0xd0, 0x22, 0xb1, 0x6d, 0x1d, + 0xe9, 0xef, 0x91, 0x6d, 0x20, 0x88, 0xef, 0xa4, + 0x6f, 0x18, 0xb1, 0x6d, 0x69, 0x01, 0x91, 0x6d, + 0xa5, 0x80, 0xcd, 0x85, 0xfe, 0xf0, 0x3b, 0xfe, + 0xfa, 0x02, 0xd0, 0x03, 0xfe, 0xfc, 0x02, 0x60, + 0xa6, 0x7f, 0xa9, 0x01, 0x9d, 0x51, 0x02, 0x60, + 0x20, 0xf1, 0xef, 0x20, 0xcf, 0xef, 0xf0, 0x36, + 0xb1, 0x6d, 0x5d, 0xe9, 0xef, 0x91, 0x6d, 0x20, + 0x88, 0xef, 0xa4, 0x6f, 0xb1, 0x6d, 0x38, 0xe9, + 0x01, 0x91, 0x6d, 0xa5, 0x80, 0xcd, 0x85, 0xfe, + 0xf0, 0x0b, 0xbd, 0xfa, 0x02, 0xd0, 0x03, 0xde, + 0xfc, 0x02, 0xde, 0xfa, 0x02, 0xbd, 0xfc, 0x02, + 0xd0, 0x0c, 0xbd, 0xfa, 0x02, 0xc9, 0x03, 0xb0, + 0x05, 0xa9, 0x72, 0x20, 0xc7, 0xe6, 0x60, 0x20, + 0x11, 0xf0, 0x98, 0x85, 0x6f, 0xa5, 0x81, 0x4a, + 0x4a, 0x4a, 0x38, 0x65, 0x6f, 0xa8, 0xa5, 0x81, + 0x29, 0x07, 0xaa, 0xb1, 0x6d, 0x3d, 0xe9, 0xef, + 0x60, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, + 0x80, 0xa9, 0xff, 0x2c, 0xf9, 0x02, 0xf0, 0x0c, + 0x10, 0x0a, 0x70, 0x08, 0xa9, 0x00, 0x8d, 0xf9, + 0x02, 0x4c, 0x8a, 0xd5, 0x60, 0x20, 0x3a, 0xef, + 0xa0, 0x00, 0x98, 0x91, 0x6d, 0xc8, 0xd0, 0xfb, + 0x60, 0xa5, 0x6f, 0x48, 0xa5, 0x70, 0x48, 0xa6, + 0x7f, 0xb5, 0xff, 0xf0, 0x05, 0xa9, 0x74, 0x20, + 0x48, 0xe6, 0x20, 0x0f, 0xf1, 0x85, 0x6f, 0x8a, + 0x0a, 0x85, 0x70, 0xaa, 0xa5, 0x80, 0xdd, 0x9d, + 0x02, 0xf0, 0x0b, 0xe8, 0x86, 0x70, 0xdd, 0x9d, + 0x02, 0xf0, 0x03, 0x20, 0x5b, 0xf0, 0xa5, 0x70, + 0xa6, 0x7f, 0x9d, 0x9b, 0x02, 0x0a, 0x0a, 0x18, + 0x69, 0xa1, 0x85, 0x6d, 0xa9, 0x02, 0x69, 0x00, + 0x85, 0x6e, 0xa0, 0x00, 0x68, 0x85, 0x70, 0x68, + 0x85, 0x6f, 0x60, 0xa6, 0x6f, 0x20, 0xdf, 0xf0, + 0xa5, 0x7f, 0xaa, 0x0a, 0x1d, 0x9b, 0x02, 0x49, + 0x01, 0x29, 0x03, 0x85, 0x70, 0x20, 0xa5, 0xf0, + 0xa5, 0xf9, 0x0a, 0xaa, 0xa5, 0x80, 0x0a, 0x0a, + 0x95, 0x99, 0xa5, 0x70, 0x0a, 0x0a, 0xa8, 0xa1, + 0x99, 0x99, 0xa1, 0x02, 0xa9, 0x00, 0x81, 0x99, + 0xf6, 0x99, 0xc8, 0x98, 0x29, 0x03, 0xd0, 0xef, + 0xa6, 0x70, 0xa5, 0x80, 0x9d, 0x9d, 0x02, 0xad, + 0xf9, 0x02, 0xd0, 0x03, 0x4c, 0x8a, 0xd5, 0x09, + 0x80, 0x8d, 0xf9, 0x02, 0x60, 0xa8, 0xb9, 0x9d, + 0x02, 0xf0, 0x25, 0x48, 0xa9, 0x00, 0x99, 0x9d, + 0x02, 0xa5, 0xf9, 0x0a, 0xaa, 0x68, 0x0a, 0x0a, + 0x95, 0x99, 0x98, 0x0a, 0x0a, 0xa8, 0xb9, 0xa1, + 0x02, 0x81, 0x99, 0xa9, 0x00, 0x99, 0xa1, 0x02, + 0xf6, 0x99, 0xc8, 0x98, 0x29, 0x03, 0xd0, 0xee, + 0x60, 0xa5, 0x7f, 0x0a, 0xaa, 0xa9, 0x00, 0x9d, + 0x9d, 0x02, 0xe8, 0x9d, 0x9d, 0x02, 0x60, 0xb5, + 0xa7, 0xc9, 0xff, 0xd0, 0x25, 0x8a, 0x48, 0x20, + 0x8e, 0xd2, 0xaa, 0x10, 0x05, 0xa9, 0x70, 0x20, + 0xc8, 0xc1, 0x86, 0xf9, 0x68, 0xa8, 0x8a, 0x09, + 0x80, 0x99, 0xa7, 0x00, 0x0a, 0xaa, 0xad, 0x85, + 0xfe, 0x95, 0x06, 0xa9, 0x00, 0x95, 0x07, 0x4c, + 0x86, 0xd5, 0x29, 0x0f, 0x85, 0xf9, 0x60, 0xa9, + 0x06, 0xa6, 0x7f, 0xd0, 0x03, 0x18, 0x69, 0x07, + 0x60, 0x20, 0x0f, 0xf1, 0xaa, 0x60, 0x20, 0x3e, + 0xde, 0xa9, 0x03, 0x85, 0x6f, 0xa9, 0x01, 0x0d, + 0xf9, 0x02, 0x8d, 0xf9, 0x02, 0xa5, 0x6f, 0x48, + 0x20, 0x11, 0xf0, 0x68, 0x85, 0x6f, 0xb1, 0x6d, + 0xd0, 0x39, 0xa5, 0x80, 0xcd, 0x85, 0xfe, 0xf0, + 0x19, 0x90, 0x1c, 0xe6, 0x80, 0xa5, 0x80, 0xcd, + 0xd7, 0xfe, 0xd0, 0xe1, 0xae, 0x85, 0xfe, 0xca, + 0x86, 0x80, 0xa9, 0x00, 0x85, 0x81, 0xc6, 0x6f, + 0xd0, 0xd3, 0xa9, 0x72, 0x20, 0xc8, 0xc1, 0xc6, + 0x80, 0xd0, 0xca, 0xae, 0x85, 0xfe, 0xe8, 0x86, + 0x80, 0xa9, 0x00, 0x85, 0x81, 0xc6, 0x6f, 0xd0, + 0xbc, 0xf0, 0xe7, 0xa5, 0x81, 0x18, 0x65, 0x69, + 0x85, 0x81, 0xa5, 0x80, 0x20, 0x4b, 0xf2, 0x8d, + 0x4e, 0x02, 0x8d, 0x4d, 0x02, 0xc5, 0x81, 0xb0, + 0x0c, 0x38, 0xa5, 0x81, 0xed, 0x4e, 0x02, 0x85, + 0x81, 0xf0, 0x02, 0xc6, 0x81, 0x20, 0xfa, 0xf1, + 0xf0, 0x03, 0x4c, 0x90, 0xef, 0xa9, 0x00, 0x85, + 0x81, 0x20, 0xfa, 0xf1, 0xd0, 0xf4, 0x4c, 0xf5, + 0xf1, 0xa9, 0x01, 0x0d, 0xf9, 0x02, 0x8d, 0xf9, + 0x02, 0xa5, 0x86, 0x48, 0xa9, 0x01, 0x85, 0x86, + 0xad, 0x85, 0xfe, 0x38, 0xe5, 0x86, 0x85, 0x80, + 0x90, 0x09, 0xf0, 0x07, 0x20, 0x11, 0xf0, 0xb1, + 0x6d, 0xd0, 0x1b, 0xad, 0x85, 0xfe, 0x18, 0x65, + 0x86, 0x85, 0x80, 0xe6, 0x86, 0xcd, 0xd7, 0xfe, + 0x90, 0x05, 0xa9, 0x67, 0x20, 0x45, 0xe6, 0x20, + 0x11, 0xf0, 0xb1, 0x6d, 0xf0, 0xd2, 0x68, 0x85, + 0x86, 0xa9, 0x00, 0x85, 0x81, 0x20, 0xfa, 0xf1, + 0xf0, 0x03, 0x4c, 0x90, 0xef, 0xa9, 0x71, 0x20, + 0x45, 0xe6, 0x20, 0x11, 0xf0, 0x98, 0x48, 0x20, + 0x20, 0xf2, 0xa5, 0x80, 0x20, 0x4b, 0xf2, 0x8d, + 0x4e, 0x02, 0x68, 0x85, 0x6f, 0xa5, 0x81, 0xcd, + 0x4e, 0x02, 0xb0, 0x09, 0x20, 0xd5, 0xef, 0xd0, + 0x06, 0xe6, 0x81, 0xd0, 0xf0, 0xa9, 0x00, 0x60, + 0xa5, 0x6f, 0x48, 0xa9, 0x00, 0x85, 0x6f, 0xac, + 0x86, 0xfe, 0x88, 0xa2, 0x07, 0xb1, 0x6d, 0x3d, + 0xe9, 0xef, 0xf0, 0x02, 0xe6, 0x6f, 0xca, 0x10, + 0xf4, 0x88, 0xd0, 0xef, 0xb1, 0x6d, 0xc5, 0x6f, + 0xd0, 0x04, 0x68, 0x85, 0x6f, 0x60, 0xa9, 0x71, + 0x20, 0x45, 0xe6, 0xae, 0xd6, 0xfe, 0xdd, 0xd6, + 0xfe, 0xca, 0xb0, 0xfa, 0xbd, 0xd1, 0xfe, 0x60, + 0x60, 0xa9, 0x6f, 0x8d, 0x02, 0x1c, 0x29, 0xf0, + 0x8d, 0x00, 0x1c, 0xad, 0x0c, 0x1c, 0x29, 0xfe, + 0x09, 0x0e, 0x09, 0xe0, 0x8d, 0x0c, 0x1c, 0xa9, + 0x41, 0x8d, 0x0b, 0x1c, 0xa9, 0x00, 0x8d, 0x06, + 0x1c, 0xa9, 0x3a, 0x8d, 0x07, 0x1c, 0x8d, 0x05, + 0x1c, 0xa9, 0x7f, 0x8d, 0x0e, 0x1c, 0xa9, 0xc0, + 0x8d, 0x0d, 0x1c, 0x8d, 0x0e, 0x1c, 0xa9, 0xff, + 0x85, 0x3e, 0x85, 0x51, 0xa9, 0x08, 0x85, 0x39, + 0xa9, 0x07, 0x85, 0x47, 0xa9, 0x05, 0x85, 0x62, + 0xa9, 0xfa, 0x85, 0x63, 0xa9, 0xc8, 0x85, 0x64, + 0xa9, 0x04, 0x85, 0x5e, 0xa9, 0x04, 0x85, 0x5f, + 0xba, 0x86, 0x49, 0xad, 0x04, 0x1c, 0xad, 0x0c, + 0x1c, 0x09, 0x0e, 0x8d, 0x0c, 0x1c, 0xa0, 0x05, + 0xb9, 0x00, 0x00, 0x10, 0x2e, 0xc9, 0xd0, 0xd0, + 0x04, 0x98, 0x4c, 0x70, 0xf3, 0x29, 0x01, 0xf0, + 0x07, 0x84, 0x3f, 0xa9, 0x0f, 0x4c, 0x69, 0xf9, + 0xaa, 0x85, 0x3d, 0xc5, 0x3e, 0xf0, 0x0a, 0x20, + 0x7e, 0xf9, 0xa5, 0x3d, 0x85, 0x3e, 0x4c, 0x9c, + 0xf9, 0xa5, 0x20, 0x30, 0x03, 0x0a, 0x10, 0x09, + 0x4c, 0x9c, 0xf9, 0x88, 0x10, 0xca, 0x4c, 0x9c, + 0xf9, 0xa9, 0x20, 0x85, 0x20, 0xa0, 0x05, 0x84, + 0x3f, 0x20, 0x93, 0xf3, 0x30, 0x1a, 0xc6, 0x3f, + 0x10, 0xf7, 0xa4, 0x41, 0x20, 0x95, 0xf3, 0xa5, + 0x42, 0x85, 0x4a, 0x06, 0x4a, 0xa9, 0x60, 0x85, + 0x20, 0xb1, 0x32, 0x85, 0x22, 0x4c, 0x9c, 0xf9, + 0x29, 0x01, 0xc5, 0x3d, 0xd0, 0xe0, 0xa5, 0x22, + 0xf0, 0x12, 0x38, 0xf1, 0x32, 0xf0, 0x0d, 0x49, + 0xff, 0x85, 0x42, 0xe6, 0x42, 0xa5, 0x3f, 0x85, + 0x41, 0x4c, 0x06, 0xf3, 0xa2, 0x04, 0xb1, 0x32, + 0x85, 0x40, 0xdd, 0xd6, 0xfe, 0xca, 0xb0, 0xfa, + 0xbd, 0xd1, 0xfe, 0x85, 0x43, 0x8a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x85, 0x44, 0xad, 0x00, 0x1c, + 0x29, 0x9f, 0x05, 0x44, 0x8d, 0x00, 0x1c, 0xa6, + 0x3d, 0xa5, 0x45, 0xc9, 0x40, 0xf0, 0x15, 0xc9, + 0x60, 0xf0, 0x03, 0x4c, 0xb1, 0xf3, 0xa5, 0x3f, + 0x18, 0x69, 0x03, 0x85, 0x31, 0xa9, 0x00, 0x85, + 0x30, 0x6c, 0x30, 0x00, 0xa9, 0x60, 0x85, 0x20, + 0xad, 0x00, 0x1c, 0x29, 0xfc, 0x8d, 0x00, 0x1c, + 0xa9, 0xa4, 0x85, 0x4a, 0xa9, 0x01, 0x85, 0x22, + 0x4c, 0x69, 0xf9, 0xa4, 0x3f, 0xb9, 0x00, 0x00, + 0x48, 0x10, 0x10, 0x29, 0x78, 0x85, 0x45, 0x98, + 0x0a, 0x69, 0x06, 0x85, 0x32, 0x98, 0x18, 0x69, + 0x03, 0x85, 0x31, 0xa0, 0x00, 0x84, 0x30, 0x68, + 0x60, 0xa2, 0x5a, 0x86, 0x4b, 0xa2, 0x00, 0xa9, + 0x52, 0x85, 0x24, 0x20, 0x56, 0xf5, 0x50, 0xfe, + 0xb8, 0xad, 0x01, 0x1c, 0xc5, 0x24, 0xd0, 0x3f, + 0x50, 0xfe, 0xb8, 0xad, 0x01, 0x1c, 0x95, 0x25, + 0xe8, 0xe0, 0x07, 0xd0, 0xf3, 0x20, 0x97, 0xf4, + 0xa0, 0x04, 0xa9, 0x00, 0x59, 0x16, 0x00, 0x88, + 0x10, 0xfa, 0xc9, 0x00, 0xd0, 0x38, 0xa6, 0x3e, + 0xa5, 0x18, 0x95, 0x22, 0xa5, 0x45, 0xc9, 0x30, + 0xf0, 0x1e, 0xa5, 0x3e, 0x0a, 0xa8, 0xb9, 0x12, + 0x00, 0xc5, 0x16, 0xd0, 0x1e, 0xb9, 0x13, 0x00, + 0xc5, 0x17, 0xd0, 0x17, 0x4c, 0x23, 0xf4, 0xc6, + 0x4b, 0xd0, 0xb0, 0xa9, 0x02, 0x20, 0x69, 0xf9, + 0xa5, 0x16, 0x85, 0x12, 0xa5, 0x17, 0x85, 0x13, + 0xa9, 0x01, 0x2c, 0xa9, 0x0b, 0x2c, 0xa9, 0x09, + 0x4c, 0x69, 0xf9, 0xa9, 0x7f, 0x85, 0x4c, 0xa5, + 0x19, 0x18, 0x69, 0x02, 0xc5, 0x43, 0x90, 0x02, + 0xe5, 0x43, 0x85, 0x4d, 0xa2, 0x05, 0x86, 0x3f, + 0xa2, 0xff, 0x20, 0x93, 0xf3, 0x10, 0x44, 0x85, + 0x44, 0x29, 0x01, 0xc5, 0x3e, 0xd0, 0x3c, 0xa0, + 0x00, 0xb1, 0x32, 0xc5, 0x40, 0xd0, 0x34, 0xa5, + 0x45, 0xc9, 0x60, 0xf0, 0x0c, 0xa0, 0x01, 0x38, + 0xb1, 0x32, 0xe5, 0x4d, 0x10, 0x03, 0x18, 0x65, + 0x43, 0xc5, 0x4c, 0xb0, 0x1e, 0x48, 0xa5, 0x45, + 0xf0, 0x14, 0x68, 0xc9, 0x09, 0x90, 0x14, 0xc9, + 0x0c, 0xb0, 0x10, 0x85, 0x4c, 0xa5, 0x3f, 0xaa, + 0x69, 0x03, 0x85, 0x31, 0xd0, 0x05, 0x68, 0xc9, + 0x06, 0x90, 0xf0, 0xc6, 0x3f, 0x10, 0xb3, 0x8a, + 0x10, 0x03, 0x4c, 0x9c, 0xf9, 0x86, 0x3f, 0x20, + 0x93, 0xf3, 0xa5, 0x45, 0x4c, 0xca, 0xf4, 0xa5, + 0x30, 0x48, 0xa5, 0x31, 0x48, 0xa9, 0x24, 0x85, + 0x30, 0xa9, 0x00, 0x85, 0x31, 0xa9, 0x00, 0x85, + 0x34, 0x20, 0xe6, 0xf7, 0xa5, 0x55, 0x85, 0x18, + 0xa5, 0x54, 0x85, 0x19, 0xa5, 0x53, 0x85, 0x1a, + 0x20, 0xe6, 0xf7, 0xa5, 0x52, 0x85, 0x17, 0xa5, + 0x53, 0x85, 0x16, 0x68, 0x85, 0x31, 0x68, 0x85, + 0x30, 0x60, 0xc9, 0x00, 0xf0, 0x03, 0x4c, 0x6e, + 0xf5, 0x20, 0x0a, 0xf5, 0x50, 0xfe, 0xb8, 0xad, + 0x01, 0x1c, 0x91, 0x30, 0xc8, 0xd0, 0xf5, 0xa0, + 0xba, 0x50, 0xfe, 0xb8, 0xad, 0x01, 0x1c, 0x99, + 0x00, 0x01, 0xc8, 0xd0, 0xf4, 0x20, 0xe0, 0xf8, + 0xa5, 0x38, 0xc5, 0x47, 0xf0, 0x05, 0xa9, 0x04, + 0x4c, 0x69, 0xf9, 0x20, 0xe9, 0xf5, 0xc5, 0x3a, + 0xf0, 0x03, 0xa9, 0x05, 0x2c, 0xa9, 0x01, 0x4c, + 0x69, 0xf9, 0x20, 0x10, 0xf5, 0x4c, 0x56, 0xf5, + 0xa5, 0x3d, 0x0a, 0xaa, 0xb5, 0x12, 0x85, 0x16, + 0xb5, 0x13, 0x85, 0x17, 0xa0, 0x00, 0xb1, 0x32, + 0x85, 0x18, 0xc8, 0xb1, 0x32, 0x85, 0x19, 0xa9, + 0x00, 0x45, 0x16, 0x45, 0x17, 0x45, 0x18, 0x45, + 0x19, 0x85, 0x1a, 0x20, 0x34, 0xf9, 0xa2, 0x5a, + 0x20, 0x56, 0xf5, 0xa0, 0x00, 0x50, 0xfe, 0xb8, + 0xad, 0x01, 0x1c, 0xd9, 0x24, 0x00, 0xd0, 0x06, + 0xc8, 0xc0, 0x08, 0xd0, 0xf0, 0x60, 0xca, 0xd0, + 0xe7, 0xa9, 0x02, 0x4c, 0x69, 0xf9, 0xa9, 0xd0, + 0x8d, 0x05, 0x18, 0xa9, 0x03, 0x2c, 0x05, 0x18, + 0x10, 0xf1, 0x2c, 0x00, 0x1c, 0x30, 0xf6, 0xad, + 0x01, 0x1c, 0xb8, 0xa0, 0x00, 0x60, 0xc9, 0x10, + 0xf0, 0x03, 0x4c, 0x91, 0xf6, 0x20, 0xe9, 0xf5, + 0x85, 0x3a, 0xad, 0x00, 0x1c, 0x29, 0x10, 0xd0, + 0x05, 0xa9, 0x08, 0x4c, 0x69, 0xf9, 0x20, 0x8f, + 0xf7, 0x20, 0x10, 0xf5, 0xa2, 0x09, 0x50, 0xfe, + 0xb8, 0xca, 0xd0, 0xfa, 0xa9, 0xff, 0x8d, 0x03, + 0x1c, 0xad, 0x0c, 0x1c, 0x29, 0x1f, 0x09, 0xc0, + 0x8d, 0x0c, 0x1c, 0xa9, 0xff, 0xa2, 0x05, 0x8d, + 0x01, 0x1c, 0xb8, 0x50, 0xfe, 0xb8, 0xca, 0xd0, + 0xfa, 0xa0, 0xbb, 0xb9, 0x00, 0x01, 0x50, 0xfe, + 0xb8, 0x8d, 0x01, 0x1c, 0xc8, 0xd0, 0xf4, 0xb1, + 0x30, 0x50, 0xfe, 0xb8, 0x8d, 0x01, 0x1c, 0xc8, + 0xd0, 0xf5, 0x50, 0xfe, 0xad, 0x0c, 0x1c, 0x09, + 0xe0, 0x8d, 0x0c, 0x1c, 0xa9, 0x00, 0x8d, 0x03, + 0x1c, 0x20, 0xf2, 0xf5, 0xa4, 0x3f, 0xb9, 0x00, + 0x00, 0x49, 0x30, 0x99, 0x00, 0x00, 0x4c, 0xb1, + 0xf3, 0xa9, 0x00, 0xa8, 0x51, 0x30, 0xc8, 0xd0, + 0xfb, 0x60, 0xa9, 0x00, 0x85, 0x2e, 0x85, 0x30, + 0x85, 0x4f, 0xa5, 0x31, 0x85, 0x4e, 0xa9, 0x01, + 0x85, 0x31, 0x85, 0x2f, 0xa9, 0xbb, 0x85, 0x34, + 0x85, 0x36, 0x20, 0xe6, 0xf7, 0xa5, 0x52, 0x85, + 0x38, 0xa4, 0x36, 0xa5, 0x53, 0x91, 0x2e, 0xc8, + 0xa5, 0x54, 0x91, 0x2e, 0xc8, 0xa5, 0x55, 0x91, + 0x2e, 0xc8, 0x84, 0x36, 0x20, 0xe6, 0xf7, 0xa4, + 0x36, 0xa5, 0x52, 0x91, 0x2e, 0xc8, 0xa5, 0x53, + 0x91, 0x2e, 0xc8, 0xf0, 0x0e, 0xa5, 0x54, 0x91, + 0x2e, 0xc8, 0xa5, 0x55, 0x91, 0x2e, 0xc8, 0x84, + 0x36, 0xd0, 0xe1, 0xa5, 0x54, 0x91, 0x30, 0xc8, + 0xa5, 0x55, 0x91, 0x30, 0xc8, 0x84, 0x36, 0x20, + 0xe6, 0xf7, 0xa4, 0x36, 0xa5, 0x52, 0x91, 0x30, + 0xc8, 0xa5, 0x53, 0x91, 0x30, 0xc8, 0xa5, 0x54, + 0x91, 0x30, 0xc8, 0xa5, 0x55, 0x91, 0x30, 0xc8, + 0x84, 0x36, 0xc0, 0xbb, 0x90, 0xe1, 0xa9, 0x45, + 0x85, 0x2e, 0xa5, 0x31, 0x85, 0x2f, 0xa0, 0xba, + 0xb1, 0x30, 0x91, 0x2e, 0x88, 0xd0, 0xf9, 0xb1, + 0x30, 0x91, 0x2e, 0xa2, 0xbb, 0xbd, 0x00, 0x01, + 0x91, 0x30, 0xc8, 0xe8, 0xd0, 0xf7, 0x86, 0x50, + 0x60, 0xc9, 0x20, 0xf0, 0x03, 0x4c, 0xca, 0xf6, + 0x20, 0xe9, 0xf5, 0x85, 0x3a, 0x20, 0x8f, 0xf7, + 0x20, 0x0a, 0xf5, 0xa0, 0xbb, 0xb9, 0x00, 0x01, + 0x50, 0xfe, 0xb8, 0x4d, 0x01, 0x1c, 0xd0, 0x15, + 0xc8, 0xd0, 0xf2, 0xb1, 0x30, 0x50, 0xfe, 0xb8, + 0x4d, 0x01, 0x1c, 0xd0, 0x08, 0xc8, 0xc0, 0xfd, + 0xd0, 0xf1, 0x4c, 0x18, 0xf4, 0xa9, 0x07, 0x4c, + 0x69, 0xf9, 0x20, 0x10, 0xf5, 0x4c, 0x18, 0xf4, + 0xa9, 0x00, 0x85, 0x57, 0x85, 0x5a, 0xa4, 0x34, + 0xa5, 0x52, 0x29, 0xf0, 0x4a, 0x4a, 0x4a, 0x4a, + 0xaa, 0xbd, 0x7f, 0xf7, 0x0a, 0x0a, 0x0a, 0x85, + 0x56, 0xa5, 0x52, 0x29, 0x0f, 0xaa, 0xbd, 0x7f, + 0xf7, 0x6a, 0x66, 0x57, 0x6a, 0x66, 0x57, 0x29, + 0x07, 0x05, 0x56, 0x91, 0x30, 0xc8, 0xa5, 0x53, + 0x29, 0xf0, 0x4a, 0x4a, 0x4a, 0x4a, 0xaa, 0xbd, + 0x7f, 0xf7, 0x0a, 0x05, 0x57, 0x85, 0x57, 0xa5, + 0x53, 0x29, 0x0f, 0xaa, 0xbd, 0x7f, 0xf7, 0x2a, + 0x2a, 0x2a, 0x2a, 0x85, 0x58, 0x2a, 0x29, 0x01, + 0x05, 0x57, 0x91, 0x30, 0xc8, 0xa5, 0x54, 0x29, + 0xf0, 0x4a, 0x4a, 0x4a, 0x4a, 0xaa, 0xbd, 0x7f, + 0xf7, 0x18, 0x6a, 0x05, 0x58, 0x91, 0x30, 0xc8, + 0x6a, 0x29, 0x80, 0x85, 0x59, 0xa5, 0x54, 0x29, + 0x0f, 0xaa, 0xbd, 0x7f, 0xf7, 0x0a, 0x0a, 0x29, + 0x7c, 0x05, 0x59, 0x85, 0x59, 0xa5, 0x55, 0x29, + 0xf0, 0x4a, 0x4a, 0x4a, 0x4a, 0xaa, 0xbd, 0x7f, + 0xf7, 0x6a, 0x66, 0x5a, 0x6a, 0x66, 0x5a, 0x6a, + 0x66, 0x5a, 0x29, 0x03, 0x05, 0x59, 0x91, 0x30, + 0xc8, 0xd0, 0x04, 0xa5, 0x2f, 0x85, 0x31, 0xa5, + 0x55, 0x29, 0x0f, 0xaa, 0xbd, 0x7f, 0xf7, 0x05, + 0x5a, 0x91, 0x30, 0xc8, 0x84, 0x34, 0x60, 0x0a, + 0x0b, 0x12, 0x13, 0x0e, 0x0f, 0x16, 0x17, 0x09, + 0x19, 0x1a, 0x1b, 0x0d, 0x1d, 0x1e, 0x15, 0xa9, + 0x00, 0x85, 0x30, 0x85, 0x2e, 0x85, 0x36, 0xa9, + 0xbb, 0x85, 0x34, 0x85, 0x50, 0xa5, 0x31, 0x85, + 0x2f, 0xa9, 0x01, 0x85, 0x31, 0xa5, 0x47, 0x85, + 0x52, 0xa4, 0x36, 0xb1, 0x2e, 0x85, 0x53, 0xc8, + 0xb1, 0x2e, 0x85, 0x54, 0xc8, 0xb1, 0x2e, 0x85, + 0x55, 0xc8, 0x84, 0x36, 0x20, 0xd0, 0xf6, 0xa4, + 0x36, 0xb1, 0x2e, 0x85, 0x52, 0xc8, 0xf0, 0x11, + 0xb1, 0x2e, 0x85, 0x53, 0xc8, 0xb1, 0x2e, 0x85, + 0x54, 0xc8, 0xb1, 0x2e, 0x85, 0x55, 0xc8, 0xd0, + 0xe1, 0xa5, 0x3a, 0x85, 0x53, 0xa9, 0x00, 0x85, + 0x54, 0x85, 0x55, 0x4c, 0xd0, 0xf6, 0xa4, 0x34, + 0xb1, 0x30, 0x29, 0xf8, 0x4a, 0x4a, 0x4a, 0x85, + 0x56, 0xb1, 0x30, 0x29, 0x07, 0x0a, 0x0a, 0x85, + 0x57, 0xc8, 0xd0, 0x06, 0xa5, 0x4e, 0x85, 0x31, + 0xa4, 0x4f, 0xb1, 0x30, 0x29, 0xc0, 0x2a, 0x2a, + 0x2a, 0x05, 0x57, 0x85, 0x57, 0xb1, 0x30, 0x29, + 0x3e, 0x4a, 0x85, 0x58, 0xb1, 0x30, 0x29, 0x01, + 0x0a, 0x0a, 0x0a, 0x0a, 0x85, 0x59, 0xc8, 0xb1, + 0x30, 0x29, 0xf0, 0x4a, 0x4a, 0x4a, 0x4a, 0x05, + 0x59, 0x85, 0x59, 0xb1, 0x30, 0x29, 0x0f, 0x0a, + 0x85, 0x5a, 0xc8, 0xb1, 0x30, 0x29, 0x80, 0x18, + 0x2a, 0x2a, 0x29, 0x01, 0x05, 0x5a, 0x85, 0x5a, + 0xb1, 0x30, 0x29, 0x7c, 0x4a, 0x4a, 0x85, 0x5b, + 0xb1, 0x30, 0x29, 0x03, 0x0a, 0x0a, 0x0a, 0x85, + 0x5c, 0xc8, 0xd0, 0x06, 0xa5, 0x4e, 0x85, 0x31, + 0xa4, 0x4f, 0xb1, 0x30, 0x29, 0xe0, 0x2a, 0x2a, + 0x2a, 0x2a, 0x05, 0x5c, 0x85, 0x5c, 0xb1, 0x30, + 0x29, 0x1f, 0x85, 0x5d, 0xc8, 0x84, 0x34, 0xa6, + 0x56, 0xbd, 0xa0, 0xf8, 0xa6, 0x57, 0x1d, 0xc0, + 0xf8, 0x85, 0x52, 0xa6, 0x58, 0xbd, 0xa0, 0xf8, + 0xa6, 0x59, 0x1d, 0xc0, 0xf8, 0x85, 0x53, 0xa6, + 0x5a, 0xbd, 0xa0, 0xf8, 0xa6, 0x5b, 0x1d, 0xc0, + 0xf8, 0x85, 0x54, 0xa6, 0x5c, 0xbd, 0xa0, 0xf8, + 0xa6, 0x5d, 0x1d, 0xc0, 0xf8, 0x85, 0x55, 0x60, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x80, 0x00, 0x10, 0xff, 0xc0, 0x40, 0x50, + 0xff, 0xff, 0x20, 0x30, 0xff, 0xf0, 0x60, 0x70, + 0xff, 0x90, 0xa0, 0xb0, 0xff, 0xd0, 0xe0, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x08, 0x00, 0x01, 0xff, 0x0c, 0x04, 0x05, + 0xff, 0xff, 0x02, 0x03, 0xff, 0x0f, 0x06, 0x07, + 0xff, 0x09, 0x0a, 0x0b, 0xff, 0x0d, 0x0e, 0xff, + 0xa9, 0x00, 0x85, 0x34, 0x85, 0x2e, 0x85, 0x36, + 0xa9, 0x01, 0x85, 0x4e, 0xa9, 0xba, 0x85, 0x4f, + 0xa5, 0x31, 0x85, 0x2f, 0x20, 0xe6, 0xf7, 0xa5, + 0x52, 0x85, 0x38, 0xa4, 0x36, 0xa5, 0x53, 0x91, + 0x2e, 0xc8, 0xa5, 0x54, 0x91, 0x2e, 0xc8, 0xa5, + 0x55, 0x91, 0x2e, 0xc8, 0x84, 0x36, 0x20, 0xe6, + 0xf7, 0xa4, 0x36, 0xa5, 0x52, 0x91, 0x2e, 0xc8, + 0xf0, 0x11, 0xa5, 0x53, 0x91, 0x2e, 0xc8, 0xa5, + 0x54, 0x91, 0x2e, 0xc8, 0xa5, 0x55, 0x91, 0x2e, + 0xc8, 0xd0, 0xe1, 0xa5, 0x53, 0x85, 0x3a, 0xa5, + 0x2f, 0x85, 0x31, 0x60, 0xa5, 0x31, 0x85, 0x2f, + 0xa9, 0x00, 0x85, 0x31, 0xa9, 0x24, 0x85, 0x34, + 0xa5, 0x39, 0x85, 0x52, 0xa5, 0x1a, 0x85, 0x53, + 0xa5, 0x19, 0x85, 0x54, 0xa5, 0x18, 0x85, 0x55, + 0x20, 0xd0, 0xf6, 0xa5, 0x17, 0x85, 0x52, 0xa5, + 0x16, 0x85, 0x53, 0xa9, 0x00, 0x85, 0x54, 0x85, + 0x55, 0x20, 0xd0, 0xf6, 0xa5, 0x2f, 0x85, 0x31, + 0x60, 0xa4, 0x3f, 0x99, 0x00, 0x00, 0xa5, 0x50, + 0xf0, 0x03, 0x20, 0xf2, 0xf5, 0x20, 0x8f, 0xf9, + 0xa6, 0x49, 0x9a, 0x4c, 0xbe, 0xf2, 0xa9, 0xa0, + 0x85, 0x20, 0xad, 0x00, 0x1c, 0x09, 0x04, 0x8d, + 0x00, 0x1c, 0xa9, 0x3c, 0x85, 0x48, 0x60, 0xa6, + 0x3e, 0xa5, 0x20, 0x09, 0x10, 0x85, 0x20, 0xa9, + 0xff, 0x85, 0x48, 0x60, 0xad, 0x07, 0x1c, 0x8d, + 0x05, 0x1c, 0xad, 0x00, 0x1c, 0x29, 0x10, 0xc5, + 0x1e, 0x85, 0x1e, 0xf0, 0x04, 0xa9, 0x01, 0x85, + 0x1c, 0xad, 0xfe, 0x02, 0xf0, 0x15, 0xc9, 0x02, + 0xd0, 0x07, 0xa9, 0x00, 0x8d, 0xfe, 0x02, 0xf0, + 0x0a, 0x85, 0x4a, 0xa9, 0x02, 0x8d, 0xfe, 0x02, + 0x4c, 0x2e, 0xfa, 0xa6, 0x3e, 0x30, 0x07, 0xa5, + 0x20, 0xa8, 0xc9, 0x20, 0xd0, 0x03, 0x4c, 0xbe, + 0xfa, 0xc6, 0x48, 0xd0, 0x1d, 0x98, 0x10, 0x04, + 0x29, 0x7f, 0x85, 0x20, 0x29, 0x10, 0xf0, 0x12, + 0xad, 0x00, 0x1c, 0x29, 0xfb, 0x8d, 0x00, 0x1c, + 0xa9, 0xff, 0x85, 0x3e, 0xa9, 0x00, 0x85, 0x20, + 0xf0, 0xdc, 0x98, 0x29, 0x40, 0xd0, 0x03, 0x4c, + 0xbe, 0xfa, 0x6c, 0x62, 0x00, 0xa5, 0x4a, 0x10, + 0x05, 0x49, 0xff, 0x18, 0x69, 0x01, 0xc5, 0x64, + 0xb0, 0x0a, 0xa9, 0x3b, 0x85, 0x62, 0xa9, 0xfa, + 0x85, 0x63, 0xd0, 0x12, 0xe5, 0x5e, 0xe5, 0x5e, + 0x85, 0x61, 0xa5, 0x5e, 0x85, 0x60, 0xa9, 0x7b, + 0x85, 0x62, 0xa9, 0xfa, 0x85, 0x63, 0xa5, 0x4a, + 0x10, 0x31, 0xe6, 0x4a, 0xae, 0x00, 0x1c, 0xca, + 0x4c, 0x69, 0xfa, 0xa5, 0x4a, 0xd0, 0xef, 0xa9, + 0x4e, 0x85, 0x62, 0xa9, 0xfa, 0x85, 0x63, 0xa9, + 0x05, 0x85, 0x60, 0x4c, 0xbe, 0xfa, 0xc6, 0x60, + 0xd0, 0x6c, 0xa5, 0x20, 0x29, 0xbf, 0x85, 0x20, + 0xa9, 0x05, 0x85, 0x62, 0xa9, 0xfa, 0x85, 0x63, + 0x4c, 0xbe, 0xfa, 0xc6, 0x4a, 0xae, 0x00, 0x1c, + 0xe8, 0x8a, 0x29, 0x03, 0x85, 0x4b, 0xad, 0x00, + 0x1c, 0x29, 0xfc, 0x05, 0x4b, 0x8d, 0x00, 0x1c, + 0x4c, 0xbe, 0xfa, 0x38, 0xad, 0x07, 0x1c, 0xe5, + 0x5f, 0x8d, 0x05, 0x1c, 0xc6, 0x60, 0xd0, 0x0c, + 0xa5, 0x5e, 0x85, 0x60, 0xa9, 0x97, 0x85, 0x62, + 0xa9, 0xfa, 0x85, 0x63, 0x4c, 0x2e, 0xfa, 0xc6, + 0x61, 0xd0, 0xf9, 0xa9, 0xa5, 0x85, 0x62, 0xa9, + 0xfa, 0x85, 0x63, 0xd0, 0xef, 0xad, 0x07, 0x1c, + 0x18, 0x65, 0x5f, 0x8d, 0x05, 0x1c, 0xc6, 0x60, + 0xd0, 0xe2, 0xa9, 0x4e, 0x85, 0x62, 0xa9, 0xfa, + 0x85, 0x63, 0xa9, 0x05, 0x85, 0x60, 0xad, 0x0c, + 0x1c, 0x29, 0xfd, 0x8d, 0x0c, 0x1c, 0x60, 0xa5, + 0x51, 0x10, 0x2a, 0xa6, 0x3d, 0xa9, 0x60, 0x95, + 0x20, 0xa9, 0x01, 0x95, 0x22, 0x85, 0x51, 0xa9, + 0xa4, 0x85, 0x4a, 0xad, 0x00, 0x1c, 0x29, 0xfc, + 0x8d, 0x00, 0x1c, 0xa9, 0x0a, 0x8d, 0x20, 0x06, + 0xa9, 0xa0, 0x8d, 0x21, 0x06, 0xa9, 0x0f, 0x8d, + 0x22, 0x06, 0x4c, 0x9c, 0xf9, 0xa0, 0x00, 0xd1, + 0x32, 0xf0, 0x05, 0x91, 0x32, 0x4c, 0x9c, 0xf9, + 0xad, 0x00, 0x1c, 0x29, 0x10, 0xd0, 0x05, 0xa9, + 0x08, 0x4c, 0xd3, 0xfd, 0x20, 0xa3, 0xfd, 0x20, + 0xc3, 0xfd, 0xa9, 0x55, 0x8d, 0x01, 0x1c, 0x20, + 0xc3, 0xfd, 0x20, 0x00, 0xfe, 0x20, 0x56, 0xf5, + 0xa9, 0x40, 0x0d, 0x0b, 0x18, 0x8d, 0x0b, 0x18, + 0xa9, 0x62, 0x8d, 0x06, 0x18, 0xa9, 0x00, 0x8d, + 0x07, 0x18, 0x8d, 0x05, 0x18, 0xa0, 0x00, 0xa2, + 0x00, 0x2c, 0x00, 0x1c, 0x30, 0xfb, 0x2c, 0x00, + 0x1c, 0x10, 0xfb, 0xad, 0x04, 0x18, 0x2c, 0x00, + 0x1c, 0x10, 0x11, 0xad, 0x0d, 0x18, 0x0a, 0x10, + 0xf5, 0xe8, 0xd0, 0xef, 0xc8, 0xd0, 0xec, 0xa9, + 0x02, 0x4c, 0xd3, 0xfd, 0x86, 0x71, 0x84, 0x72, + 0xa2, 0x00, 0xa0, 0x00, 0xad, 0x04, 0x18, 0x2c, + 0x00, 0x1c, 0x30, 0x11, 0xad, 0x0d, 0x18, 0x0a, + 0x10, 0xf5, 0xe8, 0xd0, 0xef, 0xc8, 0xd0, 0xec, + 0xa9, 0x02, 0x4c, 0xd3, 0xfd, 0x38, 0x8a, 0xe5, + 0x71, 0xaa, 0x85, 0x70, 0x98, 0xe5, 0x72, 0xa8, + 0x85, 0x71, 0x10, 0x0b, 0x49, 0xff, 0xa8, 0x8a, + 0x49, 0xff, 0xaa, 0xe8, 0xd0, 0x01, 0xc8, 0x98, + 0xd0, 0x04, 0xe0, 0x04, 0x90, 0x18, 0x06, 0x70, + 0x26, 0x71, 0x18, 0xa5, 0x70, 0x6d, 0x21, 0x06, + 0x8d, 0x21, 0x06, 0xa5, 0x71, 0x6d, 0x22, 0x06, + 0x8d, 0x22, 0x06, 0x4c, 0x0c, 0xfb, 0xa2, 0x00, + 0xa0, 0x00, 0xb8, 0xad, 0x00, 0x1c, 0x10, 0x0e, + 0x50, 0xf9, 0xb8, 0xe8, 0xd0, 0xf5, 0xc8, 0xd0, + 0xf2, 0xa9, 0x03, 0x4c, 0xd3, 0xfd, 0x8a, 0x0a, + 0x8d, 0x25, 0x06, 0x98, 0x2a, 0x8d, 0x24, 0x06, + 0xa9, 0xbf, 0x2d, 0x0b, 0x18, 0x8d, 0x0b, 0x18, + 0xa9, 0x66, 0x8d, 0x26, 0x06, 0xa6, 0x43, 0xa0, + 0x00, 0x98, 0x18, 0x6d, 0x26, 0x06, 0x90, 0x01, + 0xc8, 0xc8, 0xca, 0xd0, 0xf5, 0x49, 0xff, 0x38, + 0x69, 0x00, 0x18, 0x6d, 0x25, 0x06, 0xb0, 0x03, + 0xce, 0x24, 0x06, 0xaa, 0x98, 0x49, 0xff, 0x38, + 0x69, 0x00, 0x18, 0x6d, 0x24, 0x06, 0x10, 0x05, + 0xa9, 0x04, 0x4c, 0xd3, 0xfd, 0xa8, 0x8a, 0xa2, + 0x00, 0x38, 0xe5, 0x43, 0xb0, 0x03, 0x88, 0x30, + 0x03, 0xe8, 0xd0, 0xf5, 0x8e, 0x26, 0x06, 0xe0, + 0x04, 0xb0, 0x05, 0xa9, 0x05, 0x4c, 0xd3, 0xfd, + 0x18, 0x65, 0x43, 0x8d, 0x27, 0x06, 0xa9, 0x00, + 0x8d, 0x28, 0x06, 0xa0, 0x00, 0xa6, 0x3d, 0xa5, + 0x39, 0x99, 0x00, 0x03, 0xc8, 0xc8, 0xad, 0x28, + 0x06, 0x99, 0x00, 0x03, 0xc8, 0xa5, 0x51, 0x99, + 0x00, 0x03, 0xc8, 0xb5, 0x13, 0x99, 0x00, 0x03, + 0xc8, 0xb5, 0x12, 0x99, 0x00, 0x03, 0xc8, 0xa9, + 0x0f, 0x99, 0x00, 0x03, 0xc8, 0x99, 0x00, 0x03, + 0xc8, 0xa9, 0x00, 0x59, 0xfa, 0x02, 0x59, 0xfb, + 0x02, 0x59, 0xfc, 0x02, 0x59, 0xfd, 0x02, 0x99, + 0xf9, 0x02, 0xee, 0x28, 0x06, 0xad, 0x28, 0x06, + 0xc5, 0x43, 0x90, 0xbb, 0x98, 0x48, 0xe8, 0x8a, + 0x9d, 0x00, 0x05, 0xe8, 0xd0, 0xfa, 0xa9, 0x03, + 0x85, 0x31, 0x20, 0x30, 0xfe, 0x68, 0xa8, 0x88, + 0x20, 0xe5, 0xfd, 0x20, 0xf5, 0xfd, 0xa9, 0x05, + 0x85, 0x31, 0x20, 0xe9, 0xf5, 0x85, 0x3a, 0x20, + 0x8f, 0xf7, 0xa9, 0x00, 0x85, 0x32, 0x20, 0x0e, + 0xfe, 0xa9, 0xff, 0x8d, 0x01, 0x1c, 0xa2, 0x05, + 0x50, 0xfe, 0xb8, 0xca, 0xd0, 0xfa, 0xa2, 0x0a, + 0xa4, 0x32, 0x50, 0xfe, 0xb8, 0xb9, 0x00, 0x03, + 0x8d, 0x01, 0x1c, 0xc8, 0xca, 0xd0, 0xf3, 0xa2, + 0x09, 0x50, 0xfe, 0xb8, 0xa9, 0x55, 0x8d, 0x01, + 0x1c, 0xca, 0xd0, 0xf5, 0xa9, 0xff, 0xa2, 0x05, + 0x50, 0xfe, 0xb8, 0x8d, 0x01, 0x1c, 0xca, 0xd0, + 0xf7, 0xa2, 0xbb, 0x50, 0xfe, 0xb8, 0xbd, 0x00, + 0x01, 0x8d, 0x01, 0x1c, 0xe8, 0xd0, 0xf4, 0xa0, + 0x00, 0x50, 0xfe, 0xb8, 0xb1, 0x30, 0x8d, 0x01, + 0x1c, 0xc8, 0xd0, 0xf5, 0xa9, 0x55, 0xae, 0x26, + 0x06, 0x50, 0xfe, 0xb8, 0x8d, 0x01, 0x1c, 0xca, + 0xd0, 0xf7, 0xa5, 0x32, 0x18, 0x69, 0x0a, 0x85, + 0x32, 0xce, 0x28, 0x06, 0xd0, 0x93, 0x50, 0xfe, + 0xb8, 0x50, 0xfe, 0xb8, 0x20, 0x00, 0xfe, 0xa9, + 0xc8, 0x8d, 0x23, 0x06, 0xa9, 0x00, 0x85, 0x30, + 0xa9, 0x03, 0x85, 0x31, 0xa5, 0x43, 0x8d, 0x28, + 0x06, 0x20, 0x56, 0xf5, 0xa2, 0x0a, 0xa0, 0x00, + 0x50, 0xfe, 0xb8, 0xad, 0x01, 0x1c, 0xd1, 0x30, + 0xd0, 0x0e, 0xc8, 0xca, 0xd0, 0xf2, 0x18, 0xa5, + 0x30, 0x69, 0x0a, 0x85, 0x30, 0x4c, 0x62, 0xfd, + 0xce, 0x23, 0x06, 0xd0, 0xcf, 0xa9, 0x06, 0x4c, + 0xd3, 0xfd, 0x20, 0x56, 0xf5, 0xa0, 0xbb, 0x50, + 0xfe, 0xb8, 0xad, 0x01, 0x1c, 0xd9, 0x00, 0x01, + 0xd0, 0xe6, 0xc8, 0xd0, 0xf2, 0xa2, 0xfc, 0x50, + 0xfe, 0xb8, 0xad, 0x01, 0x1c, 0xd9, 0x00, 0x05, + 0xd0, 0xd6, 0xc8, 0xca, 0xd0, 0xf1, 0xce, 0x28, + 0x06, 0xd0, 0xae, 0xe6, 0x51, 0xa5, 0x51, 0xc9, + 0x24, 0xb0, 0x03, 0x4c, 0x9c, 0xf9, 0xa9, 0xff, + 0x85, 0x51, 0xa9, 0x00, 0x85, 0x50, 0xa9, 0x01, + 0x4c, 0x69, 0xf9, 0xad, 0x0c, 0x1c, 0x29, 0x1f, + 0x09, 0xc0, 0x8d, 0x0c, 0x1c, 0xa9, 0xff, 0x8d, + 0x03, 0x1c, 0x8d, 0x01, 0x1c, 0xa2, 0x28, 0xa0, + 0x00, 0x50, 0xfe, 0xb8, 0x88, 0xd0, 0xfa, 0xca, + 0xd0, 0xf7, 0x60, 0xae, 0x21, 0x06, 0xac, 0x22, + 0x06, 0x50, 0xfe, 0xb8, 0xca, 0xd0, 0xfa, 0x88, + 0x10, 0xf7, 0x60, 0xce, 0x20, 0x06, 0xf0, 0x03, + 0x4c, 0x9c, 0xf9, 0xa0, 0xff, 0x84, 0x51, 0xc8, + 0x84, 0x50, 0x4c, 0x69, 0xf9, 0xb9, 0x00, 0x03, + 0x99, 0x45, 0x03, 0x88, 0xd0, 0xf7, 0xad, 0x00, + 0x03, 0x8d, 0x45, 0x03, 0x60, 0xa0, 0x44, 0xb9, + 0xbb, 0x01, 0x91, 0x30, 0x88, 0x10, 0xf8, 0x60, + 0xad, 0x0c, 0x1c, 0x09, 0xe0, 0x8d, 0x0c, 0x1c, + 0xa9, 0x00, 0x8d, 0x03, 0x1c, 0x60, 0xad, 0x0c, + 0x1c, 0x29, 0x1f, 0x09, 0xc0, 0x8d, 0x0c, 0x1c, + 0xa9, 0xff, 0x8d, 0x03, 0x1c, 0xa9, 0x55, 0x8d, + 0x01, 0x1c, 0xa2, 0x28, 0xa0, 0x00, 0x50, 0xfe, + 0xb8, 0x88, 0xd0, 0xfa, 0xca, 0xd0, 0xf7, 0x60, + 0xa9, 0x00, 0x85, 0x30, 0x85, 0x2e, 0x85, 0x36, + 0xa9, 0xbb, 0x85, 0x34, 0xa5, 0x31, 0x85, 0x2f, + 0xa9, 0x01, 0x85, 0x31, 0xa4, 0x36, 0xb1, 0x2e, + 0x85, 0x52, 0xc8, 0xb1, 0x2e, 0x85, 0x53, 0xc8, + 0xb1, 0x2e, 0x85, 0x54, 0xc8, 0xb1, 0x2e, 0x85, + 0x55, 0xc8, 0xf0, 0x08, 0x84, 0x36, 0x20, 0xd0, + 0xf6, 0x4c, 0x44, 0xfe, 0x4c, 0xd0, 0xf6, 0x48, + 0x8a, 0x48, 0x98, 0x48, 0xad, 0x0d, 0x18, 0x29, + 0x02, 0xf0, 0x03, 0x20, 0x53, 0xe8, 0xad, 0x0d, + 0x1c, 0x0a, 0x10, 0x03, 0x20, 0xb0, 0xf2, 0x68, + 0xa8, 0x68, 0xaa, 0x68, 0x40, 0x12, 0x04, 0x04, + 0x90, 0x56, 0x49, 0x44, 0x4d, 0x42, 0x55, 0x50, + 0x26, 0x43, 0x52, 0x53, 0x4e, 0x84, 0x05, 0xc1, + 0xf8, 0x1b, 0x5c, 0x07, 0xa3, 0xf0, 0x88, 0x23, + 0x0d, 0xed, 0xd0, 0xc8, 0xca, 0xcc, 0xcb, 0xe2, + 0xe7, 0xc8, 0xca, 0xc8, 0xee, 0x51, 0xdd, 0x1c, + 0x9e, 0x1c, 0x52, 0x57, 0x41, 0x4d, 0x44, 0x53, + 0x50, 0x55, 0x4c, 0x44, 0x53, 0x50, 0x55, 0x52, + 0x45, 0x45, 0x52, 0x53, 0x45, 0x4c, 0x51, 0x47, + 0x52, 0x4c, 0x08, 0x00, 0x00, 0x3f, 0x7f, 0xbf, + 0xff, 0x11, 0x12, 0x13, 0x15, 0x41, 0x04, 0x24, + 0x1f, 0x19, 0x12, 0x01, 0xff, 0xff, 0x01, 0x00, + 0x03, 0x04, 0x05, 0x06, 0x07, 0x07, 0x3e, 0x6c, + 0x65, 0x00, 0x8d, 0x00, 0x1c, 0x8d, 0x02, 0x1c, + 0x4c, 0x7d, 0xea, 0x8a, 0xa2, 0x05, 0xca, 0xd0, + 0xfd, 0xaa, 0x60, 0x20, 0xae, 0xe9, 0x4c, 0x9c, + 0xe9, 0xad, 0x02, 0x02, 0xc9, 0x2d, 0xf0, 0x05, + 0x38, 0xe9, 0x2b, 0xd0, 0xda, 0x85, 0x23, 0x60, + 0x8e, 0x03, 0x18, 0xa9, 0x02, 0x8d, 0x00, 0x18, + 0xa9, 0x1a, 0x8d, 0x02, 0x18, 0x4c, 0xa7, 0xea, + 0xad, 0x00, 0x18, 0x29, 0x01, 0xd0, 0xf9, 0xa9, + 0x01, 0x8d, 0x05, 0x18, 0x4c, 0xdf, 0xe9, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xc6, 0xc8, + 0x8f, 0xf9, 0x5f, 0xcd, 0x97, 0xcd, 0x00, 0x05, + 0x03, 0x05, 0x06, 0x05, 0x09, 0x05, 0x0c, 0x05, + 0x0f, 0x05, 0x01, 0xff, 0xa0, 0xea, 0x67, 0xfe +}; diff --git a/Src/1541d64.cpp b/Src/1541d64.cpp new file mode 100644 index 0000000..ce04019 --- /dev/null +++ b/Src/1541d64.cpp @@ -0,0 +1,2167 @@ +/* + * 1541d64.cpp - 1541 emulation in disk image files (.d64/.x64/zipcode) + * + * Frodo (C) 1994-1997,2002-2005 Christian Bauer + * zipcode decoding routines (C) 1993-1997 Marko Mäkelä, Paul David Doherty + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * Incompatibilities: + * - No support for relative files + * - Unimplemented commands: P + * - Impossible to implement: B-E, M-E + */ + +#include "sysdeps.h" + +#include "1541d64.h" +#include "IEC.h" +#include "Prefs.h" +#include "C64.h" +#include "main.h" + +#define DEBUG 0 +#include "debug.h" + + +// Channel modes (IRC users listen up :-) +enum { + CHMOD_FREE, // Channel free + CHMOD_COMMAND, // Command/error channel + CHMOD_DIRECTORY, // Reading directory, using large allocated buffer + CHMOD_FILE, // Sequential file open, using buffer in 1541 RAM + CHMOD_REL, // Relative file open, using buffer in 1541 RAM + CHMOD_DIRECT // Direct buffer access ('#'), using buffer in 1541 RAM +}; + +// Directory track +const int DIR_TRACK = 18; + +// BAM structure +enum { + BAM_DIR_TRACK = 0, // Track... + BAM_DIR_SECTOR = 1, // ...and sector of first directory block (unused) + BAM_FMT_TYPE = 2, // Format type + BAM_BITMAP = 4, // Sector allocation map + BAM_DISK_NAME = 144, // Disk name + BAM_DISK_ID = 162, // Disk ID + BAM_FMT_CHAR = 165 // Format characters +}; + +// Directory structure +enum { + DIR_NEXT_TRACK = 0, // Track... + DIR_NEXT_SECTOR = 1, // ... and sector of next directory block + DIR_ENTRIES = 2, // Start of directory entries (8) + + DE_TYPE = 0, // File type/flags + DE_TRACK = 1, // Track... + DE_SECTOR = 2, // ...and sector of first data block + DE_NAME = 3, // File name + DE_SIDE_TRACK = 19, // Track... + DE_SIDE_SECTOR = 20, // ...and sector of first side sector + DE_REC_LEN = 21, // Record length + DE_OVR_TRACK = 26, // Track... + DE_OVR_SECTOR = 27, // ...and sector on overwrite (@) + DE_NUM_BLOCKS_L = 28, // Number of blocks, LSB + DE_NUM_BLOCKS_H = 29, // Number of blocks, MSB + + SIZEOF_DE = 32 // Size of directory entry +}; + +// Interleave of directory and data blocks +const int DIR_INTERLEAVE = 3; +const int DATA_INTERLEAVE = 10; + +// Number of sectors per track, for all tracks +const int num_sectors[41] = { + 0, + 21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21, + 19,19,19,19,19,19,19, + 18,18,18,18,18,18, + 17,17,17,17,17, + 17,17,17,17,17 // Tracks 36..40 +}; + +// Accumulated number of sectors +const int accum_num_sectors[41] = { + 0, + 0,21,42,63,84,105,126,147,168,189,210,231,252,273,294,315,336, + 357,376,395,414,433,452,471, + 490,508,526,544,562,580, + 598,615,632,649,666, + 683,700,717,734,751 // Tracks 36..40 +}; + +// Prototypes +static bool match(const uint8 *p, int p_len, const uint8 *n); +static FILE *open_image_file(const char *path, bool write_mode); +static bool parse_image_file(FILE *f, image_file_desc &desc); + + +/* + * Constructor: Prepare emulation, open image file + */ + +ImageDrive::ImageDrive(IEC *iec, const char *filepath) : Drive(iec), the_file(NULL), bam(ram + 0x700), bam_dirty(false) +{ + for (int i=0; i<18; i++) { + ch[i].mode = CHMOD_FREE; + ch[i].buf = NULL; + } + ch[15].mode = CHMOD_COMMAND; + + Reset(); + + // Open image file + if (change_image(filepath)) + Ready = true; +} + + +/* + * Destructor + */ + +ImageDrive::~ImageDrive() +{ + close_image(); +} + + +/* + * Close the image file + */ + +void ImageDrive::close_image(void) +{ + if (the_file) { + close_all_channels(); + if (bam_dirty) { + write_sector(DIR_TRACK, 0, bam); + bam_dirty = false; + } + fclose(the_file); + the_file = NULL; + } +} + + +/* + * Open the image file + */ + +bool ImageDrive::change_image(const char *path) +{ + // Close old image file + close_image(); + + // Open new image file (try write access first, then read-only) + write_protected = false; + the_file = open_image_file(path, true); + if (the_file == NULL) { + write_protected = true; + the_file = open_image_file(path, false); + } + if (the_file) { + + // Determine file type and fill in image_file_desc structure + if (!parse_image_file(the_file, desc)) { + fclose(the_file); + the_file = false; + return false; + } + + // Read BAM + read_sector(DIR_TRACK, 0, bam); + bam_dirty = false; + return true; + } else + return false; +} + + +/* + * Open channel + */ + +uint8 ImageDrive::Open(int channel, const uint8 *name, int name_len) +{ + D(bug("ImageDrive::Open channel %d, file %s\n", channel, name)); + + set_error(ERR_OK); + + // Channel 15: execute file name as command + if (channel == 15) { + execute_cmd(name, name_len); + return ST_OK; + } + + if (ch[channel].mode != CHMOD_FREE) { + set_error(ERR_NOCHANNEL); + return ST_OK; + } + + if (name[0] == '$') + if (channel) + return open_file_ts(channel, DIR_TRACK, 0); + else + return open_directory(name + 1, name_len - 1); + + if (name[0] == '#') + return open_direct(channel, name); + + return open_file(channel, name, name_len); +} + + +/* + * Open file + */ + +uint8 ImageDrive::open_file(int channel, const uint8 *name, int name_len) +{ + uint8 plain_name[NAMEBUF_LENGTH]; + int plain_name_len; + int mode = FMODE_READ; + int type = FTYPE_DEL; + int rec_len = 0; + parse_file_name(name, name_len, plain_name, plain_name_len, mode, type, rec_len); + if (plain_name_len > 16) + plain_name_len = 16; + + D(bug(" plain name %s, type %d, mode %d\n", plain_name, type, mode)); + + // Channel 0 is READ, channel 1 is WRITE + if (channel == 0 || channel == 1) { + mode = channel ? FMODE_WRITE : FMODE_READ; + if (type == FTYPE_DEL) + type = FTYPE_PRG; + } + + ch[channel].writing = (mode == FMODE_WRITE || mode == FMODE_APPEND); + + // Wildcards are only allowed on reading + if (ch[channel].writing && (strchr((const char *)plain_name, '*') || strchr((const char *)plain_name, '?'))) { + set_error(ERR_SYNTAX33); + return ST_OK; + } + + // Check for write-protection if writing + if (ch[channel].writing && write_protected) { + set_error(ERR_WRITEPROTECT); + return ST_OK; + } + + // Relative files are not supported + if (type == FTYPE_REL) { + set_error(ERR_UNIMPLEMENTED); + return ST_OK; + } + + // Find file in directory + int dir_track, dir_sector, entry; + if (find_first_file(plain_name, plain_name_len, dir_track, dir_sector, entry)) { + + // File exists + D(bug(" file exists, dir track %d, sector %d, entry %d\n", dir_track, dir_sector, entry)); + ch[channel].dir_track = dir_track; + ch[channel].dir_sector = dir_sector; + ch[channel].entry = entry; + uint8 *de = dir + DIR_ENTRIES + entry * SIZEOF_DE; + + // Get file type from existing file if not specified in file name + if (type == FTYPE_DEL) + type = de[DE_TYPE] & 7; + + if ((de[DE_TYPE] & 7) != type) { + + // File type doesn't match + set_error(ERR_FILETYPE); + + } else if (mode == FMODE_WRITE) { + + if (name[0] == '@') { + + // Open old file for overwriting (save-replace) + return create_file(channel, plain_name, plain_name_len, type, true); + + } else { + + // File to be written already exists, error + set_error(ERR_FILEEXISTS); + } + + } else if (mode == FMODE_APPEND) { + + // Open old file for appending + open_file_ts(channel, de[DE_TRACK], de[DE_SECTOR]); + + // Seek to end of file + int track = 0, sector = 0, num_blocks = 0; + while (ch[channel].buf[0]) { + if (!read_sector(track = ch[channel].buf[0], sector = ch[channel].buf[1], ch[channel].buf)) + return ST_OK; + num_blocks++; + } + + // Change channel mode to writing, adjust buffer pointer + ch[channel].writing = true; + ch[channel].buf_len = ch[channel].buf[1] + 1; + ch[channel].buf_ptr = ch[channel].buf + ch[channel].buf_len; + ch[channel].track = track; + ch[channel].sector = sector; + ch[channel].num_blocks = num_blocks; + + } else if (mode == FMODE_M) { + + // Open old file for reading, even if it is not closed + return open_file_ts(channel, de[DE_TRACK], de[DE_SECTOR]); + + } else { + + // Open old file for reading, error if file is open + if (de[DE_TYPE] & 0x80) + return open_file_ts(channel, de[DE_TRACK], de[DE_SECTOR]); + else + set_error(ERR_WRITEFILEOPEN); + } + + } else { + + // File doesn't exist + D(bug(" file not found\n")); + + // Set file type to SEQ if not specified in file name + if (type == FTYPE_DEL) + type = FTYPE_SEQ; + + if (mode == FMODE_WRITE) { + + // Create new file for writing + return create_file(channel, plain_name, plain_name_len, type); + + } else + set_error(ERR_FILENOTFOUND); + } + return ST_OK; +} + + +/* + * Open channel for reading from file given track/sector of first block + */ + +uint8 ImageDrive::open_file_ts(int channel, int track, int sector) +{ + D(bug("open_file_ts track %d, sector %d\n", track, sector)); + + // Allocate buffer and set channel mode + int buf = alloc_buffer(-1); + if (buf == -1) { + set_error(ERR_NOCHANNEL); + return ST_OK; + } + ch[channel].buf_num = buf; + ch[channel].buf = ram + 0x300 + buf * 0x100; + ch[channel].mode = CHMOD_FILE; + + // On the next call to Read, the first block will be read + ch[channel].buf[0] = track; + ch[channel].buf[1] = sector; + ch[channel].buf_len = 0; + + return ST_OK; +} + + +/* + * Create file and open channel for writing to file + */ + +uint8 ImageDrive::create_file(int channel, const uint8 *name, int name_len, int type, bool overwrite) +{ + D(bug("create_file %s, type %d\n", name, type)); + + // Allocate buffer + int buf = alloc_buffer(-1); + if (buf == -1) { + set_error(ERR_NOCHANNEL); + return ST_OK; + } + ch[channel].buf_num = buf; + ch[channel].buf = ram + 0x300 + buf * 0x100; + + // Allocate new directory entry if not overwriting + if (!overwrite) { + if (!alloc_dir_entry(ch[channel].dir_track, ch[channel].dir_sector, ch[channel].entry)) { + free_buffer(buf); + return ST_OK; + } + } + uint8 *de = dir + DIR_ENTRIES + ch[channel].entry * SIZEOF_DE; + + // Allocate first data block + ch[channel].track = DIR_TRACK - 1; + ch[channel].sector = -DATA_INTERLEAVE; + if (!alloc_next_block(ch[channel].track, ch[channel].sector, DATA_INTERLEAVE)) { + free_buffer(buf); + return ST_OK; + } + ch[channel].num_blocks = 1; + D(bug(" first data block on track %d, sector %d\n", ch[channel].track, ch[channel].sector)); + + // Write directory entry + memset(de, 0, SIZEOF_DE); + de[DE_TYPE] = type; // bit 7 not set -> open file + if (overwrite) { + de[DE_OVR_TRACK] = ch[channel].track; + de[DE_OVR_SECTOR] = ch[channel].sector; + } else { + de[DE_TRACK] = ch[channel].track; + de[DE_SECTOR] = ch[channel].sector; + } + memset(de + DE_NAME, 0xa0, 16); + memcpy(de + DE_NAME, name, name_len); + write_sector(ch[channel].dir_track, ch[channel].dir_sector, dir); + + // Set channel descriptor + ch[channel].mode = CHMOD_FILE; + ch[channel].writing = true; + ch[channel].buf_ptr = ch[channel].buf + 2; + ch[channel].buf_len = 2; + return ST_OK; +} + + +/* + * Prepare directory as BASIC program (channel 0) + */ + +const char type_char_1[] = "DSPUREER"; +const char type_char_2[] = "EERSELQG"; +const char type_char_3[] = "LQGRL???"; + +uint8 ImageDrive::open_directory(const uint8 *pattern, int pattern_len) +{ + // Special treatment for "$0" + if (pattern[0] == '0' && pattern_len == 1) { + pattern++; + pattern_len--; + } + + // Skip everything before the ':' in the pattern + uint8 *t = (uint8 *)memchr(pattern, ':', pattern_len); + if (t) { + t++; + pattern_len -= t - pattern; + pattern = t; + } + + ch[0].mode = CHMOD_DIRECTORY; + uint8 *p = ch[0].buf_ptr = ch[0].buf = new uint8[8192]; + + // Create directory title with disk name, ID and format type + *p++ = 0x01; // Load address $0401 (from PET days :-) + *p++ = 0x04; + *p++ = 0x01; // Dummy line link + *p++ = 0x01; + *p++ = 0; // Drive number (0) as line number + *p++ = 0; + *p++ = 0x12; // RVS ON + *p++ = '\"'; + + uint8 *q = bam + BAM_DISK_NAME; + for (int i=0; i<23; i++) { + int c; + if ((c = *q++) == 0xa0) + *p++ = ' '; // Replace 0xa0 by space + else + *p++ = c; + } + *(p-7) = '\"'; + *p++ = 0; + + // Scan all directory blocks + dir[DIR_NEXT_TRACK] = DIR_TRACK; + dir[DIR_NEXT_SECTOR] = 1; + + int num_dir_blocks = 0; + while (dir[DIR_NEXT_TRACK] && num_dir_blocks < num_sectors[DIR_TRACK]) { + if (!read_sector(dir[DIR_NEXT_TRACK], dir[DIR_NEXT_SECTOR], dir)) + return ST_OK; + num_dir_blocks++; + + // Scan all 8 entries of a block + uint8 *de = dir + DIR_ENTRIES; + for (int j=0; j<8; j++, de+=SIZEOF_DE) { + if (de[DE_TYPE] && (pattern_len == 0 || match(pattern, pattern_len, de + DE_NAME))) { + + // Dummy line link + *p++ = 0x01; + *p++ = 0x01; + + // Line number = number of blocks + *p++ = de[DE_NUM_BLOCKS_L]; + *p++ = de[DE_NUM_BLOCKS_H]; + + // Appropriate number of spaces to align file names + *p++ = ' '; + int n = (de[DE_NUM_BLOCKS_H] << 8) + de[DE_NUM_BLOCKS_L]; + if (n<10) *p++ = ' '; + if (n<100) *p++ = ' '; + + // File name enclosed in quotes + *p++ = '\"'; + q = de + DE_NAME; + uint8 c; + bool m = false; + for (int i=0; i<16; i++) { + if ((c = *q++) == 0xa0) { + if (m) + *p++ = ' '; // Replace all 0xa0 by spaces + else + m = (*p++ = '\"'); // But the first by a '"' + } else + *p++ = c; + } + if (m) + *p++ = ' '; + else + *p++ = '\"'; // No 0xa0, then append a space + + // Open files are marked by '*' + if (de[DE_TYPE] & 0x80) + *p++ = ' '; + else + *p++ = '*'; + + // File type + *p++ = type_char_1[de[DE_TYPE] & 7]; + *p++ = type_char_2[de[DE_TYPE] & 7]; + *p++ = type_char_3[de[DE_TYPE] & 7]; + + // Protected files are marked by '<' + if (de[DE_TYPE] & 0x40) + *p++ = '<'; + else + *p++ = ' '; + + // Appropriate number of spaces at the end + *p++ = ' '; + if (n >= 10) *p++ = ' '; + if (n >= 100) *p++ = ' '; + *p++ = 0; + } + } + } + + // Final line, count number of free blocks + int n = 0; + for (int i=1; i<=35; i++) { + if (i != DIR_TRACK) // exclude track 18 + n += num_free_blocks(i); + } + + *p++ = 0x01; // Dummy line link + *p++ = 0x01; + *p++ = n & 0xff; // Number of free blocks as line number + *p++ = (n >> 8) & 0xff; + + *p++ = 'B'; + *p++ = 'L'; + *p++ = 'O'; + *p++ = 'C'; + *p++ = 'K'; + *p++ = 'S'; + *p++ = ' '; + *p++ = 'F'; + *p++ = 'R'; + *p++ = 'E'; + *p++ = 'E'; + *p++ = '.'; + + memset(p, ' ', 13); + p += 13; + + *p++ = 0; + *p++ = 0; + *p++ = 0; + + ch[0].buf_len = p - ch[0].buf; + return ST_OK; +} + + +/* + * Open channel for direct buffer access + */ + +uint8 ImageDrive::open_direct(int channel, const uint8 *name) +{ + int buf = -1; + + if (name[1] == 0) + buf = alloc_buffer(-1); + else + if ((name[1] >= '0') && (name[1] <= '3') && (name[2] == 0)) + buf = alloc_buffer(name[1] - '0'); + + if (buf == -1) { + set_error(ERR_NOCHANNEL); + return ST_OK; + } + + // The buffers are in the 1541 RAM at $300 and are 256 bytes each + ch[channel].mode = CHMOD_DIRECT; + ch[channel].buf = ram + 0x300 + buf * 0x100; + ch[channel].buf_num = buf; + + // Store actual buffer number in buffer + ch[channel].buf[1] = buf + '0'; + ch[channel].buf_len = 1; + ch[channel].buf_ptr = ch[channel].buf + 1; + + return ST_OK; +} + + +/* + * Close channel + */ + +uint8 ImageDrive::Close(int channel) +{ + D(bug("ImageDrive::Close channel %d\n", channel)); + + switch (ch[channel].mode) { + case CHMOD_FREE: + break; + + case CHMOD_COMMAND: + close_all_channels(); + break; + + case CHMOD_DIRECT: + free_buffer(ch[channel].buf_num); + ch[channel].buf = NULL; + ch[channel].mode = CHMOD_FREE; + break; + + case CHMOD_FILE: + if (ch[channel].writing) { + + // Current block empty? Then write CR character + if (ch[channel].buf_len == 2) { + ch[channel].buf[2] = 0x0d; + ch[channel].buf_len++; + } + + // Write last data block + ch[channel].buf[0] = 0; + ch[channel].buf[1] = ch[channel].buf_len - 1; + D(bug(" writing last data block\n")); + if (!write_sector(ch[channel].track, ch[channel].sector, ch[channel].buf)) + goto free; + + // Close write file in directory + read_sector(ch[channel].dir_track, ch[channel].dir_sector, dir); + uint8 *de = dir + DIR_ENTRIES + ch[channel].entry * SIZEOF_DE; + de[DE_TYPE] |= 0x80; + de[DE_NUM_BLOCKS_L] = ch[channel].num_blocks & 0xff; + de[DE_NUM_BLOCKS_H] = ch[channel].num_blocks >> 8; + if (de[DE_OVR_TRACK]) { + // Overwriting, free old data blocks and set pointer to new ones + free_block_chain(de[DE_TRACK], de[DE_SECTOR]); + de[DE_TRACK] = de[DE_OVR_TRACK]; + de[DE_SECTOR] = de[DE_OVR_SECTOR]; + de[DE_OVR_TRACK] = de[DE_OVR_SECTOR] = 0; + } + write_sector(ch[channel].dir_track, ch[channel].dir_sector, dir); + D(bug(" directory entry updated\n")); + } +free: free_buffer(ch[channel].buf_num); + ch[channel].buf = NULL; + ch[channel].mode = CHMOD_FREE; + break; + + case CHMOD_DIRECTORY: + delete[] ch[channel].buf; + ch[channel].buf = NULL; + ch[channel].mode = CHMOD_FREE; + break; + } + + return ST_OK; +} + + +/* + * Close all channels + */ + +void ImageDrive::close_all_channels() +{ + for (int i=0; i<15; i++) + Close(i); + Close(16); + Close(17); + + cmd_len = 0; +} + + +/* + * Read from channel + */ + +uint8 ImageDrive::Read(int channel, uint8 &byte) +{ +// D(bug("ImageDrive::Read channel %d\n", channel)); + + switch (ch[channel].mode) { + case CHMOD_FREE: + if (current_error == ERR_OK) + set_error(ERR_FILENOTOPEN); + break; + + case CHMOD_COMMAND: + // Read error channel + byte = *error_ptr++; + if (--error_len) + return ST_OK; + else { + set_error(ERR_OK); + return ST_EOF; + } + break; + + case CHMOD_FILE: + if (ch[channel].writing) + return ST_READ_TIMEOUT; + if (current_error != ERR_OK) + return ST_READ_TIMEOUT; + + // Read next block if necessary + if (ch[channel].buf_len == 0 && ch[channel].buf[0]) { + D(bug(" reading next data block track %d, sector %d\n", ch[channel].buf[0], ch[channel].buf[1])); + if (!read_sector(ch[channel].buf[0], ch[channel].buf[1], ch[channel].buf)) + return ST_READ_TIMEOUT; + ch[channel].buf_ptr = ch[channel].buf + 2; + + // Determine block length + ch[channel].buf_len = ch[channel].buf[0] ? 254 : ch[channel].buf[1] - 1; + } + + if (ch[channel].buf_len > 0) { + byte = *(ch[channel].buf_ptr)++; + if (--(ch[channel].buf_len) == 0 && ch[channel].buf[0] == 0) + return ST_EOF; + else + return ST_OK; + } else + return ST_READ_TIMEOUT; + break; + + case CHMOD_DIRECTORY: + case CHMOD_DIRECT: + if (ch[channel].buf_len > 0) { + byte = *(ch[channel].buf_ptr)++; + if (--(ch[channel].buf_len)) + return ST_OK; + else + return ST_EOF; + } else + return ST_READ_TIMEOUT; + break; + } + return ST_READ_TIMEOUT; +} + + +/* + * Write byte to channel + */ + +uint8 ImageDrive::Write(int channel, uint8 byte, bool eoi) +{ +// D(bug("ImageDrive::Write channel %d, byte %02x, eoi %d\n", channel, byte, eoi)); + + switch (ch[channel].mode) { + case CHMOD_FREE: + if (current_error == ERR_OK) + set_error(ERR_FILENOTOPEN); + break; + + case CHMOD_COMMAND: + // Collect characters and execute command on EOI + if (cmd_len > 58) { + set_error(ERR_SYNTAX32); + return ST_TIMEOUT; + } + + cmd_buf[cmd_len++] = byte; + + if (eoi) { + execute_cmd(cmd_buf, cmd_len); + cmd_len = 0; + } + return ST_OK; + + case CHMOD_DIRECTORY: + set_error(ERR_WRITEFILEOPEN); + break; + + case CHMOD_FILE: + if (!ch[channel].writing) + return ST_TIMEOUT; + if (current_error != ERR_OK) + return ST_TIMEOUT; + + // Buffer full? + if (ch[channel].buf_len >= 256) { + + // Yes, allocate new block + int track = ch[channel].track, sector = ch[channel].sector; + if (!alloc_next_block(track, sector, DATA_INTERLEAVE)) + return ST_TIMEOUT; + ch[channel].num_blocks++; + D(bug("next data block on track %d, sector %d\n", track, sector)); + + // Write buffer with link to new block + ch[channel].buf[0] = track; + ch[channel].buf[1] = sector; + write_sector(ch[channel].track, ch[channel].sector, ch[channel].buf); + + // Reset buffer + ch[channel].buf_ptr = ch[channel].buf + 2; + ch[channel].buf_len = 2; + ch[channel].track = track; + ch[channel].sector = sector; + } + *(ch[channel].buf_ptr)++ = byte; + ch[channel].buf_len++; + return ST_OK; + + case CHMOD_DIRECT: + if (ch[channel].buf_len < 256) { + *(ch[channel].buf_ptr)++ = byte; + ch[channel].buf_len++; + return ST_OK; + } else + return ST_TIMEOUT; + break; + } + return ST_TIMEOUT; +} + + +/* + * Reset drive + */ + +void ImageDrive::Reset(void) +{ + close_all_channels(); + + cmd_len = 0; + for (int i=0; i<4; i++) + buf_free[i] = true; + + if (bam_dirty) { + write_sector(DIR_TRACK, 0, bam); + bam_dirty = false; + } + + memset(ram, 0, sizeof(ram)); + + read_sector(DIR_TRACK, 0, bam); + + set_error(ERR_STARTUP); +} + + +/* + * Allocate floppy buffer + * -> Desired buffer number or -1 + * <- Allocated buffer number or -1 + */ + +int ImageDrive::alloc_buffer(int want) +{ + if (want == -1) { + for (want=3; want>=0; want--) + if (buf_free[want]) { + buf_free[want] = false; + return want; + } + return -1; + } + + if (want < 4) + if (buf_free[want]) { + buf_free[want] = false; + return want; + } else + return -1; + else + return -1; +} + + +/* + * Free floppy buffer + */ + +void ImageDrive::free_buffer(int buf) +{ + buf_free[buf] = true; +} + + +/* + * Search file in directory, return directory track/sector and entry number + * false: not found, true: found + */ + +// Return true if name 'n' matches pattern 'p' +static bool match(const uint8 *p, int p_len, const uint8 *n) +{ + if (p_len > 16) + p_len = 16; + + int c = 0; + while (p_len-- > 0) { + if (*p == '*') // Wildcard '*' matches all following characters + return true; + if ((*p != *n) && (*p != '?')) // Wildcard '?' matches single character + return false; + p++; n++; c++; + } + + return *n == 0xa0 || c == 16; +} + +bool ImageDrive::find_file(const uint8 *pattern, int pattern_len, int &dir_track, int &dir_sector, int &entry, bool cont) +{ + // Counter to prevent cyclic directories from resulting in an infinite loop + int num_dir_blocks = 0; + + // Pointer to current directory entry + uint8 *de = NULL; + if (cont) + de = dir + DIR_ENTRIES + entry * SIZEOF_DE; + else { + dir[DIR_NEXT_TRACK] = DIR_TRACK; + dir[DIR_NEXT_SECTOR] = 1; + entry = 8; + } + + while (num_dir_blocks < num_sectors[DIR_TRACK]) { + + // Goto next entry + entry++; de += SIZEOF_DE; + if (entry >= 8) { + + // Read next directory block + if (dir[DIR_NEXT_TRACK] == 0) + return false; + if (!read_sector(dir_track = dir[DIR_NEXT_TRACK], dir_sector = dir[DIR_NEXT_SECTOR], dir)) + return false; + num_dir_blocks++; + entry = 0; + de = dir + DIR_ENTRIES; + } + + // Does entry match pattern? + if (de[DE_TYPE] && match(pattern, pattern_len, de + DE_NAME)) + return true; + } + return false; +} + +bool ImageDrive::find_first_file(const uint8 *pattern, int pattern_len, int &dir_track, int &dir_sector, int &entry) +{ + return find_file(pattern, pattern_len, dir_track, dir_sector, entry, false); +} + +bool ImageDrive::find_next_file(const uint8 *pattern, int pattern_len, int &dir_track, int &dir_sector, int &entry) +{ + return find_file(pattern, pattern_len, dir_track, dir_sector, entry, true); +} + + +/* + * Allocate new entry in directory, returns false on error (usually when + * all sectors of track 18 are allocated) + * The track/sector and entry numbers are returned + */ + +bool ImageDrive::alloc_dir_entry(int &track, int §or, int &entry) +{ + // First look for free entry in existing directory blocks + dir[DIR_NEXT_TRACK] = DIR_TRACK; + dir[DIR_NEXT_SECTOR] = 1; + while (dir[DIR_NEXT_TRACK]) { + if (!read_sector(track = dir[DIR_NEXT_TRACK], sector = dir[DIR_NEXT_SECTOR], dir)) + return false; + + uint8 *de = dir + DIR_ENTRIES; + for (entry=0; entry<8; entry++, de+=SIZEOF_DE) { + if (de[DE_TYPE] == 0) { + D(bug(" allocated entry %d in dir track %d, sector %d\n", entry, track, sector)); + return true; + } + } + } + + // No free entry found, allocate new directory block + int last_track = track, last_sector = sector; + if (!alloc_next_block(track, sector, DIR_INTERLEAVE)) + return false; + D(bug(" new directory block track %d, sector %d\n", track, sector)); + + // Write link to new block to last block + dir[DIR_NEXT_TRACK] = track; + dir[DIR_NEXT_SECTOR] = sector; + write_sector(last_track, last_sector, dir); + + // Write new empty directory block and return first entry + memset(dir, 0, 256); + dir[DIR_NEXT_SECTOR] = 0xff; + write_sector(track, sector, dir); + entry = 0; + return true; +} + +/* + * Test if block is free in BAM (track/sector are not checked for validity) + */ + +bool ImageDrive::is_block_free(int track, int sector) +{ + uint8 *p = bam + BAM_BITMAP + (track - 1) * 4; + int byte = sector / 8 + 1; + int bit = sector & 7; + return p[byte] & (1 << bit); +} + + +/* + * Get number of free blocks on a track + */ + +int ImageDrive::num_free_blocks(int track) +{ + return bam[BAM_BITMAP + (track - 1) * 4]; +} + + +/* + * Clear BAM, mark all blocks as free + */ + +static void clear_bam(uint8 *bam) +{ + for (int track=1; track<=35; track++) { + static const uint8 num2bits[8] = {0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff}; + (bam + BAM_BITMAP)[(track-1) * 4 + 0] = num_sectors[track]; + (bam + BAM_BITMAP)[(track-1) * 4 + 1] = 0xff; + (bam + BAM_BITMAP)[(track-1) * 4 + 2] = 0xff; + (bam + BAM_BITMAP)[(track-1) * 4 + 3] = num2bits[num_sectors[track] - 16]; + } +} + + +/* + * Allocate block in BAM, returns error code + */ + +int ImageDrive::alloc_block(int track, int sector) +{ + if (track < 1 || track > 35 || sector < 0 || sector >= num_sectors[track]) + return ERR_ILLEGALTS; + + uint8 *p = bam + BAM_BITMAP + (track - 1) * 4; + int byte = sector / 8 + 1; + int bit = sector & 7; + + // Block free? + if (p[byte] & (1 << bit)) { + + // Yes, allocate and decrement free block count + D(bug("allocating block at track %d, sector %d\n", track, sector)); + p[byte] &= ~(1 << bit); + p[0]--; + bam_dirty = true; + return ERR_OK; + + } else + return ERR_NOBLOCK; +} + + +/* + * Free block in BAM, returns error code + */ + +int ImageDrive::free_block(int track, int sector) +{ + if (track < 1 || track > 35 || sector < 0 || sector >= num_sectors[track]) + return ERR_ILLEGALTS; + + uint8 *p = bam + BAM_BITMAP + (track - 1) * 4; + int byte = sector / 8 + 1; + int bit = sector & 7; + + // Block allocated? + if (!(p[byte] & (1 << bit))) { + + // Yes, free and increment free block count + D(bug("freeing block at track %d, sector %d\n", track, sector)); + p[byte] |= (1 << bit); + p[0]++; + bam_dirty = true; + } + return ERR_OK; +} + + +/* + * Allocate chain of data blocks in BAM + */ + +bool ImageDrive::alloc_block_chain(int track, int sector) +{ + uint8 buf[256]; + while (alloc_block(track, sector) == ERR_OK) { + if (!read_sector(track, sector, buf)) + return false; + track = buf[0]; + sector = buf[1]; + } + return true; +} + + +/* + * Free chain of data blocks in BAM + */ + +bool ImageDrive::free_block_chain(int track, int sector) +{ + uint8 buf[256]; + while (free_block(track, sector) == ERR_OK) { + if (!read_sector(track, sector, buf)) + return false; + track = buf[0]; + sector = buf[1]; + } + return true; +} + + +/* + * Search and allocate next free block, returns false if no more blocks + * are free (ERR_DISKFULL is also set in this case) + * "track" and "sector" must be set to the block where the search should + * begin + */ + +bool ImageDrive::alloc_next_block(int &track, int §or, int interleave) +{ + // Find track with free blocks + bool side_changed = false; + while (num_free_blocks(track) == 0) { + if (track == DIR_TRACK) { // Directory doesn't grow to other tracks +full: track = sector = 0; + set_error(ERR_DISKFULL); + return false; + } else if (track > DIR_TRACK) { + track++; + if (track > 35) { + if (!side_changed) + side_changed = true; + else + goto full; + track = DIR_TRACK - 1; + sector = 0; + } + } else { + track--; + if (track < 1) { + if (!side_changed) + side_changed = true; + else + goto full; + track = DIR_TRACK + 1; + sector = 0; + } + } + } + + // Find next free block on track + int num = num_sectors[track]; + sector = sector + interleave; + if (sector >= num) { + sector -= num; + if (sector) + sector--; + } + while (!is_block_free(track, sector)) { + sector++; + if (sector >= num_sectors[track]) { + sector = 0; + while (!is_block_free(track, sector)) { + sector++; + if (sector >= num_sectors[track]) { + // Something is wrong: the BAM free block count for this + // track was >0, but we found no free blocks + track = sector = 0; + set_error(ERR_DIRERROR); + return false; + } + } + } + } + + alloc_block(track, sector); + return true; +} + + +/* + * Sector reading/writing routines + */ + +static long offset_from_ts(const image_file_desc &desc, int track, int sector) +{ + if ((track < 1) || (track > desc.num_tracks) + || (sector < 0) || (sector >= num_sectors[track])) + return -1; + + return ((accum_num_sectors[track] + sector) << 8) + desc.header_size; +} + +// Get number of sectors per given track +int sectors_per_track(const image_file_desc &desc, int track) +{ + return num_sectors[track]; +} + +// Get reference to error info byte of given track/sector +uint8 &error_info_for_sector(image_file_desc &desc, int track, int sector) +{ + return desc.error_info[accum_num_sectors[track] + sector]; +} + +static inline const uint8 &error_info_for_sector(const image_file_desc &desc, int track, int sector) +{ + return desc.error_info[accum_num_sectors[track] + sector]; +} + +const int conv_job_error[16] = { + ERR_OK, // 0 -> 00 OK + ERR_OK, // 1 -> 00 OK + ERR_READ20, // 2 -> 20 READ ERROR + ERR_READ21, // 3 -> 21 READ ERROR + ERR_READ22, // 4 -> 22 READ ERROR + ERR_READ23, // 5 -> 23 READ ERROR + ERR_READ24, // 6 -> 24 READ ERROR (undetected by 1541) + ERR_WRITE25, // 7 -> 25 WRITE ERROR + ERR_WRITEPROTECT, // 8 -> 26 WRITE PROTECT ON + ERR_READ27, // 9 -> 27 READ ERROR + ERR_WRITE28, // 10 -> 28 WRITE ERROR + ERR_DISKID, // 11 -> 29 DISK ID MISMATCH + ERR_OK, // 12 -> 00 OK + ERR_OK, // 13 -> 00 OK + ERR_OK, // 14 -> 00 OK + ERR_NOTREADY // 15 -> 74 DRIVE NOT READY +}; + +// Read sector, return error code +static int read_sector(FILE *f, const image_file_desc &desc, int track, int sector, uint8 *buffer) +{ + // Convert track/sector to byte offset in file + long offset = offset_from_ts(desc, track, sector); + if (offset < 0) + return ERR_ILLEGALTS; + + if (f == NULL) + return ERR_NOTREADY; + + fseek(f, offset, SEEK_SET); + if (fread(buffer, 1, 256, f) != 256) + return ERR_READ22; + else { + unsigned int error = error_info_for_sector(desc, track, sector); + return conv_job_error[error & 0x0f]; + } +} + +// Write sector, return error code +static int write_sector(FILE *f, const image_file_desc &desc, int track, int sector, uint8 *buffer) +{ + // Convert track/sector to byte offset in file + long offset = offset_from_ts(desc, track, sector); + if (offset < 0) + return ERR_ILLEGALTS; + + if (f == NULL) + return ERR_NOTREADY; + + fseek(f, offset, SEEK_SET); + if (fwrite(buffer, 1, 256, f) != 256) + return ERR_WRITE25; + else + return ERR_OK; +} + +// Read sector and set error message, returns false on error +bool ImageDrive::read_sector(int track, int sector, uint8 *buffer) +{ + int error = ::read_sector(the_file, desc, track, sector, buffer); + if (error) + set_error(error, track, sector); + return error == ERR_OK; +} + +// Write sector and set error message, returns false on error +bool ImageDrive::write_sector(int track, int sector, uint8 *buffer) +{ + int error = ::write_sector(the_file, desc, track, sector, buffer); + if (error) + set_error(error, track, sector); + return error == ERR_OK; +} + +// Write error info back to image file +static void write_back_error_info(FILE *f, const image_file_desc &desc) +{ + if (desc.type == TYPE_D64 && desc.has_error_info) { + int num_sectors = desc.num_tracks == 40 ? NUM_SECTORS_40 : NUM_SECTORS_35; + fseek(f, num_sectors * 256, SEEK_SET); + fwrite(desc.error_info, num_sectors, 1, f); + } +} + +// Format disk image +static bool format_image(FILE *f, image_file_desc &desc, bool lowlevel, uint8 id1, uint8 id2, const uint8 *disk_name, int disk_name_len) +{ + uint8 p[256]; + + if (lowlevel) { + + // Fill buffer with 1541 empty sector pattern (4b 01 01 ..., + // except on track 1 where it's 01 01 01 ...) + memset(p, 1, 256); + + // Overwrite all blocks + for (int track=1; track<=35; track++) { + if (track == 2) + p[0] = 0x4b; + for (int sector=0; sector 16) + disk_name_len = 16; + memcpy(p + BAM_DISK_NAME, disk_name, disk_name_len); + p[BAM_DISK_ID] = id1; + p[BAM_DISK_ID + 1] = id2; + p[BAM_FMT_CHAR] = '2'; + p[BAM_FMT_CHAR + 1] = 'A'; + if (write_sector(f, desc, DIR_TRACK, 0, p) != ERR_OK) + return false; + + // Create and write empty directory + memset(p, 0, 256); + p[1] = 255; + return write_sector(f, desc, DIR_TRACK, 1, p) == ERR_OK; +} + + +/* + * Execute drive commands + */ + +// BLOCK-READ:channel,0,track,sector +void ImageDrive::block_read_cmd(int channel, int track, int sector, bool user_cmd) +{ + if (channel >= 16 || ch[channel].mode != CHMOD_DIRECT) { + set_error(ERR_NOCHANNEL); + return; + } + if (!read_sector(track, sector, ch[channel].buf)) + return; + if (user_cmd) { + ch[channel].buf_len = 256; + ch[channel].buf_ptr = ch[channel].buf; + } else { + ch[channel].buf_len = ch[channel].buf[0]; + ch[channel].buf_ptr = ch[channel].buf + 1; + } +} + +// BLOCK-WRITE:channel,0,track,sector +void ImageDrive::block_write_cmd(int channel, int track, int sector, bool user_cmd) +{ + if (write_protected) { + set_error(ERR_WRITEPROTECT); + return; + } + if (channel >= 16 || ch[channel].mode != CHMOD_DIRECT) { + set_error(ERR_NOCHANNEL); + return; + } + if (!user_cmd) + ch[channel].buf[0] = ch[channel].buf_len ? ch[channel].buf_len - 1 : 1; + if (!write_sector(track, sector, ch[channel].buf)) + return; + if (!user_cmd) { + ch[channel].buf_len = 1; + ch[channel].buf_ptr = ch[channel].buf + 1; + } +} + +// BLOCK-ALLOCATE:0,track,sector +void ImageDrive::block_allocate_cmd(int track, int sector) +{ + int err = alloc_block(track, sector); + if (err) { + if (err == ERR_NOBLOCK) { + // Find next free block and return its track/sector address in the + // error message (only look on higher tracks) + for (;;) { + sector++; + if (sector >= num_sectors[track]) { + track++; + sector = 0; + if (track > 35) { + set_error(ERR_NOBLOCK, 0, 0); + return; + } + } + if (is_block_free(track, sector)) { + set_error(ERR_NOBLOCK, track, sector); + return; + } + } + } else + set_error(err, track, sector); + } +} + +// BLOCK-FREE:0,track,sector +void ImageDrive::block_free_cmd(int track, int sector) +{ + int err = free_block(track, sector); + if (err) + set_error(err, track, sector); +} + +// BUFFER-POINTER:channel,pos +void ImageDrive::buffer_pointer_cmd(int channel, int pos) +{ + if (channel >= 16 || ch[channel].mode != CHMOD_DIRECT) { + set_error(ERR_NOCHANNEL); + return; + } + ch[channel].buf_ptr = ch[channel].buf + pos; + ch[channel].buf_len = 256 - pos; +} + +// M-R[] +void ImageDrive::mem_read_cmd(uint16 adr, uint8 len) +{ + error_len = len; + if (adr >= 0x300 && adr < 0x1000) { + // Read from RAM + error_ptr = (char *)ram + (adr & 0x7ff); + } else if (adr >= 0xc000) { + // Read from ROM + error_ptr = (char *)(TheC64->ROM1541) + (adr - 0xc000); + } else { + unsupp_cmd(); + memset(error_buf, 0, len); + error_ptr = error_buf; + } +} + +// M-W +void ImageDrive::mem_write_cmd(uint16 adr, uint8 len, uint8 *p) +{ + while (len) { + if (adr >= 0x300 && adr < 0x1000) { + // Write to RAM + ram[adr & 0x7ff] = *p; + } else if (adr < 0xc000) { + unsupp_cmd(); + return; + } + len--; adr++; p++; + } +} + +// COPY:new=file1,file2,... +// ^ ^ +// new_file old_files +void ImageDrive::copy_cmd(const uint8 *new_file, int new_file_len, const uint8 *old_files, int old_files_len) +{ + // Check if destination file is already present + int dir_track, dir_sector, entry; + if (find_first_file(new_file, new_file_len, dir_track, dir_sector, entry)) { + set_error(ERR_FILEEXISTS); + return; + } + + // Loop for all source files + bool first = true; + while (old_files_len > 0) { + uint8 *comma = (uint8 *)memchr(old_files, ',', old_files_len); + int name_len = comma ? comma - old_files : old_files_len; + + // Check if source file is present + if (!find_first_file(old_files, name_len, dir_track, dir_sector, entry)) { + set_error(ERR_FILENOTFOUND); + Close(17); + return; + } + uint8 *de = dir + DIR_ENTRIES + entry * SIZEOF_DE; + uint8 type = de[DE_TYPE] & 7, track = de[DE_TRACK], sector = de[DE_SECTOR]; + + // If this is the first source file, open internal write channel for destination file + if (first) { + create_file(17, new_file, new_file_len, type, false); + if (ch[17].mode == CHMOD_FREE) + return; + first = false; + } + + // Open internal read channel for source file + open_file_ts(16, track, sector); + if (ch[16].mode == CHMOD_FREE) { + Close(17); + return; + } + + // Copy file + uint8 byte, st; + do { + st = Read(16, byte); + Write(17, byte, false); + } while (st == ST_OK); + Close(16); + if (st != ST_EOF) { + Close(17); + return; + } + + if (comma) { + old_files_len -= name_len + 1; + old_files = comma + 1; + } else + old_files_len = 0; + } + Close(17); +} + +// RENAME:new=old +// ^ ^ +// new_file old_file +void ImageDrive::rename_cmd(const uint8 *new_file, int new_file_len, const uint8 *old_file, int old_file_len) +{ + // Check if destination file is already present + int dir_track, dir_sector, entry; + if (find_first_file(new_file, new_file_len, dir_track, dir_sector, entry)) { + set_error(ERR_FILEEXISTS); + return; + } + + // Check if source file is present + if (!find_first_file(old_file, old_file_len, dir_track, dir_sector, entry)) { + set_error(ERR_FILENOTFOUND); + return; + } + + // Check for write-protection + if (write_protected) { + set_error(ERR_WRITEPROTECT); + return; + } + + // Rename file in directory entry + uint8 *p = dir + DIR_ENTRIES + entry * SIZEOF_DE; + memset(p + DE_NAME, 0xa0, 16); + memcpy(p + DE_NAME, new_file, new_file_len); + write_sector(dir_track, dir_sector, dir); +} + +// SCRATCH:file1,file2,... +// ^ +// files +void ImageDrive::scratch_cmd(const uint8 *files, int files_len) +{ + // Check for write-protection + if (write_protected) { + set_error(ERR_WRITEPROTECT); + return; + } + + // Loop for all files + int num_files = 0; + while (files_len > 0) { + uint8 *comma = (uint8 *)memchr(files, ',', files_len); + int name_len = comma ? comma - files : files_len; + + int dir_track, dir_sector, entry; + if (find_first_file(files, name_len, dir_track, dir_sector, entry)) { + do { + uint8 *de = dir + DIR_ENTRIES + entry * SIZEOF_DE; + + // File protected? Then skip + if (de[DE_TYPE] & 0x40) + continue; + + // Free allocated data blocks and side sectors + free_block_chain(de[DE_TRACK], de[DE_SECTOR]); + free_block_chain(de[DE_SIDE_TRACK], de[DE_SIDE_SECTOR]); + + // Clear file type + de[DE_TYPE] = 0; + + // Write directory block back + write_sector(dir_track, dir_sector, dir); + num_files++; + } while (find_next_file(files, name_len, dir_track, dir_sector, entry)); + } + + if (comma) { + files_len -= name_len + 1; + files = comma + 1; + } else + files_len = 0; + } + + // Report number of files scratched + set_error(ERR_SCRATCHED, num_files); +} + +// INITIALIZE +void ImageDrive::initialize_cmd(void) +{ + // Close all channels and re-read BAM + close_all_channels(); + if (bam_dirty) { + write_sector(DIR_TRACK, 0, bam); + bam_dirty = false; + } + read_sector(DIR_TRACK, 0, bam); +} + +// NEW:name,id +// ^ ^ +// name comma (or NULL) +void ImageDrive::new_cmd(const uint8 *name, int name_len, const uint8 *comma) +{ + // Check for write-protection + if (write_protected) { + set_error(ERR_WRITEPROTECT); + return; + } + + // Remember current ID + uint8 id1 = bam[BAM_DISK_ID], id2 = bam[BAM_DISK_ID + 1]; + + // Formatting with ID? + if (comma) { + + close_all_channels(); + + // Clear BAM buffer + memset(bam, 0, 256); + + // Get ID from command + if (comma[1]) { + id1 = comma[1]; + id2 = comma[2] ? comma[2] : ' '; + } else { + id1 = id2 = ' '; + } + } + + // Format disk image + format_image(the_file, desc, comma, id1, id2, name, name_len); + + // Re-read BAM + read_sector(DIR_TRACK, 0, bam); + bam_dirty = false; +} + +// VALIDATE +void ImageDrive::validate_cmd(void) +{ + // Backup of old BAM in case something goes amiss + uint8 old_bam[256]; + memcpy(old_bam, bam, 256); + + // Clear BAM + clear_bam(bam); + bam_dirty = true; + + // Allocate BAM and directory + if (!alloc_block_chain(DIR_TRACK, 0)) { + memcpy(bam, old_bam, 256); + return; + } + + // Allocate all file data and side sector blocks + int dir_track, dir_sector, entry; + if (find_first_file((uint8 *)"*", 1, dir_track, dir_sector, entry)) { + do { + uint8 *de = dir + DIR_ENTRIES + entry * SIZEOF_DE; + + if (de[DE_TYPE] & 0x80) { + // Closed file, allocate all file data and side sector blocks + if (!alloc_block_chain(de[DE_TRACK], de[DE_SECTOR]) || !alloc_block_chain(de[DE_SIDE_TRACK], de[DE_SIDE_SECTOR])) { + memcpy(bam, old_bam, 256); + return; + } + } else { + // Open file, delete it + de[DE_TYPE] = 0; + write_sector(dir_track, dir_sector, dir); + } + } while (find_next_file((uint8 *)"*", 1, dir_track, dir_sector, entry)); + } +} + + +/* + * Check whether file with given header (64 bytes) and size looks like one + * of the file types supported by this module + */ + +static bool is_d64_file(const uint8 *header, long size) +{ + return size == NUM_SECTORS_35 * 256 || size == NUM_SECTORS_35 * 257 + || size == NUM_SECTORS_40 * 256 || size == NUM_SECTORS_40 * 257; +} + +static bool is_ed64_file(const uint8 *header, long size) +{ + // 35-track d64 file with header ID at the end (only used internally for + // converted zipcode files) + return size == NUM_SECTORS_35 * 256 + 2; +} + +static bool is_x64_file(const uint8 *header, long size) +{ + return memcmp(header, "C\x15\x41\x64\x01\x02", 6) == 0; +} + +static bool is_zipcode_file(const char *path) +{ +#if 0 + string base, part; + SplitPath(path, base, part); + return part.length() > 2 && part[0] >= '1' && part[0] <= '4' && part[1] == '!'; +#else + return false; +#endif +} + +bool IsImageFile(const char *path, const uint8 *header, long size) +{ + return is_d64_file(header, size) || is_x64_file(header, size) || is_zipcode_file(path); +} + + +#if 0 +/* + * Convert zipcode file to extended d64 file (d64 file with header ID) + */ + +static FILE *open_zipcode_file(FILE *old, int num, const string &base, string &part, uint8 &id1, uint8 &id2) +{ + if (old) + fclose(old); + part[0] = num + '1'; + FILE *f = fopen(AddToPath(base, part).c_str(), "rb"); + if (f == NULL) + return NULL; + if (fseek(f, 2, SEEK_SET) < 0) { + fclose(f); + return NULL; + } + if (num == 0) { + id1 = getc(f); + id2 = getc(f); + } + return f; +} + +static FILE *convert_zipcode_to_ed64(const string &path) +{ + FILE *in = NULL, *out = NULL; + uint8 id1, id2; + + // Split input file name + string base, part; + SplitPath(path, base, part); + + // Open output file + out = tmpfile(); + if (out == NULL) + goto error; + + // Decode all tracks + for (int track=1; track<=35; track++) { + int max_sect = 17 + ((track < 31) ? 1 : 0) + ((track < 25) ? 1 : 0) + ((track < 18) ? 2 : 0); + + // Select appropriate input file + switch (track) { + case 1: + if ((in = open_zipcode_file(NULL, 0, base, part, id1, id2)) == NULL) + goto error; + break; + case 9: + if ((in = open_zipcode_file(in, 1, base, part, id1, id2)) == NULL) + goto error; + break; + case 17: + if ((in = open_zipcode_file(in, 2, base, part, id1, id2)) == NULL) + goto error; + break; + case 26: + if ((in = open_zipcode_file(in, 3, base, part, id1, id2)) == NULL) + goto error; + break; + } + + // Clear "sector read" flags + bool sect_flag[21]; + for (int i=0; i= max_sect || sect_flag[s] || feof(in)) + goto error; + sect_flag[s] = true; + uint8 *p = act_track + s * 256; + + // Uncompress sector + if (t & 0x80) { + // Run-length encoded sector + uint8 len = getc(in); + uint8 rep = getc(in); + int count = 0; + for (int j=0; j 40) + return false; + + // Read header ID from BAM (use error_info as buffer) + fseek(f, desc.header_size + accum_num_sectors[18] * 256, SEEK_SET); + fread(desc.error_info, 1, 256, f); + desc.id1 = desc.error_info[BAM_DISK_ID]; + desc.id2 = desc.error_info[BAM_DISK_ID + 1]; + + // .x64 files have no error info + memset(desc.error_info, 1, sizeof(desc.error_info)); + desc.has_error_info = false; + return true; +} + +static bool parse_image_file(FILE *f, image_file_desc &desc) +{ + // Read header + uint8 header[64]; + fread(header, 1, sizeof(header), f); + + // Determine file size + fseek(f, 0, SEEK_END); + long size = ftell(f); + + // Determine file type and fill in image_file_desc structure + if (is_x64_file(header, size)) + return parse_x64_file(f, desc); + else if (is_d64_file(header, size)) + return parse_d64_file(f, desc, false); + else if (is_ed64_file(header, size)) + return parse_d64_file(f, desc, true); + else + return false; +} + + +/* + * Read directory of disk image file into (empty) c64_dir_entry vector, + * returns false on error + */ + +bool ReadImageDirectory(const char *path, vector &vec) +{ + bool result = false; + + // Open file + FILE *f = open_image_file(path, false); + if (f) { + int num_dir_blocks = 0; + + // Determine file type and fill in image_file_desc structure + image_file_desc desc; + if (!parse_image_file(f, desc)) + goto done; + + // Scan all directory blocks + uint8 dir[256]; + dir[DIR_NEXT_TRACK] = DIR_TRACK; + dir[DIR_NEXT_SECTOR] = 1; + + while (dir[DIR_NEXT_TRACK] && num_dir_blocks < num_sectors[DIR_TRACK]) { + if (read_sector(f, desc, dir[DIR_NEXT_TRACK], dir[DIR_NEXT_SECTOR], dir) != ERR_OK) + break; + num_dir_blocks++; + + // Scan all 8 entries of a block + uint8 *de = dir + DIR_ENTRIES; + for (int j=0; j<8; j++, de+=SIZEOF_DE) { + + // Skip empty entries + if (de[DE_TYPE] == 0) + continue; + + // Convert file name (strip everything after and including the first trailing space) + uint8 name_buf[17]; + memcpy(name_buf, de + DE_NAME, 16); + name_buf[16] = 0; + uint8 *p = (uint8 *)memchr(name_buf, 0xa0, 16); + if (p) + *p = 0; + + // Convert file type + int type = de[DE_TYPE] & 7; + if (type > 4) + type = FTYPE_UNKNOWN; + + // Read start address + uint8 sa_lo = 0, sa_hi = 0; + uint8 buf[256]; + if (read_sector(f, desc, de[DE_TRACK], de[DE_SECTOR], buf) == ERR_OK) { + sa_lo = buf[2]; + sa_hi = buf[3]; + } + + // Add entry + vec.push_back(c64_dir_entry(name_buf, type, !(de[DE_TYPE] & 0x80), de[DE_TYPE] & 0x40, ((de[DE_NUM_BLOCKS_H] << 8) + de[DE_NUM_BLOCKS_L]) * 254, 0, sa_lo, sa_hi)); + } + } + + result = true; +done: fclose(f); + } + return result; +} + + +/* + * Create new blank disk image file, returns false on error + */ + +bool CreateImageFile(const char *path) +{ + // Open file for writing + FILE *f = fopen(path, "wb"); + if (f == NULL) + return false; + + // Create descriptor + image_file_desc desc; + desc.type = TYPE_D64; + desc.header_size = 0; + desc.num_tracks = 35; + desc.id1 = 'F'; + desc.id1 = 'R'; + memset(desc.error_info, 1, sizeof(desc.error_info)); + desc.has_error_info = false; + + // Format image file + if (!format_image(f, desc, true, 'F', 'R', (uint8 *)"D64 FILE", 8)) { + fclose(f); + remove(path); + return false; + } + + // Close file + fclose(f); + return true; +} diff --git a/Src/1541d64.h b/Src/1541d64.h new file mode 100644 index 0000000..29a1327 --- /dev/null +++ b/Src/1541d64.h @@ -0,0 +1,152 @@ +/* + * 1541d64.h - 1541 emulation in disk image files (.d64/.x64/zipcode) + * + * Frodo (C) 1994-1997,2002-2005 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _1541D64_H +#define _1541D64_H + +#include "IEC.h" + + +/* + * Definitions + */ + +// Constants +const int NUM_SECTORS_35 = 683; // Number of sectors in a 35-track image +const int NUM_SECTORS_40 = 768; // Number of sectors in a 40-track image + +// Disk image types +enum { + TYPE_D64, // D64 file + TYPE_ED64, // Converted zipcode file (D64 with header ID) + TYPE_X64 // x64 file +}; + +// Channel descriptor +struct channel_desc { + int mode; // Channel mode + bool writing; // Flag: writing to file (for file channels) + int buf_num; // Buffer number for direct access and file channels + uint8 *buf; // Pointer to start of buffer + uint8 *buf_ptr; // Pointer to current position in buffer + int buf_len; // Remaining bytes in buffer + int track, sector; // Track and sector the buffer will be written to (for writing to file channels) + int num_blocks; // Number of blocks in file (for writing to file channels) + int dir_track; // Track... + int dir_sector; // ...and sector of directory block containing file entry + int entry; // Number of entry in directory block +}; + +// Disk image file descriptor +struct image_file_desc { + int type; // See definitions above + int header_size; // Size of file header + int num_tracks; // Number of tracks + uint8 id1, id2; // Block header ID (as opposed to BAM ID) + uint8 error_info[NUM_SECTORS_40]; // Sector error information (1 byte/sector) + bool has_error_info; // Flag: error info present in file +}; + +// Disk image drive class +class ImageDrive : public Drive { +public: + ImageDrive(IEC *iec, const char *filepath); + virtual ~ImageDrive(); + + virtual uint8 Open(int channel, const uint8 *name, int name_len); + virtual uint8 Close(int channel); + virtual uint8 Read(int channel, uint8 &byte); + virtual uint8 Write(int channel, uint8 byte, bool eoi); + virtual void Reset(void); + +private: + void close_image(void); + bool change_image(const char *path); + + uint8 open_file(int channel, const uint8 *name, int name_len); + uint8 open_file_ts(int channel, int track, int sector); + uint8 create_file(int channel, const uint8 *name, int name_len, int type, bool overwrite = false); + uint8 open_directory(const uint8 *pattern, int pattern_len); + uint8 open_direct(int channel, const uint8 *filename); + void close_all_channels(void); + + int alloc_buffer(int want); + void free_buffer(int buf); + + bool find_file(const uint8 *pattern, int pattern_len, int &dir_track, int &dir_sector, int &entry, bool cont); + bool find_first_file(const uint8 *pattern, int pattern_len, int &dir_track, int &dir_sector, int &entry); + bool find_next_file(const uint8 *pattern, int pattern_len, int &dir_track, int &dir_sector, int &entry); + bool alloc_dir_entry(int &track, int §or, int &entry); + + bool is_block_free(int track, int sector); + int num_free_blocks(int track); + int alloc_block(int track, int sector); + int free_block(int track, int sector); + bool alloc_block_chain(int track, int sector); + bool free_block_chain(int track, int sector); + bool alloc_next_block(int &track, int §or, int interleave); + + bool read_sector(int track, int sector, uint8 *buffer); + bool write_sector(int track, int sector, uint8 *buffer); + void write_error_info(void); + + virtual void block_read_cmd(int channel, int track, int sector, bool user_cmd = false); + virtual void block_write_cmd(int channel, int track, int sector, bool user_cmd = false); + virtual void block_allocate_cmd(int track, int sector); + virtual void block_free_cmd(int track, int sector); + virtual void buffer_pointer_cmd(int channel, int pos); + virtual void mem_read_cmd(uint16 adr, uint8 len); + virtual void mem_write_cmd(uint16 adr, uint8 len, uint8 *p); + virtual void copy_cmd(const uint8 *new_file, int new_file_len, const uint8 *old_files, int old_files_len); + virtual void rename_cmd(const uint8 *new_file, int new_file_len, const uint8 *old_file, int old_file_len); + virtual void scratch_cmd(const uint8 *files, int files_len); + virtual void initialize_cmd(void); + virtual void new_cmd(const uint8 *name, int name_len, const uint8 *comma); + virtual void validate_cmd(void); + + FILE *the_file; // File pointer for image file + image_file_desc desc; // Image file descriptor + bool write_protected; // Flag: image file write-protected + + uint8 ram[0x800]; // 2k 1541 RAM + uint8 dir[258]; // Buffer for directory blocks + uint8 *bam; // Pointer to BAM in 1541 RAM (buffer 4, upper 256 bytes) + bool bam_dirty; // Flag: BAM modified, needs to be written back + + channel_desc ch[18]; // Descriptors for channels 0..17 (16 = internal read, 17 = internal write) + bool buf_free[4]; // Flags: buffer 0..3 free? +}; + + +/* + * Functions + */ + +// Check whether file with given header (64 bytes) and size looks like one +// of the file types supported by this module +extern bool IsImageFile(const char *path, const uint8 *header, long size); + +// Read directory of disk image file into (empty) c64_dir_entry vector +extern bool ReadImageDirectory(const char *path, vector &vec); + +// Create new blank disk image file +extern bool CreateImageFile(const char *path); + +#endif diff --git a/Src/1541fs.cpp b/Src/1541fs.cpp new file mode 100644 index 0000000..767f81c --- /dev/null +++ b/Src/1541fs.cpp @@ -0,0 +1,604 @@ +/* + * 1541fs.cpp - 1541 emulation in host file system + * + * Frodo (C) 1994-1997,2002-2005 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * Notes: + * ------ + * + * - If the directory is opened (file name "$"), a temporary file + * with the structure of a 1541 directory file is created and + * opened. It can then be accessed in the same way as all other + * files. + * + * Incompatibilities: + * ------------------ + * + * - No "raw" directory reading + * - No relative/sequential/user files + * - Only "I" and "UJ" commands implemented + */ + +#include "sysdeps.h" + +#include "1541fs.h" +#include "IEC.h" +#include "main.h" +#include "Prefs.h" + +#ifdef __riscos__ +#include "ROlib.h" +#endif + + +// Prototypes +static bool match(const char *p, const char *n); + + +/* + * Constructor: Prepare emulation + */ + +FSDrive::FSDrive(IEC *iec, const char *path) : Drive(iec) +{ + strcpy(orig_dir_path, path); + dir_path[0] = 0; + + if (change_dir(orig_dir_path)) { + for (int i=0; i<16; i++) + file[i] = NULL; + + Reset(); + + Ready = true; + } +} + + +/* + * Destructor + */ + +FSDrive::~FSDrive() +{ + if (Ready) { + close_all_channels(); + Ready = false; + } +} + + +/* + * Change emulation directory + */ + +bool FSDrive::change_dir(char *dirpath) +{ +#ifndef __riscos__ + DIR *dir; + + if ((dir = opendir(dirpath)) != NULL) { + closedir(dir); + strcpy(dir_path, dirpath); + strncpy(dir_title, dir_path, 16); + return true; + } else + return false; +#else + int Info[4]; + + if ((ReadCatalogueInfo(dirpath,Info) & 2) != 0) { // Directory or image file + strcpy(dir_path, dirpath); + strncpy(dir_title, dir_path, 16); + return true; + } else + return false; +#endif +} + + +/* + * Open channel + */ + +uint8 FSDrive::Open(int channel, const uint8 *name, int name_len) +{ + set_error(ERR_OK); + + // Channel 15: Execute file name as command + if (channel == 15) { + execute_cmd(name, name_len); + return ST_OK; + } + + // Close previous file if still open + if (file[channel]) { + fclose(file[channel]); + file[channel] = NULL; + } + + if (name[0] == '#') { + set_error(ERR_NOCHANNEL); + return ST_OK; + } + + if (name[0] == '$') + return open_directory(channel, name + 1, name_len - 1); + + return open_file(channel, name, name_len); +} + + +/* + * Open file + */ + +uint8 FSDrive::open_file(int channel, const uint8 *name, int name_len) +{ + char plain_name[NAMEBUF_LENGTH]; + int plain_name_len; + int mode = FMODE_READ; + int type = FTYPE_PRG; + int rec_len = 0; + parse_file_name(name, name_len, (uint8 *)plain_name, plain_name_len, mode, type, rec_len, true); + + // Channel 0 is READ, channel 1 is WRITE + if (channel == 0 || channel == 1) { + mode = channel ? FMODE_WRITE : FMODE_READ; + if (type == FTYPE_DEL) + type = FTYPE_PRG; + } + + bool writing = (mode == FMODE_WRITE || mode == FMODE_APPEND); + + // Expand wildcards (only allowed on reading) + if (strchr(plain_name, '*') || strchr(plain_name, '?')) { + if (writing) { + set_error(ERR_SYNTAX33); + return ST_OK; + } else + find_first_file(plain_name); + } + + // Relative files are not supported + if (type == FTYPE_REL) { + set_error(ERR_UNIMPLEMENTED); + return ST_OK; + } + + // Select fopen() mode according to file mode + const char *mode_str = "rb"; + switch (mode) { + case FMODE_WRITE: + mode_str = "wb"; + break; + case FMODE_APPEND: + mode_str = "ab"; + break; + } + + // Open file +#ifndef __riscos__ + if (chdir(dir_path)) + set_error(ERR_NOTREADY); + else if ((file[channel] = fopen(plain_name, mode_str)) != NULL) { + if (mode == FMODE_READ || mode == FMODE_M) // Read and buffer first byte + read_char[channel] = fgetc(file[channel]); + } else + set_error(ERR_FILENOTFOUND); + chdir(AppDirPath); +#else + { + char fullname[NAMEBUF_LENGTH]; + + // On RISC OS make a full filename + sprintf(fullname,"%s.%s",dir_path,plain_name); + if ((file[channel] = fopen(fullname, mode)) != NULL) + { + if (mode == FMODE_READ || mode == FMODE_M) + { + read_char[channel] = fgetc(file[channel]); + } + } + else + { + set_error(ERR_FILENOTFOUND); + } + } +#endif + + return ST_OK; +} + + +/* + * Find first file matching wildcard pattern and get its real name + */ + +// Return true if name 'n' matches pattern 'p' +static bool match(const char *p, const char *n) +{ + if (!*p) // Null pattern matches everything + return true; + + do { + if (*p == '*') // Wildcard '*' matches all following characters + return true; + if ((*p != *n) && (*p != '?')) // Wildcard '?' matches single character + return false; + p++; n++; + } while (*p); + + return !*n; +} + +void FSDrive::find_first_file(char *pattern) +{ +#ifndef __riscos__ + DIR *dir; + struct dirent *de; + + // Open directory for reading and skip '.' and '..' + if ((dir = opendir(dir_path)) == NULL) + return; + de = readdir(dir); + while (de && (0 == strcmp(".", de->d_name) || 0 == strcmp("..", de->d_name))) + de = readdir(dir); + + while (de) { + + // Match found? Then copy real file name + if (match(pattern, de->d_name)) { + strncpy(pattern, de->d_name, NAMEBUF_LENGTH); + closedir(dir); + return; + } + + // Get next directory entry + de = readdir(dir); + } + + closedir(dir); +#else + dir_env de; + char Buffer[NAMEBUF_LENGTH]; + + de.offset = 0; de.buffsize = NAMEBUF_LENGTH; de.match = name; + do { + de.readno = 1; + if (ReadDirName(dir_path,Buffer,&de) != NULL) + de.offset = -1; + else if (de.offset != -1 && match(name,Buffer)) { + strncpy(name, Buffer, NAMEBUF_LENGTH); + return; + } + } while (de.readno > 0); +#endif +} + + +/* + * Open directory, create temporary file + */ + +uint8 FSDrive::open_directory(int channel, const uint8 *pattern, int pattern_len) +{ + char buf[] = "\001\004\001\001\0\0\022\042 \042 00 2A"; + char str[NAMEBUF_LENGTH]; + char *p, *q; + int i; + int filemode; + int filetype; + bool wildflag; + +#ifndef __riscos__ + DIR *dir; + struct dirent *de; + struct stat statbuf; + + // Special treatment for "$0" + if (pattern[0] == '0' && pattern[1] == 0) { + pattern++; + pattern_len--; + } + + // Skip everything before the ':' in the pattern + uint8 *t = (uint8 *)memchr(pattern, ':', pattern_len); + if (t) + pattern = t + 1; + + // Convert pattern to ASCII + char ascii_pattern[NAMEBUF_LENGTH]; + petscii2ascii(ascii_pattern, pattern, NAMEBUF_LENGTH); + + // Open directory for reading and skip '.' and '..' + if ((dir = opendir(dir_path)) == NULL) { + set_error(ERR_NOTREADY); + return ST_OK; + } + de = readdir(dir); + while (de && (0 == strcmp(".", de->d_name) || 0 == strcmp("..", de->d_name))) + de = readdir(dir); + + // Create temporary file + if ((file[channel] = tmpfile()) == NULL) { + closedir(dir); + return ST_OK; + } + + // Create directory title + p = &buf[8]; + for (i=0; i<16 && dir_title[i]; i++) + *p++ = ascii2petscii(dir_title[i]); + fwrite(buf, 1, 32, file[channel]); + + // Create and write one line for every directory entry + while (de) { + + // Include only files matching the ascii_pattern + if (match(ascii_pattern, de->d_name)) { + + // Get file statistics + chdir(dir_path); + stat(de->d_name, &statbuf); + chdir(AppDirPath); + + // Clear line with spaces and terminate with null byte + memset(buf, ' ', 31); + buf[31] = 0; + + p = buf; + *p++ = 0x01; // Dummy line link + *p++ = 0x01; + + // Calculate size in blocks (254 bytes each) + i = (statbuf.st_size + 254) / 254; + *p++ = i & 0xff; + *p++ = (i >> 8) & 0xff; + + p++; + if (i < 10) p++; // Less than 10: add one space + if (i < 100) p++; // Less than 100: add another space + + // Convert and insert file name + strcpy(str, de->d_name); + *p++ = '\"'; + q = p; + for (i=0; i<16 && str[i]; i++) + *q++ = ascii2petscii(str[i]); + *q++ = '\"'; + p += 18; + + // File type + if (S_ISDIR(statbuf.st_mode)) { + *p++ = 'D'; + *p++ = 'I'; + *p++ = 'R'; + } else { + *p++ = 'P'; + *p++ = 'R'; + *p++ = 'G'; + } + + // Write line + fwrite(buf, 1, 32, file[channel]); + } + + // Get next directory entry + de = readdir(dir); + } +#else + dir_full_info di; + dir_env de; + unsigned char c; + + // Much of this is very similar to the original + if ((pattern[0] == '0') && (pattern[1] == 0)) {pattern++;} + + // Concatenate dir_path and ascii_pattern in buffer ascii_pattern ==> read subdirs! + strcpy(ascii_pattern,dir_path); i = strlen(ascii_pattern); ascii_pattern[i++] = '.'; ascii_pattern[i] = 0; + convert_filename(pattern, ascii_pattern + i, &filemode, &filetype, &wildflag); + p = ascii_pattern + i; q = p; + do {c = *q++; if (c == '.') p = q;} while (c >= 32); + *(p-1) = 0; // separate directory-path and ascii_pattern + if ((uint8)(*p) < 32) {*p = '*'; *(p+1) = 0;} + + // We don't use tmpfile() -- problems involved! + DeleteFile(RO_TEMPFILE); // first delete it, if it exists + if ((file[channel] = fopen(RO_TEMPFILE,"wb+")) == NULL) + return(ST_OK); + de.offset = 0; de.buffsize = NAMEBUF_LENGTH; de.match = p; + + // Create directory title - copied from above + p = &buf[8]; + for (i=0; i<16 && dir_title[i]; i++) + *p++ = conv_to_64(dir_title[i], false); + fwrite(buf, 1, 32, file[channel]); + + do { + de.readno = 1; + if (ReadDirNameInfo(ascii_pattern,&di,&de) != NULL) + de.offset = -1; + else if (de.readno > 0) { // don't have to check for match here + memset(buf,' ',31); buf[31] = 0; // most of this: see above + p = buf; *p++ = 0x01; *p++ = 0x01; + i = (di.length + 254) / 254; *p++ = i & 0xff; *p++ = (i>>8) & 0xff; + p++; + if (i < 10) + *p++ = ' '; + if (i < 100) + *p++ = ' '; + strcpy(str, di.name); + *p++ = '\"'; q = p; + for (i=0; (i<16 && str[i]); i++) + *q++ = conv_to_64(str[i], true); + *q++ = '\"'; p += 18; + if ((di.otype & 2) == 0) { + *p++ = 'P'; *p++ = 'R'; *p++ = 'G'; + } else { + *p++ = 'D'; *p++ = 'I'; *p++ = 'R'; + } + fwrite(buf, 1, 32, file[channel]); + } + } while (de.offset != -1); +#endif + + // Final line + fwrite("\001\001\0\0BLOCKS FREE. \0\0", 1, 32, file[channel]); + + // Rewind file for reading and read first byte + rewind(file[channel]); + read_char[channel] = fgetc(file[channel]); + +#ifndef __riscos + // Close directory + closedir(dir); +#endif + + return ST_OK; +} + + +/* + * Close channel + */ + +uint8 FSDrive::Close(int channel) +{ + if (channel == 15) { + close_all_channels(); + return ST_OK; + } + + if (file[channel]) { + fclose(file[channel]); + file[channel] = NULL; + } + + return ST_OK; +} + + +/* + * Close all channels + */ + +void FSDrive::close_all_channels(void) +{ + for (int i=0; i<15; i++) + Close(i); + + cmd_len = 0; +} + + +/* + * Read from channel + */ + +uint8 FSDrive::Read(int channel, uint8 &byte) +{ + int c; + + // Channel 15: Error channel + if (channel == 15) { + byte = *error_ptr++; + + if (byte != '\r') + return ST_OK; + else { // End of message + set_error(ERR_OK); + return ST_EOF; + } + } + + if (!file[channel]) return ST_READ_TIMEOUT; + + // Read one byte + byte = read_char[channel]; + c = fgetc(file[channel]); + if (c == EOF) + return ST_EOF; + else { + read_char[channel] = c; + return ST_OK; + } +} + + +/* + * Write to channel + */ + +uint8 FSDrive::Write(int channel, uint8 byte, bool eoi) +{ + // Channel 15: Collect chars and execute command on EOI + if (channel == 15) { + if (cmd_len >= 58) + return ST_TIMEOUT; + + cmd_buf[cmd_len++] = byte; + + if (eoi) { + execute_cmd(cmd_buf, cmd_len); + cmd_len = 0; + } + return ST_OK; + } + + if (!file[channel]) { + set_error(ERR_FILENOTOPEN); + return ST_TIMEOUT; + } + + if (putc(byte, file[channel]) == EOF) { + set_error(ERR_WRITE25); + return ST_TIMEOUT; + } + + return ST_OK; +} + + +/* + * Execute drive commands + */ + +// INITIALIZE +void FSDrive::initialize_cmd(void) +{ + close_all_channels(); +} + +// VALIDATE +void FSDrive::validate_cmd(void) +{ +} + + +/* + * Reset drive + */ + +void FSDrive::Reset(void) +{ + close_all_channels(); + cmd_len = 0; + set_error(ERR_STARTUP); +} diff --git a/Src/1541fs.h b/Src/1541fs.h new file mode 100644 index 0000000..9688547 --- /dev/null +++ b/Src/1541fs.h @@ -0,0 +1,57 @@ +/* + * 1541fs.h - 1541 emulation in host file system + * + * Frodo (C) 1994-1997,2002-2005 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _1541FS_H +#define _1541FS_H + +#include "IEC.h" + + +class FSDrive : public Drive { +public: + FSDrive(IEC *iec, const char *path); + virtual ~FSDrive(); + + virtual uint8 Open(int channel, const uint8 *name, int name_len); + virtual uint8 Close(int channel); + virtual uint8 Read(int channel, uint8 &byte); + virtual uint8 Write(int channel, uint8 byte, bool eoi); + virtual void Reset(void); + +private: + bool change_dir(char *dirpath); + + uint8 open_file(int channel, const uint8 *name, int name_len); + uint8 open_directory(int channel, const uint8 *pattern, int pattern_len); + void find_first_file(char *pattern); + void close_all_channels(void); + + virtual void initialize_cmd(void); + virtual void validate_cmd(void); + + char dir_path[256]; // Path to directory + char orig_dir_path[256]; // Original directory path + char dir_title[16]; // Directory title + FILE *file[16]; // File pointers for each of the 16 channels + + uint8 read_char[16]; // Buffers for one-byte read-ahead +}; + +#endif diff --git a/Src/1541job.cpp b/Src/1541job.cpp new file mode 100644 index 0000000..548660b --- /dev/null +++ b/Src/1541job.cpp @@ -0,0 +1,476 @@ +/* + * 1541job.cpp - Emulation of 1541 GCR disk reading/writing + * + * Frodo (C) 1994-1997,2002-2005 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * Notes: + * ------ + * + * - This is only used for processor-level 1541 emulation. + * It simulates the 1541 disk controller hardware (R/W head, + * GCR reading/writing). + * - The preferences settings for drive 8 are used to + * specify the .d64 file + * + * Incompatibilities: + * ------------------ + * + * - No GCR writing possible (WriteSector is a ROM patch) + * - Programs depending on the exact timing of head movement/disk + * rotation don't work + * - The .d64 error info is unused + */ + +#include "sysdeps.h" + +#include "1541job.h" +#include "CPU1541.h" +#include "Prefs.h" + + +// Number of tracks/sectors +const int NUM_TRACKS = 35; +const int NUM_SECTORS = 683; + +// Size of GCR encoded data +const int GCR_SECTOR_SIZE = 1+10+9+1+325+8; // SYNC Header Gap SYNC Data Gap (should be 5 SYNC bytes each) +const int GCR_TRACK_SIZE = GCR_SECTOR_SIZE * 21; // Each track in gcr_data has 21 sectors +const int GCR_DISK_SIZE = GCR_TRACK_SIZE * NUM_TRACKS; + +// Job return codes +const int RET_OK = 1; // No error +const int RET_NOT_FOUND = 2; // Block not found +const int RET_NOT_READY = 15; // Drive not ready + + +// Number of sectors of each track +const int num_sectors[36] = { + 0, + 21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21, + 19,19,19,19,19,19,19, + 18,18,18,18,18,18, + 17,17,17,17,17 +}; + +// Sector offset of start of track in .d64 file +const int sector_offset[36] = { + 0, + 0,21,42,63,84,105,126,147,168,189,210,231,252,273,294,315,336, + 357,376,395,414,433,452,471, + 490,508,526,544,562,580, + 598,615,632,649,666 +}; + + +/* + * Constructor: Open .d64 file if processor-level 1541 + * emulation is enabled + */ + +Job1541::Job1541(uint8 *ram1541) : ram(ram1541) +{ + the_file = NULL; + + gcr_data = gcr_ptr = gcr_track_start = new uint8[GCR_DISK_SIZE]; + gcr_track_end = gcr_track_start + GCR_TRACK_SIZE; + current_halftrack = 2; + + disk_changed = true; + + if (ThePrefs.Emul1541Proc) + open_d64_file(ThePrefs.DrivePath[0]); +} + + +/* + * Destructor: Close .d64 file + */ + +Job1541::~Job1541() +{ + close_d64_file(); + delete[] gcr_data; +} + + +/* + * Preferences may have changed + */ + +void Job1541::NewPrefs(Prefs *prefs) +{ + // 1541 emulation turned off? + if (!prefs->Emul1541Proc) + close_d64_file(); + + // 1541 emulation turned on? + else if (!ThePrefs.Emul1541Proc && prefs->Emul1541Proc) + open_d64_file(prefs->DrivePath[0]); + + // .d64 file name changed? + else if (strcmp(ThePrefs.DrivePath[0], prefs->DrivePath[0])) { + close_d64_file(); + open_d64_file(prefs->DrivePath[0]); + disk_changed = true; + } +} + + +/* + * Open .d64 file + */ + +void Job1541::open_d64_file(char *filepath) +{ + long size; + uint8 magic[4]; + uint8 bam[256]; + + // Clear GCR buffer + memset(gcr_data, 0x55, GCR_DISK_SIZE); + + // Try opening the file for reading/writing first, then for reading only + write_protected = false; + the_file = fopen(filepath, "rb+"); + if (the_file == NULL) { + write_protected = true; + the_file = fopen(filepath, "rb"); + } + if (the_file != NULL) { + + // Check length + fseek(the_file, 0, SEEK_END); + if ((size = ftell(the_file)) < NUM_SECTORS * 256) { + fclose(the_file); + the_file = NULL; + return; + } + + // x64 image? + fseek(the_file, 0, SEEK_SET); + fread(&magic, 4, 1, the_file); + if (magic[0] == 0x43 && magic[1] == 0x15 && magic[2] == 0x41 && magic[3] == 0x64) + image_header = 64; + else + image_header = 0; + + // Preset error info (all sectors no error) + memset(error_info, 1, NUM_SECTORS); + + // Load sector error info from .d64 file, if present + if (!image_header && size == NUM_SECTORS * 257) { + fseek(the_file, NUM_SECTORS * 256, SEEK_SET); + fread(&error_info, NUM_SECTORS, 1, the_file); + }; + + // Read BAM and get ID + read_sector(18, 0, bam); + id1 = bam[162]; + id2 = bam[163]; + + // Create GCR encoded disk data from image + disk2gcr(); + } +} + + +/* + * Close .d64 file + */ + +void Job1541::close_d64_file(void) +{ + if (the_file != NULL) { + fclose(the_file); + the_file = NULL; + } +} + + +/* + * Write sector to disk (1541 ROM patch) + */ + +void Job1541::WriteSector(void) +{ + int track = ram[0x18]; + int sector = ram[0x19]; + uint16 buf = ram[0x30] | (ram[0x31] << 8); + + if (buf <= 0x0700) + if (write_sector(track, sector, ram + buf)) + sector2gcr(track, sector); +} + + +/* + * Format one track (1541 ROM patch) + */ + +void Job1541::FormatTrack(void) +{ + int track = ram[0x51]; + + // Get new ID + uint8 bufnum = ram[0x3d]; + id1 = ram[0x12 + bufnum]; + id2 = ram[0x13 + bufnum]; + + // Create empty block + uint8 buf[256]; + memset(buf, 1, 256); + buf[0] = 0x4b; + + // Write block to all sectors on track + for(int sector=0; sector GCR + * true: success, false: error + */ + +bool Job1541::write_sector(int track, int sector, uint8 *buffer) +{ + int offset; + + // Convert track/sector to byte offset in file + if ((offset = offset_from_ts(track, sector)) < 0) + return false; + +#ifdef AMIGA + if (offset != ftell(the_file)) + fseek(the_file, offset + image_header, SEEK_SET); +#else + fseek(the_file, offset + image_header, SEEK_SET); +#endif + fwrite(buffer, 256, 1, the_file); + return true; +} + + +/* + * Convert track/sector to offset + */ + +int Job1541::secnum_from_ts(int track, int sector) +{ + return sector_offset[track] + sector; +} + +int Job1541::offset_from_ts(int track, int sector) +{ + if ((track < 1) || (track > NUM_TRACKS) || + (sector < 0) || (sector >= num_sectors[track])) + return -1; + + return (sector_offset[track] + sector) << 8; +} + + +/* + * Convert 4 bytes to 5 GCR encoded bytes + */ + +const uint16 gcr_table[16] = { + 0x0a, 0x0b, 0x12, 0x13, 0x0e, 0x0f, 0x16, 0x17, + 0x09, 0x19, 0x1a, 0x1b, 0x0d, 0x1d, 0x1e, 0x15 +}; + +void Job1541::gcr_conv4(uint8 *from, uint8 *to) +{ + uint16 g; + + g = (gcr_table[*from >> 4] << 5) | gcr_table[*from & 15]; + *to++ = g >> 2; + *to = (g << 6) & 0xc0; + from++; + + g = (gcr_table[*from >> 4] << 5) | gcr_table[*from & 15]; + *to++ |= (g >> 4) & 0x3f; + *to = (g << 4) & 0xf0; + from++; + + g = (gcr_table[*from >> 4] << 5) | gcr_table[*from & 15]; + *to++ |= (g >> 6) & 0x0f; + *to = (g << 2) & 0xfc; + from++; + + g = (gcr_table[*from >> 4] << 5) | gcr_table[*from & 15]; + *to++ |= (g >> 8) & 0x03; + *to = g; +} + + +/* + * Create GCR encoded disk data from image + */ + +void Job1541::sector2gcr(int track, int sector) +{ + uint8 block[256]; + uint8 buf[4]; + uint8 *p = gcr_data + (track-1) * GCR_TRACK_SIZE + sector * GCR_SECTOR_SIZE; + + read_sector(track, sector, block); + + // Create GCR header + *p++ = 0xff; // SYNC + buf[0] = 0x08; // Header mark + buf[1] = sector ^ track ^ id2 ^ id1; // Checksum + buf[2] = sector; + buf[3] = track; + gcr_conv4(buf, p); + buf[0] = id2; + buf[1] = id1; + buf[2] = 0x0f; + buf[3] = 0x0f; + gcr_conv4(buf, p+5); + p += 10; + memset(p, 0x55, 9); // Gap + p += 9; + + // Create GCR data + uint8 sum; + *p++ = 0xff; // SYNC + buf[0] = 0x07; // Data mark + sum = buf[1] = block[0]; + sum ^= buf[2] = block[1]; + sum ^= buf[3] = block[2]; + gcr_conv4(buf, p); + p += 5; + for (int i=3; i<255; i+=4) { + sum ^= buf[0] = block[i]; + sum ^= buf[1] = block[i+1]; + sum ^= buf[2] = block[i+2]; + sum ^= buf[3] = block[i+3]; + gcr_conv4(buf, p); + p += 5; + } + sum ^= buf[0] = block[255]; + buf[1] = sum; // Checksum + buf[2] = 0; + buf[3] = 0; + gcr_conv4(buf, p); + p += 5; + memset(p, 0x55, 8); // Gap +} + +void Job1541::disk2gcr(void) +{ + // Convert all tracks and sectors + for (int track=1; track<=NUM_TRACKS; track++) + for(int sector=0; sector> 1) - 1) * GCR_TRACK_SIZE; + gcr_track_end = gcr_track_start + num_sectors[current_halftrack >> 1] * GCR_SECTOR_SIZE; +} + + +/* + * Move R/W head in (higher track numbers) + */ + +void Job1541::MoveHeadIn(void) +{ + if (current_halftrack == NUM_TRACKS*2) + return; + current_halftrack++; +#ifndef __riscos__ + printf("Head move %d\n", current_halftrack); +#endif + gcr_ptr = gcr_track_start = gcr_data + ((current_halftrack >> 1) - 1) * GCR_TRACK_SIZE; + gcr_track_end = gcr_track_start + num_sectors[current_halftrack >> 1] * GCR_SECTOR_SIZE; +} + + +/* + * Get state + */ + +void Job1541::GetState(Job1541State *state) +{ + state->current_halftrack = current_halftrack; + state->gcr_ptr = gcr_ptr - gcr_data; + state->write_protected = write_protected; + state->disk_changed = disk_changed; +} + + +/* + * Set state + */ + +void Job1541::SetState(Job1541State *state) +{ + current_halftrack = state->current_halftrack; + gcr_ptr = gcr_data + state->gcr_ptr; + gcr_track_start = gcr_data + ((current_halftrack >> 1) - 1) * GCR_TRACK_SIZE; + gcr_track_end = gcr_track_start + num_sectors[current_halftrack >> 1] * GCR_SECTOR_SIZE; + write_protected = state->write_protected; + disk_changed = state->disk_changed; +} diff --git a/Src/1541job.h b/Src/1541job.h new file mode 100644 index 0000000..9150783 --- /dev/null +++ b/Src/1541job.h @@ -0,0 +1,126 @@ +/* + * 1541job.h - Emulation of 1541 GCR disk reading/writing + * + * Frodo (C) 1994-1997,2002-2005 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _1541JOB_H +#define _1541JOB_H + + +class MOS6502_1541; +class Prefs; +struct Job1541State; + +class Job1541 { +public: + Job1541(uint8 *ram1541); + ~Job1541(); + + void GetState(Job1541State *state); + void SetState(Job1541State *state); + void NewPrefs(Prefs *prefs); + void MoveHeadOut(void); + void MoveHeadIn(void); + bool SyncFound(void); + uint8 ReadGCRByte(void); + uint8 WPState(void); + void WriteSector(void); + void FormatTrack(void); + +private: + void open_d64_file(char *filepath); + void close_d64_file(void); + bool read_sector(int track, int sector, uint8 *buffer); + bool write_sector(int track, int sector, uint8 *buffer); + void format_disk(void); + int secnum_from_ts(int track, int sector); + int offset_from_ts(int track, int sector); + void gcr_conv4(uint8 *from, uint8 *to); + void sector2gcr(int track, int sector); + void disk2gcr(void); + + uint8 *ram; // Pointer to 1541 RAM + FILE *the_file; // File pointer for .d64 file + int image_header; // Length of .d64/.x64 file header + + uint8 id1, id2; // ID of disk + uint8 error_info[683]; // Sector error information (1 byte/sector) + + uint8 *gcr_data; // Pointer to GCR encoded disk data + uint8 *gcr_ptr; // Pointer to GCR data under R/W head + uint8 *gcr_track_start; // Pointer to start of GCR data of current track + uint8 *gcr_track_end; // Pointer to end of GCR data of current track + int current_halftrack; // Current halftrack number (2..70) + + bool write_protected; // Flag: Disk write-protected + bool disk_changed; // Flag: Disk changed (WP sensor strobe control) +}; + +// 1541 GCR state +struct Job1541State { + int current_halftrack; + uint32 gcr_ptr; + bool write_protected; + bool disk_changed; +}; + + +/* + * Check if R/W head is over SYNC + */ + +inline bool Job1541::SyncFound(void) +{ + if (*gcr_ptr == 0xff) + return true; + else { + gcr_ptr++; // Rotate disk + if (gcr_ptr == gcr_track_end) + gcr_ptr = gcr_track_start; + return false; + } +} + + +/* + * Read one GCR byte from disk + */ + +inline uint8 Job1541::ReadGCRByte(void) +{ + uint8 byte = *gcr_ptr++; // Rotate disk + if (gcr_ptr == gcr_track_end) + gcr_ptr = gcr_track_start; + return byte; +} + + +/* + * Return state of write protect sensor + */ + +inline uint8 Job1541::WPState(void) +{ + if (disk_changed) { // Disk change -> WP sensor strobe + disk_changed = false; + return write_protected ? 0x10 : 0; + } else + return write_protected ? 0 : 0x10; +} + +#endif diff --git a/Src/1541t64.cpp b/Src/1541t64.cpp new file mode 100644 index 0000000..5daee25 --- /dev/null +++ b/Src/1541t64.cpp @@ -0,0 +1,749 @@ +/* + * 1541t64.cpp - 1541 emulation in archive-type files (.t64/LYNX/.p00) + * + * Frodo (C) 1994-1997,2002-2005 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * NOTES: + * - This module handles access to files inside (uncompressed) archives + * and makes the archive look like a disk. It supports C64S tape images + * (.t64), C64 LYNX archives and .p00 files. + * - If any file is opened, the contents of the file in the archive file are + * copied into a temporary file which is used for reading. This is done + * to insert the load address. + * + * Incompatibilities: + * - Only read accesses possible + * - No "raw" directory reading + * - No relative/sequential/user files + * - Unimplemented commands: B-P, M-R, M-W, C, S, P, N + * - Impossible to implement: B-R, B-W, B-E, B-A, B-F, M-E + */ + +#include "sysdeps.h" + +#include "1541t64.h" +#include "IEC.h" +#include "Prefs.h" + +#define DEBUG 0 +#include "debug.h" + + +// Prototypes +static bool is_t64_header(const uint8 *header); +static bool is_lynx_header(const uint8 *header); +static bool is_p00_header(const uint8 *header); +static bool parse_t64_file(FILE *f, vector &vec, char *dir_title); +static bool parse_lynx_file(FILE *f, vector &vec, char *dir_title); +static bool parse_p00_file(FILE *f, vector &vec, char *dir_title); + + +/* + * Constructor: Prepare emulation + */ + +ArchDrive::ArchDrive(IEC *iec, const char *filepath) : Drive(iec), the_file(NULL) +{ + for (int i=0; i<16; i++) + file[i] = NULL; + Reset(); + + // Open archive file + if (change_arch(filepath)) + Ready = true; +} + + +/* + * Destructor + */ + +ArchDrive::~ArchDrive() +{ + // Close archive file + if (the_file) { + close_all_channels(); + fclose(the_file); + } + Ready = false; +} + + +/* + * Open the archive file + */ + +bool ArchDrive::change_arch(const char *path) +{ + FILE *new_file; + + // Open new archive file + if ((new_file = fopen(path, "rb")) != NULL) { + + file_info.clear(); + + // Read header, determine archive type and parse archive contents + uint8 header[64]; + fread(header, 1, 64, new_file); + bool parsed_ok = false; + if (is_t64_header(header)) { + archive_type = TYPE_T64; + parsed_ok = parse_t64_file(new_file, file_info, dir_title); + } else if (is_lynx_header(header)) { + archive_type = TYPE_LYNX; + parsed_ok = parse_lynx_file(new_file, file_info, dir_title); + } else if (is_p00_header(header)) { + archive_type = TYPE_P00; + parsed_ok = parse_p00_file(new_file, file_info, dir_title); + } + + if (!parsed_ok) { + fclose(new_file); + if (the_file) { + close_all_channels(); + fclose(the_file); + the_file = NULL; + } + return false; + } + + // Close old archive if open, and set new file + if (the_file) { + close_all_channels(); + fclose(the_file); + the_file = NULL; + } + the_file = new_file; + return true; + } + return false; +} + + +/* + * Open channel + */ + +uint8 ArchDrive::Open(int channel, const uint8 *name, int name_len) +{ + D(bug("ArchDrive::Open channel %d, file %s\n", channel, name)); + + set_error(ERR_OK); + + // Channel 15: Execute file name as command + if (channel == 15) { + execute_cmd(name, name_len); + return ST_OK; + } + + // Close previous file if still open + if (file[channel]) { + fclose(file[channel]); + file[channel] = NULL; + } + + if (name[0] == '#') { + set_error(ERR_NOCHANNEL); + return ST_OK; + } + + if (the_file == NULL) { + set_error(ERR_NOTREADY); + return ST_OK; + } + + if (name[0] == '$') + return open_directory(channel, name + 1, name_len - 1); + + return open_file(channel, name, name_len); +} + + +/* + * Open file + */ + +uint8 ArchDrive::open_file(int channel, const uint8 *name, int name_len) +{ + uint8 plain_name[NAMEBUF_LENGTH]; + int plain_name_len; + int mode = FMODE_READ; + int type = FTYPE_DEL; + int rec_len = 0; + parse_file_name(name, name_len, plain_name, plain_name_len, mode, type, rec_len); + + // Channel 0 is READ, channel 1 is WRITE + if (channel == 0 || channel == 1) { + mode = channel ? FMODE_WRITE : FMODE_READ; + if (type == FTYPE_DEL) + type = FTYPE_PRG; + } + + bool writing = (mode == FMODE_WRITE || mode == FMODE_APPEND); + + // Wildcards are only allowed on reading + if (writing && (strchr((const char *)plain_name, '*') || strchr((const char *)plain_name, '?'))) { + set_error(ERR_SYNTAX33); + return ST_OK; + } + + // Allow only read accesses + if (writing) { + set_error(ERR_WRITEPROTECT); + return ST_OK; + } + + // Relative files are not supported + if (type == FTYPE_REL) { + set_error(ERR_UNIMPLEMENTED); + return ST_OK; + } + + // Find file + int num; + if (find_first_file(plain_name, plain_name_len, num)) { + + // Open temporary file + if ((file[channel] = tmpfile()) != NULL) { + + // Write load address (.t64 only) + if (archive_type == TYPE_T64) { + fwrite(&file_info[num].sa_lo, 1, 1, file[channel]); + fwrite(&file_info[num].sa_hi, 1, 1, file[channel]); + } + + // Copy file contents from archive file to temp file + uint8 *buf = new uint8[file_info[num].size]; + fseek(the_file, file_info[num].offset, SEEK_SET); + fread(buf, file_info[num].size, 1, the_file); + fwrite(buf, file_info[num].size, 1, file[channel]); + rewind(file[channel]); + delete[] buf; + + if (mode == FMODE_READ) // Read and buffer first byte + read_char[channel] = getc(file[channel]); + } + } else + set_error(ERR_FILENOTFOUND); + + return ST_OK; +} + + +/* + * Find first file matching wildcard pattern + */ + +// Return true if name 'n' matches pattern 'p' +static bool match(const uint8 *p, int p_len, const uint8 *n) +{ + while (p_len-- > 0) { + if (*p == '*') // Wildcard '*' matches all following characters + return true; + if ((*p != *n) && (*p != '?')) // Wildcard '?' matches single character + return false; + p++; n++; + } + + return *n == 0; +} + +bool ArchDrive::find_first_file(const uint8 *pattern, int pattern_len, int &num) +{ + vector::const_iterator i, end = file_info.end(); + for (i = file_info.begin(), num = 0; i != end; i++, num++) { + if (match(pattern, pattern_len, (uint8 *)i->name)) + return true; + } + return false; +} + + +/* + * Open directory, create temporary file + */ + +uint8 ArchDrive::open_directory(int channel, const uint8 *pattern, int pattern_len) +{ + // Special treatment for "$0" + if (pattern[0] == '0' && pattern_len == 1) { + pattern++; + pattern_len--; + } + + // Skip everything before the ':' in the pattern + uint8 *t = (uint8 *)memchr(pattern, ':', pattern_len); + if (t) { + t++; + pattern_len -= t - pattern; + pattern = t; + } + + // Create temporary file + if ((file[channel] = tmpfile()) == NULL) + return ST_OK; + + // Create directory title + uint8 buf[] = "\001\004\001\001\0\0\022\042 \042 00 2A"; + for (int i=0; i<16 && dir_title[i]; i++) + buf[i + 8] = dir_title[i]; + fwrite(buf, 1, 32, file[channel]); + + // Create and write one line for every directory entry + vector::const_iterator i, end = file_info.end(); + for (i = file_info.begin(); i != end; i++) { + + // Include only files matching the pattern + if (pattern_len == 0 || match(pattern, pattern_len, (uint8 *)i->name)) { + + // Clear line with spaces and terminate with null byte + memset(buf, ' ', 31); + buf[31] = 0; + + uint8 *p = (uint8 *)buf; + *p++ = 0x01; // Dummy line link + *p++ = 0x01; + + // Calculate size in blocks (254 bytes each) + int n = (i->size + 254) / 254; + *p++ = n & 0xff; + *p++ = (n >> 8) & 0xff; + + p++; + if (n < 10) p++; // Less than 10: add one space + if (n < 100) p++; // Less than 100: add another space + + // Convert and insert file name + *p++ = '\"'; + uint8 *q = p; + for (int j=0; j<16 && i->name[j]; j++) + *q++ = i->name[j]; + *q++ = '\"'; + p += 18; + + // File type + switch (i->type) { + case FTYPE_DEL: + *p++ = 'D'; + *p++ = 'E'; + *p++ = 'L'; + break; + case FTYPE_SEQ: + *p++ = 'S'; + *p++ = 'E'; + *p++ = 'Q'; + break; + case FTYPE_PRG: + *p++ = 'P'; + *p++ = 'R'; + *p++ = 'G'; + break; + case FTYPE_USR: + *p++ = 'U'; + *p++ = 'S'; + *p++ = 'R'; + break; + case FTYPE_REL: + *p++ = 'R'; + *p++ = 'E'; + *p++ = 'L'; + break; + default: + *p++ = '?'; + *p++ = '?'; + *p++ = '?'; + break; + } + + // Write line + fwrite(buf, 1, 32, file[channel]); + } + } + + // Final line + fwrite("\001\001\0\0BLOCKS FREE. \0\0", 1, 32, file[channel]); + + // Rewind file for reading and read first byte + rewind(file[channel]); + read_char[channel] = getc(file[channel]); + + return ST_OK; +} + + +/* + * Close channel + */ + +uint8 ArchDrive::Close(int channel) +{ + D(bug("ArchDrive::Close channel %d\n", channel)); + + if (channel == 15) { + close_all_channels(); + return ST_OK; + } + + if (file[channel]) { + fclose(file[channel]); + file[channel] = NULL; + } + + return ST_OK; +} + + +/* + * Close all channels + */ + +void ArchDrive::close_all_channels(void) +{ + for (int i=0; i<15; i++) + Close(i); + + cmd_len = 0; +} + + +/* + * Read from channel + */ + +uint8 ArchDrive::Read(int channel, uint8 &byte) +{ + D(bug("ArchDrive::Read channel %d\n", channel)); + + // Channel 15: Error channel + if (channel == 15) { + byte = *error_ptr++; + + if (byte != '\x0d') + return ST_OK; + else { // End of message + set_error(ERR_OK); + return ST_EOF; + } + } + + if (!file[channel]) return ST_READ_TIMEOUT; + + // Get char from buffer and read next + byte = read_char[channel]; + int c = getc(file[channel]); + if (c == EOF) + return ST_EOF; + else { + read_char[channel] = c; + return ST_OK; + } +} + + +/* + * Write to channel + */ + +uint8 ArchDrive::Write(int channel, uint8 byte, bool eoi) +{ + D(bug("ArchDrive::Write channel %d, byte %02x, eoi %d\n", channel, byte, eoi)); + + // Channel 15: Collect chars and execute command on EOI + if (channel == 15) { + if (cmd_len > 58) { + set_error(ERR_SYNTAX32); + return ST_TIMEOUT; + } + + cmd_buf[cmd_len++] = byte; + + if (eoi) { + execute_cmd(cmd_buf, cmd_len); + cmd_len = 0; + } + return ST_OK; + } + + if (!file[channel]) + set_error(ERR_FILENOTOPEN); + else + set_error(ERR_WRITEPROTECT); + + return ST_TIMEOUT; +} + + +/* + * Execute drive commands + */ + +// RENAME:new=old +// ^ ^ +// new_file old_file +void ArchDrive::rename_cmd(const uint8 *new_file, int new_file_len, const uint8 *old_file, int old_file_len) +{ + // Check if destination file is already present + int num; + if (find_first_file(new_file, new_file_len, num)) { + set_error(ERR_FILEEXISTS); + return; + } + + // Check if source file is present + if (!find_first_file(old_file, old_file_len, num)) { + set_error(ERR_FILENOTFOUND); + return; + } + + set_error(ERR_WRITEPROTECT); +} + +// INITIALIZE +void ArchDrive::initialize_cmd(void) +{ + close_all_channels(); +} + +// VALIDATE +void ArchDrive::validate_cmd(void) +{ +} + + +/* + * Reset drive + */ + +void ArchDrive::Reset(void) +{ + close_all_channels(); + cmd_len = 0; + set_error(ERR_STARTUP); +} + + +/* + * Check whether file with given header (64 bytes) and size looks like one + * of the file types supported by this module + */ + +static bool is_t64_header(const uint8 *header) +{ + if (memcmp(header, "C64S tape file", 14) == 0 + || memcmp(header, "C64 tape image", 14) == 0 + || memcmp(header, "C64S tape image", 15) == 0) + return true; + else + return false; +} + +static bool is_lynx_header(const uint8 *header) +{ + return memcmp(header + 0x38, "USE LYNX", 8) == 0; +} + +static bool is_p00_header(const uint8 *header) +{ + return memcmp(header, "C64File", 7) == 0; +} + +bool IsArchFile(const char *path, const uint8 *header, long size) +{ + return is_t64_header(header) || is_lynx_header(header) || is_p00_header(header); +} + + +/* + * Read directory of archive file into (empty) c64_dir_entry vector, + * returns false on error + */ + +static bool parse_t64_file(FILE *f, vector &vec, char *dir_title) +{ + // Read header and get maximum number of files contained + fseek(f, 32, SEEK_SET); + uint8 buf[32]; + fread(&buf, 32, 1, f); + int max = (buf[3] << 8) | buf[2]; + if (max == 0) + max = 1; + + memcpy(dir_title, buf+8, 16); + + // Allocate buffer for file records and read them + uint8 *buf2 = new uint8[max * 32]; + fread(buf2, 32, max, f); + + // Determine number of files contained + int num_files = 0; + for (int i=0; i &vec, char *dir_title) +{ + // Dummy directory title + strcpy(dir_title, "LYNX ARCHIVE "); + + // Read header and get number of directory blocks and files contained + fseek(f, 0x60, SEEK_SET); + int dir_blocks; + fscanf(f, "%d", &dir_blocks); + while (getc(f) != 0x0d) + if (feof(f)) + return false; + int num_files; + fscanf(f, "%d\x0d", &num_files); + + // Construct file information array + vec.reserve(num_files); + int cur_offset = dir_blocks * 254; + for (int i=0; i &vec, char *dir_title) +{ + // Dummy directory title + strcpy(dir_title, ".P00 FILE "); + + // Contains only one file + vec.reserve(1); + + // Read file name and start address + uint8 name_buf[17]; + fseek(f, 8, SEEK_SET); + fread(name_buf, 17, 1, f); + name_buf[16] = 0; + uint8 sa_lo, sa_hi; + fseek(f, 26, SEEK_SET); + fread(&sa_lo, 1, 1, f); + fread(&sa_hi, 1, 1, f); + + // Get file size + fseek(f, 0, SEEK_END); + size_t size = ftell(f) - 26; + + // Add entry + vec.push_back(c64_dir_entry(name_buf, FTYPE_PRG, false, false, size, 26, sa_lo, sa_hi)); + return true; +} + +bool ReadArchDirectory(const char *path, vector &vec) +{ + // Open file + FILE *f = fopen(path, "rb"); + if (f) { + + // Read header + uint8 header[64]; + fread(header, 1, sizeof(header), f); + + // Determine archive type and parse archive + bool result = false; + char dir_title[16]; + if (is_t64_header(header)) + result = parse_t64_file(f, vec, dir_title); + else if (is_lynx_header(header)) + result = parse_lynx_file(f, vec, dir_title); + else if (is_p00_header(header)) + result = parse_p00_file(f, vec, dir_title); + + fclose(f); + return result; + } else + return false; +} diff --git a/Src/1541t64.h b/Src/1541t64.h new file mode 100644 index 0000000..1b3dccf --- /dev/null +++ b/Src/1541t64.h @@ -0,0 +1,84 @@ +/* + * 1541t64.h - 1541 emulation in archive-type files (.t64/LYNX/.p00) + * + * Frodo (C) 1994-1997,2002-2005 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _1541T64_H +#define _1541T64_H + +#include "IEC.h" + + +/* + * Definitions + */ + +// Archive types +enum { + TYPE_T64, // C64S tape file + TYPE_LYNX, // C64 LYNX archive + TYPE_P00 // .p00 file +}; + +// Archive file drive class +class ArchDrive : public Drive { +public: + ArchDrive(IEC *iec, const char *filepath); + virtual ~ArchDrive(); + + virtual uint8 Open(int channel, const uint8 *name, int name_len); + virtual uint8 Close(int channel); + virtual uint8 Read(int channel, uint8 &byte); + virtual uint8 Write(int channel, uint8 byte, bool eoi); + virtual void Reset(void); + +private: + bool change_arch(const char *path); + + uint8 open_file(int channel, const uint8 *name, int name_len); + uint8 open_directory(int channel, const uint8 *pattern, int pattern_len); + bool find_first_file(const uint8 *pattern, int pattern_len, int &num); + void close_all_channels(void); + + virtual void rename_cmd(const uint8 *new_file, int new_file_len, const uint8 *old_file, int old_file_len); + virtual void initialize_cmd(void); + virtual void validate_cmd(void); + + FILE *the_file; // File pointer for archive file + int archive_type; // File/archive type (see defines above) + vector file_info; // Vector of file information structs for all files in the archive + + char dir_title[16]; // Directory title + FILE *file[16]; // File pointers for each of the 16 channels (all temporary files) + + uint8 read_char[16]; // Buffers for one-byte read-ahead +}; + + +/* + * Functions + */ + +// Check whether file with given header (64 bytes) and size looks like one +// of the file types supported by this module +extern bool IsArchFile(const char *path, const uint8 *header, long size); + +// Read directory of archive file into (empty) c64_dir_entry vector +extern bool ReadArchDirectory(const char *path, vector &vec); + +#endif diff --git a/Src/AcornGUI.cc b/Src/AcornGUI.cc new file mode 100644 index 0000000..9c204d4 --- /dev/null +++ b/Src/AcornGUI.cc @@ -0,0 +1,1804 @@ +/* + * AcornGUI.cc - Frodo's Graphical User Interface for Acorn RISC OS machines + * + * (C) 1997 Andreas Dehmel + * + * Frodo (C) 1994-1997,2002-2005 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "sysdeps.h" + +#include "C64.h" +#include "VIC.h" +#include "Display.h" +#include "Prefs.h" +#include "main.h" +#include "ROlib.h" +#include "AcornGUI.h" +#include "Version.h" + + + +#define CUSTOM_SPRITES "FrodoRsrc:Sprites" +#define TEMPLATES_FILE "FrodoRsrc:Templates" +#define WIMP_SCRAP_FILE "" + + +#define FileType_Data 0xffd +#define FileType_Text 0xfff +#define FileType_C64File 0x064 +#define FileType_D64File 0x164 + +#define IconSpriteSize 68 // OS units of an icon sprite (for dragging) +#define EstimatedPrefsSize 1024 // can't say how big it'll be before saving... +#define EstimatedConfSize 256 +#define EstimatedRAMSize 0x10400 +#define EstimatedSnapSize 0x11000 + + + + +// For the scanning of the joystick keys in the SysConfig window +#define IntKey_ScanFrom 3 +// This key gets translated to 0xff, i.e. none (joystick inactive) +#define IntKey_Void 44 + + +#define Key_Return 13 + + + + +// LED number to Icon number translation: +char LEDtoIcon[4] = {Icon_Pane_LED0, Icon_Pane_LED1, Icon_Pane_LED2, Icon_Pane_LED3}; +// Drive number to Icon number (Pane) translation: +char PDrvToIcon[4] = {Icon_Pane_Drive0, Icon_Pane_Drive1, Icon_Pane_Drive2, Icon_Pane_Drive3}; +// Drive number to Icon number (Prefs) translation: +#define IconsPerDrive 4 +char DriveToIcon[16] = { + Icon_Prefs_Dr8DIR, Icon_Prefs_Dr8D64, Icon_Prefs_Dr8T64, Icon_Prefs_Dr8Path, + Icon_Prefs_Dr9DIR, Icon_Prefs_Dr9D64, Icon_Prefs_Dr9T64, Icon_Prefs_Dr9Path, + Icon_Prefs_Dr10DIR, Icon_Prefs_Dr10D64, Icon_Prefs_Dr10T64, Icon_Prefs_Dr10Path, + Icon_Prefs_Dr11DIR, Icon_Prefs_Dr11D64, Icon_Prefs_Dr11T64, Icon_Prefs_Dr11Path +}; +// SID type to Icon number translation: +char SIDtoIcon[3] = { + Icon_Prefs_SIDNone, Icon_Prefs_SIDDigi, Icon_Prefs_SIDCard +}; +// REU type to Icon number translation: +char REUtoIcon[4] = { + Icon_Prefs_REUNone, Icon_Prefs_REU128, Icon_Prefs_REU256, Icon_Prefs_REU512 +}; +char KJoyToIcon[2][5] = { + {Icon_Conf_Joy1Up, Icon_Conf_Joy1Down, Icon_Conf_Joy1Left, Icon_Conf_Joy1Right, Icon_Conf_Joy1Fire}, + {Icon_Conf_Joy2Up, Icon_Conf_Joy2Down, Icon_Conf_Joy2Left, Icon_Conf_Joy2Right, Icon_Conf_Joy2Fire} +}; + + + + + +// Internal keynumbers lookup table +// Single characters are represented by single chars, others by pointers to strings +int IntKeyTable[128] = { + (int)"Shft", (int)"Ctrl", (int)"Alt", (int)"ShftL", + (int)"CtrlL", (int)"AltL", (int)"ShftR", (int)"CtrlR", + (int)"AltR", (int)"Slct", (int)"Menu", (int)"Adjst", + 0, 0, 0, 0, + 'q', '3', '4', '5', + (int)"F4", '8', (int)"F7", '-', + '6', (int)"Left", (int)"num6", (int)"num7", + (int)"F11", (int)"F12", (int)"F10", (int)"ScLck", + (int)"Prnt", 'w', 'e', 't', + '7', 'i', '9', '0', + '-', (int)"Down", (int)"num8", (int)"num9", + (int)"Brk", '`', '£', (int)"Del", + '1', '2', 'd', 'r', + '6', 'u', 'o', 'p', + '[', (int)"Up", (int)"num+", (int)"num-", + (int)"nmEnt", (int)"Isrt", (int)"Home", (int)"PgUp", + (int)"CpLck", 'a', 'x', 'f', + 'y', 'j', 'k', '2', + ';', (int)"Ret", (int)"num/", 0, + (int)"num.", (int)"nmLck", (int)"PgDwn", '\'', + 0, 's', 'c', 'g', + 'h', 'n', 'l', ';', + ']', (int)"Del", (int)"num#", (int)"num*", + 0, '=', (int)"Extra", 0, + (int)"Tab", 'z', (int)"Space", 'v', + 'b', 'm', ',', '.', + '/', (int)"Copy", (int)"num0", (int)"num1", + (int)"num3", 0, 0, 0, + (int)"Esc", (int)"F1", (int)"F2", (int)"F3", + (int)"F5", (int)"F6", (int)"F8", (int)"F9", + '\\', (int)"Right", (int)"num4", (int)"num5", + (int)"num2", 0, 0, 0 +}; + + + + + +// The icon bar icon +RO_IconDesc IBarIcon = { + -1, 0, 0, 68, 68, 0x301a, +#ifdef FRODO_SC + "!frodosc" +#else +# ifdef FRODO_PC + "!frodopc" +# else + "!frodo" +# endif +#endif +}; + + + + + +// The menus + +char *MIBTextPrefs = "Preferences..."; +char *MIBTextConf = "Configuration..."; + + +struct MenuIconBar { + RO_MenuHead head; + RO_MenuItem item[Menu_IBar_Items]; +} MenuIconBar = { + { +#ifdef FRODO_SC + "FrodoSC", +#else +# ifdef FRODO_PC + "FrodoPC", +# else + "Frodo", +# endif +#endif + 7, 2, 7, 0, Menu_IBar_Width, Menu_Height, 0}, + { + {0, (RO_MenuHead*)-1, Menu_Flags, "Info"}, + {0, (RO_MenuHead*)-1, Menu_Flags + IFlg_Indir, ""}, + {0, (RO_MenuHead*)-1, Menu_Flags + IFlg_Indir, ""}, + {0, (RO_MenuHead*)-1, Menu_Flags, "Sound"}, + {MFlg_LastItem, (RO_MenuHead*)-1, Menu_Flags, "Quit"} + } +}; + +struct MenuEmuWindow { + RO_MenuHead head; + RO_MenuItem item[Menu_EWind_Items]; +} MenuEmuWindow = { + { +#ifdef FRODO_SC + "FrodoSC Job", +#else +# ifdef FRODO_PC + "FrodoPC Job", +# else + "Frodo Job", +# endif +#endif + 7, 2, 7, 0, Menu_EWind_Width, Menu_Height, 0}, + { + {0, (RO_MenuHead*)-1, Menu_Flags, "Info"}, + {0, (RO_MenuHead*)-1, Menu_Flags, "Sound"}, + {MFlg_Warning, (RO_MenuHead*)-1, Menu_Flags, "Save RAM"}, + {MFlg_LastItem | MFlg_Warning, (RO_MenuHead*)-1, Menu_Flags, "Snapshot"} + } +}; + + + + +// For less writing in LoadATemplate... +#define wic(base,no,off) base[RO_WINDOW_WORDS + no*RO_ICON_WORDS + off] + +// For less writing in WindowToThePrefs... +#define pread_opt(var,icn) PrefsWindow->GetIconState(Icon_Prefs_##icn,AuxBlock);\ + prefs->##var = ((AuxBlock[IconB_Flags] & IFlg_Slct) == 0) ? false : true; + + + +// +// WIMP member-functions +// + +WIMP::WIMP(C64 *my_c64) +{ + int x,y,i; + WIdata *wid; + + CMOS_DragType = ReadDragType(); + + // Determine the dimensions of the icon bar sprite first. + if (Wimp_SpriteInfo((char*)(&IBarIcon.dat),&x,&y,&i) == NULL) + { + IBarIcon.maxx = x << OS_ReadModeVariable(i,4); + IBarIcon.maxy = y << OS_ReadModeVariable(i,5); + } + + IBicon = new Icon(0,&IBarIcon); Mask = 0x00000830; + LastMenu = LastClick = LastDrag = MenuType = DragType = 0; + UseScrap = false; EmuPaused = false; UseNULL = 0; // one NULL client - the emulator + the_c64 = my_c64; + EmuZoom = 1; + + // Read the customized sprite area (LEDs etc) + if ((ReadCatalogueInfo(CUSTOM_SPRITES,AuxBlock) & 1) != 0) // (image) file + { + int i; + FILE *fp; + + i = AuxBlock[2]; SpriteArea = (int*)(new char[i+16]); // allocate space for sprite + fp = fopen(CUSTOM_SPRITES,"rb"); + i = fread(SpriteArea+1,1,i,fp); SpriteArea[0] = i+4; + fclose(fp); + } + else + { + WimpError.errnum = 0; sprintf(WimpError.errmess,"Can't read sprites!!!"); + Wimp_ReportError(&WimpError,1,TASKNAME); + } + + // Read Template and create windows + if (Wimp_OpenTemplate(TEMPLATES_FILE) == NULL) + { + // Load Pane first + LoadATemplate("EmuPane",&EmuPane); + LoadATemplate("EmuWindow",&EmuWindow); + LoadATemplate("ThePrefs",&PrefsWindow); + LoadATemplate("ConfigWind",&ConfigWindow); + LoadATemplate("InfoWindow",&InfoWindow); + LoadATemplate("SoundWindow",&SoundWindow); + LoadATemplate("SaveBox",&SaveBox); + } + else + { + Wimp_ReportError(&WimpError,1,TASKNAME); + WimpError.errnum = 0; sprintf(WimpError.errmess,"Error in Templates!!!"); + } + Wimp_CloseTemplate(); + + // Add info window to icon bar menu + MenuIconBar.item[Menu_IBar_Info].submenu = + MenuEmuWindow.item[Menu_EWind_Info].submenu = (RO_MenuHead*)InfoWindow->MyHandle(); + MenuIconBar.item[Menu_IBar_Sound].submenu = + MenuEmuWindow.item[Menu_EWind_Sound].submenu = (RO_MenuHead*)SoundWindow->MyHandle(); + MenuEmuWindow.item[Menu_EWind_SaveRAM].submenu = + MenuEmuWindow.item[Menu_EWind_Snapshot].submenu = (RO_MenuHead*)SaveBox->MyHandle(); + // I couldn't find ONE FUCKING WAY to do this in the initialisation directly and I'm + // REALLY PISSED OFF! + wid = &(MenuIconBar.item[Menu_IBar_Prefs].dat); + wid->ind.tit = (int*)MIBTextPrefs; wid->ind.val = NULL; wid->ind.len = sizeof(MIBTextPrefs); + wid = &(MenuIconBar.item[Menu_IBar_Config].dat); + wid->ind.tit = (int*)MIBTextConf; wid->ind.val = NULL; wid->ind.len = sizeof(MIBTextConf); + + // Write default path into config window + ConfigWindow->WriteIconText(Icon_Conf_ConfPath,DEFAULT_SYSCONF); + + // Set up the contents of the prefs window and init with the default prefs path + ThePrefsToWindow(); + PrefsWindow->WriteIconText(Icon_Prefs_PrefPath,DEFAULT_PREFS); + // Grey out SID card icon -- not supported! + PrefsWindow->SetIconState(Icon_Prefs_SIDCard,IFlg_Grey,IFlg_Grey); + + // Open Emulator window + pane in the center of the screen and give it the input focus + Wimp_GetCaretPosition(&LastCaret); + OpenEmuWindow(); + Wimp_SetCaretPosition(EmuWindow->MyHandle(),-1,-100,100,-1,-1); // emu window gets input focus + + // Init export files + sprintf(RAMFile+44,"C64_RAM"); sprintf(SnapFile+44,"FrodoSnap"); +} + + +WIMP::~WIMP(void) +{ + delete IBicon; + delete EmuWindow; delete EmuPane; delete PrefsWindow; delete ConfigWindow; + delete InfoWindow; delete SoundWindow; delete SaveBox; +} + + +bool WIMP::LoadATemplate(char *Name, Window **Which) +{ + char *Buffer, *Indirect, *Desc; + int IndSize, Pos; + char TempName[12]; + + strncpy((char*)TempName,Name,12); + Buffer = NULL; Pos = 0; + if (Wimp_LoadTemplate(&Buffer,&Indirect,0,(char*)-1,TempName,&Pos) != NULL) {return(false);} + Buffer = new char[(int)Buffer + 4]; + IndSize = (int)Indirect; Indirect = new char[IndSize]; + Pos = 0; Desc = Buffer+4; + Wimp_LoadTemplate(&Desc,&Indirect,Indirect+IndSize,(char*)-1,TempName,&Pos); + if (Which == &EmuWindow) + { + int eigen, dx, dy; + RO_Window *row = (RO_Window*)Buffer; + RORes res; + + // Center emulator window on screen + eigen = (res.eigx < res.eigy) ? res.eigx : res.eigy; + dx = (DISPLAY_X << eigen); dy = (DISPLAY_Y << eigen); + row->vminx = res.resx/2 - dx/2; row->vmaxx = row->vminx + dx; + row->vminy = res.resy/2 - dy/2; row->vmaxy = row->vminy + dy; + row->wminy = -dy; row->wmaxx = dx; + Indirect = (char*)VERSION_STRING; + } + else + { + if (Which == &EmuPane) // EmuPane needs customized sprites + { + register RO_Window *row = (RO_Window*)Buffer; + register int *b = (int*)Buffer; + + // Insert sprite pointer in window and icon definitions + row->SpriteAreaPtr = (int)SpriteArea; + wic(b,Icon_Pane_LED0,RawIB_Data1) = wic(b,Icon_Pane_LED1,RawIB_Data1) = + wic(b,Icon_Pane_LED2,RawIB_Data1) = wic(b,Icon_Pane_LED3,RawIB_Data1) = (int)SpriteArea; + sprintf((char*)wic(b,Icon_Pane_Pause,RawIB_Data0),PANE_TEXT_PAUSE); + sprintf((char*)wic(b,Icon_Pane_Toggle,RawIB_Data0),PANE_TEXT_ZOOM2); + } + else if (Which == &SoundWindow) // ditto + { + register RO_Window *row = (RO_Window*)Buffer; + register int *b = (int*)Buffer; + register int orr; + + row->SpriteAreaPtr = (int)SpriteArea; + // if sound emulation off grey out notes + if (ThePrefs.SIDType == SIDTYPE_NONE) {orr = IFlg_Grey;} else {orr = 0;} + wic(b,Icon_Sound_Notes,RawIB_Flags)=(wic(b,Icon_Sound_Notes,RawIB_Flags)&~IFlg_Grey)|orr; + } + else if (Which == &InfoWindow) // ditto + { + register RO_Window *row = (RO_Window*)Buffer; + + row->SpriteAreaPtr = (int)SpriteArea; + } + Indirect = NULL; + } + *Which = new Window((int*)Buffer,Indirect); + return(true); +} + + +// All changes in position of the Emulator window have to be treated seperately +// to keep the pane attached to it at all times! +void WIMP::OpenEmuWindow(void) +{ + RO_Window *wind; + int work[WindowB_WFlags], i; + + wind = EmuWindow->Descriptor(); + EmuPane->GetWorkArea(&work[WindowB_VMinX]); i = work[WindowB_VMaxX] - work[WindowB_VMinX]; + if ((work[WindowB_VMinX] = wind->vminx - EmuPaneSpace - i) < 0) + { + // if main window still on screen then keep pane on screen as well + if (wind->vminx >= 0) {work[WindowB_VMinX] = 0;} + // otherwise align pane and main window on the left + else {work[WindowB_VMinX] = wind->vminx;} + } + work[WindowB_VMaxX] = work[WindowB_VMinX] + i; + work[WindowB_VMinY] = wind->vmaxy - (work[WindowB_VMaxY]-work[WindowB_VMinY]); + work[WindowB_VMaxY] = wind->vmaxy; + work[WindowB_Handle] = EmuPane->MyHandle(); + work[WindowB_ScrollX] = work[WindowB_ScrollY] = 0; + work[WindowB_Stackpos] = -1; // open on top of stack + // open the pane first + EmuPane->open(work); + wind->stackpos = EmuPane->MyHandle(); // open window behind pane. + EmuWindow->open(); +} + + +void WIMP::OpenEmuWindow(int *Where) +{ + int work[WindowB_WFlags], i; + + EmuPane->GetWorkArea(&work[WindowB_VMinX]); i = work[WindowB_VMaxX] - work[WindowB_VMinX]; + if ((work[WindowB_VMinX] = Where[WindowB_VMinX] - EmuPaneSpace - i) < 0) + { + if (Where[WindowB_VMinX] >= 0) {work[WindowB_VMinX] = 0;} + else {work[WindowB_VMinX] = Where[WindowB_VMinX];} + } + work[WindowB_VMaxX] = work[WindowB_VMinX] + i; + work[WindowB_VMinY] = Where[WindowB_VMaxY] - (work[WindowB_VMaxY]-work[WindowB_VMinY]); + work[WindowB_VMaxY] = Where[WindowB_VMaxY]; + work[WindowB_Handle] = EmuPane->MyHandle(); + work[WindowB_ScrollX] = work[WindowB_ScrollY] = 0; + work[WindowB_Stackpos] = Where[WindowB_Stackpos]; + // open the pane first + EmuPane->open(work); + Where[WindowB_Stackpos] = EmuPane->MyHandle(); // open window behind pane + EmuWindow->open(Where); +} + + +void WIMP::CloseEmuWindow(void) +{ + EmuWindow->close(); EmuPane->close(); +} + + +void WIMP::UpdateEmuWindow(void) +{ + C64Display *disp = the_c64->TheDisplay; + + EmuWindow->update(disp->BitmapBase(),disp); +} + + +// Write the values given in ThePrefs into the Prefs Window +void WIMP::ThePrefsToWindow(void) +{ + int i, j, k; + + // Update the data for each of the drives + for (i=0; i<4; i++) + { + switch(ThePrefs.DriveType[i]) + { + case DRVTYPE_D64: j=1; break; + case DRVTYPE_T64: j=2; break; + default: j=0; break; // otherwise type 0 = DIR + } + // unselect all but other icons + for (k=0; k<3; k++) + { + if (k != j) {PrefsWindow->SetIconState(DriveToIcon[i*IconsPerDrive + k],0,IFlg_Slct);} + } + // and select the correct one. + PrefsWindow->SetIconState(DriveToIcon[i*IconsPerDrive + j],IFlg_Slct,IFlg_Slct); + // Now update the path name (buffer won't change, i.e. use original data) + PrefsWindow->WriteIconText(DriveToIcon[i*IconsPerDrive + 3],ThePrefs.DrivePath[i]); + } + if (ThePrefs.Emul1541Proc) {i = IFlg_Slct;} else {i = 0;} + PrefsWindow->SetIconState(Icon_Prefs_Emul1541,i,IFlg_Slct); + SetLEDIcons(ThePrefs.Emul1541Proc); + if (ThePrefs.MapSlash) {i = IFlg_Slct;} else {i = 0;} + PrefsWindow->SetIconState(Icon_Prefs_MapSlash,i,IFlg_Slct); + + // SID prefs + switch (ThePrefs.SIDType) + { + case SIDTYPE_DIGITAL: i = 1; break; + case SIDTYPE_SIDCARD: i = 2; break; + default: i = 0; break; // includes NONE + } + for (j=0; j<3; j++) + { + if (j != i) {PrefsWindow->SetIconState(SIDtoIcon[j],0,IFlg_Slct);} + } + PrefsWindow->SetIconState(SIDtoIcon[i],IFlg_Slct,IFlg_Slct); + PrefsWindow->SetIconState(Icon_Prefs_SIDFilter,(ThePrefs.SIDFilters)?IFlg_Slct:0,IFlg_Slct); + + // REU prefs + switch (ThePrefs.REUSize) + { + case REU_128K: i=1; break; + case REU_256K: i=2; break; + case REU_512K: i=3; break; + default: i=0; break; + } + for (j=0; j<4; j++) + { + if (j != i) {PrefsWindow->SetIconState(REUtoIcon[j],0,IFlg_Slct);} + } + PrefsWindow->SetIconState(REUtoIcon[i],IFlg_Slct,IFlg_Slct); + + // Skip Frames + PrefsWindow->WriteIconNumber(Icon_Prefs_SkipFText,ThePrefs.SkipFrames); + + // Sprites + PrefsWindow->SetIconState(Icon_Prefs_SprOn,(ThePrefs.SpritesOn)?IFlg_Slct:0,IFlg_Slct); + PrefsWindow->SetIconState(Icon_Prefs_SprColl,(ThePrefs.SpriteCollisions)?IFlg_Slct:0,IFlg_Slct); + // Joystick + PrefsWindow->SetIconState(Icon_Prefs_Joy1On,(ThePrefs.Joystick1On)?IFlg_Slct:0,IFlg_Slct); + PrefsWindow->SetIconState(Icon_Prefs_Joy2On,(ThePrefs.Joystick2On)?IFlg_Slct:0,IFlg_Slct); + PrefsWindow->SetIconState(Icon_Prefs_JoySwap,(ThePrefs.JoystickSwap)?IFlg_Slct:0,IFlg_Slct); + + // Misc + SetSpeedLimiter(ThePrefs.LimitSpeed); + PrefsWindow->SetIconState(Icon_Prefs_FastReset,(ThePrefs.FastReset)?IFlg_Slct:0,IFlg_Slct); + PrefsWindow->SetIconState(Icon_Prefs_CIAHack,(ThePrefs.CIAIRQHack)?IFlg_Slct:0,IFlg_Slct); + + // Cycles + PrefsWindow->WriteIconNumber(Icon_Prefs_CycleNorm,ThePrefs.NormalCycles); + PrefsWindow->WriteIconNumber(Icon_Prefs_CycleBad,ThePrefs.BadLineCycles); + PrefsWindow->WriteIconNumber(Icon_Prefs_CycleCIA,ThePrefs.CIACycles); + PrefsWindow->WriteIconNumber(Icon_Prefs_CycleFloppy,ThePrefs.FloppyCycles); + +#ifdef SUPPORT_XROM + // XROM + PrefsWindow->SetIconState(Icon_Prefs_XROMOn,(ThePrefs.XPandROMOn)?IFlg_Slct:0,IFlg_Slct); + PrefsWindow->WriteIconText(Icon_Prefs_XROMPath,ThePrefs.XPandROMFile); +#endif +} + + +// Update ThePrefs according to the values given in the Prefs Window +void WIMP::WindowToThePrefs(void) +{ + int i, j, k; + Prefs *prefs = new Prefs(ThePrefs); + + for (i=0; i<4; i++) + { + // find out which of the drive type icons is selected + j = -1; + do + { + ++j; PrefsWindow->GetIconState(DriveToIcon[i*IconsPerDrive + j],AuxBlock); + } + while ((j < 3) && ((AuxBlock[IconB_Flags] & IFlg_Slct) == 0)); + switch (j) + { + case 1: prefs->DriveType[i] = DRVTYPE_D64; break; + case 2: prefs->DriveType[i] = DRVTYPE_T64; break; + default: prefs->DriveType[i] = DRVTYPE_DIR; break; + } + strcpy(prefs->DrivePath[i],PrefsWindow->ReadIconText(DriveToIcon[i*IconsPerDrive + 3])); + } + // Emulation of the 1541 processor is a special case as it also affects LED1-3 + pread_opt(Emul1541Proc,Emul1541); + SetLEDIcons(prefs->Emul1541Proc); + pread_opt(MapSlash,MapSlash); + + // SID + j = -1; + do + { + ++j; PrefsWindow->GetIconState(SIDtoIcon[j],AuxBlock); + } + while ((j < 3) && ((AuxBlock[IconB_Flags] & IFlg_Slct) == 0)); + switch (j) + { + case 1: prefs->SIDType = SIDTYPE_DIGITAL; break; + case 2: prefs->SIDType = SIDTYPE_SIDCARD; break; + default: prefs->SIDType = SIDTYPE_NONE; break; + } + pread_opt(SIDFilters,SIDFilter); + SoundWindow->SetIconState(Icon_Sound_Notes,(prefs->SIDType==SIDTYPE_NONE)?IFlg_Grey:0,IFlg_Grey); + + // REU + j = -1; + do + { + ++j; PrefsWindow->GetIconState(REUtoIcon[j],AuxBlock); + } + while ((j < 4) && ((AuxBlock[IconB_Flags] & IFlg_Slct) == 0)); + switch (j) + { + case 1: prefs->REUSize = REU_128K; break; + case 2: prefs->REUSize = REU_256K; break; + case 3: prefs->REUSize = REU_512K; break; + default: prefs->REUSize = REU_NONE; break; + } + + // Skip Frames + prefs->SkipFrames = PrefsWindow->ReadIconNumber(Icon_Prefs_SkipFText); + + // Sprites + pread_opt(SpritesOn,SprOn); pread_opt(SpriteCollisions,SprColl); + + // Joystick + pread_opt(Joystick1On,Joy1On); pread_opt(Joystick2On,Joy2On); pread_opt(JoystickSwap,JoySwap); + + // Misc + pread_opt(LimitSpeed,LimSpeed); pread_opt(FastReset,FastReset); pread_opt(CIAIRQHack,CIAHack); + + // Cycles + prefs->NormalCycles = PrefsWindow->ReadIconNumber(Icon_Prefs_CycleNorm); + prefs->BadLineCycles = PrefsWindow->ReadIconNumber(Icon_Prefs_CycleBad); + prefs->CIACycles = PrefsWindow->ReadIconNumber(Icon_Prefs_CycleCIA); + prefs->FloppyCycles = PrefsWindow->ReadIconNumber(Icon_Prefs_CycleFloppy); + +#ifdef SUPPORT_XROM + // XROM + pread_opt(XPandROMOn,XROMOn); + strcpy(prefs->XPandROMFile,PrefsWindow->ReadIconText(Icon_Prefs_XROMPath)); +#endif + + // Finally make the changes known to the system: + the_c64->NewPrefs(prefs); + ThePrefs = *prefs; + delete prefs; +} + + +// Update the SysConfig window according to the values used +void WIMP::SysConfToWindow(void) +{ + int i, j, k; + Joy_Keys *jk; + char OneChar[2], *b; + + OneChar[1] = 0; + + // Write timings + the_c64->ReadTimings(&i,&j,&k); + ConfigWindow->WriteIconNumber(Icon_Conf_PollAfter, i); + ConfigWindow->WriteIconNumber(Icon_Conf_SpeedAfter, j); + ConfigWindow->WriteIconNumber(Icon_Conf_SoundAfter, k); + + // Write joystick keys + for (i=0; i<2; i++) + { + jk = &(the_c64->TheDisplay->JoystickKeys[i]); NewJoyKeys[i] = *jk; + if ((j = jk->up) >= 128) {j = 127;} + j = IntKeyTable[j]; if (j < 256) {OneChar[0] = j; b = OneChar;} else {b = (char*)j;} + ConfigWindow->WriteIconText(KJoyToIcon[i][0], b); + if ((j = jk->down) >= 128) {j = 127;} + j = IntKeyTable[j]; if (j < 256) {OneChar[0] = j; b = OneChar;} else {b = (char*)j;} + ConfigWindow->WriteIconText(KJoyToIcon[i][1], b); + if ((j = jk->left) >= 128) {j = 127;} + j = IntKeyTable[j]; if (j < 256) {OneChar[0] = j; b = OneChar;} else {b = (char*)j;} + ConfigWindow->WriteIconText(KJoyToIcon[i][2], b); + if ((j = jk->right) >= 128) {j = 127;} + j = IntKeyTable[j]; if (j < 256) {OneChar[0] = j; b = OneChar;} else {b = (char*)j;} + ConfigWindow->WriteIconText(KJoyToIcon[i][3], b); + if ((j = jk->fire) >= 128) {j = 127;} + j = IntKeyTable[j]; if (j < 256) {OneChar[0] = j; b = OneChar;} else {b = (char*)j;} + ConfigWindow->WriteIconText(KJoyToIcon[i][4], b); + } +} + + +// Update SysConfig according to the values in the window +void WIMP::WindowToSysConf(void) +{ + int i, j, k; + Joy_Keys *jk; + + // Read timings + i = ConfigWindow->ReadIconNumber(Icon_Conf_PollAfter); + j = ConfigWindow->ReadIconNumber(Icon_Conf_SpeedAfter); + k = ConfigWindow->ReadIconNumber(Icon_Conf_SoundAfter); + the_c64->WriteTimings(i,j,k); + + // Read joystick keys + for (i=0; i<2; i++) + { + jk = &(the_c64->TheDisplay->JoystickKeys[i]); *jk = NewJoyKeys[i]; + } +} + + +// Low-level keyboard scan in SysConfig Window +void WIMP::PollSysConfWindow(void) +{ + Wimp_GetPointerInfo(Block); + if (Block[MouseB_Window] == ConfigWindow->MyHandle()) + { + int i, j; + + for (i=0; i<2; i++) + { + for (j=0; j<5; j++) + { + if (Block[MouseB_Icon] == KJoyToIcon[i][j]) + { + int key; + + // Gain caret (to window, but none of its icons!) + Wimp_GetCaretPosition(&LastCaret); + Wimp_SetCaretPosition(ConfigWindow->MyHandle(),-1,0,0,-1,-1); + if ((key = ScanKeys(IntKey_ScanFrom)) != 0xff) + { + char OneChar[2], *b; + + if (key == IntKey_Void) {key = 0xff;} + switch (j) + { + case 0: NewJoyKeys[i].up = key; break; + case 1: NewJoyKeys[i].down = key; break; + case 2: NewJoyKeys[i].left = key; break; + case 3: NewJoyKeys[i].right = key; break; + case 4: NewJoyKeys[i].fire = key; break; + } + if (key >= 128) {key = 127;} + key = IntKeyTable[key]; + if (key < 256) {OneChar[0]=key; OneChar[1]=0; b=OneChar;} else {b=(char*)key;} + ConfigWindow->WriteIconText(KJoyToIcon[i][j], b); + } + } + } + } + } +} + + +// Start a drag operation on the icon in the window +void WIMP::DragIconSprite(Window *host, unsigned int number) +{ + host->GetIconState(number,AuxBlock); + if ((AuxBlock[IconB_Flags] & IFlg_Sprite) != 0) // it needs to have a sprite, of course + { + char spritename[16] = "\0"; + + if ((AuxBlock[IconB_Flags] & IFlg_Indir) == 0) // not indirected + { + strncpy(spritename,((char*)AuxBlock+IconB_Data0),15); + } + else + { + if ((AuxBlock[IconB_Flags] & IFlg_Text) == 0) + { + strncpy(spritename,(char*)AuxBlock[IconB_Data0],15); + } + else // this necessitates parsing the validation string + { + register char *b, *d, *s, c; + + s = spritename; + if ((b = (char*)AuxBlock[IconB_Data1]) != NULL) // pointer to val str + { + do + { + c = *b++; + if ((c == 's') || (c == 'S')) // sprite command + { + c = *b++; while ((c != ';') && (c >= 32)) {*s++ = c; c = *b++;} + c = 0; *s++ = c; // we can stop now + } + else if (c >= 32) // some other command ==> skip to next. + { + c = *b++; + while ((c != ';') && (c >= 32)) {if (c == '\\') {b++;} c = *b++;} + } + } + while (c >= 32); + } + } + } + // we should now have the spritename + if (spritename[0] != '\0') + { + LastDrag = host->MyHandle(); LastIcon = number; + if (CMOS_DragType == 0) // Drag outline + { + ROScreen *screen = the_c64->TheDisplay->screen; + + AuxBlock[DragB_Handle] = LastDrag; AuxBlock[DragB_Type] = 5; + Wimp_GetPointerInfo(AuxBlock + DragB_IMinX); + // Drag fixed sized box of this size: + AuxBlock[DragB_IMinX] -= IconSpriteSize/2; + AuxBlock[DragB_IMaxX] = AuxBlock[DragB_IMinX] + IconSpriteSize; + AuxBlock[DragB_IMinY] -= IconSpriteSize/2; + AuxBlock[DragB_IMaxY] = AuxBlock[DragB_IMinY] + IconSpriteSize; + // Parent box is whole screen + AuxBlock[DragB_BBMinX] = AuxBlock[DragB_BBMinY] = 0; + AuxBlock[DragB_BBMaxX] = screen->resx; AuxBlock[DragB_BBMaxY] = screen->resy; + Wimp_DragBox(AuxBlock); + } + else // DragASprite + { + Wimp_GetPointerInfo(AuxBlock); + AuxBlock[DASB_MinX] -= IconSpriteSize/2; + AuxBlock[DASB_MaxX] = AuxBlock[DASB_MinX] + IconSpriteSize; + AuxBlock[DASB_MinY] -= IconSpriteSize/2; + AuxBlock[DASB_MaxY] = AuxBlock[DASB_MinY] + IconSpriteSize; + DragASprite_Start(0xc5,1,spritename,AuxBlock,NULL); + } + } + } +} + + +// Blk is a block as in MouseClick or PointerInfo +int WIMP::CalculateVolume(int *Blk) +{ + int orgx, vol; + + SoundWindow->getstate(AuxBlock); + orgx = AuxBlock[WindowB_VMinX] - AuxBlock[WindowB_ScrollX]; + SoundWindow->GetIconState(Icon_Sound_Volume, AuxBlock); + vol = (MaximumVolume*((Blk[MouseB_PosX] - orgx) - AuxBlock[IconB_MinX] - WellBorder)) / (AuxBlock[IconB_MaxX] - AuxBlock[IconB_MinX] - 2*WellBorder); + if (vol < 0) {vol = 0;} if (vol > MaximumVolume) {vol = MaximumVolume;} + return(vol); +} + + +// Check whether a filename contains a full pathname and reports an error if not. +int WIMP::CheckFilename(char *name) +{ + bool OK = false; + register char *b, *d, c; + + b = d = name; c = *b++; + while (c > 32) + { + // valid path must contain '$' or ':' + if ((c == '$') || (c == ':')) {OK = true; d = b;} + if (c == '.') {d = b;} + c = *b++; + } + if ((b - d) == 1) {OK = false;} // filename mustn't end with '$', ':' or '.' + if (OK) {return(0);} + + WimpError.errnum = 0; sprintf(WimpError.errmess,"Bad filename %s",name); + Wimp_ReportError(&WimpError,1,TASKNAME); + return(-1); +} + + +void WIMP::SnapshotSaved(bool OK) +{ + if (OK) + { + int *b = (int*)SnapFile; + + if (b[MsgB_Sender] != 0) + { + b[MsgB_YourRef] = b[MsgB_MyRef]; b[MsgB_Action] = Message_DataLoad; + Wimp_SendMessage(18,b,b[MsgB_Sender],b[6]); + } + else {Wimp_CreateMenu((int*)-1,0,0);} + SaveType = 0; + } +} + + +void WIMP::IssueSnapshotRequest(void) +{ + if (EmuPaused) + { + EmuPane->WriteIconTextU(Icon_Pane_Pause, PANE_TEXT_PAUSE); + EmuPaused = false; + } + the_c64->RequestSnapshot(); +} + + +// Sets the Emu pane's LEDs according to the floppy emulation state +void WIMP::SetLEDIcons(bool FloppyEmulation) +{ + int i, eor; + + if (FloppyEmulation) {eor = IFlg_Grey;} else {eor = 0;} + for (i=1; i<4; i++) + { + EmuPane->SetIconState(LEDtoIcon[i],eor,IFlg_Grey); + } +} + + + +// Doesn't open window, just resizes... +void WIMP::SetEmuWindowSize(void) +{ + register int i; + register C64Display *disp = the_c64->TheDisplay; + + i = (disp->screen->eigx < disp->screen->eigy) ? disp->screen->eigx : disp->screen->eigy; + if (EmuZoom == 2) {i++;} + EmuWindow->extent(0,-(DISPLAY_Y << i),(DISPLAY_X << i),0); +} + + + +// switch between zoom 1 and zoom 2 +void WIMP::ToggleEmuWindowSize(void) +{ + int x,y; + + // Icon in pane shows _alternative_ zoom mode + if (EmuZoom == 1) + { + EmuZoom = 2; + EmuPane->WriteIconText(Icon_Pane_Toggle,"1 x"); + } + else + { + EmuZoom = 1; + EmuPane->WriteIconText(Icon_Pane_Toggle,"2 x"); + } + SetEmuWindowSize(); + EmuWindow->GetWorkArea(AuxBlock); + x = AuxBlock[2] - AuxBlock[0]; y = AuxBlock[3] - AuxBlock[1]; + EmuWindow->getstate(AuxBlock); + AuxBlock[WindowB_VMaxX] = AuxBlock[WindowB_VMinX] + x; + AuxBlock[WindowB_VMinY] = AuxBlock[WindowB_VMaxY] - y; + // Open emu window alone to get the dimensions set by the WIMP + Wimp_OpenWindow(AuxBlock); + // Then open with the pane at the correct position + EmuWindow->getstate(AuxBlock); OpenEmuWindow(AuxBlock); UpdateEmuWindow(); +} + + + +int WIMP::ReadEmuWindowSize(void) +{ + return EmuZoom; +} + + + +// Set a new drive path for drive DrNum. MsgBlock is the DataLoad MessageBlock. +void WIMP::NewDriveImage(int DrNum, int *MsgBlock, bool SetNow) +{ + int type, j = -1, map = -1; + + // determine currently selected type + do + { + ++j; PrefsWindow->GetIconState(DriveToIcon[DrNum*IconsPerDrive + j], AuxBlock); + } + while ((j < 3) && ((AuxBlock[6] & IFlg_Slct) == 0)); + if (j >= 3) {j = 0;} + // Check the type and set the drive type accordingly + type = ReadCatalogueInfo(((char*)Block)+44,AuxBlock); + // New path is directory but DRVTYPE != DIR ==> set DIR + if ((type == 2) && (j != 0)) {map = 0;} + // New path is not directory/image but DRVTYPE == DIR ==> remap to D64 + if (((type & 2) == 0) && (j == 0)) {map = 1;} + // Filetype indicated D64 image? + if (((type = AuxBlock[0]) & 0xfff00000) == 0xfff00000) // typed file + { + type = (type >> 8) & 0xfff; + // D64 image can also be used in DIR mode (-> D64ImageFS). Only remap from T64! + if ((type == FileType_D64File) && (j == 2)) {map = 1;} + } + // Do we need to remap? + if (map >= 0) + { + PrefsWindow->SetIconState(DriveToIcon[DrNum*IconsPerDrive+j],0,IFlg_Slct); + PrefsWindow->SetIconState(DriveToIcon[DrNum*IconsPerDrive+map],IFlg_Slct,IFlg_Slct); + j = map; + } + // j is the number of the emulation mode (DIR (0), D64 (1), T64 (2)) + PrefsWindow->WriteIconText(DriveToIcon[DrNum*IconsPerDrive+3],((char*)Block)+44); + // Send acknowledge message + Block[MsgB_YourRef] = Block[MsgB_MyRef]; Block[MsgB_Action] = Message_DataLoadAck; + Wimp_SendMessage(17,Block,Block[MsgB_Sender],Block[6]); + + // Set this drive path immediately? + if (SetNow) + { + Prefs *prefs = new Prefs(ThePrefs); + + prefs->DriveType[DrNum] = j; strcpy(prefs->DrivePath[DrNum],((char*)Block)+44); + the_c64->NewPrefs(prefs); + ThePrefs = *prefs; + delete prefs; + } +} + + + +void WIMP::SetSpeedLimiter(bool LimitSpeed) +{ + RO_Icon *roi; + char *b, c; + + PrefsWindow->SetIconState(Icon_Prefs_LimSpeed,(LimitSpeed) ? IFlg_Slct : 0, IFlg_Slct); + roi = EmuPane->GetIcon(Icon_Pane_Speed); + if ((b = (char*)roi->dat.ind.val) != NULL) + { + do + { + c = *b++; + if ((c == 'r') || (c == 'R')) // boRder command? + { + if (LimitSpeed) {*b++ = '1';} else {*b++ = '2';} + c = 0; // stop now + } + else if (c >= 32) // skip to next command + { + c = *b++; + while ((c >= 32) && (c != ';')) {if (c == '\\') {b++;} c = *b++;} + } + } + while (c >= 32); + EmuPane->UpdateIcon(Icon_Pane_Speed); + } +} + + + + + +void WIMP::Poll(bool Paused) +{ + int event; + bool LastPause; + + LastPause = EmuPaused; EmuPaused = Paused; + + // If emulator is paused disable null events + if ((!EmuPaused) || (UseNULL > 0)) {Mask &= 0xfffffffe;} else {Mask |= 1;} + + do + { + // Loop until a null event is received, then continue the emulation. + // (this looks best) + do + { + event = Wimp_Poll(Mask,Block,NULL); + switch (event) + { + case 1: Redraw(); break; + case 2: OpenWindow(); break; + case 3: CloseWindow(); break; + case 6: MouseClick(); break; + case 7: UserDrag(); break; + case 8: if ((EmuPaused) && (Block[KeyPB_Window] == EmuWindow->MyHandle())) + { + if (the_c64->TheDisplay->CheckForUnpause(!LastPause)) {EmuPaused = false;} + } + KeyPressed(); break; + case 9: MenuSelection(); break; + case 17: + case 18: UserMessage(); break; + case 19: UserMessageAck(); break; + default: break; + } + // This is important + if ((!EmuPaused) || (UseNULL > 0)) {Mask &= 0xfffffffe;} else {Mask |= 1;} + LastPause = EmuPaused; + } + while (event != 0); + if (UseNULL > 0) {PollSysConfWindow();} + // This should probably become a new member-function one day... + if (DragType == DRAG_VolumeWell) + { + Wimp_GetPointerInfo(Block); + if (Block[MouseB_Icon] == Icon_Sound_Volume) // should always be the case (BBox)! + { + the_c64->HostVolume = CalculateVolume(Block); + SoundWindow->ForceIconRedraw(Icon_Sound_Volume); + } + } + } + while (EmuPaused); +} + + +void WIMP::Redraw(void) +{ + if (Block[RedrawB_Handle] == EmuWindow->MyHandle()) // emulator main window + { + C64Display *disp = the_c64->TheDisplay; + + EmuWindow->redraw(Block,disp->BitmapBase(),disp); + } + else if (Block[RedrawB_Handle] == SoundWindow->MyHandle()) // sound window + { + int more; + int minx, miny, maxx, maxy, thresh; + + more = Wimp_RedrawWindow(Block); + // compute screen coordinates of work (0,0) + minx = Block[RedrawB_VMinX] - Block[RedrawB_ScrollX]; + maxy = Block[RedrawB_VMaxY] - Block[RedrawB_ScrollY]; + // get volume well bounding box + SoundWindow->GetIconState(Icon_Sound_Volume, AuxBlock); + maxx = minx + AuxBlock[IconB_MaxX] - WellBorder; minx += AuxBlock[IconB_MinX] + WellBorder; + miny = maxy + AuxBlock[IconB_MinY] + WellBorder; maxy += AuxBlock[IconB_MaxY] - WellBorder; + thresh = minx + (the_c64->HostVolume * (maxx - minx))/ MaximumVolume; + while (more != 0) + { + // clip + if ((minx <= Block[RedrawB_CMaxX]) && (maxx >= Block[RedrawB_CMinX]) && + (miny <= Block[RedrawB_CMaxY]) && (maxy >= Block[RedrawB_CMinY])) + { + if (Block[RedrawB_CMinX] < thresh) + { + ColourTrans_SetGCOL(0x00ff0000,0,0); // green + OS_Plot(0x04,minx,miny); OS_Plot(0x65,thresh,maxy); + } + if (Block[RedrawB_CMaxX] > thresh) + { + ColourTrans_SetGCOL(0xffffff00,0,0); // white + OS_Plot(0x04,thresh,miny); OS_Plot(0x65,maxx,maxy); + } + } + more = Wimp_GetRectangle(Block); + } + } + else // Dummy redraw loop + { + int more; + + more = Wimp_RedrawWindow(Block); + while (more != 0) + { + more = Wimp_GetRectangle(Block); + } + } +} + + +void WIMP::OpenWindow(void) +{ + if (Block[WindowB_Handle] == EmuWindow->MyHandle()) {OpenEmuWindow(Block);} + else if (Block[WindowB_Handle] != EmuPane->MyHandle()) + { + Wimp_OpenWindow(Block); + } +} + + +void WIMP::CloseWindow(void) +{ + if (Block[WindowB_Handle] == EmuWindow->MyHandle()) {CloseEmuWindow();} + else if (Block[WindowB_Handle] != EmuPane->MyHandle()) + { + if (Block[WindowB_Handle] == ConfigWindow->MyHandle()) {UseNULL--;} + Wimp_CloseWindow(Block); + } +} + + +void WIMP::MouseClick(void) +{ + if ((Block[MouseB_Window] == -2) && (Block[MouseB_Icon] == IBicon->IHandle)) // Icon Bar icon + { + if (Block[MouseB_Buttons] == 2) // Menu + { + Wimp_CreateMenu((int*)&MenuIconBar,Block[MouseB_PosX]-MenuIconBar.head.width/2,96+Menu_Height*Menu_IBar_Items); + LastMenu = Menu_IBar; + } + else // Some other click + { + if (!EmuWindow->OpenStatus(Block)) {Block[WindowB_Stackpos] = -1; OpenEmuWindow(Block);} + } + } + else if (Block[MouseB_Window] == EmuWindow->MyHandle()) // Emulator window + { + if (Block[MouseB_Buttons] >= 256) // click + { + Wimp_GetCaretPosition(&LastCaret); + Wimp_SetCaretPosition(Block[MouseB_Window],-1,-100,100,-1,-1); // gain input focus + } + if (Block[MouseB_Buttons] == 2) // menu + { + Wimp_CreateMenu((int*)&MenuEmuWindow,Block[MouseB_PosX]-MenuEmuWindow.head.width/2,Block[MouseB_PosY]); + LastMenu = Menu_Emulator; + } + } + else if (Block[MouseB_Window] == EmuPane->MyHandle()) // Emulator pane + { + if ((Block[MouseB_Buttons] == 1) || (Block[MouseB_Buttons] == 4)) + { + switch (Block[MouseB_Icon]) + { + case Icon_Pane_Pause: // Pause icon + if (EmuPaused) + { + EmuPane->WriteIconText(Icon_Pane_Pause,PANE_TEXT_PAUSE); + the_c64->Resume(); EmuPaused = false; + } + else + { + EmuPane->WriteIconText(Icon_Pane_Pause,PANE_TEXT_RESUME); + the_c64->Pause(); EmuPaused = true; + // Update the window now! + UpdateEmuWindow(); + } + break; + case Icon_Pane_Reset: the_c64->Reset(); break; + case Icon_Pane_Toggle: ToggleEmuWindowSize(); break; + case Icon_Pane_Speed: + ThePrefs.LimitSpeed = !ThePrefs.LimitSpeed; SetSpeedLimiter(ThePrefs.LimitSpeed); + break; + default: break; + } + } + } + else if (Block[MouseB_Window] == PrefsWindow->MyHandle()) // Prefs window + { + if ((Block[MouseB_Buttons] == 1) || (Block[MouseB_Buttons] == 4)) // normal click + { + register int i; + + switch(Block[MouseB_Icon]) + { + case Icon_Prefs_SkipFLeft: + i = PrefsWindow->ReadIconNumber(Icon_Prefs_SkipFText); + if (i > 0) + { + PrefsWindow->WriteIconNumber(Icon_Prefs_SkipFText,--i); + ThePrefs.SkipFrames = i; // instant update + } + break; + case Icon_Prefs_SkipFRight: + i = PrefsWindow->ReadIconNumber(Icon_Prefs_SkipFText); + PrefsWindow->WriteIconNumber(Icon_Prefs_SkipFText,++i); + ThePrefs.SkipFrames = i; // instant update + break; + case Icon_Prefs_Cancel: ThePrefsToWindow(); break; // restore current settings + case Icon_Prefs_OK: WindowToThePrefs(); + if (Block[MouseB_Buttons] == 4) {PrefsWindow->close();} + break; // use new prefs + case Icon_Prefs_Save: + WindowToThePrefs(); + if (CheckFilename(PrefsWindow->ReadIconText(Icon_Prefs_PrefPath)) == 0) + { + ThePrefs.Save(PrefsWindow->ReadIconText(Icon_Prefs_PrefPath)); + } + break; + default: break; + } + } + else if ((Block[MouseB_Buttons] == 16) || (Block[MouseB_Buttons] == 64)) + // drag (only the sprite) + { + if (Block[MouseB_Icon] == Icon_Prefs_PrefSprite) + { + DragIconSprite(PrefsWindow, Icon_Prefs_PrefSprite); + DragType = DRAG_PrefsSprite; + } + } + } + else if (Block[MouseB_Window] == ConfigWindow->MyHandle()) // sys config window + { + if ((Block[MouseB_Buttons] == 1) || (Block[MouseB_Buttons] == 4)) + { + if (Block[MouseB_Icon] == Icon_Conf_OK) + { + WindowToSysConf(); UseNULL--; + if (Block[MouseB_Buttons] == 4) {ConfigWindow->close();} + } + else if (Block[MouseB_Icon] == Icon_Conf_Save) + { + if (CheckFilename(ConfigWindow->ReadIconText(Icon_Conf_ConfPath)) == 0) + { + WindowToSysConf(); UseNULL--; ConfigWindow->close(); + the_c64->SaveSystemConfig(ConfigWindow->ReadIconText(Icon_Conf_ConfPath)); + } + } + } + else if ((Block[MouseB_Buttons] == 16) || (Block[MouseB_Buttons] == 64)) + { + if (Block[MouseB_Icon] == Icon_Conf_ConfSprite) + { + DragIconSprite(ConfigWindow, Icon_Conf_ConfSprite); + DragType = DRAG_ConfSprite; + } + } + } + else if (Block[MouseB_Window] == SoundWindow->MyHandle()) // sound window + { + if (Block[MouseB_Icon] == Icon_Sound_Volume) + { + if ((Block[MouseB_Buttons] == 1) || (Block[MouseB_Buttons] == 4)) // click + { + the_c64->HostVolume = CalculateVolume(Block); Sound_Volume(the_c64->HostVolume); + SoundWindow->ForceIconRedraw(Icon_Sound_Volume); + } + else if ((Block[MouseB_Buttons] == 16) || (Block[MouseB_Buttons] == 64)) // drag + { + int orgx, orgy; + + SoundWindow->getstate(AuxBlock); + orgx = AuxBlock[WindowB_VMinX] - AuxBlock[WindowB_ScrollX]; + orgy = AuxBlock[WindowB_VMaxY] - AuxBlock[WindowB_ScrollY]; + SoundWindow->GetIconState(Icon_Sound_Volume, &AuxBlock[DragB_BBMinX - IconB_MinX]); + AuxBlock[DragB_BBMinX] += orgx; AuxBlock[DragB_BBMinY] += orgy; + AuxBlock[DragB_BBMaxX] += orgx; AuxBlock[DragB_BBMaxY] += orgy; + AuxBlock[DragB_Type] = 7; + Wimp_DragBox(AuxBlock); + DragType = DRAG_VolumeWell; UseNULL++; + } + } + } + else if (Block[MouseB_Window] == SaveBox->MyHandle()) + { + if ((Block[MouseB_Buttons] == 1) || (Block[MouseB_Buttons] == 4)) + { + if (Block[MouseB_Icon] == Icon_Save_OK) + { + if (CheckFilename(SaveBox->ReadIconText(Icon_Save_Path)) == 0) + { + if (SaveType == SAVE_RAM) + { + strcpy(RAMFile+44,SaveBox->ReadIconText(Icon_Save_Path)); + the_c64->SaveRAM(RAMFile+44); + Wimp_CreateMenu((int*)-1,0,0); + SaveType = 0; + } + else if (SaveType == SAVE_Snapshot) + { + *(((int*)SnapFile) + MsgB_Sender) = 0; + strcpy(SnapFile+44,SaveBox->ReadIconText(Icon_Save_Path)); + IssueSnapshotRequest(); + } + } + } + } + else if ((Block[MouseB_Buttons] == 16) || (Block[MouseB_Buttons] == 64)) + { + if (Block[MouseB_Icon] == Icon_Save_Sprite) + { + DragIconSprite(SaveBox, Icon_Save_Sprite); + DragType = DRAG_SaveSprite; + } + } + } +} + + +// A drag operation has terminated +void WIMP::UserDrag(void) +{ + char *b = NULL; + int filetype, size; + + if ((CMOS_DragType == 0) || (DragType == DRAG_VolumeWell)) + { + Wimp_DragBox(NULL); + } + else + { + DragASprite_Stop(); + } + + if (DragType == DRAG_VolumeWell) + { + UseNULL--; DragType = 0; Sound_Volume(the_c64->HostVolume); // just set the new volume. + } + + // Drag of the path sprite + if (DragType == DRAG_PrefsSprite) + { + b = PrefsWindow->ReadIconText(Icon_Prefs_PrefPath); filetype = FileType_Text; + size = EstimatedPrefsSize; // can't say how large it's gonna be + } + else if (DragType == DRAG_ConfSprite) + { + b = ConfigWindow->ReadIconText(Icon_Conf_ConfPath); filetype = FileType_Text; + size = EstimatedConfSize; + } + else if (DragType == DRAG_SaveSprite) + { + b = SaveBox->ReadIconText(Icon_Save_Path); filetype = FileType_Data; + if (SaveType == SAVE_RAM) {size = EstimatedRAMSize;} + else if (SaveType == SAVE_Snapshot) {size = EstimatedSnapSize;} + else {size = 0;} + } + + // now b should point to the path and filetype should contain the type + if (b != NULL) + { + Wimp_GetPointerInfo(Block); + // Not on background and not on my own icon bar icon + if ((Block[MouseB_Window] != -1) && ((Block[MouseB_Window] != -2) || (Block[MouseB_Icon] != IBicon->IHandle))) + { + int handle = Block[MouseB_Window]; + + // None of my windows + if ((handle != EmuWindow->MyHandle()) && (handle != EmuPane->MyHandle()) && + (handle != PrefsWindow->MyHandle()) && (handle != ConfigWindow->MyHandle()) && + (handle != InfoWindow->MyHandle()) && (handle != SaveBox->MyHandle())) + { + char *d, c; + + d = b; c = *b++; + // get pointer to leafname in d + while (c > 32) {if ((c == '.') || (c == ':')) {d = b;} c = *b++;} + // Build message block + Block[5] = Block[MouseB_Window]; Block[6] = Block[MouseB_Icon]; + Block[7] = Block[MouseB_PosX]; Block[8] = Block[MouseB_PosY]; + Block[9] = size; Block[10] = filetype; + Block[MsgB_YourRef] = 0; Block[MsgB_Action] = Message_DataSave; + b = ((char*)Block) + 44; c = *d++; + while (c > 32) {*b++ = c; c = *d++;} + *b++ = 0; Block[MsgB_Size] = (((int)(b - (char*)Block)) + 3) & 0xfffffffc; + Wimp_SendMessage(18,Block,Block[5],Block[6]); + } + } + } + else {DragType = 0;} + LastDrag = 0; +} + + +void WIMP::KeyPressed(void) +{ + register int key = Block[KeyPB_Key]; + + if (Block[KeyPB_Window] == EmuWindow->MyHandle()) + { + if ((key >= 0x180) && (key <= 0x1fd)) // special keys (incl. FKeys) + { + key -= 0x180; + if ((((key & 0x4f) >= 0x05) && ((key & 0x4f) <= 0x09)) || // F5 -F9 [shift|ctrl] + (((key & 0x4f) >= 0x4a) && ((key & 0x4f) <= 0x4c))) // F10-F12 [shift|ctrl] + { + if ((key != 0x06) && (key != 0x07) && (key != 0x08)) + { + Wimp_ProcessKey(Block[KeyPB_Key]); + } + } + } + return; + } + else if (Block[KeyPB_Window] == PrefsWindow->MyHandle()) + { + if (key == Key_Return) + { + WindowToThePrefs(); + if (Block[KeyPB_Icon] == Icon_Prefs_PrefPath) // return pressed in path string icon? + { + if (CheckFilename(PrefsWindow->ReadIconText(Icon_Prefs_PrefPath)) == 0) + { + ThePrefs.Save(PrefsWindow->ReadIconText(Icon_Prefs_PrefPath)); + } + } + return; + } + } + else if (Block[KeyPB_Window] == ConfigWindow->MyHandle()) + { + if ((key == Key_Return) && (Block[KeyPB_Icon] != -1)) + { + WindowToSysConf(); UseNULL--; ConfigWindow->close(); + if (Block[KeyPB_Icon] == Icon_Conf_ConfPath) // return pressed in path string icon? + { + if (CheckFilename(ConfigWindow->ReadIconText(Icon_Conf_ConfPath)) == 0) + { + the_c64->SaveSystemConfig(ConfigWindow->ReadIconText(Icon_Conf_ConfPath)); + } + } + return; + } + } + else if (Block[KeyPB_Window] == SaveBox->MyHandle()) + { + if (key == Key_Return) + { + if (Block[KeyPB_Icon] == Icon_Save_Path) + { + if (CheckFilename(SaveBox->ReadIconText(Icon_Save_Path)) == 0) + { + if (SaveType == SAVE_RAM) + { + strcpy(RAMFile+44,SaveBox->ReadIconText(Icon_Save_Path)); + the_c64->SaveRAM(RAMFile+44); + Wimp_CreateMenu((int*)-1,0,0); + SaveType = 0; + } + else if (SaveType == SAVE_Snapshot) + { + *(((int*)SnapFile) + MsgB_Sender) = 0; + strcpy(SnapFile+44,SaveBox->ReadIconText(Icon_Save_Path)); + IssueSnapshotRequest(); + } + } + } + return; + } + } + Wimp_ProcessKey(Block[KeyPB_Key]); +} + + +void WIMP::MenuSelection(void) +{ + int Buttons; + + Wimp_GetPointerInfo(AuxBlock); Buttons = AuxBlock[MouseB_Buttons]; + + switch (LastMenu) + { + case Menu_IBar: + if (Block[0] == Menu_IBar_Quit) {EmuPaused = false; the_c64->Quit();} + else if (Block[0] == Menu_IBar_Prefs) + { + // Is it already open? Then don't do anything + if (!PrefsWindow->OpenStatus()) + { + int y; + + // Open Prefs window with top left corner in top left corner of screen + PrefsWindow->getstate(AuxBlock); + y = the_c64->TheDisplay->screen->resy; + AuxBlock[WindowB_VMaxX] -= AuxBlock[WindowB_VMinX]; AuxBlock[WindowB_VMinX] = 0; + AuxBlock[WindowB_VMinY] = y - (AuxBlock[WindowB_VMaxY] - AuxBlock[WindowB_VMinY]); + AuxBlock[WindowB_VMaxY] = y; + // Open Prefs window on top + AuxBlock[WindowB_Stackpos] = -1; + PrefsWindow->open(AuxBlock); + } + else + { + PrefsWindow->getstate(AuxBlock); + AuxBlock[WindowB_Stackpos] = -1; PrefsWindow->open(AuxBlock); + } + } + else if (Block[0] == Menu_IBar_Config) + { + if (!ConfigWindow->OpenStatus()) + { + int x, y; + + // Update window contents + SysConfToWindow(); + // Open config window in top right corner of screen + ConfigWindow->getstate(AuxBlock); + x = the_c64->TheDisplay->screen->resx; y = the_c64->TheDisplay->screen->resy; + AuxBlock[WindowB_VMinX] = x - (AuxBlock[WindowB_VMaxX] - AuxBlock[WindowB_VMinX]); + AuxBlock[WindowB_VMaxX] = x; + AuxBlock[WindowB_VMinY] = y - (AuxBlock[WindowB_VMaxY] - AuxBlock[WindowB_VMinY]); + AuxBlock[WindowB_VMaxY] = y; + AuxBlock[WindowB_Stackpos] = -1; + ConfigWindow->open(AuxBlock); + // We need NULL-events for the low-level keyboard scan. + UseNULL++; + } + else + { + ConfigWindow->getstate(AuxBlock); + AuxBlock[WindowB_Stackpos] = -1; ConfigWindow->open(AuxBlock); + } + } + if (Buttons == 1) {Wimp_CreateMenu((int*)&MenuIconBar,0,0);} + break; + case Menu_Emulator: + if (Buttons == 1) {Wimp_CreateMenu((int*)&MenuEmuWindow,0,0);} + break; + default: break; + } +} + + +// Handle regular messages +void WIMP::UserMessage(void) +{ + C64Display *disp = the_c64->TheDisplay; + int i; + + switch (Block[MsgB_Action]) // Message Action + { + case Message_Quit: + EmuPaused = false; the_c64->Quit(); break; + case Message_ModeChange: + disp->ModeChange(); the_c64->TheVIC->ReInitColors(); SetEmuWindowSize(); + // The window could have changed position ==> reposition pane as well! + // we have to force the window to the screen manually + EmuWindow->getstate(AuxBlock); + if ((AuxBlock[WindowB_WFlags] & (1<<16)) != 0) // is it open anyway? + { + i = AuxBlock[WindowB_VMaxY] - AuxBlock[WindowB_VMinY]; + if (AuxBlock[WindowB_VMaxY] > disp->screen->resy - TitleBarHeight) + { + AuxBlock[WindowB_VMaxY] = disp->screen->resy - TitleBarHeight; + if ((AuxBlock[WindowB_VMinY] = AuxBlock[WindowB_VMaxY] - i) < TitleBarHeight) + { + AuxBlock[WindowB_VMinY] = TitleBarHeight; + } + } + i = AuxBlock[WindowB_VMaxX] - AuxBlock[WindowB_VMinX]; + if (AuxBlock[WindowB_VMaxX] > disp->screen->resx - TitleBarHeight) + { + AuxBlock[WindowB_VMaxX] = disp->screen->resx - TitleBarHeight; + if ((AuxBlock[WindowB_VMinX] = AuxBlock[WindowB_VMaxX] - i) < 0) + { + AuxBlock[WindowB_VMinX] = 0; + } + } + // Don't you just love it -- you can't open the window directly, you need + // a delay... like for instance by sending yourself an OpenWindow message... + Wimp_SendMessage(2,AuxBlock,TaskHandle,0); + } + break; + case Message_PaletteChange: + // Updating EmuWindow is pointless since the bitmap still contains data for another mode + disp->ModeChange(); the_c64->TheVIC->ReInitColors(); + break; + case Message_DataSave: + i = -1; // indicator whether file is accepted + if ((Block[5] == EmuWindow->MyHandle()) && (Block[10] == FileType_C64File)) {i=0;} + else if ((Block[5] == EmuWindow->MyHandle()) && (Block[10] == FileType_Data)) {i=0;} + else if ((Block[5] == PrefsWindow->MyHandle()) && ((Block[10] == FileType_Text) || (Block[10] == FileType_C64File))) {i=0;} + else if ((Block[5] == ConfigWindow->MyHandle()) && (Block[10] == FileType_Text)) {i=0;} + if (i >= 0) + { + Block[9] = -1; // file unsafe + strcpy(((char*)Block)+44,WIMP_SCRAP_FILE); + Block[MsgB_Size] = (48 + strlen(WIMP_SCRAP_FILE)) & 0xfffffffc; + Block[MsgB_YourRef] = Block[MsgB_MyRef]; Block[MsgB_Action] = Message_DataSaveAck; + Wimp_SendMessage(17,Block,Block[MsgB_Sender],Block[6]); + UseScrap = true; + } + break; + case Message_DataLoad: + if (Block[5] == EmuWindow->MyHandle()) // Emulator window: load file? + { + if (Block[10] == FileType_C64File) // Load only files with type &64 this way + { + FILE *fp; + + if ((fp = fopen(((char*)Block)+44,"rb")) != NULL) + { + uint8 lo, hi, *mem = the_c64->RAM; + int length; + + lo = fgetc(fp); hi = fgetc(fp); length = lo + (hi<<8); + length += fread(mem+length,1,0x10000-length,fp); + // length is now end-address + fclose(fp); + mem[0xc3] = lo; mem[0xc4] = hi; // Load-address + lo = length & 0xff; hi = (length >> 8) & 0xff; + mem[0xae] = mem[0x2d] = mem[0x2f] = mem[0x31] = mem[0x33] = lo; + mem[0xaf] = mem[0x2e] = mem[0x30] = mem[0x32] = mem[0x34] = hi; + Block[MsgB_YourRef] = Block[MsgB_MyRef]; Block[MsgB_Action] = Message_DataLoadAck; + Wimp_SendMessage(17,Block,Block[MsgB_Sender],Block[6]); + } + } + else if (Block[10] == FileType_Data) + { + if (the_c64->LoadSnapshot(((char*)Block)+44)) + { + Block[MsgB_YourRef] = Block[MsgB_MyRef]; Block[MsgB_Action] = Message_DataLoadAck; + Wimp_SendMessage(17,Block,Block[MsgB_Sender],Block[6]); + } + } + } + else if (Block[5] == PrefsWindow->MyHandle()) // Prefs window? + { + if (Block[10] == FileType_Text) // load a prefs file? + { + Prefs *prefs = new Prefs(ThePrefs); + + prefs->Load(((char*)Block)+44); + the_c64->NewPrefs(prefs); + ThePrefs = *prefs; + delete prefs; + PrefsWindow->WriteIconText(Icon_Prefs_PrefPath,((char*)Block)+44); + ThePrefsToWindow(); + Block[MsgB_YourRef] = Block[MsgB_MyRef]; Block[MsgB_Action] = Message_DataLoadAck; + Wimp_SendMessage(17,Block,Block[MsgB_Sender],Block[6]); + } + else if ((Block[6] == Icon_Prefs_XROMPath) && (Block[10] == FileType_C64File)) + { + PrefsWindow->WriteIconText(Icon_Prefs_XROMPath,((char*)Block)+44); + Block[MsgB_YourRef] = Block[MsgB_MyRef]; Block[MsgB_Action] = Message_DataLoadAck; + Wimp_SendMessage(17,Block,Block[MsgB_Sender],Block[6]); + } + else // interpret as drive path (if dragged on one of the drive path icons) + { + switch (Block[6]) + { + case Icon_Prefs_Dr8Path: i = 0; break; + case Icon_Prefs_Dr9Path: i = 1; break; + case Icon_Prefs_Dr10Path: i = 2; break; + case Icon_Prefs_Dr11Path: i = 3; break; + default: i = -1; break; + } + if (i >= 0) {NewDriveImage(i,Block,false);} + } + } + else if (Block[5] == ConfigWindow->MyHandle()) // load sys config file + { + if (Block[10] == FileType_Text) + { + the_c64->LoadSystemConfig(((char*)Block)+44); + SysConfToWindow(); + ConfigWindow->WriteIconText(Icon_Conf_ConfPath,((char*)Block)+44); + Block[MsgB_YourRef] = Block[MsgB_MyRef]; Block[MsgB_Action] = Message_DataLoadAck; + Wimp_SendMessage(17,Block,Block[MsgB_Sender],Block[6]); + } + } + else if (Block[5] == EmuPane->MyHandle()) // emulator pane + { + switch (Block[6]) + { + case Icon_Pane_Drive0: + case Icon_Pane_LED0: i = 0; break; + case Icon_Pane_Drive1: + case Icon_Pane_LED1: i = 1; break; + case Icon_Pane_Drive2: + case Icon_Pane_LED2: i = 2; break; + case Icon_Pane_Drive3: + case Icon_Pane_LED3: i = 3; break; + default: i = -1; break; + } + if (i >= 0) {NewDriveImage(i,Block,true);} + } + // Clean up if necessary + if (UseScrap) {DeleteFile(WIMP_SCRAP_FILE); UseScrap = false;} + break; + case Message_DataSaveAck: + if (DragType == DRAG_PrefsSprite) + { + WindowToThePrefs(); // read window entries + ThePrefs.Save(((char*)Block)+44); + if (Block[9] != -1) // we're talking to the filer ==> set new pathname + { + PrefsWindow->WriteIconText(Icon_Prefs_PrefPath,((char*)Block)+44); + } + Block[MsgB_YourRef] = Block[MsgB_MyRef]; Block[MsgB_Action] = Message_DataLoad; + Wimp_SendMessage(18,Block,Block[MsgB_Sender],Block[6]); + } + else if (DragType == DRAG_ConfSprite) + { + WindowToSysConf(); // read window entries + the_c64->SaveSystemConfig(((char*)Block)+44); + if (Block[9] != -1) + { + ConfigWindow->WriteIconText(Icon_Conf_ConfPath,((char*)Block)+44); + } + Block[MsgB_YourRef] = Block[MsgB_MyRef]; Block[MsgB_Action] = Message_DataLoad; + Wimp_SendMessage(18,Block,Block[MsgB_Sender],Block[6]); + } + else if (DragType == DRAG_SaveSprite) + { + if (SaveType == SAVE_RAM) + { + memcpy(RAMFile,(char*)Block,256); the_c64->SaveRAM(RAMFile+44); + Block[MsgB_YourRef] = Block[MsgB_MyRef]; Block[MsgB_Action] = Message_DataLoad; + Wimp_SendMessage(18,Block,Block[MsgB_Sender],Block[6]); + } + else if (SaveType == SAVE_Snapshot) + { + memcpy(SnapFile,(char*)Block,256); + IssueSnapshotRequest(); + } + } + break; + case Message_DataLoadAck: + if (DragType == DRAG_ConfSprite) + { + UseNULL--; ConfigWindow->close(); + } + if (DragType == DRAG_SaveSprite) + { + Wimp_CreateMenu((int*)-1,0,0); + } + DragType = SaveType = 0; break; + case Message_MenuWarning: + if (LastMenu == Menu_Emulator) + { + if (Block[8] == Menu_EWind_SaveRAM) + { + SaveType = SAVE_RAM; SaveBox->WriteIconText(Icon_Save_Path,RAMFile+44); + } + else if (Block[8] == Menu_EWind_Snapshot) + { + SaveType = SAVE_Snapshot; SaveBox->WriteIconText(Icon_Save_Path,SnapFile+44); + } + else {SaveType = 0;} + Wimp_CreateSubMenu((int*)Block[5],Block[6],Block[7]); + } + break; + default: break; + } +} + + +// If a recorded message was not answered, i.e. something went wrong. +void WIMP::UserMessageAck(void) +{ + switch(Block[MsgB_Action]) + { + case Message_DataSave: + sprintf(WimpError.errmess,"Can't save data."); break; + case Message_DataLoad: + sprintf(WimpError.errmess,"Receiver couldn't load data."); break; + default: + sprintf(WimpError.errmess,"Some error occurred..."); break; + } + WimpError.errnum = 0; Wimp_ReportError(&WimpError,1,TASKNAME); +} diff --git a/Src/AcornGUI.h b/Src/AcornGUI.h new file mode 100644 index 0000000..f8da0aa --- /dev/null +++ b/Src/AcornGUI.h @@ -0,0 +1,357 @@ +/* + * AcornGUI.h - Defines variables for the WIMP interface + * + * (C) 1997 Andreas Dehmel + * + * Frodo (C) 1994-1997,2002-2005 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + + +#ifndef _ACORN_GUI_H_ +#define _ACORN_GUI_H_ + + +// Determine which paths to load from +#ifdef FRODO_SC +# define DEFAULT_PREFS "FrodoSC:Prefs" +# define DEFAULT_SYSCONF "FrodoSC:SysConf" +#else +# ifdef FRODO_PC +# define DEFAULT_PREFS "FrodoPC:Prefs" +# define DEFAULT_SYSCONF "FrodoPC:SysConf" +# else +# define DEFAULT_PREFS "Frodo:Prefs" +# define DEFAULT_SYSCONF "Frodo:SysConf" +# endif +#endif + + +// Text written in pane icons: +#define PANE_TEXT_PAUSE "Pause" +#define PANE_TEXT_RESUME "Cont" +#define PANE_TEXT_ZOOM1 "1 x" +#define PANE_TEXT_ZOOM2 "2 x" + + +// OS units of extra space between EmuWindow and Pane +#define EmuPaneSpace 2 +// OS units of the (volume) well's border +#define WellBorder 12 +// Height of title bar in OS units +#define TitleBarHeight 44 +// Maximum volume of the Sound system +#define MaximumVolume 127 + + + + +// Message Block indices +#define MsgB_Size 0 +#define MsgB_Sender 1 +#define MsgB_MyRef 2 +#define MsgB_YourRef 3 +#define MsgB_Action 4 + + +// Messages +#define Message_Quit 0x00000 +#define Message_DataSave 0x00001 +#define Message_DataSaveAck 0x00002 +#define Message_DataLoad 0x00003 +#define Message_DataLoadAck 0x00004 +#define Message_DataOpen 0x00005 +#define Message_RAMFetch 0x00006 +#define Message_RAMTransmit 0x00007 +#define Message_PreQuit 0x00008 +#define Message_PaletteChange 0x00009 +#define Message_MenuWarning 0x400c0 +#define Message_ModeChange 0x400c1 + + +// Redraw Window Block +#define RedrawB_Handle 0 +#define RedrawB_VMinX 1 +#define RedrawB_VMinY 2 +#define RedrawB_VMaxX 3 +#define RedrawB_VMaxY 4 +#define RedrawB_ScrollX 5 +#define RedrawB_ScrollY 6 +#define RedrawB_CMinX 7 +#define RedrawB_CMinY 8 +#define RedrawB_CMaxX 9 +#define RedrawB_CMaxY 10 + + +// Window block (e.g. open, getstate.... For create: subtract -1 (no handle)) +#define WindowB_Handle 0 +#define WindowB_VMinX 1 +#define WindowB_VMinY 2 +#define WindowB_VMaxX 3 +#define WindowB_VMaxY 4 +#define WindowB_ScrollX 5 +#define WindowB_ScrollY 6 +#define WindowB_Stackpos 7 +#define WindowB_WFlags 8 +#define WindowB_Colours1 9 +#define WindowB_Colours2 10 +#define WindowB_WMinX 11 +#define WindowB_WMinY 12 +#define WindowB_WMaxX 13 +#define WindowB_WMaxY 14 +#define WindowB_TFlags 15 +#define WindowB_WAFlags 16 +#define WindowB_SpriteArea 17 +#define WindowB_MinDims 18 +#define WindowB_Data 19 +#define WindowB_Icons 22 + + +// Raw icon block +#define RawIB_MinX 0 +#define RawIB_MinY 1 +#define RawIB_MaxX 2 +#define RawIB_MaxY 3 +#define RawIB_Flags 4 +#define RawIB_Data0 5 +#define RawIB_Data1 6 +#define RawIB_Data2 7 + + +// Icon block (as in GetIconState) +#define IconB_Handle 0 +#define IconB_Number 1 +#define IconB_MinX 2 +#define IconB_MinY 3 +#define IconB_MaxX 4 +#define IconB_MaxY 5 +#define IconB_Flags 6 +#define IconB_Data0 7 +#define IconB_Data1 8 +#define IconB_Data2 9 + + +// Mouse click block (also: get pointer info): +#define MouseB_PosX 0 +#define MouseB_PosY 1 +#define MouseB_Buttons 2 +#define MouseB_Window 3 +#define MouseB_Icon 4 + + +// Key pressed block +#define KeyPB_Window 0 +#define KeyPB_Icon 1 +#define KeyPB_PosX 2 +#define KeyPB_PosY 3 +#define KeyPB_CHeight 4 +#define KeyPB_Index 5 +#define KeyPB_Key 6 + + +// Drag Block +#define DragB_Handle 0 +#define DragB_Type 1 +#define DragB_IMinX 2 +#define DragB_IMinY 3 +#define DragB_IMaxX 4 +#define DragB_IMaxY 5 +#define DragB_BBMinX 6 +#define DragB_BBMinY 7 +#define DragB_BBMaxX 8 +#define DragB_BBMaxY 9 +#define DragB_R12 10 +#define DragB_DrawCode 11 +#define DragB_RemoveCode 12 +#define DragB_MoveCode 13 + + +// Drag A Sprite Block +#define DASB_MinX 0 +#define DASB_MinY 1 +#define DASB_MaxX 2 +#define DASB_MaxY 3 + + + + + +// Menu definitions +#define Menu_IBar 1 +#define Menu_Emulator 2 + +#define Menu_Height 44 +#define Menu_Flags 0x07003011 + +#define Menu_IBar_Items 5 +#define Menu_IBar_Width 256 +#define Menu_IBar_Info 0 +#define Menu_IBar_Prefs 1 +#define Menu_IBar_Config 2 +#define Menu_IBar_Sound 3 +#define Menu_IBar_Quit 4 + +#define Menu_EWind_Items 4 +#define Menu_EWind_Width 200 +#define Menu_EWind_Info 0 +#define Menu_EWind_Sound 1 +#define Menu_EWind_SaveRAM 2 +#define Menu_EWind_Snapshot 3 + + + + + +// Icons used in window definitions: +#define Icon_Pane_LED0 1 +#define Icon_Pane_LED1 3 +#define Icon_Pane_LED2 5 +#define Icon_Pane_LED3 7 +#define Icon_Pane_Drive0 0 +#define Icon_Pane_Drive1 2 +#define Icon_Pane_Drive2 4 +#define Icon_Pane_Drive3 6 +#define Icon_Pane_Reset 8 +#define Icon_Pane_Pause 9 +#define Icon_Pane_Speed 10 +#define Icon_Pane_Toggle 11 + +#define Icon_Prefs_Dr8DIR 6 +#define Icon_Prefs_Dr8D64 7 +#define Icon_Prefs_Dr8T64 8 +#define Icon_Prefs_Dr8Path 9 +#define Icon_Prefs_Dr9DIR 11 +#define Icon_Prefs_Dr9D64 12 +#define Icon_Prefs_Dr9T64 13 +#define Icon_Prefs_Dr9Path 14 +#define Icon_Prefs_Dr10DIR 16 +#define Icon_Prefs_Dr10D64 17 +#define Icon_Prefs_Dr10T64 18 +#define Icon_Prefs_Dr10Path 19 +#define Icon_Prefs_Dr11DIR 21 +#define Icon_Prefs_Dr11D64 22 +#define Icon_Prefs_Dr11T64 23 +#define Icon_Prefs_Dr11Path 24 +#define Icon_Prefs_Emul1541 25 +#define Icon_Prefs_MapSlash 26 +#define Icon_Prefs_SIDNone 29 +#define Icon_Prefs_SIDDigi 30 +#define Icon_Prefs_SIDCard 31 +#define Icon_Prefs_SIDFilter 32 +#define Icon_Prefs_REUNone 35 +#define Icon_Prefs_REU128 36 +#define Icon_Prefs_REU256 37 +#define Icon_Prefs_REU512 38 +#define Icon_Prefs_SkipFLeft 41 +#define Icon_Prefs_SkipFRight 42 +#define Icon_Prefs_SkipFText 43 +#define Icon_Prefs_SprOn 47 +#define Icon_Prefs_SprColl 48 +#define Icon_Prefs_Joy1On 50 +#define Icon_Prefs_Joy2On 51 +#define Icon_Prefs_JoySwap 52 +#define Icon_Prefs_LimSpeed 55 +#define Icon_Prefs_FastReset 56 +#define Icon_Prefs_CIAHack 57 +#define Icon_Prefs_CycleNorm 64 +#define Icon_Prefs_CycleBad 65 +#define Icon_Prefs_CycleCIA 66 +#define Icon_Prefs_CycleFloppy 67 +#define Icon_Prefs_Cancel 68 +#define Icon_Prefs_OK 69 +#define Icon_Prefs_PrefPath 70 +#define Icon_Prefs_Save 71 +#define Icon_Prefs_PrefSprite 72 +#define Icon_Prefs_XROMOn 75 +#define Icon_Prefs_XROMPath 76 + +#define Icon_Conf_PollAfter 3 +#define Icon_Conf_SpeedAfter 5 +#define Icon_Conf_Joy1Up 15 +#define Icon_Conf_Joy1Down 16 +#define Icon_Conf_Joy1Left 17 +#define Icon_Conf_Joy1Right 18 +#define Icon_Conf_Joy1Fire 19 +#define Icon_Conf_Joy2Up 27 +#define Icon_Conf_Joy2Down 28 +#define Icon_Conf_Joy2Left 29 +#define Icon_Conf_Joy2Right 30 +#define Icon_Conf_Joy2Fire 31 +#define Icon_Conf_OK 32 +#define Icon_Conf_Save 33 +#define Icon_Conf_ConfPath 34 +#define Icon_Conf_ConfSprite 35 +#define Icon_Conf_SoundAfter 37 + +#define Icon_Info_Name 4 +#define Icon_Info_Purpose 5 +#define Icon_Info_Author 6 +#define Icon_Info_AuthorPort 7 +#define Icon_Info_Version 8 + +#define Icon_Sound_Volume 0 +#define Icon_Sound_Notes 1 + +#define Icon_Save_Sprite 0 +#define Icon_Save_Path 1 +#define Icon_Save_OK 2 + + + + +// Drag types +#define DRAG_PrefsSprite 1 +#define DRAG_ConfSprite 2 +#define DRAG_SaveSprite 3 +#define DRAG_VolumeWell 16 + + + +// Save types +#define SAVE_RAM 1 +#define SAVE_Snapshot 2 + + + + +// variables + +extern char LEDtoIcon[4]; +extern char DriveToIcon[16]; +extern char SIDtoIcon[3]; +extern char REUtoIcon[4]; + + + + + +// Plotter structs and variables +typedef struct { + int x, y, dimx, dimy; +} graph_env; + +#define PLOTTER_ARGS const graph_env *GraphEnv, const int *Clipwindow,\ + const uint8 *Bitmap, const unsigned int *TransTab + +// Plotters provided in Plotters.s -- declare as C-functions ! +extern "C" +{ +extern void PlotZoom1(PLOTTER_ARGS); +extern void PlotZoom2(PLOTTER_ARGS); +} + +#endif diff --git a/Src/AcornGUI_SC.cc b/Src/AcornGUI_SC.cc new file mode 100644 index 0000000..5721819 --- /dev/null +++ b/Src/AcornGUI_SC.cc @@ -0,0 +1,26 @@ +/* + * AcornGUI_SC.cc + * + * The RISC OS port needs to recompile AcornGUI_SC.cc with FRODO_SC defined + * or it won't work. Source code is identical with AcornGUI.cc + * + * (C) 1997 Andreas Dehmel + * + * Frodo (C) 1994-1997,2002-2005 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "AcornGUI.cc" diff --git a/Src/AmigaGUI.c b/Src/AmigaGUI.c new file mode 100644 index 0000000..eef5cb7 --- /dev/null +++ b/Src/AmigaGUI.c @@ -0,0 +1,407 @@ +/* + * Source machine generated by GadToolsBox V2.0b + * which is (c) Copyright 1991-1993 Jaba Development + * + * GUI Designed by : Christian Bauer + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "AmigaGUI.h" + +struct Screen *Scr = NULL; +UBYTE *PubScreenName = NULL; +APTR VisualInfo = NULL; +struct Window *PrefsWnd = NULL; +struct Gadget *PrefsGList = NULL; +struct Menu *PrefsMenus = NULL; +struct IntuiMessage PrefsMsg; +UWORD PrefsZoom[4]; +struct Gadget *PrefsGadgets[31]; +UWORD PrefsLeft = 0; +UWORD PrefsTop = 16; +UWORD PrefsWidth = 561; +UWORD PrefsHeight = 238; +UBYTE *PrefsWdt = (UBYTE *)"Frodo Preferences"; +struct TextAttr *Font, Attr; +UWORD FontX, FontY; +UWORD OffX, OffY; + +UBYTE *SIDType0Labels[] = { + (UBYTE *)"None", + (UBYTE *)"Digital (AHI)", + (UBYTE *)"SID Card", + NULL }; + +UBYTE *REUSize0Labels[] = { + (UBYTE *)"None", + (UBYTE *)"128K", + (UBYTE *)"256K", + (UBYTE *)"512K", + NULL }; + +UBYTE *DriveType80Labels[] = { + (UBYTE *)"DIR", + (UBYTE *)"D64", + (UBYTE *)"T64", + NULL }; + +UBYTE *DriveType90Labels[] = { + (UBYTE *)"DIR", + (UBYTE *)"D64", + (UBYTE *)"T64", + NULL }; + +UBYTE *DriveType100Labels[] = { + (UBYTE *)"DIR", + (UBYTE *)"D64", + (UBYTE *)"T64", + NULL }; + +UBYTE *DriveType110Labels[] = { + (UBYTE *)"DIR", + (UBYTE *)"D64", + (UBYTE *)"T64", + NULL }; + +struct IntuiText PrefsIText[] = { + 2, 0, JAM1,34, 115, NULL, (UBYTE *)"Drive", NULL }; + +#define Prefs_TNUM 1 + +struct NewMenu PrefsNewMenu[] = { + NM_TITLE, (STRPTR)"Preferences", NULL, 0, NULL, NULL, + NM_ITEM, (STRPTR)"Open...", (STRPTR)"O", 0, 0L, (APTR)PrefsOpen, + NM_ITEM, (STRPTR)"Save", (STRPTR)"S", 0, 0L, (APTR)PrefsSave, + NM_ITEM, (STRPTR)"Save As...", (STRPTR)"A", 0, 0L, (APTR)PrefsSaveAs, + NM_ITEM, (STRPTR)"Revert", (STRPTR)"R", 0, 0L, (APTR)PrefsRevert, + NM_ITEM, (STRPTR)NM_BARLABEL, NULL, 0, 0L, NULL, + NM_ITEM, (STRPTR)"OK", NULL, 0, 0L, (APTR)PrefsOK, + NM_ITEM, (STRPTR)"Cancel", NULL, 0, 0L, (APTR)PrefsCancel, + NM_END, NULL, NULL, 0, 0L, NULL }; + +UWORD PrefsGTypes[] = { + CHECKBOX_KIND, + CHECKBOX_KIND, + CHECKBOX_KIND, + CHECKBOX_KIND, + CHECKBOX_KIND, + CHECKBOX_KIND, + CHECKBOX_KIND, + CHECKBOX_KIND, + INTEGER_KIND, + INTEGER_KIND, + INTEGER_KIND, + INTEGER_KIND, + INTEGER_KIND, + CYCLE_KIND, + CYCLE_KIND, + STRING_KIND, + CYCLE_KIND, + STRING_KIND, + CYCLE_KIND, + STRING_KIND, + CYCLE_KIND, + STRING_KIND, + CYCLE_KIND, + CHECKBOX_KIND, + CHECKBOX_KIND, + BUTTON_KIND, + BUTTON_KIND, + BUTTON_KIND, + BUTTON_KIND, + BUTTON_KIND, + BUTTON_KIND +}; + +struct NewGadget PrefsNGad[] = { + 8, 4, 26, 11, (UBYTE *)"Sprite display", NULL, GD_SpritesOn, PLACETEXT_RIGHT, NULL, (APTR)SpritesOnClicked, + 8, 16, 26, 11, (UBYTE *)"Sprite collisions", NULL, GD_SpriteCollisions, PLACETEXT_RIGHT, NULL, (APTR)SpriteCollisionsClicked, + 8, 28, 26, 11, (UBYTE *)"Joystick connected", NULL, GD_Joystick2On, PLACETEXT_RIGHT, NULL, (APTR)Joystick2OnClicked, + 8, 40, 26, 11, (UBYTE *)"Map joystick to port 1", NULL, GD_JoystickSwap, PLACETEXT_RIGHT, NULL, (APTR)JoystickSwapClicked, + 8, 52, 26, 11, (UBYTE *)"Limit speed", NULL, GD_LimitSpeed, PLACETEXT_RIGHT, NULL, (APTR)LimitSpeedClicked, + 8, 64, 26, 11, (UBYTE *)"Fast reset", NULL, GD_FastReset, PLACETEXT_RIGHT, NULL, (APTR)FastResetClicked, + 8, 76, 26, 11, (UBYTE *)"Clear CIA ICR on write", NULL, GD_CIAIRQHack, PLACETEXT_RIGHT, NULL, (APTR)CIAIRQHackClicked, + 8, 88, 26, 11, (UBYTE *)"SID filters", NULL, GD_SIDFilters, PLACETEXT_RIGHT, NULL, (APTR)SIDFiltersClicked, + 490, 4, 65, 14, (UBYTE *)"Cycles per line (CPU)", NULL, GD_NormalCycles, PLACETEXT_LEFT, NULL, (APTR)NormalCyclesClicked, + 490, 19, 65, 14, (UBYTE *)"Cycles per Bad Line (CPU)", NULL, GD_BadLineCycles, PLACETEXT_LEFT, NULL, (APTR)BadLineCyclesClicked, + 490, 34, 65, 14, (UBYTE *)"Cycles per line (CIA)", NULL, GD_CIACycles, PLACETEXT_LEFT, NULL, (APTR)CIACyclesClicked, + 490, 49, 65, 14, (UBYTE *)"Cycles per line (1541)", NULL, GD_FloppyCycles, PLACETEXT_LEFT, NULL, (APTR)FloppyCyclesClicked, + 490, 64, 65, 14, (UBYTE *)"Draw every n-th frame", NULL, GD_SkipFrames, PLACETEXT_LEFT, NULL, (APTR)SkipFramesClicked, + 426, 79, 129, 14, (UBYTE *)"SID emulation type", NULL, GD_SIDType, PLACETEXT_LEFT, NULL, (APTR)SIDTypeClicked, + 426, 94, 129, 14, (UBYTE *)"REU size", NULL, GD_REUSize, PLACETEXT_LEFT, NULL, (APTR)REUSizeClicked, + 47, 123, 401, 14, (UBYTE *)"8", NULL, GD_DrivePath8, PLACETEXT_LEFT, NULL, (APTR)DrivePath8Clicked, + 470, 123, 65, 14, NULL, NULL, GD_DriveType8, 0, NULL, (APTR)DriveType8Clicked, + 47, 138, 401, 14, (UBYTE *)"9", NULL, GD_DrivePath9, PLACETEXT_LEFT, NULL, (APTR)DrivePath9Clicked, + 470, 138, 65, 14, NULL, NULL, GD_DriveType9, 0, NULL, (APTR)DriveType9Clicked, + 47, 153, 401, 14, (UBYTE *)"10", NULL, GD_DrivePath10, PLACETEXT_LEFT, NULL, (APTR)DrivePath10Clicked, + 470, 153, 65, 14, NULL, NULL, GD_DriveType10, 0, NULL, (APTR)DriveType10Clicked, + 47, 168, 401, 14, (UBYTE *)"11", NULL, GD_DrivePath11, PLACETEXT_LEFT, NULL, (APTR)DrivePath11Clicked, + 470, 168, 65, 14, NULL, NULL, GD_DriveType11, 0, NULL, (APTR)DriveType11Clicked, + 20, 186, 26, 11, (UBYTE *)"Map '/'<->'\' in filenames", NULL, GD_MapSlash, PLACETEXT_RIGHT, NULL, (APTR)MapSlashClicked, + 20, 198, 26, 11, (UBYTE *)"Enable 1541 processor emulation", NULL, GD_Emul1541Proc, PLACETEXT_RIGHT, NULL, (APTR)Emul1541ProcClicked, + 61, 218, 81, 16, (UBYTE *)"_OK", NULL, GD_OK, PLACETEXT_IN, NULL, (APTR)OKClicked, + 416, 218, 81, 16, (UBYTE *)"_Cancel", NULL, GD_Cancel, PLACETEXT_IN, NULL, (APTR)CancelClicked, + 448, 123, 20, 14, (UBYTE *)"·", NULL, GD_GetDrive8, PLACETEXT_IN, NULL, (APTR)GetDrive8Clicked, + 448, 138, 20, 14, (UBYTE *)"·", NULL, GD_GetDrive9, PLACETEXT_IN, NULL, (APTR)GetDrive9Clicked, + 448, 153, 20, 14, (UBYTE *)"·", NULL, GD_GetDrive10, PLACETEXT_IN, NULL, (APTR)GetDrive10Clicked, + 448, 168, 20, 14, (UBYTE *)"·", NULL, GD_GetDrive11, PLACETEXT_IN, NULL, (APTR)GetDrive11Clicked +}; + +ULONG PrefsGTags[] = { + (TAG_DONE), + (TAG_DONE), + (TAG_DONE), + (TAG_DONE), + (TAG_DONE), + (TAG_DONE), + (TAG_DONE), + (TAG_DONE), + (GTIN_Number), 0, (GTIN_MaxChars), 10, (STRINGA_Justification), (GACT_STRINGRIGHT), (TAG_DONE), + (GTIN_Number), 0, (GTIN_MaxChars), 10, (STRINGA_Justification), (GACT_STRINGRIGHT), (TAG_DONE), + (GTIN_Number), 0, (GTIN_MaxChars), 10, (STRINGA_Justification), (GACT_STRINGRIGHT), (TAG_DONE), + (GTIN_Number), 0, (GTIN_MaxChars), 10, (STRINGA_Justification), (GACT_STRINGRIGHT), (TAG_DONE), + (GTIN_Number), 0, (GTIN_MaxChars), 10, (STRINGA_Justification), (GACT_STRINGRIGHT), (TAG_DONE), + (GTCY_Labels), (ULONG)&SIDType0Labels[ 0 ], (TAG_DONE), + (GTCY_Labels), (ULONG)&REUSize0Labels[ 0 ], (TAG_DONE), + (GTST_MaxChars), 256, (TAG_DONE), + (GTCY_Labels), (ULONG)&DriveType80Labels[ 0 ], (TAG_DONE), + (GTST_MaxChars), 256, (TAG_DONE), + (GTCY_Labels), (ULONG)&DriveType90Labels[ 0 ], (TAG_DONE), + (GTST_MaxChars), 256, (TAG_DONE), + (GTCY_Labels), (ULONG)&DriveType100Labels[ 0 ], (TAG_DONE), + (GTST_MaxChars), 256, (TAG_DONE), + (GTCY_Labels), (ULONG)&DriveType110Labels[ 0 ], (TAG_DONE), + (TAG_DONE), + (TAG_DONE), + (GT_Underscore), '_', (TAG_DONE), + (GT_Underscore), '_', (TAG_DONE), + (TAG_DONE), + (TAG_DONE), + (TAG_DONE), + (TAG_DONE) +}; + +static UWORD ComputeX( UWORD value ) +{ + return(( UWORD )((( FontX * value ) + 4 ) / 8 )); +} + +static UWORD ComputeY( UWORD value ) +{ + return(( UWORD )((( FontY * value ) + 4 ) / 8 )); +} + +static void ComputeFont( UWORD width, UWORD height ) +{ + Font = &Attr; + Font->ta_Name = (STRPTR)Scr->RastPort.Font->tf_Message.mn_Node.ln_Name; + Font->ta_YSize = FontY = Scr->RastPort.Font->tf_YSize; + FontX = Scr->RastPort.Font->tf_XSize; + + OffX = Scr->WBorLeft; + OffY = Scr->RastPort.TxHeight + Scr->WBorTop + 1; + + if ( width && height ) { + if (( ComputeX( width ) + OffX + Scr->WBorRight ) > Scr->Width ) + goto UseTopaz; + if (( ComputeY( height ) + OffY + Scr->WBorBottom ) > Scr->Height ) + goto UseTopaz; + } + return; + +UseTopaz: + Font->ta_Name = (STRPTR)"topaz.font"; + FontX = FontY = Font->ta_YSize = 8; +} + +int SetupScreen( void ) +{ + if ( ! ( Scr = LockPubScreen( PubScreenName ))) + return( 1L ); + + ComputeFont( 0, 0 ); + + if ( ! ( VisualInfo = GetVisualInfo( Scr, TAG_DONE ))) + return( 2L ); + + return( 0L ); +} + +void CloseDownScreen( void ) +{ + if ( VisualInfo ) { + FreeVisualInfo( VisualInfo ); + VisualInfo = NULL; + } + + if ( Scr ) { + UnlockPubScreen( NULL, Scr ); + Scr = NULL; + } +} + +void PrefsRender( void ) +{ + struct IntuiText it; + UWORD cnt; + + ComputeFont( PrefsWidth, PrefsHeight ); + + + for ( cnt = 0; cnt < Prefs_TNUM; cnt++ ) { + CopyMem(( char * )&PrefsIText[ cnt ], ( char * )&it, (long)sizeof( struct IntuiText )); + it.ITextFont = Font; + it.LeftEdge = OffX + ComputeX( it.LeftEdge ) - ( IntuiTextLength( &it ) >> 1 ); + it.TopEdge = OffY + ComputeY( it.TopEdge ) - ( Font->ta_YSize >> 1 ); + PrintIText( PrefsWnd->RPort, &it, 0, 0 ); + } +} + +int HandlePrefsIDCMP( void ) +{ + struct IntuiMessage *m; + struct MenuItem *n; + int (*func)(); + BOOL running = TRUE; + + while( m = GT_GetIMsg( PrefsWnd->UserPort )) { + + CopyMem(( char * )m, ( char * )&PrefsMsg, (long)sizeof( struct IntuiMessage )); + + GT_ReplyIMsg( m ); + + switch ( PrefsMsg.Class ) { + + case IDCMP_REFRESHWINDOW: + GT_BeginRefresh( PrefsWnd ); + PrefsRender(); + GT_EndRefresh( PrefsWnd, TRUE ); + break; + + case IDCMP_VANILLAKEY: + running = PrefsVanillaKey(); + break; + + case IDCMP_GADGETUP: + func = ( void * )(( struct Gadget * )PrefsMsg.IAddress )->UserData; + running = func(); + break; + + case IDCMP_MENUPICK: + while( PrefsMsg.Code != MENUNULL ) { + n = ItemAddress( PrefsMenus, PrefsMsg.Code ); + func = (void *)(GTMENUITEM_USERDATA( n )); + running = func(); + PrefsMsg.Code = n->NextSelect; + } + break; + } + } + return( running ); +} + +int OpenPrefsWindow( void ) +{ + struct NewGadget ng; + struct Gadget *g; + UWORD lc, tc; + UWORD wleft = PrefsLeft, wtop = PrefsTop, ww, wh; + + ComputeFont( PrefsWidth, PrefsHeight ); + + ww = ComputeX( PrefsWidth ); + wh = ComputeY( PrefsHeight ); + + if (( wleft + ww + OffX + Scr->WBorRight ) > Scr->Width ) wleft = Scr->Width - ww; + if (( wtop + wh + OffY + Scr->WBorBottom ) > Scr->Height ) wtop = Scr->Height - wh; + + if ( ! ( g = CreateContext( &PrefsGList ))) + return( 1L ); + + for( lc = 0, tc = 0; lc < Prefs_CNT; lc++ ) { + + CopyMem((char * )&PrefsNGad[ lc ], (char * )&ng, (long)sizeof( struct NewGadget )); + + ng.ng_VisualInfo = VisualInfo; + ng.ng_TextAttr = Font; + ng.ng_LeftEdge = OffX + ComputeX( ng.ng_LeftEdge ); + ng.ng_TopEdge = OffY + ComputeY( ng.ng_TopEdge ); + ng.ng_Width = ComputeX( ng.ng_Width ); + ng.ng_Height = ComputeY( ng.ng_Height); + + PrefsGadgets[ lc ] = g = CreateGadgetA((ULONG)PrefsGTypes[ lc ], g, &ng, ( struct TagItem * )&PrefsGTags[ tc ] ); + + while( PrefsGTags[ tc ] ) tc += 2; + tc++; + + if ( NOT g ) + return( 2L ); + } + + if ( ! ( PrefsMenus = CreateMenus( PrefsNewMenu, GTMN_FrontPen, 0L, TAG_DONE ))) + return( 3L ); + + LayoutMenus( PrefsMenus, VisualInfo, TAG_DONE ); + + PrefsZoom[0] = PrefsZoom[1] = 0; + if ( PrefsWdt ) + PrefsZoom[2] = TextLength( &Scr->RastPort, (UBYTE *)PrefsWdt, strlen((char *)PrefsWdt )) + 80; + else + PrefsZoom[2] = 80L; + PrefsZoom[3] = Scr->WBorTop + Scr->RastPort.TxHeight + 1; + + if ( ! ( PrefsWnd = OpenWindowTags( NULL, + WA_Left, wleft, + WA_Top, wtop, + WA_Width, ww + OffX + Scr->WBorRight, + WA_Height, wh + OffY + Scr->WBorBottom, + WA_IDCMP, CHECKBOXIDCMP|INTEGERIDCMP|CYCLEIDCMP|STRINGIDCMP|BUTTONIDCMP|IDCMP_MENUPICK|IDCMP_VANILLAKEY|IDCMP_REFRESHWINDOW, + WA_Flags, WFLG_DRAGBAR|WFLG_DEPTHGADGET|WFLG_SMART_REFRESH|WFLG_SIMPLE_REFRESH|WFLG_ACTIVATE, + WA_Gadgets, PrefsGList, + WA_Title, PrefsWdt, + WA_ScreenTitle, "Frodo C64 Emulator", + WA_PubScreen, Scr, + WA_Zoom, PrefsZoom, + TAG_DONE ))) + return( 4L ); + + SetMenuStrip( PrefsWnd, PrefsMenus ); + GT_RefreshWindow( PrefsWnd, NULL ); + + PrefsRender(); + + return( 0L ); +} + +void ClosePrefsWindow( void ) +{ + if ( PrefsMenus ) { + ClearMenuStrip( PrefsWnd ); + FreeMenus( PrefsMenus ); + PrefsMenus = NULL; } + + if ( PrefsWnd ) { + CloseWindow( PrefsWnd ); + PrefsWnd = NULL; + } + + if ( PrefsGList ) { + FreeGadgets( PrefsGList ); + PrefsGList = NULL; + } +} + diff --git a/Src/AmigaGUI.gui b/Src/AmigaGUI.gui new file mode 100644 index 0000000000000000000000000000000000000000..162c72cfbd958c465c2d1d19cb4d4ada995e8fdd GIT binary patch literal 2068 zcmYjRd0dj&8vghMg(NU-QWFGH!#2gzqKuGSvUJLFoVsN$EmJc!h5oFeMLx(;GEFmY zrE@j4MRTUi%6qf>OQlfEoKbG8t$<`%60t}k+qs|3A2aVi=e*}U?{nVuBqhyf5hf9d z_qS;aQ={Xgl+=`UalZuC;=jR^HDG^8%pt~MPhk&7tvy{G z0Z2b(3h7WH^kGJoUP@gZwF7+r?(|faOT_$r3;wg96s$xLdw9;LEqgN(XWo^r3edN6 zwGq4y0HiB^-$;OBstHG{&m{OeT0WcYUXeY`q6*sP z65=+(pVmyr5WPgz1qx_h6_&EJKst)Gr+jbem>J&@hH_i+X0?dydI89o?ybEiwy1?q9V= zZpaLBf&p}YW?3q)XYc7W6O#v?#O9yXfgLdM#byCzDEOHPj3QXDHdu-m2b2X6b9tW zGO99%I?txpxe8>@FtzwA(@x_zYw=WVl^|y?0OO$X&>CjXyyI6+y&_T$d@-TvSF@~d zld>e0nx&bVUTohP=g?E&NP4^ON$>sbZpq1B8xa&}k-aC#DKDRpSfh}T1?W7l^Wau4 z04HxR%*sw#xG!v<253WR0BFdMm_9#cac(qYEHX8|k~rzRcyVC!NinBd99e$xYVSEN z0FE0yL9vABt-Jguxcd-2P>(RJAJ zRLoYsA=G=A*AHHz{yPPTC?u`ti9+6|M2S%OBT{-+ZMv5+My8XU>NhcXmU!eu$~Wl% zJSF856SwVISL19Ie1Yox6>IqBLH^AoKcMrsYyv>92W3IHP{&d(GJzN)37D<>4S}16 zF%H?#w|LwjpV4yT-z@q?UE9HV=>RMub^fTOP&xUW;@!d;8y|HN^-0~OqDsTBuuP>@ zJRLxHj>~L#@q;o-kHDZAAD%V_0d9CiAWHm zK2lI|2n;nW@_o}XQ*gkzVS#J_W0KvwQ^T~rz$J`wx` z&)W0-mf@`#ym-c}SqTnR;QR|m#jLvW!kS%tt)5itDfM6$1ZUId$yzZVW%i?9OUNL5 z1XwC^ew}@}ar?V;OM@Lax|P&Y)74$Ul(y=|%hL4_+Xs_)d3PcNAGsd3V5RJFcP8A# zVc1VZ*dcS44OrXJkk|Vo2DJv=07t4&^jOyA>yk=OpU}-&BhJmsxaO?)v>^agyFREJ zJ)4%r#0Uf;PO1>x@9zNMad(Yf@MlTZ`(Ieqs>iXmO5(jI%pIu*K^wW>k(z@Mq8YKa zzB!lDo#>U_9(A^fgBVwIe|ZsfOG+ou=B%EW-%2*5xU#cF6FSP=Z#i=qUFLeB^YL3r z6Q<%kI=-c@&9Cu#vx}(KwUn&1>C9+*>(xnJcja7*{w>5_VNkzW0#4t_$55x z`eWO_juH4i>gR3{r-X)}+|Z}VveS=IB;v4KN!Oa8h4JR*XQ10z{$rPMZ<|W9^8Qf{lXk1GrH)P-lg?(d{&zm6kz6 z5dhS6V<(nTPJB%rn51%$_-NWtQJC_OkGs(NSSkNpqtM5l5K3CVloSl}?{uJmXakQ| zPuUL@yM#lvm^)^l<&hqNIhl~4P0EA4{R=+R-CAfUZd7JS_MXB_STZv%E*7+eKz_L5 zz0NsXjH|9M6wzl5PQRYLzmN>g;AOZMXGTK{l9_X+9RGTe9m7FTYy0zT=CdscVk8vR zY=MXAj7NReCyf#DYS*)xLY#CsltpuE6xF*-Km`+(gAx?8YcUcYcD QqVj>UX*Z`rGdKGDAEb|T+yDRo literal 0 HcmV?d00001 diff --git a/Src/AmigaGUI.h b/Src/AmigaGUI.h new file mode 100644 index 0000000..3009b45 --- /dev/null +++ b/Src/AmigaGUI.h @@ -0,0 +1,153 @@ +/* + * Source machine generated by GadToolsBox V2.0b + * which is (c) Copyright 1991-1993 Jaba Development + * + * GUI Designed by : Christian Bauer + */ + +#define GetString( g ) ((( struct StringInfo * )g->SpecialInfo )->Buffer ) +#define GetNumber( g ) ((( struct StringInfo * )g->SpecialInfo )->LongInt ) + +#define GD_SpritesOn 0 +#define GD_SpriteCollisions 1 +#define GD_Joystick2On 2 +#define GD_JoystickSwap 3 +#define GD_LimitSpeed 4 +#define GD_FastReset 5 +#define GD_CIAIRQHack 6 +#define GD_SIDFilters 7 +#define GD_NormalCycles 8 +#define GD_BadLineCycles 9 +#define GD_CIACycles 10 +#define GD_FloppyCycles 11 +#define GD_SkipFrames 12 +#define GD_SIDType 13 +#define GD_REUSize 14 +#define GD_DrivePath8 15 +#define GD_DriveType8 16 +#define GD_DrivePath9 17 +#define GD_DriveType9 18 +#define GD_DrivePath10 19 +#define GD_DriveType10 20 +#define GD_DrivePath11 21 +#define GD_DriveType11 22 +#define GD_MapSlash 23 +#define GD_Emul1541Proc 24 +#define GD_OK 25 +#define GD_Cancel 26 +#define GD_GetDrive8 27 +#define GD_GetDrive9 28 +#define GD_GetDrive10 29 +#define GD_GetDrive11 30 + +#define GDX_SpritesOn 0 +#define GDX_SpriteCollisions 1 +#define GDX_Joystick2On 2 +#define GDX_JoystickSwap 3 +#define GDX_LimitSpeed 4 +#define GDX_FastReset 5 +#define GDX_CIAIRQHack 6 +#define GDX_SIDFilters 7 +#define GDX_NormalCycles 8 +#define GDX_BadLineCycles 9 +#define GDX_CIACycles 10 +#define GDX_FloppyCycles 11 +#define GDX_SkipFrames 12 +#define GDX_SIDType 13 +#define GDX_REUSize 14 +#define GDX_DrivePath8 15 +#define GDX_DriveType8 16 +#define GDX_DrivePath9 17 +#define GDX_DriveType9 18 +#define GDX_DrivePath10 19 +#define GDX_DriveType10 20 +#define GDX_DrivePath11 21 +#define GDX_DriveType11 22 +#define GDX_MapSlash 23 +#define GDX_Emul1541Proc 24 +#define GDX_OK 25 +#define GDX_Cancel 26 +#define GDX_GetDrive8 27 +#define GDX_GetDrive9 28 +#define GDX_GetDrive10 29 +#define GDX_GetDrive11 30 + +#define Prefs_CNT 31 + +extern struct IntuitionBase *IntuitionBase; +extern struct Library *GadToolsBase; + +extern struct Screen *Scr; +extern UBYTE *PubScreenName; +extern APTR VisualInfo; +extern struct Window *PrefsWnd; +extern struct Gadget *PrefsGList; +extern struct Menu *PrefsMenus; +extern struct IntuiMessage PrefsMsg; +extern UWORD PrefsZoom[4]; +extern struct Gadget *PrefsGadgets[31]; +extern UWORD PrefsLeft; +extern UWORD PrefsTop; +extern UWORD PrefsWidth; +extern UWORD PrefsHeight; +extern UBYTE *PrefsWdt; +extern struct TextAttr *Font, Attr; +extern UWORD FontX, FontY; +extern UWORD OffX, OffY; +extern UBYTE *SIDType0Labels[]; +extern UBYTE *REUSize0Labels[]; +extern UBYTE *DriveType80Labels[]; +extern UBYTE *DriveType90Labels[]; +extern UBYTE *DriveType100Labels[]; +extern UBYTE *DriveType110Labels[]; +extern struct IntuiText PrefsIText[]; +extern struct NewMenu PrefsNewMenu[]; +extern UWORD PrefsGTypes[]; +extern struct NewGadget PrefsNGad[]; +extern ULONG PrefsGTags[]; + +extern int SpritesOnClicked( void ); +extern int SpriteCollisionsClicked( void ); +extern int Joystick2OnClicked( void ); +extern int JoystickSwapClicked( void ); +extern int LimitSpeedClicked( void ); +extern int FastResetClicked( void ); +extern int CIAIRQHackClicked( void ); +extern int SIDFiltersClicked( void ); +extern int NormalCyclesClicked( void ); +extern int BadLineCyclesClicked( void ); +extern int CIACyclesClicked( void ); +extern int FloppyCyclesClicked( void ); +extern int SkipFramesClicked( void ); +extern int SIDTypeClicked( void ); +extern int REUSizeClicked( void ); +extern int DrivePath8Clicked( void ); +extern int DriveType8Clicked( void ); +extern int DrivePath9Clicked( void ); +extern int DriveType9Clicked( void ); +extern int DrivePath10Clicked( void ); +extern int DriveType10Clicked( void ); +extern int DrivePath11Clicked( void ); +extern int DriveType11Clicked( void ); +extern int MapSlashClicked( void ); +extern int Emul1541ProcClicked( void ); +extern int OKClicked( void ); +extern int CancelClicked( void ); +extern int GetDrive8Clicked( void ); +extern int GetDrive9Clicked( void ); +extern int GetDrive10Clicked( void ); +extern int GetDrive11Clicked( void ); +extern int PrefsOpen( void ); +extern int PrefsSave( void ); +extern int PrefsSaveAs( void ); +extern int PrefsRevert( void ); +extern int PrefsOK( void ); +extern int PrefsCancel( void ); + +extern int SetupScreen( void ); +extern void CloseDownScreen( void ); +extern void PrefsRender( void ); +extern int HandlePrefsIDCMP( void ); +extern int PrefsVanillaKey(); +extern int OpenPrefsWindow( void ); +extern void ClosePrefsWindow( void ); diff --git a/Src/Basic_ROM.h b/Src/Basic_ROM.h new file mode 100644 index 0000000..472dd57 --- /dev/null +++ b/Src/Basic_ROM.h @@ -0,0 +1,1033 @@ +/* + * Basic_ROM.h - C64 BASIC ROM + * + * Frodo (C) 1994-1997,2002-2005 Christian Bauer + * C64/1541 ROMs (C) Commodore Business Machines + */ + +static const uint8 builtin_basic_rom[BASIC_ROM_SIZE] = { + 0x94, 0xe3, 0x7b, 0xe3, 0x43, 0x42, 0x4d, 0x42, + 0x41, 0x53, 0x49, 0x43, 0x30, 0xa8, 0x41, 0xa7, + 0x1d, 0xad, 0xf7, 0xa8, 0xa4, 0xab, 0xbe, 0xab, + 0x80, 0xb0, 0x05, 0xac, 0xa4, 0xa9, 0x9f, 0xa8, + 0x70, 0xa8, 0x27, 0xa9, 0x1c, 0xa8, 0x82, 0xa8, + 0xd1, 0xa8, 0x3a, 0xa9, 0x2e, 0xa8, 0x4a, 0xa9, + 0x2c, 0xb8, 0x67, 0xe1, 0x55, 0xe1, 0x64, 0xe1, + 0xb2, 0xb3, 0x23, 0xb8, 0x7f, 0xaa, 0x9f, 0xaa, + 0x56, 0xa8, 0x9b, 0xa6, 0x5d, 0xa6, 0x85, 0xaa, + 0x29, 0xe1, 0xbd, 0xe1, 0xc6, 0xe1, 0x7a, 0xab, + 0x41, 0xa6, 0x39, 0xbc, 0xcc, 0xbc, 0x58, 0xbc, + 0x10, 0x03, 0x7d, 0xb3, 0x9e, 0xb3, 0x71, 0xbf, + 0x97, 0xe0, 0xea, 0xb9, 0xed, 0xbf, 0x64, 0xe2, + 0x6b, 0xe2, 0xb4, 0xe2, 0x0e, 0xe3, 0x0d, 0xb8, + 0x7c, 0xb7, 0x65, 0xb4, 0xad, 0xb7, 0x8b, 0xb7, + 0xec, 0xb6, 0x00, 0xb7, 0x2c, 0xb7, 0x37, 0xb7, + 0x79, 0x69, 0xb8, 0x79, 0x52, 0xb8, 0x7b, 0x2a, + 0xba, 0x7b, 0x11, 0xbb, 0x7f, 0x7a, 0xbf, 0x50, + 0xe8, 0xaf, 0x46, 0xe5, 0xaf, 0x7d, 0xb3, 0xbf, + 0x5a, 0xd3, 0xae, 0x64, 0x15, 0xb0, 0x45, 0x4e, + 0xc4, 0x46, 0x4f, 0xd2, 0x4e, 0x45, 0x58, 0xd4, + 0x44, 0x41, 0x54, 0xc1, 0x49, 0x4e, 0x50, 0x55, + 0x54, 0xa3, 0x49, 0x4e, 0x50, 0x55, 0xd4, 0x44, + 0x49, 0xcd, 0x52, 0x45, 0x41, 0xc4, 0x4c, 0x45, + 0xd4, 0x47, 0x4f, 0x54, 0xcf, 0x52, 0x55, 0xce, + 0x49, 0xc6, 0x52, 0x45, 0x53, 0x54, 0x4f, 0x52, + 0xc5, 0x47, 0x4f, 0x53, 0x55, 0xc2, 0x52, 0x45, + 0x54, 0x55, 0x52, 0xce, 0x52, 0x45, 0xcd, 0x53, + 0x54, 0x4f, 0xd0, 0x4f, 0xce, 0x57, 0x41, 0x49, + 0xd4, 0x4c, 0x4f, 0x41, 0xc4, 0x53, 0x41, 0x56, + 0xc5, 0x56, 0x45, 0x52, 0x49, 0x46, 0xd9, 0x44, + 0x45, 0xc6, 0x50, 0x4f, 0x4b, 0xc5, 0x50, 0x52, + 0x49, 0x4e, 0x54, 0xa3, 0x50, 0x52, 0x49, 0x4e, + 0xd4, 0x43, 0x4f, 0x4e, 0xd4, 0x4c, 0x49, 0x53, + 0xd4, 0x43, 0x4c, 0xd2, 0x43, 0x4d, 0xc4, 0x53, + 0x59, 0xd3, 0x4f, 0x50, 0x45, 0xce, 0x43, 0x4c, + 0x4f, 0x53, 0xc5, 0x47, 0x45, 0xd4, 0x4e, 0x45, + 0xd7, 0x54, 0x41, 0x42, 0xa8, 0x54, 0xcf, 0x46, + 0xce, 0x53, 0x50, 0x43, 0xa8, 0x54, 0x48, 0x45, + 0xce, 0x4e, 0x4f, 0xd4, 0x53, 0x54, 0x45, 0xd0, + 0xab, 0xad, 0xaa, 0xaf, 0xde, 0x41, 0x4e, 0xc4, + 0x4f, 0xd2, 0xbe, 0xbd, 0xbc, 0x53, 0x47, 0xce, + 0x49, 0x4e, 0xd4, 0x41, 0x42, 0xd3, 0x55, 0x53, + 0xd2, 0x46, 0x52, 0xc5, 0x50, 0x4f, 0xd3, 0x53, + 0x51, 0xd2, 0x52, 0x4e, 0xc4, 0x4c, 0x4f, 0xc7, + 0x45, 0x58, 0xd0, 0x43, 0x4f, 0xd3, 0x53, 0x49, + 0xce, 0x54, 0x41, 0xce, 0x41, 0x54, 0xce, 0x50, + 0x45, 0x45, 0xcb, 0x4c, 0x45, 0xce, 0x53, 0x54, + 0x52, 0xa4, 0x56, 0x41, 0xcc, 0x41, 0x53, 0xc3, + 0x43, 0x48, 0x52, 0xa4, 0x4c, 0x45, 0x46, 0x54, + 0xa4, 0x52, 0x49, 0x47, 0x48, 0x54, 0xa4, 0x4d, + 0x49, 0x44, 0xa4, 0x47, 0xcf, 0x00, 0x54, 0x4f, + 0x4f, 0x20, 0x4d, 0x41, 0x4e, 0x59, 0x20, 0x46, + 0x49, 0x4c, 0x45, 0xd3, 0x46, 0x49, 0x4c, 0x45, + 0x20, 0x4f, 0x50, 0x45, 0xce, 0x46, 0x49, 0x4c, + 0x45, 0x20, 0x4e, 0x4f, 0x54, 0x20, 0x4f, 0x50, + 0x45, 0xce, 0x46, 0x49, 0x4c, 0x45, 0x20, 0x4e, + 0x4f, 0x54, 0x20, 0x46, 0x4f, 0x55, 0x4e, 0xc4, + 0x44, 0x45, 0x56, 0x49, 0x43, 0x45, 0x20, 0x4e, + 0x4f, 0x54, 0x20, 0x50, 0x52, 0x45, 0x53, 0x45, + 0x4e, 0xd4, 0x4e, 0x4f, 0x54, 0x20, 0x49, 0x4e, + 0x50, 0x55, 0x54, 0x20, 0x46, 0x49, 0x4c, 0xc5, + 0x4e, 0x4f, 0x54, 0x20, 0x4f, 0x55, 0x54, 0x50, + 0x55, 0x54, 0x20, 0x46, 0x49, 0x4c, 0xc5, 0x4d, + 0x49, 0x53, 0x53, 0x49, 0x4e, 0x47, 0x20, 0x46, + 0x49, 0x4c, 0x45, 0x20, 0x4e, 0x41, 0x4d, 0xc5, + 0x49, 0x4c, 0x4c, 0x45, 0x47, 0x41, 0x4c, 0x20, + 0x44, 0x45, 0x56, 0x49, 0x43, 0x45, 0x20, 0x4e, + 0x55, 0x4d, 0x42, 0x45, 0xd2, 0x4e, 0x45, 0x58, + 0x54, 0x20, 0x57, 0x49, 0x54, 0x48, 0x4f, 0x55, + 0x54, 0x20, 0x46, 0x4f, 0xd2, 0x53, 0x59, 0x4e, + 0x54, 0x41, 0xd8, 0x52, 0x45, 0x54, 0x55, 0x52, + 0x4e, 0x20, 0x57, 0x49, 0x54, 0x48, 0x4f, 0x55, + 0x54, 0x20, 0x47, 0x4f, 0x53, 0x55, 0xc2, 0x4f, + 0x55, 0x54, 0x20, 0x4f, 0x46, 0x20, 0x44, 0x41, + 0x54, 0xc1, 0x49, 0x4c, 0x4c, 0x45, 0x47, 0x41, + 0x4c, 0x20, 0x51, 0x55, 0x41, 0x4e, 0x54, 0x49, + 0x54, 0xd9, 0x4f, 0x56, 0x45, 0x52, 0x46, 0x4c, + 0x4f, 0xd7, 0x4f, 0x55, 0x54, 0x20, 0x4f, 0x46, + 0x20, 0x4d, 0x45, 0x4d, 0x4f, 0x52, 0xd9, 0x55, + 0x4e, 0x44, 0x45, 0x46, 0x27, 0x44, 0x20, 0x53, + 0x54, 0x41, 0x54, 0x45, 0x4d, 0x45, 0x4e, 0xd4, + 0x42, 0x41, 0x44, 0x20, 0x53, 0x55, 0x42, 0x53, + 0x43, 0x52, 0x49, 0x50, 0xd4, 0x52, 0x45, 0x44, + 0x49, 0x4d, 0x27, 0x44, 0x20, 0x41, 0x52, 0x52, + 0x41, 0xd9, 0x44, 0x49, 0x56, 0x49, 0x53, 0x49, + 0x4f, 0x4e, 0x20, 0x42, 0x59, 0x20, 0x5a, 0x45, + 0x52, 0xcf, 0x49, 0x4c, 0x4c, 0x45, 0x47, 0x41, + 0x4c, 0x20, 0x44, 0x49, 0x52, 0x45, 0x43, 0xd4, + 0x54, 0x59, 0x50, 0x45, 0x20, 0x4d, 0x49, 0x53, + 0x4d, 0x41, 0x54, 0x43, 0xc8, 0x53, 0x54, 0x52, + 0x49, 0x4e, 0x47, 0x20, 0x54, 0x4f, 0x4f, 0x20, + 0x4c, 0x4f, 0x4e, 0xc7, 0x46, 0x49, 0x4c, 0x45, + 0x20, 0x44, 0x41, 0x54, 0xc1, 0x46, 0x4f, 0x52, + 0x4d, 0x55, 0x4c, 0x41, 0x20, 0x54, 0x4f, 0x4f, + 0x20, 0x43, 0x4f, 0x4d, 0x50, 0x4c, 0x45, 0xd8, + 0x43, 0x41, 0x4e, 0x27, 0x54, 0x20, 0x43, 0x4f, + 0x4e, 0x54, 0x49, 0x4e, 0x55, 0xc5, 0x55, 0x4e, + 0x44, 0x45, 0x46, 0x27, 0x44, 0x20, 0x46, 0x55, + 0x4e, 0x43, 0x54, 0x49, 0x4f, 0xce, 0x56, 0x45, + 0x52, 0x49, 0x46, 0xd9, 0x4c, 0x4f, 0x41, 0xc4, + 0x9e, 0xa1, 0xac, 0xa1, 0xb5, 0xa1, 0xc2, 0xa1, + 0xd0, 0xa1, 0xe2, 0xa1, 0xf0, 0xa1, 0xff, 0xa1, + 0x10, 0xa2, 0x25, 0xa2, 0x35, 0xa2, 0x3b, 0xa2, + 0x4f, 0xa2, 0x5a, 0xa2, 0x6a, 0xa2, 0x72, 0xa2, + 0x7f, 0xa2, 0x90, 0xa2, 0x9d, 0xa2, 0xaa, 0xa2, + 0xba, 0xa2, 0xc8, 0xa2, 0xd5, 0xa2, 0xe4, 0xa2, + 0xed, 0xa2, 0x00, 0xa3, 0x0e, 0xa3, 0x1e, 0xa3, + 0x24, 0xa3, 0x83, 0xa3, 0x0d, 0x4f, 0x4b, 0x0d, + 0x00, 0x20, 0x20, 0x45, 0x52, 0x52, 0x4f, 0x52, + 0x00, 0x20, 0x49, 0x4e, 0x20, 0x00, 0x0d, 0x0a, + 0x52, 0x45, 0x41, 0x44, 0x59, 0x2e, 0x0d, 0x0a, + 0x00, 0x0d, 0x0a, 0x42, 0x52, 0x45, 0x41, 0x4b, + 0x00, 0xa0, 0xba, 0xe8, 0xe8, 0xe8, 0xe8, 0xbd, + 0x01, 0x01, 0xc9, 0x81, 0xd0, 0x21, 0xa5, 0x4a, + 0xd0, 0x0a, 0xbd, 0x02, 0x01, 0x85, 0x49, 0xbd, + 0x03, 0x01, 0x85, 0x4a, 0xdd, 0x03, 0x01, 0xd0, + 0x07, 0xa5, 0x49, 0xdd, 0x02, 0x01, 0xf0, 0x07, + 0x8a, 0x18, 0x69, 0x12, 0xaa, 0xd0, 0xd8, 0x60, + 0x20, 0x08, 0xa4, 0x85, 0x31, 0x84, 0x32, 0x38, + 0xa5, 0x5a, 0xe5, 0x5f, 0x85, 0x22, 0xa8, 0xa5, + 0x5b, 0xe5, 0x60, 0xaa, 0xe8, 0x98, 0xf0, 0x23, + 0xa5, 0x5a, 0x38, 0xe5, 0x22, 0x85, 0x5a, 0xb0, + 0x03, 0xc6, 0x5b, 0x38, 0xa5, 0x58, 0xe5, 0x22, + 0x85, 0x58, 0xb0, 0x08, 0xc6, 0x59, 0x90, 0x04, + 0xb1, 0x5a, 0x91, 0x58, 0x88, 0xd0, 0xf9, 0xb1, + 0x5a, 0x91, 0x58, 0xc6, 0x5b, 0xc6, 0x59, 0xca, + 0xd0, 0xf2, 0x60, 0x0a, 0x69, 0x3e, 0xb0, 0x35, + 0x85, 0x22, 0xba, 0xe4, 0x22, 0x90, 0x2e, 0x60, + 0xc4, 0x34, 0x90, 0x28, 0xd0, 0x04, 0xc5, 0x33, + 0x90, 0x22, 0x48, 0xa2, 0x09, 0x98, 0x48, 0xb5, + 0x57, 0xca, 0x10, 0xfa, 0x20, 0x26, 0xb5, 0xa2, + 0xf7, 0x68, 0x95, 0x61, 0xe8, 0x30, 0xfa, 0x68, + 0xa8, 0x68, 0xc4, 0x34, 0x90, 0x06, 0xd0, 0x05, + 0xc5, 0x33, 0xb0, 0x01, 0x60, 0xa2, 0x10, 0x6c, + 0x00, 0x03, 0x8a, 0x0a, 0xaa, 0xbd, 0x26, 0xa3, + 0x85, 0x22, 0xbd, 0x27, 0xa3, 0x85, 0x23, 0x20, + 0xcc, 0xff, 0xa9, 0x00, 0x85, 0x13, 0x20, 0xd7, + 0xaa, 0x20, 0x45, 0xab, 0xa0, 0x00, 0xb1, 0x22, + 0x48, 0x29, 0x7f, 0x20, 0x47, 0xab, 0xc8, 0x68, + 0x10, 0xf4, 0x20, 0x7a, 0xa6, 0xa9, 0x69, 0xa0, + 0xa3, 0x20, 0x1e, 0xab, 0xa4, 0x3a, 0xc8, 0xf0, + 0x03, 0x20, 0xc2, 0xbd, 0xa9, 0x76, 0xa0, 0xa3, + 0x20, 0x1e, 0xab, 0xa9, 0x80, 0x20, 0x90, 0xff, + 0x6c, 0x02, 0x03, 0x20, 0x60, 0xa5, 0x86, 0x7a, + 0x84, 0x7b, 0x20, 0x73, 0x00, 0xaa, 0xf0, 0xf0, + 0xa2, 0xff, 0x86, 0x3a, 0x90, 0x06, 0x20, 0x79, + 0xa5, 0x4c, 0xe1, 0xa7, 0x20, 0x6b, 0xa9, 0x20, + 0x79, 0xa5, 0x84, 0x0b, 0x20, 0x13, 0xa6, 0x90, + 0x44, 0xa0, 0x01, 0xb1, 0x5f, 0x85, 0x23, 0xa5, + 0x2d, 0x85, 0x22, 0xa5, 0x60, 0x85, 0x25, 0xa5, + 0x5f, 0x88, 0xf1, 0x5f, 0x18, 0x65, 0x2d, 0x85, + 0x2d, 0x85, 0x24, 0xa5, 0x2e, 0x69, 0xff, 0x85, + 0x2e, 0xe5, 0x60, 0xaa, 0x38, 0xa5, 0x5f, 0xe5, + 0x2d, 0xa8, 0xb0, 0x03, 0xe8, 0xc6, 0x25, 0x18, + 0x65, 0x22, 0x90, 0x03, 0xc6, 0x23, 0x18, 0xb1, + 0x22, 0x91, 0x24, 0xc8, 0xd0, 0xf9, 0xe6, 0x23, + 0xe6, 0x25, 0xca, 0xd0, 0xf2, 0x20, 0x59, 0xa6, + 0x20, 0x33, 0xa5, 0xad, 0x00, 0x02, 0xf0, 0x88, + 0x18, 0xa5, 0x2d, 0x85, 0x5a, 0x65, 0x0b, 0x85, + 0x58, 0xa4, 0x2e, 0x84, 0x5b, 0x90, 0x01, 0xc8, + 0x84, 0x59, 0x20, 0xb8, 0xa3, 0xa5, 0x14, 0xa4, + 0x15, 0x8d, 0xfe, 0x01, 0x8c, 0xff, 0x01, 0xa5, + 0x31, 0xa4, 0x32, 0x85, 0x2d, 0x84, 0x2e, 0xa4, + 0x0b, 0x88, 0xb9, 0xfc, 0x01, 0x91, 0x5f, 0x88, + 0x10, 0xf8, 0x20, 0x59, 0xa6, 0x20, 0x33, 0xa5, + 0x4c, 0x80, 0xa4, 0xa5, 0x2b, 0xa4, 0x2c, 0x85, + 0x22, 0x84, 0x23, 0x18, 0xa0, 0x01, 0xb1, 0x22, + 0xf0, 0x1d, 0xa0, 0x04, 0xc8, 0xb1, 0x22, 0xd0, + 0xfb, 0xc8, 0x98, 0x65, 0x22, 0xaa, 0xa0, 0x00, + 0x91, 0x22, 0xa5, 0x23, 0x69, 0x00, 0xc8, 0x91, + 0x22, 0x86, 0x22, 0x85, 0x23, 0x90, 0xdd, 0x60, + 0xa2, 0x00, 0x20, 0x12, 0xe1, 0xc9, 0x0d, 0xf0, + 0x0d, 0x9d, 0x00, 0x02, 0xe8, 0xe0, 0x59, 0x90, + 0xf1, 0xa2, 0x17, 0x4c, 0x37, 0xa4, 0x4c, 0xca, + 0xaa, 0x6c, 0x04, 0x03, 0xa6, 0x7a, 0xa0, 0x04, + 0x84, 0x0f, 0xbd, 0x00, 0x02, 0x10, 0x07, 0xc9, + 0xff, 0xf0, 0x3e, 0xe8, 0xd0, 0xf4, 0xc9, 0x20, + 0xf0, 0x37, 0x85, 0x08, 0xc9, 0x22, 0xf0, 0x56, + 0x24, 0x0f, 0x70, 0x2d, 0xc9, 0x3f, 0xd0, 0x04, + 0xa9, 0x99, 0xd0, 0x25, 0xc9, 0x30, 0x90, 0x04, + 0xc9, 0x3c, 0x90, 0x1d, 0x84, 0x71, 0xa0, 0x00, + 0x84, 0x0b, 0x88, 0x86, 0x7a, 0xca, 0xc8, 0xe8, + 0xbd, 0x00, 0x02, 0x38, 0xf9, 0x9e, 0xa0, 0xf0, + 0xf5, 0xc9, 0x80, 0xd0, 0x30, 0x05, 0x0b, 0xa4, + 0x71, 0xe8, 0xc8, 0x99, 0xfb, 0x01, 0xb9, 0xfb, + 0x01, 0xf0, 0x36, 0x38, 0xe9, 0x3a, 0xf0, 0x04, + 0xc9, 0x49, 0xd0, 0x02, 0x85, 0x0f, 0x38, 0xe9, + 0x55, 0xd0, 0x9f, 0x85, 0x08, 0xbd, 0x00, 0x02, + 0xf0, 0xdf, 0xc5, 0x08, 0xf0, 0xdb, 0xc8, 0x99, + 0xfb, 0x01, 0xe8, 0xd0, 0xf0, 0xa6, 0x7a, 0xe6, + 0x0b, 0xc8, 0xb9, 0x9d, 0xa0, 0x10, 0xfa, 0xb9, + 0x9e, 0xa0, 0xd0, 0xb4, 0xbd, 0x00, 0x02, 0x10, + 0xbe, 0x99, 0xfd, 0x01, 0xc6, 0x7b, 0xa9, 0xff, + 0x85, 0x7a, 0x60, 0xa5, 0x2b, 0xa6, 0x2c, 0xa0, + 0x01, 0x85, 0x5f, 0x86, 0x60, 0xb1, 0x5f, 0xf0, + 0x1f, 0xc8, 0xc8, 0xa5, 0x15, 0xd1, 0x5f, 0x90, + 0x18, 0xf0, 0x03, 0x88, 0xd0, 0x09, 0xa5, 0x14, + 0x88, 0xd1, 0x5f, 0x90, 0x0c, 0xf0, 0x0a, 0x88, + 0xb1, 0x5f, 0xaa, 0x88, 0xb1, 0x5f, 0xb0, 0xd7, + 0x18, 0x60, 0xd0, 0xfd, 0xa9, 0x00, 0xa8, 0x91, + 0x2b, 0xc8, 0x91, 0x2b, 0xa5, 0x2b, 0x18, 0x69, + 0x02, 0x85, 0x2d, 0xa5, 0x2c, 0x69, 0x00, 0x85, + 0x2e, 0x20, 0x8e, 0xa6, 0xa9, 0x00, 0xd0, 0x2d, + 0x20, 0xe7, 0xff, 0xa5, 0x37, 0xa4, 0x38, 0x85, + 0x33, 0x84, 0x34, 0xa5, 0x2d, 0xa4, 0x2e, 0x85, + 0x2f, 0x84, 0x30, 0x85, 0x31, 0x84, 0x32, 0x20, + 0x1d, 0xa8, 0xa2, 0x19, 0x86, 0x16, 0x68, 0xa8, + 0x68, 0xa2, 0xfa, 0x9a, 0x48, 0x98, 0x48, 0xa9, + 0x00, 0x85, 0x3e, 0x85, 0x10, 0x60, 0x18, 0xa5, + 0x2b, 0x69, 0xff, 0x85, 0x7a, 0xa5, 0x2c, 0x69, + 0xff, 0x85, 0x7b, 0x60, 0x90, 0x06, 0xf0, 0x04, + 0xc9, 0xab, 0xd0, 0xe9, 0x20, 0x6b, 0xa9, 0x20, + 0x13, 0xa6, 0x20, 0x79, 0x00, 0xf0, 0x0c, 0xc9, + 0xab, 0xd0, 0x8e, 0x20, 0x73, 0x00, 0x20, 0x6b, + 0xa9, 0xd0, 0x86, 0x68, 0x68, 0xa5, 0x14, 0x05, + 0x15, 0xd0, 0x06, 0xa9, 0xff, 0x85, 0x14, 0x85, + 0x15, 0xa0, 0x01, 0x84, 0x0f, 0xb1, 0x5f, 0xf0, + 0x43, 0x20, 0x2c, 0xa8, 0x20, 0xd7, 0xaa, 0xc8, + 0xb1, 0x5f, 0xaa, 0xc8, 0xb1, 0x5f, 0xc5, 0x15, + 0xd0, 0x04, 0xe4, 0x14, 0xf0, 0x02, 0xb0, 0x2c, + 0x84, 0x49, 0x20, 0xcd, 0xbd, 0xa9, 0x20, 0xa4, + 0x49, 0x29, 0x7f, 0x20, 0x47, 0xab, 0xc9, 0x22, + 0xd0, 0x06, 0xa5, 0x0f, 0x49, 0xff, 0x85, 0x0f, + 0xc8, 0xf0, 0x11, 0xb1, 0x5f, 0xd0, 0x10, 0xa8, + 0xb1, 0x5f, 0xaa, 0xc8, 0xb1, 0x5f, 0x86, 0x5f, + 0x85, 0x60, 0xd0, 0xb5, 0x4c, 0x86, 0xe3, 0x6c, + 0x06, 0x03, 0x10, 0xd7, 0xc9, 0xff, 0xf0, 0xd3, + 0x24, 0x0f, 0x30, 0xcf, 0x38, 0xe9, 0x7f, 0xaa, + 0x84, 0x49, 0xa0, 0xff, 0xca, 0xf0, 0x08, 0xc8, + 0xb9, 0x9e, 0xa0, 0x10, 0xfa, 0x30, 0xf5, 0xc8, + 0xb9, 0x9e, 0xa0, 0x30, 0xb2, 0x20, 0x47, 0xab, + 0xd0, 0xf5, 0xa9, 0x80, 0x85, 0x10, 0x20, 0xa5, + 0xa9, 0x20, 0x8a, 0xa3, 0xd0, 0x05, 0x8a, 0x69, + 0x0f, 0xaa, 0x9a, 0x68, 0x68, 0xa9, 0x09, 0x20, + 0xfb, 0xa3, 0x20, 0x06, 0xa9, 0x18, 0x98, 0x65, + 0x7a, 0x48, 0xa5, 0x7b, 0x69, 0x00, 0x48, 0xa5, + 0x3a, 0x48, 0xa5, 0x39, 0x48, 0xa9, 0xa4, 0x20, + 0xff, 0xae, 0x20, 0x8d, 0xad, 0x20, 0x8a, 0xad, + 0xa5, 0x66, 0x09, 0x7f, 0x25, 0x62, 0x85, 0x62, + 0xa9, 0x8b, 0xa0, 0xa7, 0x85, 0x22, 0x84, 0x23, + 0x4c, 0x43, 0xae, 0xa9, 0xbc, 0xa0, 0xb9, 0x20, + 0xa2, 0xbb, 0x20, 0x79, 0x00, 0xc9, 0xa9, 0xd0, + 0x06, 0x20, 0x73, 0x00, 0x20, 0x8a, 0xad, 0x20, + 0x2b, 0xbc, 0x20, 0x38, 0xae, 0xa5, 0x4a, 0x48, + 0xa5, 0x49, 0x48, 0xa9, 0x81, 0x48, 0x20, 0x2c, + 0xa8, 0xa5, 0x7a, 0xa4, 0x7b, 0xc0, 0x02, 0xea, + 0xf0, 0x04, 0x85, 0x3d, 0x84, 0x3e, 0xa0, 0x00, + 0xb1, 0x7a, 0xd0, 0x43, 0xa0, 0x02, 0xb1, 0x7a, + 0x18, 0xd0, 0x03, 0x4c, 0x4b, 0xa8, 0xc8, 0xb1, + 0x7a, 0x85, 0x39, 0xc8, 0xb1, 0x7a, 0x85, 0x3a, + 0x98, 0x65, 0x7a, 0x85, 0x7a, 0x90, 0x02, 0xe6, + 0x7b, 0x6c, 0x08, 0x03, 0x20, 0x73, 0x00, 0x20, + 0xed, 0xa7, 0x4c, 0xae, 0xa7, 0xf0, 0x3c, 0xe9, + 0x80, 0x90, 0x11, 0xc9, 0x23, 0xb0, 0x17, 0x0a, + 0xa8, 0xb9, 0x0d, 0xa0, 0x48, 0xb9, 0x0c, 0xa0, + 0x48, 0x4c, 0x73, 0x00, 0x4c, 0xa5, 0xa9, 0xc9, + 0x3a, 0xf0, 0xd6, 0x4c, 0x08, 0xaf, 0xc9, 0x4b, + 0xd0, 0xf9, 0x20, 0x73, 0x00, 0xa9, 0xa4, 0x20, + 0xff, 0xae, 0x4c, 0xa0, 0xa8, 0x38, 0xa5, 0x2b, + 0xe9, 0x01, 0xa4, 0x2c, 0xb0, 0x01, 0x88, 0x85, + 0x41, 0x84, 0x42, 0x60, 0x20, 0xe1, 0xff, 0xb0, + 0x01, 0x18, 0xd0, 0x3c, 0xa5, 0x7a, 0xa4, 0x7b, + 0xa6, 0x3a, 0xe8, 0xf0, 0x0c, 0x85, 0x3d, 0x84, + 0x3e, 0xa5, 0x39, 0xa4, 0x3a, 0x85, 0x3b, 0x84, + 0x3c, 0x68, 0x68, 0xa9, 0x81, 0xa0, 0xa3, 0x90, + 0x03, 0x4c, 0x69, 0xa4, 0x4c, 0x86, 0xe3, 0xd0, + 0x17, 0xa2, 0x1a, 0xa4, 0x3e, 0xd0, 0x03, 0x4c, + 0x37, 0xa4, 0xa5, 0x3d, 0x85, 0x7a, 0x84, 0x7b, + 0xa5, 0x3b, 0xa4, 0x3c, 0x85, 0x39, 0x84, 0x3a, + 0x60, 0x08, 0xa9, 0x00, 0x20, 0x90, 0xff, 0x28, + 0xd0, 0x03, 0x4c, 0x59, 0xa6, 0x20, 0x60, 0xa6, + 0x4c, 0x97, 0xa8, 0xa9, 0x03, 0x20, 0xfb, 0xa3, + 0xa5, 0x7b, 0x48, 0xa5, 0x7a, 0x48, 0xa5, 0x3a, + 0x48, 0xa5, 0x39, 0x48, 0xa9, 0x8d, 0x48, 0x20, + 0x79, 0x00, 0x20, 0xa0, 0xa8, 0x4c, 0xae, 0xa7, + 0x20, 0x6b, 0xa9, 0x20, 0x09, 0xa9, 0x38, 0xa5, + 0x39, 0xe5, 0x14, 0xa5, 0x3a, 0xe5, 0x15, 0xb0, + 0x0b, 0x98, 0x38, 0x65, 0x7a, 0xa6, 0x7b, 0x90, + 0x07, 0xe8, 0xb0, 0x04, 0xa5, 0x2b, 0xa6, 0x2c, + 0x20, 0x17, 0xa6, 0x90, 0x1e, 0xa5, 0x5f, 0xe9, + 0x01, 0x85, 0x7a, 0xa5, 0x60, 0xe9, 0x00, 0x85, + 0x7b, 0x60, 0xd0, 0xfd, 0xa9, 0xff, 0x85, 0x4a, + 0x20, 0x8a, 0xa3, 0x9a, 0xc9, 0x8d, 0xf0, 0x0b, + 0xa2, 0x0c, 0x2c, 0xa2, 0x11, 0x4c, 0x37, 0xa4, + 0x4c, 0x08, 0xaf, 0x68, 0x68, 0x85, 0x39, 0x68, + 0x85, 0x3a, 0x68, 0x85, 0x7a, 0x68, 0x85, 0x7b, + 0x20, 0x06, 0xa9, 0x98, 0x18, 0x65, 0x7a, 0x85, + 0x7a, 0x90, 0x02, 0xe6, 0x7b, 0x60, 0xa2, 0x3a, + 0x2c, 0xa2, 0x00, 0x86, 0x07, 0xa0, 0x00, 0x84, + 0x08, 0xa5, 0x08, 0xa6, 0x07, 0x85, 0x07, 0x86, + 0x08, 0xb1, 0x7a, 0xf0, 0xe8, 0xc5, 0x08, 0xf0, + 0xe4, 0xc8, 0xc9, 0x22, 0xd0, 0xf3, 0xf0, 0xe9, + 0x20, 0x9e, 0xad, 0x20, 0x79, 0x00, 0xc9, 0x89, + 0xf0, 0x05, 0xa9, 0xa7, 0x20, 0xff, 0xae, 0xa5, + 0x61, 0xd0, 0x05, 0x20, 0x09, 0xa9, 0xf0, 0xbb, + 0x20, 0x79, 0x00, 0xb0, 0x03, 0x4c, 0xa0, 0xa8, + 0x4c, 0xed, 0xa7, 0x20, 0x9e, 0xb7, 0x48, 0xc9, + 0x8d, 0xf0, 0x04, 0xc9, 0x89, 0xd0, 0x91, 0xc6, + 0x65, 0xd0, 0x04, 0x68, 0x4c, 0xef, 0xa7, 0x20, + 0x73, 0x00, 0x20, 0x6b, 0xa9, 0xc9, 0x2c, 0xf0, + 0xee, 0x68, 0x60, 0xa2, 0x00, 0x86, 0x14, 0x86, + 0x15, 0xb0, 0xf7, 0xe9, 0x2f, 0x85, 0x07, 0xa5, + 0x15, 0x85, 0x22, 0xc9, 0x19, 0xb0, 0xd4, 0xa5, + 0x14, 0x0a, 0x26, 0x22, 0x0a, 0x26, 0x22, 0x65, + 0x14, 0x85, 0x14, 0xa5, 0x22, 0x65, 0x15, 0x85, + 0x15, 0x06, 0x14, 0x26, 0x15, 0xa5, 0x14, 0x65, + 0x07, 0x85, 0x14, 0x90, 0x02, 0xe6, 0x15, 0x20, + 0x73, 0x00, 0x4c, 0x71, 0xa9, 0x20, 0x8b, 0xb0, + 0x85, 0x49, 0x84, 0x4a, 0xa9, 0xb2, 0x20, 0xff, + 0xae, 0xa5, 0x0e, 0x48, 0xa5, 0x0d, 0x48, 0x20, + 0x9e, 0xad, 0x68, 0x2a, 0x20, 0x90, 0xad, 0xd0, + 0x18, 0x68, 0x10, 0x12, 0x20, 0x1b, 0xbc, 0x20, + 0xbf, 0xb1, 0xa0, 0x00, 0xa5, 0x64, 0x91, 0x49, + 0xc8, 0xa5, 0x65, 0x91, 0x49, 0x60, 0x4c, 0xd0, + 0xbb, 0x68, 0xa4, 0x4a, 0xc0, 0xbf, 0xd0, 0x4c, + 0x20, 0xa6, 0xb6, 0xc9, 0x06, 0xd0, 0x3d, 0xa0, + 0x00, 0x84, 0x61, 0x84, 0x66, 0x84, 0x71, 0x20, + 0x1d, 0xaa, 0x20, 0xe2, 0xba, 0xe6, 0x71, 0xa4, + 0x71, 0x20, 0x1d, 0xaa, 0x20, 0x0c, 0xbc, 0xaa, + 0xf0, 0x05, 0xe8, 0x8a, 0x20, 0xed, 0xba, 0xa4, + 0x71, 0xc8, 0xc0, 0x06, 0xd0, 0xdf, 0x20, 0xe2, + 0xba, 0x20, 0x9b, 0xbc, 0xa6, 0x64, 0xa4, 0x63, + 0xa5, 0x65, 0x4c, 0xdb, 0xff, 0xb1, 0x22, 0x20, + 0x80, 0x00, 0x90, 0x03, 0x4c, 0x48, 0xb2, 0xe9, + 0x2f, 0x4c, 0x7e, 0xbd, 0xa0, 0x02, 0xb1, 0x64, + 0xc5, 0x34, 0x90, 0x17, 0xd0, 0x07, 0x88, 0xb1, + 0x64, 0xc5, 0x33, 0x90, 0x0e, 0xa4, 0x65, 0xc4, + 0x2e, 0x90, 0x08, 0xd0, 0x0d, 0xa5, 0x64, 0xc5, + 0x2d, 0xb0, 0x07, 0xa5, 0x64, 0xa4, 0x65, 0x4c, + 0x68, 0xaa, 0xa0, 0x00, 0xb1, 0x64, 0x20, 0x75, + 0xb4, 0xa5, 0x50, 0xa4, 0x51, 0x85, 0x6f, 0x84, + 0x70, 0x20, 0x7a, 0xb6, 0xa9, 0x61, 0xa0, 0x00, + 0x85, 0x50, 0x84, 0x51, 0x20, 0xdb, 0xb6, 0xa0, + 0x00, 0xb1, 0x50, 0x91, 0x49, 0xc8, 0xb1, 0x50, + 0x91, 0x49, 0xc8, 0xb1, 0x50, 0x91, 0x49, 0x60, + 0x20, 0x86, 0xaa, 0x4c, 0xb5, 0xab, 0x20, 0x9e, + 0xb7, 0xf0, 0x05, 0xa9, 0x2c, 0x20, 0xff, 0xae, + 0x08, 0x86, 0x13, 0x20, 0x18, 0xe1, 0x28, 0x4c, + 0xa0, 0xaa, 0x20, 0x21, 0xab, 0x20, 0x79, 0x00, + 0xf0, 0x35, 0xf0, 0x43, 0xc9, 0xa3, 0xf0, 0x50, + 0xc9, 0xa6, 0x18, 0xf0, 0x4b, 0xc9, 0x2c, 0xf0, + 0x37, 0xc9, 0x3b, 0xf0, 0x5e, 0x20, 0x9e, 0xad, + 0x24, 0x0d, 0x30, 0xde, 0x20, 0xdd, 0xbd, 0x20, + 0x87, 0xb4, 0x20, 0x21, 0xab, 0x20, 0x3b, 0xab, + 0xd0, 0xd3, 0xa9, 0x00, 0x9d, 0x00, 0x02, 0xa2, + 0xff, 0xa0, 0x01, 0xa5, 0x13, 0xd0, 0x10, 0xa9, + 0x0d, 0x20, 0x47, 0xab, 0x24, 0x13, 0x10, 0x05, + 0xa9, 0x0a, 0x20, 0x47, 0xab, 0x49, 0xff, 0x60, + 0x38, 0x20, 0xf0, 0xff, 0x98, 0x38, 0xe9, 0x0a, + 0xb0, 0xfc, 0x49, 0xff, 0x69, 0x01, 0xd0, 0x16, + 0x08, 0x38, 0x20, 0xf0, 0xff, 0x84, 0x09, 0x20, + 0x9b, 0xb7, 0xc9, 0x29, 0xd0, 0x59, 0x28, 0x90, + 0x06, 0x8a, 0xe5, 0x09, 0x90, 0x05, 0xaa, 0xe8, + 0xca, 0xd0, 0x06, 0x20, 0x73, 0x00, 0x4c, 0xa2, + 0xaa, 0x20, 0x3b, 0xab, 0xd0, 0xf2, 0x20, 0x87, + 0xb4, 0x20, 0xa6, 0xb6, 0xaa, 0xa0, 0x00, 0xe8, + 0xca, 0xf0, 0xbc, 0xb1, 0x22, 0x20, 0x47, 0xab, + 0xc8, 0xc9, 0x0d, 0xd0, 0xf3, 0x20, 0xe5, 0xaa, + 0x4c, 0x28, 0xab, 0xa5, 0x13, 0xf0, 0x03, 0xa9, + 0x20, 0x2c, 0xa9, 0x1d, 0x2c, 0xa9, 0x3f, 0x20, + 0x0c, 0xe1, 0x29, 0xff, 0x60, 0xa5, 0x11, 0xf0, + 0x11, 0x30, 0x04, 0xa0, 0xff, 0xd0, 0x04, 0xa5, + 0x3f, 0xa4, 0x40, 0x85, 0x39, 0x84, 0x3a, 0x4c, + 0x08, 0xaf, 0xa5, 0x13, 0xf0, 0x05, 0xa2, 0x18, + 0x4c, 0x37, 0xa4, 0xa9, 0x0c, 0xa0, 0xad, 0x20, + 0x1e, 0xab, 0xa5, 0x3d, 0xa4, 0x3e, 0x85, 0x7a, + 0x84, 0x7b, 0x60, 0x20, 0xa6, 0xb3, 0xc9, 0x23, + 0xd0, 0x10, 0x20, 0x73, 0x00, 0x20, 0x9e, 0xb7, + 0xa9, 0x2c, 0x20, 0xff, 0xae, 0x86, 0x13, 0x20, + 0x1e, 0xe1, 0xa2, 0x01, 0xa0, 0x02, 0xa9, 0x00, + 0x8d, 0x01, 0x02, 0xa9, 0x40, 0x20, 0x0f, 0xac, + 0xa6, 0x13, 0xd0, 0x13, 0x60, 0x20, 0x9e, 0xb7, + 0xa9, 0x2c, 0x20, 0xff, 0xae, 0x86, 0x13, 0x20, + 0x1e, 0xe1, 0x20, 0xce, 0xab, 0xa5, 0x13, 0x20, + 0xcc, 0xff, 0xa2, 0x00, 0x86, 0x13, 0x60, 0xc9, + 0x22, 0xd0, 0x0b, 0x20, 0xbd, 0xae, 0xa9, 0x3b, + 0x20, 0xff, 0xae, 0x20, 0x21, 0xab, 0x20, 0xa6, + 0xb3, 0xa9, 0x2c, 0x8d, 0xff, 0x01, 0x20, 0xf9, + 0xab, 0xa5, 0x13, 0xf0, 0x0d, 0x20, 0xb7, 0xff, + 0x29, 0x02, 0xf0, 0x06, 0x20, 0xb5, 0xab, 0x4c, + 0xf8, 0xa8, 0xad, 0x00, 0x02, 0xd0, 0x1e, 0xa5, + 0x13, 0xd0, 0xe3, 0x20, 0x06, 0xa9, 0x4c, 0xfb, + 0xa8, 0xa5, 0x13, 0xd0, 0x06, 0x20, 0x45, 0xab, + 0x20, 0x3b, 0xab, 0x4c, 0x60, 0xa5, 0xa6, 0x41, + 0xa4, 0x42, 0xa9, 0x98, 0x2c, 0xa9, 0x00, 0x85, + 0x11, 0x86, 0x43, 0x84, 0x44, 0x20, 0x8b, 0xb0, + 0x85, 0x49, 0x84, 0x4a, 0xa5, 0x7a, 0xa4, 0x7b, + 0x85, 0x4b, 0x84, 0x4c, 0xa6, 0x43, 0xa4, 0x44, + 0x86, 0x7a, 0x84, 0x7b, 0x20, 0x79, 0x00, 0xd0, + 0x20, 0x24, 0x11, 0x50, 0x0c, 0x20, 0x24, 0xe1, + 0x8d, 0x00, 0x02, 0xa2, 0xff, 0xa0, 0x01, 0xd0, + 0x0c, 0x30, 0x75, 0xa5, 0x13, 0xd0, 0x03, 0x20, + 0x45, 0xab, 0x20, 0xf9, 0xab, 0x86, 0x7a, 0x84, + 0x7b, 0x20, 0x73, 0x00, 0x24, 0x0d, 0x10, 0x31, + 0x24, 0x11, 0x50, 0x09, 0xe8, 0x86, 0x7a, 0xa9, + 0x00, 0x85, 0x07, 0xf0, 0x0c, 0x85, 0x07, 0xc9, + 0x22, 0xf0, 0x07, 0xa9, 0x3a, 0x85, 0x07, 0xa9, + 0x2c, 0x18, 0x85, 0x08, 0xa5, 0x7a, 0xa4, 0x7b, + 0x69, 0x00, 0x90, 0x01, 0xc8, 0x20, 0x8d, 0xb4, + 0x20, 0xe2, 0xb7, 0x20, 0xda, 0xa9, 0x4c, 0x91, + 0xac, 0x20, 0xf3, 0xbc, 0xa5, 0x0e, 0x20, 0xc2, + 0xa9, 0x20, 0x79, 0x00, 0xf0, 0x07, 0xc9, 0x2c, + 0xf0, 0x03, 0x4c, 0x4d, 0xab, 0xa5, 0x7a, 0xa4, + 0x7b, 0x85, 0x43, 0x84, 0x44, 0xa5, 0x4b, 0xa4, + 0x4c, 0x85, 0x7a, 0x84, 0x7b, 0x20, 0x79, 0x00, + 0xf0, 0x2d, 0x20, 0xfd, 0xae, 0x4c, 0x15, 0xac, + 0x20, 0x06, 0xa9, 0xc8, 0xaa, 0xd0, 0x12, 0xa2, + 0x0d, 0xc8, 0xb1, 0x7a, 0xf0, 0x6c, 0xc8, 0xb1, + 0x7a, 0x85, 0x3f, 0xc8, 0xb1, 0x7a, 0xc8, 0x85, + 0x40, 0x20, 0xfb, 0xa8, 0x20, 0x79, 0x00, 0xaa, + 0xe0, 0x83, 0xd0, 0xdc, 0x4c, 0x51, 0xac, 0xa5, + 0x43, 0xa4, 0x44, 0xa6, 0x11, 0x10, 0x03, 0x4c, + 0x27, 0xa8, 0xa0, 0x00, 0xb1, 0x43, 0xf0, 0x0b, + 0xa5, 0x13, 0xd0, 0x07, 0xa9, 0xfc, 0xa0, 0xac, + 0x4c, 0x1e, 0xab, 0x60, 0x3f, 0x45, 0x58, 0x54, + 0x52, 0x41, 0x20, 0x49, 0x47, 0x4e, 0x4f, 0x52, + 0x45, 0x44, 0x0d, 0x00, 0x3f, 0x52, 0x45, 0x44, + 0x4f, 0x20, 0x46, 0x52, 0x4f, 0x4d, 0x20, 0x53, + 0x54, 0x41, 0x52, 0x54, 0x0d, 0x00, 0xd0, 0x04, + 0xa0, 0x00, 0xf0, 0x03, 0x20, 0x8b, 0xb0, 0x85, + 0x49, 0x84, 0x4a, 0x20, 0x8a, 0xa3, 0xf0, 0x05, + 0xa2, 0x0a, 0x4c, 0x37, 0xa4, 0x9a, 0x8a, 0x18, + 0x69, 0x04, 0x48, 0x69, 0x06, 0x85, 0x24, 0x68, + 0xa0, 0x01, 0x20, 0xa2, 0xbb, 0xba, 0xbd, 0x09, + 0x01, 0x85, 0x66, 0xa5, 0x49, 0xa4, 0x4a, 0x20, + 0x67, 0xb8, 0x20, 0xd0, 0xbb, 0xa0, 0x01, 0x20, + 0x5d, 0xbc, 0xba, 0x38, 0xfd, 0x09, 0x01, 0xf0, + 0x17, 0xbd, 0x0f, 0x01, 0x85, 0x39, 0xbd, 0x10, + 0x01, 0x85, 0x3a, 0xbd, 0x12, 0x01, 0x85, 0x7a, + 0xbd, 0x11, 0x01, 0x85, 0x7b, 0x4c, 0xae, 0xa7, + 0x8a, 0x69, 0x11, 0xaa, 0x9a, 0x20, 0x79, 0x00, + 0xc9, 0x2c, 0xd0, 0xf1, 0x20, 0x73, 0x00, 0x20, + 0x24, 0xad, 0x20, 0x9e, 0xad, 0x18, 0x24, 0x38, + 0x24, 0x0d, 0x30, 0x03, 0xb0, 0x03, 0x60, 0xb0, + 0xfd, 0xa2, 0x16, 0x4c, 0x37, 0xa4, 0xa6, 0x7a, + 0xd0, 0x02, 0xc6, 0x7b, 0xc6, 0x7a, 0xa2, 0x00, + 0x24, 0x48, 0x8a, 0x48, 0xa9, 0x01, 0x20, 0xfb, + 0xa3, 0x20, 0x83, 0xae, 0xa9, 0x00, 0x85, 0x4d, + 0x20, 0x79, 0x00, 0x38, 0xe9, 0xb1, 0x90, 0x17, + 0xc9, 0x03, 0xb0, 0x13, 0xc9, 0x01, 0x2a, 0x49, + 0x01, 0x45, 0x4d, 0xc5, 0x4d, 0x90, 0x61, 0x85, + 0x4d, 0x20, 0x73, 0x00, 0x4c, 0xbb, 0xad, 0xa6, + 0x4d, 0xd0, 0x2c, 0xb0, 0x7b, 0x69, 0x07, 0x90, + 0x77, 0x65, 0x0d, 0xd0, 0x03, 0x4c, 0x3d, 0xb6, + 0x69, 0xff, 0x85, 0x22, 0x0a, 0x65, 0x22, 0xa8, + 0x68, 0xd9, 0x80, 0xa0, 0xb0, 0x67, 0x20, 0x8d, + 0xad, 0x48, 0x20, 0x20, 0xae, 0x68, 0xa4, 0x4b, + 0x10, 0x17, 0xaa, 0xf0, 0x56, 0xd0, 0x5f, 0x46, + 0x0d, 0x8a, 0x2a, 0xa6, 0x7a, 0xd0, 0x02, 0xc6, + 0x7b, 0xc6, 0x7a, 0xa0, 0x1b, 0x85, 0x4d, 0xd0, + 0xd7, 0xd9, 0x80, 0xa0, 0xb0, 0x48, 0x90, 0xd9, + 0xb9, 0x82, 0xa0, 0x48, 0xb9, 0x81, 0xa0, 0x48, + 0x20, 0x33, 0xae, 0xa5, 0x4d, 0x4c, 0xa9, 0xad, + 0x4c, 0x08, 0xaf, 0xa5, 0x66, 0xbe, 0x80, 0xa0, + 0xa8, 0x68, 0x85, 0x22, 0xe6, 0x22, 0x68, 0x85, + 0x23, 0x98, 0x48, 0x20, 0x1b, 0xbc, 0xa5, 0x65, + 0x48, 0xa5, 0x64, 0x48, 0xa5, 0x63, 0x48, 0xa5, + 0x62, 0x48, 0xa5, 0x61, 0x48, 0x6c, 0x22, 0x00, + 0xa0, 0xff, 0x68, 0xf0, 0x23, 0xc9, 0x64, 0xf0, + 0x03, 0x20, 0x8d, 0xad, 0x84, 0x4b, 0x68, 0x4a, + 0x85, 0x12, 0x68, 0x85, 0x69, 0x68, 0x85, 0x6a, + 0x68, 0x85, 0x6b, 0x68, 0x85, 0x6c, 0x68, 0x85, + 0x6d, 0x68, 0x85, 0x6e, 0x45, 0x66, 0x85, 0x6f, + 0xa5, 0x61, 0x60, 0x6c, 0x0a, 0x03, 0xa9, 0x00, + 0x85, 0x0d, 0x20, 0x73, 0x00, 0xb0, 0x03, 0x4c, + 0xf3, 0xbc, 0x20, 0x13, 0xb1, 0x90, 0x03, 0x4c, + 0x28, 0xaf, 0xc9, 0xff, 0xd0, 0x0f, 0xa9, 0xa8, + 0xa0, 0xae, 0x20, 0xa2, 0xbb, 0x4c, 0x73, 0x00, + 0x82, 0x49, 0x0f, 0xda, 0xa1, 0xc9, 0x2e, 0xf0, + 0xde, 0xc9, 0xab, 0xf0, 0x58, 0xc9, 0xaa, 0xf0, + 0xd1, 0xc9, 0x22, 0xd0, 0x0f, 0xa5, 0x7a, 0xa4, + 0x7b, 0x69, 0x00, 0x90, 0x01, 0xc8, 0x20, 0x87, + 0xb4, 0x4c, 0xe2, 0xb7, 0xc9, 0xa8, 0xd0, 0x13, + 0xa0, 0x18, 0xd0, 0x3b, 0x20, 0xbf, 0xb1, 0xa5, + 0x65, 0x49, 0xff, 0xa8, 0xa5, 0x64, 0x49, 0xff, + 0x4c, 0x91, 0xb3, 0xc9, 0xa5, 0xd0, 0x03, 0x4c, + 0xf4, 0xb3, 0xc9, 0xb4, 0x90, 0x03, 0x4c, 0xa7, + 0xaf, 0x20, 0xfa, 0xae, 0x20, 0x9e, 0xad, 0xa9, + 0x29, 0x2c, 0xa9, 0x28, 0x2c, 0xa9, 0x2c, 0xa0, + 0x00, 0xd1, 0x7a, 0xd0, 0x03, 0x4c, 0x73, 0x00, + 0xa2, 0x0b, 0x4c, 0x37, 0xa4, 0xa0, 0x15, 0x68, + 0x68, 0x4c, 0xfa, 0xad, 0x38, 0xa5, 0x64, 0xe9, + 0x00, 0xa5, 0x65, 0xe9, 0xa0, 0x90, 0x08, 0xa9, + 0xa2, 0xe5, 0x64, 0xa9, 0xe3, 0xe5, 0x65, 0x60, + 0x20, 0x8b, 0xb0, 0x85, 0x64, 0x84, 0x65, 0xa6, + 0x45, 0xa4, 0x46, 0xa5, 0x0d, 0xf0, 0x26, 0xa9, + 0x00, 0x85, 0x70, 0x20, 0x14, 0xaf, 0x90, 0x1c, + 0xe0, 0x54, 0xd0, 0x18, 0xc0, 0xc9, 0xd0, 0x14, + 0x20, 0x84, 0xaf, 0x84, 0x5e, 0x88, 0x84, 0x71, + 0xa0, 0x06, 0x84, 0x5d, 0xa0, 0x24, 0x20, 0x68, + 0xbe, 0x4c, 0x6f, 0xb4, 0x60, 0x24, 0x0e, 0x10, + 0x0d, 0xa0, 0x00, 0xb1, 0x64, 0xaa, 0xc8, 0xb1, + 0x64, 0xa8, 0x8a, 0x4c, 0x91, 0xb3, 0x20, 0x14, + 0xaf, 0x90, 0x2d, 0xe0, 0x54, 0xd0, 0x1b, 0xc0, + 0x49, 0xd0, 0x25, 0x20, 0x84, 0xaf, 0x98, 0xa2, + 0xa0, 0x4c, 0x4f, 0xbc, 0x20, 0xde, 0xff, 0x86, + 0x64, 0x84, 0x63, 0x85, 0x65, 0xa0, 0x00, 0x84, + 0x62, 0x60, 0xe0, 0x53, 0xd0, 0x0a, 0xc0, 0x54, + 0xd0, 0x06, 0x20, 0xb7, 0xff, 0x4c, 0x3c, 0xbc, + 0xa5, 0x64, 0xa4, 0x65, 0x4c, 0xa2, 0xbb, 0x0a, + 0x48, 0xaa, 0x20, 0x73, 0x00, 0xe0, 0x8f, 0x90, + 0x20, 0x20, 0xfa, 0xae, 0x20, 0x9e, 0xad, 0x20, + 0xfd, 0xae, 0x20, 0x8f, 0xad, 0x68, 0xaa, 0xa5, + 0x65, 0x48, 0xa5, 0x64, 0x48, 0x8a, 0x48, 0x20, + 0x9e, 0xb7, 0x68, 0xa8, 0x8a, 0x48, 0x4c, 0xd6, + 0xaf, 0x20, 0xf1, 0xae, 0x68, 0xa8, 0xb9, 0xea, + 0x9f, 0x85, 0x55, 0xb9, 0xeb, 0x9f, 0x85, 0x56, + 0x20, 0x54, 0x00, 0x4c, 0x8d, 0xad, 0xa0, 0xff, + 0x2c, 0xa0, 0x00, 0x84, 0x0b, 0x20, 0xbf, 0xb1, + 0xa5, 0x64, 0x45, 0x0b, 0x85, 0x07, 0xa5, 0x65, + 0x45, 0x0b, 0x85, 0x08, 0x20, 0xfc, 0xbb, 0x20, + 0xbf, 0xb1, 0xa5, 0x65, 0x45, 0x0b, 0x25, 0x08, + 0x45, 0x0b, 0xa8, 0xa5, 0x64, 0x45, 0x0b, 0x25, + 0x07, 0x45, 0x0b, 0x4c, 0x91, 0xb3, 0x20, 0x90, + 0xad, 0xb0, 0x13, 0xa5, 0x6e, 0x09, 0x7f, 0x25, + 0x6a, 0x85, 0x6a, 0xa9, 0x69, 0xa0, 0x00, 0x20, + 0x5b, 0xbc, 0xaa, 0x4c, 0x61, 0xb0, 0xa9, 0x00, + 0x85, 0x0d, 0xc6, 0x4d, 0x20, 0xa6, 0xb6, 0x85, + 0x61, 0x86, 0x62, 0x84, 0x63, 0xa5, 0x6c, 0xa4, + 0x6d, 0x20, 0xaa, 0xb6, 0x86, 0x6c, 0x84, 0x6d, + 0xaa, 0x38, 0xe5, 0x61, 0xf0, 0x08, 0xa9, 0x01, + 0x90, 0x04, 0xa6, 0x61, 0xa9, 0xff, 0x85, 0x66, + 0xa0, 0xff, 0xe8, 0xc8, 0xca, 0xd0, 0x07, 0xa6, + 0x66, 0x30, 0x0f, 0x18, 0x90, 0x0c, 0xb1, 0x6c, + 0xd1, 0x62, 0xf0, 0xef, 0xa2, 0xff, 0xb0, 0x02, + 0xa2, 0x01, 0xe8, 0x8a, 0x2a, 0x25, 0x12, 0xf0, + 0x02, 0xa9, 0xff, 0x4c, 0x3c, 0xbc, 0x20, 0xfd, + 0xae, 0xaa, 0x20, 0x90, 0xb0, 0x20, 0x79, 0x00, + 0xd0, 0xf4, 0x60, 0xa2, 0x00, 0x20, 0x79, 0x00, + 0x86, 0x0c, 0x85, 0x45, 0x20, 0x79, 0x00, 0x20, + 0x13, 0xb1, 0xb0, 0x03, 0x4c, 0x08, 0xaf, 0xa2, + 0x00, 0x86, 0x0d, 0x86, 0x0e, 0x20, 0x73, 0x00, + 0x90, 0x05, 0x20, 0x13, 0xb1, 0x90, 0x0b, 0xaa, + 0x20, 0x73, 0x00, 0x90, 0xfb, 0x20, 0x13, 0xb1, + 0xb0, 0xf6, 0xc9, 0x24, 0xd0, 0x06, 0xa9, 0xff, + 0x85, 0x0d, 0xd0, 0x10, 0xc9, 0x25, 0xd0, 0x13, + 0xa5, 0x10, 0xd0, 0xd0, 0xa9, 0x80, 0x85, 0x0e, + 0x05, 0x45, 0x85, 0x45, 0x8a, 0x09, 0x80, 0xaa, + 0x20, 0x73, 0x00, 0x86, 0x46, 0x38, 0x05, 0x10, + 0xe9, 0x28, 0xd0, 0x03, 0x4c, 0xd1, 0xb1, 0xa0, + 0x00, 0x84, 0x10, 0xa5, 0x2d, 0xa6, 0x2e, 0x86, + 0x60, 0x85, 0x5f, 0xe4, 0x30, 0xd0, 0x04, 0xc5, + 0x2f, 0xf0, 0x22, 0xa5, 0x45, 0xd1, 0x5f, 0xd0, + 0x08, 0xa5, 0x46, 0xc8, 0xd1, 0x5f, 0xf0, 0x7d, + 0x88, 0x18, 0xa5, 0x5f, 0x69, 0x07, 0x90, 0xe1, + 0xe8, 0xd0, 0xdc, 0xc9, 0x41, 0x90, 0x05, 0xe9, + 0x5b, 0x38, 0xe9, 0xa5, 0x60, 0x68, 0x48, 0xc9, + 0x2a, 0xd0, 0x05, 0xa9, 0x13, 0xa0, 0xbf, 0x60, + 0xa5, 0x45, 0xa4, 0x46, 0xc9, 0x54, 0xd0, 0x0b, + 0xc0, 0xc9, 0xf0, 0xef, 0xc0, 0x49, 0xd0, 0x03, + 0x4c, 0x08, 0xaf, 0xc9, 0x53, 0xd0, 0x04, 0xc0, + 0x54, 0xf0, 0xf5, 0xa5, 0x2f, 0xa4, 0x30, 0x85, + 0x5f, 0x84, 0x60, 0xa5, 0x31, 0xa4, 0x32, 0x85, + 0x5a, 0x84, 0x5b, 0x18, 0x69, 0x07, 0x90, 0x01, + 0xc8, 0x85, 0x58, 0x84, 0x59, 0x20, 0xb8, 0xa3, + 0xa5, 0x58, 0xa4, 0x59, 0xc8, 0x85, 0x2f, 0x84, + 0x30, 0xa0, 0x00, 0xa5, 0x45, 0x91, 0x5f, 0xc8, + 0xa5, 0x46, 0x91, 0x5f, 0xa9, 0x00, 0xc8, 0x91, + 0x5f, 0xc8, 0x91, 0x5f, 0xc8, 0x91, 0x5f, 0xc8, + 0x91, 0x5f, 0xc8, 0x91, 0x5f, 0xa5, 0x5f, 0x18, + 0x69, 0x02, 0xa4, 0x60, 0x90, 0x01, 0xc8, 0x85, + 0x47, 0x84, 0x48, 0x60, 0xa5, 0x0b, 0x0a, 0x69, + 0x05, 0x65, 0x5f, 0xa4, 0x60, 0x90, 0x01, 0xc8, + 0x85, 0x58, 0x84, 0x59, 0x60, 0x90, 0x80, 0x00, + 0x00, 0x00, 0x20, 0xbf, 0xb1, 0xa5, 0x64, 0xa4, + 0x65, 0x60, 0x20, 0x73, 0x00, 0x20, 0x9e, 0xad, + 0x20, 0x8d, 0xad, 0xa5, 0x66, 0x30, 0x0d, 0xa5, + 0x61, 0xc9, 0x90, 0x90, 0x09, 0xa9, 0xa5, 0xa0, + 0xb1, 0x20, 0x5b, 0xbc, 0xd0, 0x7a, 0x4c, 0x9b, + 0xbc, 0xa5, 0x0c, 0x05, 0x0e, 0x48, 0xa5, 0x0d, + 0x48, 0xa0, 0x00, 0x98, 0x48, 0xa5, 0x46, 0x48, + 0xa5, 0x45, 0x48, 0x20, 0xb2, 0xb1, 0x68, 0x85, + 0x45, 0x68, 0x85, 0x46, 0x68, 0xa8, 0xba, 0xbd, + 0x02, 0x01, 0x48, 0xbd, 0x01, 0x01, 0x48, 0xa5, + 0x64, 0x9d, 0x02, 0x01, 0xa5, 0x65, 0x9d, 0x01, + 0x01, 0xc8, 0x20, 0x79, 0x00, 0xc9, 0x2c, 0xf0, + 0xd2, 0x84, 0x0b, 0x20, 0xf7, 0xae, 0x68, 0x85, + 0x0d, 0x68, 0x85, 0x0e, 0x29, 0x7f, 0x85, 0x0c, + 0xa6, 0x2f, 0xa5, 0x30, 0x86, 0x5f, 0x85, 0x60, + 0xc5, 0x32, 0xd0, 0x04, 0xe4, 0x31, 0xf0, 0x39, + 0xa0, 0x00, 0xb1, 0x5f, 0xc8, 0xc5, 0x45, 0xd0, + 0x06, 0xa5, 0x46, 0xd1, 0x5f, 0xf0, 0x16, 0xc8, + 0xb1, 0x5f, 0x18, 0x65, 0x5f, 0xaa, 0xc8, 0xb1, + 0x5f, 0x65, 0x60, 0x90, 0xd7, 0xa2, 0x12, 0x2c, + 0xa2, 0x0e, 0x4c, 0x37, 0xa4, 0xa2, 0x13, 0xa5, + 0x0c, 0xd0, 0xf7, 0x20, 0x94, 0xb1, 0xa5, 0x0b, + 0xa0, 0x04, 0xd1, 0x5f, 0xd0, 0xe7, 0x4c, 0xea, + 0xb2, 0x20, 0x94, 0xb1, 0x20, 0x08, 0xa4, 0xa0, + 0x00, 0x84, 0x72, 0xa2, 0x05, 0xa5, 0x45, 0x91, + 0x5f, 0x10, 0x01, 0xca, 0xc8, 0xa5, 0x46, 0x91, + 0x5f, 0x10, 0x02, 0xca, 0xca, 0x86, 0x71, 0xa5, + 0x0b, 0xc8, 0xc8, 0xc8, 0x91, 0x5f, 0xa2, 0x0b, + 0xa9, 0x00, 0x24, 0x0c, 0x50, 0x08, 0x68, 0x18, + 0x69, 0x01, 0xaa, 0x68, 0x69, 0x00, 0xc8, 0x91, + 0x5f, 0xc8, 0x8a, 0x91, 0x5f, 0x20, 0x4c, 0xb3, + 0x86, 0x71, 0x85, 0x72, 0xa4, 0x22, 0xc6, 0x0b, + 0xd0, 0xdc, 0x65, 0x59, 0xb0, 0x5d, 0x85, 0x59, + 0xa8, 0x8a, 0x65, 0x58, 0x90, 0x03, 0xc8, 0xf0, + 0x52, 0x20, 0x08, 0xa4, 0x85, 0x31, 0x84, 0x32, + 0xa9, 0x00, 0xe6, 0x72, 0xa4, 0x71, 0xf0, 0x05, + 0x88, 0x91, 0x58, 0xd0, 0xfb, 0xc6, 0x59, 0xc6, + 0x72, 0xd0, 0xf5, 0xe6, 0x59, 0x38, 0xa5, 0x31, + 0xe5, 0x5f, 0xa0, 0x02, 0x91, 0x5f, 0xa5, 0x32, + 0xc8, 0xe5, 0x60, 0x91, 0x5f, 0xa5, 0x0c, 0xd0, + 0x62, 0xc8, 0xb1, 0x5f, 0x85, 0x0b, 0xa9, 0x00, + 0x85, 0x71, 0x85, 0x72, 0xc8, 0x68, 0xaa, 0x85, + 0x64, 0x68, 0x85, 0x65, 0xd1, 0x5f, 0x90, 0x0e, + 0xd0, 0x06, 0xc8, 0x8a, 0xd1, 0x5f, 0x90, 0x07, + 0x4c, 0x45, 0xb2, 0x4c, 0x35, 0xa4, 0xc8, 0xa5, + 0x72, 0x05, 0x71, 0x18, 0xf0, 0x0a, 0x20, 0x4c, + 0xb3, 0x8a, 0x65, 0x64, 0xaa, 0x98, 0xa4, 0x22, + 0x65, 0x65, 0x86, 0x71, 0xc6, 0x0b, 0xd0, 0xca, + 0x85, 0x72, 0xa2, 0x05, 0xa5, 0x45, 0x10, 0x01, + 0xca, 0xa5, 0x46, 0x10, 0x02, 0xca, 0xca, 0x86, + 0x28, 0xa9, 0x00, 0x20, 0x55, 0xb3, 0x8a, 0x65, + 0x58, 0x85, 0x47, 0x98, 0x65, 0x59, 0x85, 0x48, + 0xa8, 0xa5, 0x47, 0x60, 0x84, 0x22, 0xb1, 0x5f, + 0x85, 0x28, 0x88, 0xb1, 0x5f, 0x85, 0x29, 0xa9, + 0x10, 0x85, 0x5d, 0xa2, 0x00, 0xa0, 0x00, 0x8a, + 0x0a, 0xaa, 0x98, 0x2a, 0xa8, 0xb0, 0xa4, 0x06, + 0x71, 0x26, 0x72, 0x90, 0x0b, 0x18, 0x8a, 0x65, + 0x28, 0xaa, 0x98, 0x65, 0x29, 0xa8, 0xb0, 0x93, + 0xc6, 0x5d, 0xd0, 0xe3, 0x60, 0xa5, 0x0d, 0xf0, + 0x03, 0x20, 0xa6, 0xb6, 0x20, 0x26, 0xb5, 0x38, + 0xa5, 0x33, 0xe5, 0x31, 0xa8, 0xa5, 0x34, 0xe5, + 0x32, 0xa2, 0x00, 0x86, 0x0d, 0x85, 0x62, 0x84, + 0x63, 0xa2, 0x90, 0x4c, 0x44, 0xbc, 0x38, 0x20, + 0xf0, 0xff, 0xa9, 0x00, 0xf0, 0xeb, 0xa6, 0x3a, + 0xe8, 0xd0, 0xa0, 0xa2, 0x15, 0x2c, 0xa2, 0x1b, + 0x4c, 0x37, 0xa4, 0x20, 0xe1, 0xb3, 0x20, 0xa6, + 0xb3, 0x20, 0xfa, 0xae, 0xa9, 0x80, 0x85, 0x10, + 0x20, 0x8b, 0xb0, 0x20, 0x8d, 0xad, 0x20, 0xf7, + 0xae, 0xa9, 0xb2, 0x20, 0xff, 0xae, 0x48, 0xa5, + 0x48, 0x48, 0xa5, 0x47, 0x48, 0xa5, 0x7b, 0x48, + 0xa5, 0x7a, 0x48, 0x20, 0xf8, 0xa8, 0x4c, 0x4f, + 0xb4, 0xa9, 0xa5, 0x20, 0xff, 0xae, 0x09, 0x80, + 0x85, 0x10, 0x20, 0x92, 0xb0, 0x85, 0x4e, 0x84, + 0x4f, 0x4c, 0x8d, 0xad, 0x20, 0xe1, 0xb3, 0xa5, + 0x4f, 0x48, 0xa5, 0x4e, 0x48, 0x20, 0xf1, 0xae, + 0x20, 0x8d, 0xad, 0x68, 0x85, 0x4e, 0x68, 0x85, + 0x4f, 0xa0, 0x02, 0xb1, 0x4e, 0x85, 0x47, 0xaa, + 0xc8, 0xb1, 0x4e, 0xf0, 0x99, 0x85, 0x48, 0xc8, + 0xb1, 0x47, 0x48, 0x88, 0x10, 0xfa, 0xa4, 0x48, + 0x20, 0xd4, 0xbb, 0xa5, 0x7b, 0x48, 0xa5, 0x7a, + 0x48, 0xb1, 0x4e, 0x85, 0x7a, 0xc8, 0xb1, 0x4e, + 0x85, 0x7b, 0xa5, 0x48, 0x48, 0xa5, 0x47, 0x48, + 0x20, 0x8a, 0xad, 0x68, 0x85, 0x4e, 0x68, 0x85, + 0x4f, 0x20, 0x79, 0x00, 0xf0, 0x03, 0x4c, 0x08, + 0xaf, 0x68, 0x85, 0x7a, 0x68, 0x85, 0x7b, 0xa0, + 0x00, 0x68, 0x91, 0x4e, 0x68, 0xc8, 0x91, 0x4e, + 0x68, 0xc8, 0x91, 0x4e, 0x68, 0xc8, 0x91, 0x4e, + 0x68, 0xc8, 0x91, 0x4e, 0x60, 0x20, 0x8d, 0xad, + 0xa0, 0x00, 0x20, 0xdf, 0xbd, 0x68, 0x68, 0xa9, + 0xff, 0xa0, 0x00, 0xf0, 0x12, 0xa6, 0x64, 0xa4, + 0x65, 0x86, 0x50, 0x84, 0x51, 0x20, 0xf4, 0xb4, + 0x86, 0x62, 0x84, 0x63, 0x85, 0x61, 0x60, 0xa2, + 0x22, 0x86, 0x07, 0x86, 0x08, 0x85, 0x6f, 0x84, + 0x70, 0x85, 0x62, 0x84, 0x63, 0xa0, 0xff, 0xc8, + 0xb1, 0x6f, 0xf0, 0x0c, 0xc5, 0x07, 0xf0, 0x04, + 0xc5, 0x08, 0xd0, 0xf3, 0xc9, 0x22, 0xf0, 0x01, + 0x18, 0x84, 0x61, 0x98, 0x65, 0x6f, 0x85, 0x71, + 0xa6, 0x70, 0x90, 0x01, 0xe8, 0x86, 0x72, 0xa5, + 0x70, 0xf0, 0x04, 0xc9, 0x02, 0xd0, 0x0b, 0x98, + 0x20, 0x75, 0xb4, 0xa6, 0x6f, 0xa4, 0x70, 0x20, + 0x88, 0xb6, 0xa6, 0x16, 0xe0, 0x22, 0xd0, 0x05, + 0xa2, 0x19, 0x4c, 0x37, 0xa4, 0xa5, 0x61, 0x95, + 0x00, 0xa5, 0x62, 0x95, 0x01, 0xa5, 0x63, 0x95, + 0x02, 0xa0, 0x00, 0x86, 0x64, 0x84, 0x65, 0x84, + 0x70, 0x88, 0x84, 0x0d, 0x86, 0x17, 0xe8, 0xe8, + 0xe8, 0x86, 0x16, 0x60, 0x46, 0x0f, 0x48, 0x49, + 0xff, 0x38, 0x65, 0x33, 0xa4, 0x34, 0xb0, 0x01, + 0x88, 0xc4, 0x32, 0x90, 0x11, 0xd0, 0x04, 0xc5, + 0x31, 0x90, 0x0b, 0x85, 0x33, 0x84, 0x34, 0x85, + 0x35, 0x84, 0x36, 0xaa, 0x68, 0x60, 0xa2, 0x10, + 0xa5, 0x0f, 0x30, 0xb6, 0x20, 0x26, 0xb5, 0xa9, + 0x80, 0x85, 0x0f, 0x68, 0xd0, 0xd0, 0xa6, 0x37, + 0xa5, 0x38, 0x86, 0x33, 0x85, 0x34, 0xa0, 0x00, + 0x84, 0x4f, 0x84, 0x4e, 0xa5, 0x31, 0xa6, 0x32, + 0x85, 0x5f, 0x86, 0x60, 0xa9, 0x19, 0xa2, 0x00, + 0x85, 0x22, 0x86, 0x23, 0xc5, 0x16, 0xf0, 0x05, + 0x20, 0xc7, 0xb5, 0xf0, 0xf7, 0xa9, 0x07, 0x85, + 0x53, 0xa5, 0x2d, 0xa6, 0x2e, 0x85, 0x22, 0x86, + 0x23, 0xe4, 0x30, 0xd0, 0x04, 0xc5, 0x2f, 0xf0, + 0x05, 0x20, 0xbd, 0xb5, 0xf0, 0xf3, 0x85, 0x58, + 0x86, 0x59, 0xa9, 0x03, 0x85, 0x53, 0xa5, 0x58, + 0xa6, 0x59, 0xe4, 0x32, 0xd0, 0x07, 0xc5, 0x31, + 0xd0, 0x03, 0x4c, 0x06, 0xb6, 0x85, 0x22, 0x86, + 0x23, 0xa0, 0x00, 0xb1, 0x22, 0xaa, 0xc8, 0xb1, + 0x22, 0x08, 0xc8, 0xb1, 0x22, 0x65, 0x58, 0x85, + 0x58, 0xc8, 0xb1, 0x22, 0x65, 0x59, 0x85, 0x59, + 0x28, 0x10, 0xd3, 0x8a, 0x30, 0xd0, 0xc8, 0xb1, + 0x22, 0xa0, 0x00, 0x0a, 0x69, 0x05, 0x65, 0x22, + 0x85, 0x22, 0x90, 0x02, 0xe6, 0x23, 0xa6, 0x23, + 0xe4, 0x59, 0xd0, 0x04, 0xc5, 0x58, 0xf0, 0xba, + 0x20, 0xc7, 0xb5, 0xf0, 0xf3, 0xb1, 0x22, 0x30, + 0x35, 0xc8, 0xb1, 0x22, 0x10, 0x30, 0xc8, 0xb1, + 0x22, 0xf0, 0x2b, 0xc8, 0xb1, 0x22, 0xaa, 0xc8, + 0xb1, 0x22, 0xc5, 0x34, 0x90, 0x06, 0xd0, 0x1e, + 0xe4, 0x33, 0xb0, 0x1a, 0xc5, 0x60, 0x90, 0x16, + 0xd0, 0x04, 0xe4, 0x5f, 0x90, 0x10, 0x86, 0x5f, + 0x85, 0x60, 0xa5, 0x22, 0xa6, 0x23, 0x85, 0x4e, + 0x86, 0x4f, 0xa5, 0x53, 0x85, 0x55, 0xa5, 0x53, + 0x18, 0x65, 0x22, 0x85, 0x22, 0x90, 0x02, 0xe6, + 0x23, 0xa6, 0x23, 0xa0, 0x00, 0x60, 0xa5, 0x4f, + 0x05, 0x4e, 0xf0, 0xf5, 0xa5, 0x55, 0x29, 0x04, + 0x4a, 0xa8, 0x85, 0x55, 0xb1, 0x4e, 0x65, 0x5f, + 0x85, 0x5a, 0xa5, 0x60, 0x69, 0x00, 0x85, 0x5b, + 0xa5, 0x33, 0xa6, 0x34, 0x85, 0x58, 0x86, 0x59, + 0x20, 0xbf, 0xa3, 0xa4, 0x55, 0xc8, 0xa5, 0x58, + 0x91, 0x4e, 0xaa, 0xe6, 0x59, 0xa5, 0x59, 0xc8, + 0x91, 0x4e, 0x4c, 0x2a, 0xb5, 0xa5, 0x65, 0x48, + 0xa5, 0x64, 0x48, 0x20, 0x83, 0xae, 0x20, 0x8f, + 0xad, 0x68, 0x85, 0x6f, 0x68, 0x85, 0x70, 0xa0, + 0x00, 0xb1, 0x6f, 0x18, 0x71, 0x64, 0x90, 0x05, + 0xa2, 0x17, 0x4c, 0x37, 0xa4, 0x20, 0x75, 0xb4, + 0x20, 0x7a, 0xb6, 0xa5, 0x50, 0xa4, 0x51, 0x20, + 0xaa, 0xb6, 0x20, 0x8c, 0xb6, 0xa5, 0x6f, 0xa4, + 0x70, 0x20, 0xaa, 0xb6, 0x20, 0xca, 0xb4, 0x4c, + 0xb8, 0xad, 0xa0, 0x00, 0xb1, 0x6f, 0x48, 0xc8, + 0xb1, 0x6f, 0xaa, 0xc8, 0xb1, 0x6f, 0xa8, 0x68, + 0x86, 0x22, 0x84, 0x23, 0xa8, 0xf0, 0x0a, 0x48, + 0x88, 0xb1, 0x22, 0x91, 0x35, 0x98, 0xd0, 0xf8, + 0x68, 0x18, 0x65, 0x35, 0x85, 0x35, 0x90, 0x02, + 0xe6, 0x36, 0x60, 0x20, 0x8f, 0xad, 0xa5, 0x64, + 0xa4, 0x65, 0x85, 0x22, 0x84, 0x23, 0x20, 0xdb, + 0xb6, 0x08, 0xa0, 0x00, 0xb1, 0x22, 0x48, 0xc8, + 0xb1, 0x22, 0xaa, 0xc8, 0xb1, 0x22, 0xa8, 0x68, + 0x28, 0xd0, 0x13, 0xc4, 0x34, 0xd0, 0x0f, 0xe4, + 0x33, 0xd0, 0x0b, 0x48, 0x18, 0x65, 0x33, 0x85, + 0x33, 0x90, 0x02, 0xe6, 0x34, 0x68, 0x86, 0x22, + 0x84, 0x23, 0x60, 0xc4, 0x18, 0xd0, 0x0c, 0xc5, + 0x17, 0xd0, 0x08, 0x85, 0x16, 0xe9, 0x03, 0x85, + 0x17, 0xa0, 0x00, 0x60, 0x20, 0xa1, 0xb7, 0x8a, + 0x48, 0xa9, 0x01, 0x20, 0x7d, 0xb4, 0x68, 0xa0, + 0x00, 0x91, 0x62, 0x68, 0x68, 0x4c, 0xca, 0xb4, + 0x20, 0x61, 0xb7, 0xd1, 0x50, 0x98, 0x90, 0x04, + 0xb1, 0x50, 0xaa, 0x98, 0x48, 0x8a, 0x48, 0x20, + 0x7d, 0xb4, 0xa5, 0x50, 0xa4, 0x51, 0x20, 0xaa, + 0xb6, 0x68, 0xa8, 0x68, 0x18, 0x65, 0x22, 0x85, + 0x22, 0x90, 0x02, 0xe6, 0x23, 0x98, 0x20, 0x8c, + 0xb6, 0x4c, 0xca, 0xb4, 0x20, 0x61, 0xb7, 0x18, + 0xf1, 0x50, 0x49, 0xff, 0x4c, 0x06, 0xb7, 0xa9, + 0xff, 0x85, 0x65, 0x20, 0x79, 0x00, 0xc9, 0x29, + 0xf0, 0x06, 0x20, 0xfd, 0xae, 0x20, 0x9e, 0xb7, + 0x20, 0x61, 0xb7, 0xf0, 0x4b, 0xca, 0x8a, 0x48, + 0x18, 0xa2, 0x00, 0xf1, 0x50, 0xb0, 0xb6, 0x49, + 0xff, 0xc5, 0x65, 0x90, 0xb1, 0xa5, 0x65, 0xb0, + 0xad, 0x20, 0xf7, 0xae, 0x68, 0xa8, 0x68, 0x85, + 0x55, 0x68, 0x68, 0x68, 0xaa, 0x68, 0x85, 0x50, + 0x68, 0x85, 0x51, 0xa5, 0x55, 0x48, 0x98, 0x48, + 0xa0, 0x00, 0x8a, 0x60, 0x20, 0x82, 0xb7, 0x4c, + 0xa2, 0xb3, 0x20, 0xa3, 0xb6, 0xa2, 0x00, 0x86, + 0x0d, 0xa8, 0x60, 0x20, 0x82, 0xb7, 0xf0, 0x08, + 0xa0, 0x00, 0xb1, 0x22, 0xa8, 0x4c, 0xa2, 0xb3, + 0x4c, 0x48, 0xb2, 0x20, 0x73, 0x00, 0x20, 0x8a, + 0xad, 0x20, 0xb8, 0xb1, 0xa6, 0x64, 0xd0, 0xf0, + 0xa6, 0x65, 0x4c, 0x79, 0x00, 0x20, 0x82, 0xb7, + 0xd0, 0x03, 0x4c, 0xf7, 0xb8, 0xa6, 0x7a, 0xa4, + 0x7b, 0x86, 0x71, 0x84, 0x72, 0xa6, 0x22, 0x86, + 0x7a, 0x18, 0x65, 0x22, 0x85, 0x24, 0xa6, 0x23, + 0x86, 0x7b, 0x90, 0x01, 0xe8, 0x86, 0x25, 0xa0, + 0x00, 0xb1, 0x24, 0x48, 0x98, 0x91, 0x24, 0x20, + 0x79, 0x00, 0x20, 0xf3, 0xbc, 0x68, 0xa0, 0x00, + 0x91, 0x24, 0xa6, 0x71, 0xa4, 0x72, 0x86, 0x7a, + 0x84, 0x7b, 0x60, 0x20, 0x8a, 0xad, 0x20, 0xf7, + 0xb7, 0x20, 0xfd, 0xae, 0x4c, 0x9e, 0xb7, 0xa5, + 0x66, 0x30, 0x9d, 0xa5, 0x61, 0xc9, 0x91, 0xb0, + 0x97, 0x20, 0x9b, 0xbc, 0xa5, 0x64, 0xa4, 0x65, + 0x84, 0x14, 0x85, 0x15, 0x60, 0xa5, 0x15, 0x48, + 0xa5, 0x14, 0x48, 0x20, 0xf7, 0xb7, 0xa0, 0x00, + 0xb1, 0x14, 0xa8, 0x68, 0x85, 0x14, 0x68, 0x85, + 0x15, 0x4c, 0xa2, 0xb3, 0x20, 0xeb, 0xb7, 0x8a, + 0xa0, 0x00, 0x91, 0x14, 0x60, 0x20, 0xeb, 0xb7, + 0x86, 0x49, 0xa2, 0x00, 0x20, 0x79, 0x00, 0xf0, + 0x03, 0x20, 0xf1, 0xb7, 0x86, 0x4a, 0xa0, 0x00, + 0xb1, 0x14, 0x45, 0x4a, 0x25, 0x49, 0xf0, 0xf8, + 0x60, 0xa9, 0x11, 0xa0, 0xbf, 0x4c, 0x67, 0xb8, + 0x20, 0x8c, 0xba, 0xa5, 0x66, 0x49, 0xff, 0x85, + 0x66, 0x45, 0x6e, 0x85, 0x6f, 0xa5, 0x61, 0x4c, + 0x6a, 0xb8, 0x20, 0x99, 0xb9, 0x90, 0x3c, 0x20, + 0x8c, 0xba, 0xd0, 0x03, 0x4c, 0xfc, 0xbb, 0xa6, + 0x70, 0x86, 0x56, 0xa2, 0x69, 0xa5, 0x69, 0xa8, + 0xf0, 0xce, 0x38, 0xe5, 0x61, 0xf0, 0x24, 0x90, + 0x12, 0x84, 0x61, 0xa4, 0x6e, 0x84, 0x66, 0x49, + 0xff, 0x69, 0x00, 0xa0, 0x00, 0x84, 0x56, 0xa2, + 0x61, 0xd0, 0x04, 0xa0, 0x00, 0x84, 0x70, 0xc9, + 0xf9, 0x30, 0xc7, 0xa8, 0xa5, 0x70, 0x56, 0x01, + 0x20, 0xb0, 0xb9, 0x24, 0x6f, 0x10, 0x57, 0xa0, + 0x61, 0xe0, 0x69, 0xf0, 0x02, 0xa0, 0x69, 0x38, + 0x49, 0xff, 0x65, 0x56, 0x85, 0x70, 0xb9, 0x04, + 0x00, 0xf5, 0x04, 0x85, 0x65, 0xb9, 0x03, 0x00, + 0xf5, 0x03, 0x85, 0x64, 0xb9, 0x02, 0x00, 0xf5, + 0x02, 0x85, 0x63, 0xb9, 0x01, 0x00, 0xf5, 0x01, + 0x85, 0x62, 0xb0, 0x03, 0x20, 0x47, 0xb9, 0xa0, + 0x00, 0x98, 0x18, 0xa6, 0x62, 0xd0, 0x4a, 0xa6, + 0x63, 0x86, 0x62, 0xa6, 0x64, 0x86, 0x63, 0xa6, + 0x65, 0x86, 0x64, 0xa6, 0x70, 0x86, 0x65, 0x84, + 0x70, 0x69, 0x08, 0xc9, 0x20, 0xd0, 0xe4, 0xa9, + 0x00, 0x85, 0x61, 0x85, 0x66, 0x60, 0x65, 0x56, + 0x85, 0x70, 0xa5, 0x65, 0x65, 0x6d, 0x85, 0x65, + 0xa5, 0x64, 0x65, 0x6c, 0x85, 0x64, 0xa5, 0x63, + 0x65, 0x6b, 0x85, 0x63, 0xa5, 0x62, 0x65, 0x6a, + 0x85, 0x62, 0x4c, 0x36, 0xb9, 0x69, 0x01, 0x06, + 0x70, 0x26, 0x65, 0x26, 0x64, 0x26, 0x63, 0x26, + 0x62, 0x10, 0xf2, 0x38, 0xe5, 0x61, 0xb0, 0xc7, + 0x49, 0xff, 0x69, 0x01, 0x85, 0x61, 0x90, 0x0e, + 0xe6, 0x61, 0xf0, 0x42, 0x66, 0x62, 0x66, 0x63, + 0x66, 0x64, 0x66, 0x65, 0x66, 0x70, 0x60, 0xa5, + 0x66, 0x49, 0xff, 0x85, 0x66, 0xa5, 0x62, 0x49, + 0xff, 0x85, 0x62, 0xa5, 0x63, 0x49, 0xff, 0x85, + 0x63, 0xa5, 0x64, 0x49, 0xff, 0x85, 0x64, 0xa5, + 0x65, 0x49, 0xff, 0x85, 0x65, 0xa5, 0x70, 0x49, + 0xff, 0x85, 0x70, 0xe6, 0x70, 0xd0, 0x0e, 0xe6, + 0x65, 0xd0, 0x0a, 0xe6, 0x64, 0xd0, 0x06, 0xe6, + 0x63, 0xd0, 0x02, 0xe6, 0x62, 0x60, 0xa2, 0x0f, + 0x4c, 0x37, 0xa4, 0xa2, 0x25, 0xb4, 0x04, 0x84, + 0x70, 0xb4, 0x03, 0x94, 0x04, 0xb4, 0x02, 0x94, + 0x03, 0xb4, 0x01, 0x94, 0x02, 0xa4, 0x68, 0x94, + 0x01, 0x69, 0x08, 0x30, 0xe8, 0xf0, 0xe6, 0xe9, + 0x08, 0xa8, 0xa5, 0x70, 0xb0, 0x14, 0x16, 0x01, + 0x90, 0x02, 0xf6, 0x01, 0x76, 0x01, 0x76, 0x01, + 0x76, 0x02, 0x76, 0x03, 0x76, 0x04, 0x6a, 0xc8, + 0xd0, 0xec, 0x18, 0x60, 0x81, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x7f, 0x5e, 0x56, 0xcb, 0x79, 0x80, + 0x13, 0x9b, 0x0b, 0x64, 0x80, 0x76, 0x38, 0x93, + 0x16, 0x82, 0x38, 0xaa, 0x3b, 0x20, 0x80, 0x35, + 0x04, 0xf3, 0x34, 0x81, 0x35, 0x04, 0xf3, 0x34, + 0x80, 0x80, 0x00, 0x00, 0x00, 0x80, 0x31, 0x72, + 0x17, 0xf8, 0x20, 0x2b, 0xbc, 0xf0, 0x02, 0x10, + 0x03, 0x4c, 0x48, 0xb2, 0xa5, 0x61, 0xe9, 0x7f, + 0x48, 0xa9, 0x80, 0x85, 0x61, 0xa9, 0xd6, 0xa0, + 0xb9, 0x20, 0x67, 0xb8, 0xa9, 0xdb, 0xa0, 0xb9, + 0x20, 0x0f, 0xbb, 0xa9, 0xbc, 0xa0, 0xb9, 0x20, + 0x50, 0xb8, 0xa9, 0xc1, 0xa0, 0xb9, 0x20, 0x43, + 0xe0, 0xa9, 0xe0, 0xa0, 0xb9, 0x20, 0x67, 0xb8, + 0x68, 0x20, 0x7e, 0xbd, 0xa9, 0xe5, 0xa0, 0xb9, + 0x20, 0x8c, 0xba, 0xd0, 0x03, 0x4c, 0x8b, 0xba, + 0x20, 0xb7, 0xba, 0xa9, 0x00, 0x85, 0x26, 0x85, + 0x27, 0x85, 0x28, 0x85, 0x29, 0xa5, 0x70, 0x20, + 0x59, 0xba, 0xa5, 0x65, 0x20, 0x59, 0xba, 0xa5, + 0x64, 0x20, 0x59, 0xba, 0xa5, 0x63, 0x20, 0x59, + 0xba, 0xa5, 0x62, 0x20, 0x5e, 0xba, 0x4c, 0x8f, + 0xbb, 0xd0, 0x03, 0x4c, 0x83, 0xb9, 0x4a, 0x09, + 0x80, 0xa8, 0x90, 0x19, 0x18, 0xa5, 0x29, 0x65, + 0x6d, 0x85, 0x29, 0xa5, 0x28, 0x65, 0x6c, 0x85, + 0x28, 0xa5, 0x27, 0x65, 0x6b, 0x85, 0x27, 0xa5, + 0x26, 0x65, 0x6a, 0x85, 0x26, 0x66, 0x26, 0x66, + 0x27, 0x66, 0x28, 0x66, 0x29, 0x66, 0x70, 0x98, + 0x4a, 0xd0, 0xd6, 0x60, 0x85, 0x22, 0x84, 0x23, + 0xa0, 0x04, 0xb1, 0x22, 0x85, 0x6d, 0x88, 0xb1, + 0x22, 0x85, 0x6c, 0x88, 0xb1, 0x22, 0x85, 0x6b, + 0x88, 0xb1, 0x22, 0x85, 0x6e, 0x45, 0x66, 0x85, + 0x6f, 0xa5, 0x6e, 0x09, 0x80, 0x85, 0x6a, 0x88, + 0xb1, 0x22, 0x85, 0x69, 0xa5, 0x61, 0x60, 0xa5, + 0x69, 0xf0, 0x1f, 0x18, 0x65, 0x61, 0x90, 0x04, + 0x30, 0x1d, 0x18, 0x2c, 0x10, 0x14, 0x69, 0x80, + 0x85, 0x61, 0xd0, 0x03, 0x4c, 0xfb, 0xb8, 0xa5, + 0x6f, 0x85, 0x66, 0x60, 0xa5, 0x66, 0x49, 0xff, + 0x30, 0x05, 0x68, 0x68, 0x4c, 0xf7, 0xb8, 0x4c, + 0x7e, 0xb9, 0x20, 0x0c, 0xbc, 0xaa, 0xf0, 0x10, + 0x18, 0x69, 0x02, 0xb0, 0xf2, 0xa2, 0x00, 0x86, + 0x6f, 0x20, 0x77, 0xb8, 0xe6, 0x61, 0xf0, 0xe7, + 0x60, 0x84, 0x20, 0x00, 0x00, 0x00, 0x20, 0x0c, + 0xbc, 0xa9, 0xf9, 0xa0, 0xba, 0xa2, 0x00, 0x86, + 0x6f, 0x20, 0xa2, 0xbb, 0x4c, 0x12, 0xbb, 0x20, + 0x8c, 0xba, 0xf0, 0x76, 0x20, 0x1b, 0xbc, 0xa9, + 0x00, 0x38, 0xe5, 0x61, 0x85, 0x61, 0x20, 0xb7, + 0xba, 0xe6, 0x61, 0xf0, 0xba, 0xa2, 0xfc, 0xa9, + 0x01, 0xa4, 0x6a, 0xc4, 0x62, 0xd0, 0x10, 0xa4, + 0x6b, 0xc4, 0x63, 0xd0, 0x0a, 0xa4, 0x6c, 0xc4, + 0x64, 0xd0, 0x04, 0xa4, 0x6d, 0xc4, 0x65, 0x08, + 0x2a, 0x90, 0x09, 0xe8, 0x95, 0x29, 0xf0, 0x32, + 0x10, 0x34, 0xa9, 0x01, 0x28, 0xb0, 0x0e, 0x06, + 0x6d, 0x26, 0x6c, 0x26, 0x6b, 0x26, 0x6a, 0xb0, + 0xe6, 0x30, 0xce, 0x10, 0xe2, 0xa8, 0xa5, 0x6d, + 0xe5, 0x65, 0x85, 0x6d, 0xa5, 0x6c, 0xe5, 0x64, + 0x85, 0x6c, 0xa5, 0x6b, 0xe5, 0x63, 0x85, 0x6b, + 0xa5, 0x6a, 0xe5, 0x62, 0x85, 0x6a, 0x98, 0x4c, + 0x4f, 0xbb, 0xa9, 0x40, 0xd0, 0xce, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x85, 0x70, 0x28, 0x4c, + 0x8f, 0xbb, 0xa2, 0x14, 0x4c, 0x37, 0xa4, 0xa5, + 0x26, 0x85, 0x62, 0xa5, 0x27, 0x85, 0x63, 0xa5, + 0x28, 0x85, 0x64, 0xa5, 0x29, 0x85, 0x65, 0x4c, + 0xd7, 0xb8, 0x85, 0x22, 0x84, 0x23, 0xa0, 0x04, + 0xb1, 0x22, 0x85, 0x65, 0x88, 0xb1, 0x22, 0x85, + 0x64, 0x88, 0xb1, 0x22, 0x85, 0x63, 0x88, 0xb1, + 0x22, 0x85, 0x66, 0x09, 0x80, 0x85, 0x62, 0x88, + 0xb1, 0x22, 0x85, 0x61, 0x84, 0x70, 0x60, 0xa2, + 0x5c, 0x2c, 0xa2, 0x57, 0xa0, 0x00, 0xf0, 0x04, + 0xa6, 0x49, 0xa4, 0x4a, 0x20, 0x1b, 0xbc, 0x86, + 0x22, 0x84, 0x23, 0xa0, 0x04, 0xa5, 0x65, 0x91, + 0x22, 0x88, 0xa5, 0x64, 0x91, 0x22, 0x88, 0xa5, + 0x63, 0x91, 0x22, 0x88, 0xa5, 0x66, 0x09, 0x7f, + 0x25, 0x62, 0x91, 0x22, 0x88, 0xa5, 0x61, 0x91, + 0x22, 0x84, 0x70, 0x60, 0xa5, 0x6e, 0x85, 0x66, + 0xa2, 0x05, 0xb5, 0x68, 0x95, 0x60, 0xca, 0xd0, + 0xf9, 0x86, 0x70, 0x60, 0x20, 0x1b, 0xbc, 0xa2, + 0x06, 0xb5, 0x60, 0x95, 0x68, 0xca, 0xd0, 0xf9, + 0x86, 0x70, 0x60, 0xa5, 0x61, 0xf0, 0xfb, 0x06, + 0x70, 0x90, 0xf7, 0x20, 0x6f, 0xb9, 0xd0, 0xf2, + 0x4c, 0x38, 0xb9, 0xa5, 0x61, 0xf0, 0x09, 0xa5, + 0x66, 0x2a, 0xa9, 0xff, 0xb0, 0x02, 0xa9, 0x01, + 0x60, 0x20, 0x2b, 0xbc, 0x85, 0x62, 0xa9, 0x00, + 0x85, 0x63, 0xa2, 0x88, 0xa5, 0x62, 0x49, 0xff, + 0x2a, 0xa9, 0x00, 0x85, 0x65, 0x85, 0x64, 0x86, + 0x61, 0x85, 0x70, 0x85, 0x66, 0x4c, 0xd2, 0xb8, + 0x46, 0x66, 0x60, 0x85, 0x24, 0x84, 0x25, 0xa0, + 0x00, 0xb1, 0x24, 0xc8, 0xaa, 0xf0, 0xc4, 0xb1, + 0x24, 0x45, 0x66, 0x30, 0xc2, 0xe4, 0x61, 0xd0, + 0x21, 0xb1, 0x24, 0x09, 0x80, 0xc5, 0x62, 0xd0, + 0x19, 0xc8, 0xb1, 0x24, 0xc5, 0x63, 0xd0, 0x12, + 0xc8, 0xb1, 0x24, 0xc5, 0x64, 0xd0, 0x0b, 0xc8, + 0xa9, 0x7f, 0xc5, 0x70, 0xb1, 0x24, 0xe5, 0x65, + 0xf0, 0x28, 0xa5, 0x66, 0x90, 0x02, 0x49, 0xff, + 0x4c, 0x31, 0xbc, 0xa5, 0x61, 0xf0, 0x4a, 0x38, + 0xe9, 0xa0, 0x24, 0x66, 0x10, 0x09, 0xaa, 0xa9, + 0xff, 0x85, 0x68, 0x20, 0x4d, 0xb9, 0x8a, 0xa2, + 0x61, 0xc9, 0xf9, 0x10, 0x06, 0x20, 0x99, 0xb9, + 0x84, 0x68, 0x60, 0xa8, 0xa5, 0x66, 0x29, 0x80, + 0x46, 0x62, 0x05, 0x62, 0x85, 0x62, 0x20, 0xb0, + 0xb9, 0x84, 0x68, 0x60, 0xa5, 0x61, 0xc9, 0xa0, + 0xb0, 0x20, 0x20, 0x9b, 0xbc, 0x84, 0x70, 0xa5, + 0x66, 0x84, 0x66, 0x49, 0x80, 0x2a, 0xa9, 0xa0, + 0x85, 0x61, 0xa5, 0x65, 0x85, 0x07, 0x4c, 0xd2, + 0xb8, 0x85, 0x62, 0x85, 0x63, 0x85, 0x64, 0x85, + 0x65, 0xa8, 0x60, 0xa0, 0x00, 0xa2, 0x0a, 0x94, + 0x5d, 0xca, 0x10, 0xfb, 0x90, 0x0f, 0xc9, 0x2d, + 0xd0, 0x04, 0x86, 0x67, 0xf0, 0x04, 0xc9, 0x2b, + 0xd0, 0x05, 0x20, 0x73, 0x00, 0x90, 0x5b, 0xc9, + 0x2e, 0xf0, 0x2e, 0xc9, 0x45, 0xd0, 0x30, 0x20, + 0x73, 0x00, 0x90, 0x17, 0xc9, 0xab, 0xf0, 0x0e, + 0xc9, 0x2d, 0xf0, 0x0a, 0xc9, 0xaa, 0xf0, 0x08, + 0xc9, 0x2b, 0xf0, 0x04, 0xd0, 0x07, 0x66, 0x60, + 0x20, 0x73, 0x00, 0x90, 0x5c, 0x24, 0x60, 0x10, + 0x0e, 0xa9, 0x00, 0x38, 0xe5, 0x5e, 0x4c, 0x49, + 0xbd, 0x66, 0x5f, 0x24, 0x5f, 0x50, 0xc3, 0xa5, + 0x5e, 0x38, 0xe5, 0x5d, 0x85, 0x5e, 0xf0, 0x12, + 0x10, 0x09, 0x20, 0xfe, 0xba, 0xe6, 0x5e, 0xd0, + 0xf9, 0xf0, 0x07, 0x20, 0xe2, 0xba, 0xc6, 0x5e, + 0xd0, 0xf9, 0xa5, 0x67, 0x30, 0x01, 0x60, 0x4c, + 0xb4, 0xbf, 0x48, 0x24, 0x5f, 0x10, 0x02, 0xe6, + 0x5d, 0x20, 0xe2, 0xba, 0x68, 0x38, 0xe9, 0x30, + 0x20, 0x7e, 0xbd, 0x4c, 0x0a, 0xbd, 0x48, 0x20, + 0x0c, 0xbc, 0x68, 0x20, 0x3c, 0xbc, 0xa5, 0x6e, + 0x45, 0x66, 0x85, 0x6f, 0xa6, 0x61, 0x4c, 0x6a, + 0xb8, 0xa5, 0x5e, 0xc9, 0x0a, 0x90, 0x09, 0xa9, + 0x64, 0x24, 0x60, 0x30, 0x11, 0x4c, 0x7e, 0xb9, + 0x0a, 0x0a, 0x18, 0x65, 0x5e, 0x0a, 0x18, 0xa0, + 0x00, 0x71, 0x7a, 0x38, 0xe9, 0x30, 0x85, 0x5e, + 0x4c, 0x30, 0xbd, 0x9b, 0x3e, 0xbc, 0x1f, 0xfd, + 0x9e, 0x6e, 0x6b, 0x27, 0xfd, 0x9e, 0x6e, 0x6b, + 0x28, 0x00, 0xa9, 0x71, 0xa0, 0xa3, 0x20, 0xda, + 0xbd, 0xa5, 0x3a, 0xa6, 0x39, 0x85, 0x62, 0x86, + 0x63, 0xa2, 0x90, 0x38, 0x20, 0x49, 0xbc, 0x20, + 0xdf, 0xbd, 0x4c, 0x1e, 0xab, 0xa0, 0x01, 0xa9, + 0x20, 0x24, 0x66, 0x10, 0x02, 0xa9, 0x2d, 0x99, + 0xff, 0x00, 0x85, 0x66, 0x84, 0x71, 0xc8, 0xa9, + 0x30, 0xa6, 0x61, 0xd0, 0x03, 0x4c, 0x04, 0xbf, + 0xa9, 0x00, 0xe0, 0x80, 0xf0, 0x02, 0xb0, 0x09, + 0xa9, 0xbd, 0xa0, 0xbd, 0x20, 0x28, 0xba, 0xa9, + 0xf7, 0x85, 0x5d, 0xa9, 0xb8, 0xa0, 0xbd, 0x20, + 0x5b, 0xbc, 0xf0, 0x1e, 0x10, 0x12, 0xa9, 0xb3, + 0xa0, 0xbd, 0x20, 0x5b, 0xbc, 0xf0, 0x02, 0x10, + 0x0e, 0x20, 0xe2, 0xba, 0xc6, 0x5d, 0xd0, 0xee, + 0x20, 0xfe, 0xba, 0xe6, 0x5d, 0xd0, 0xdc, 0x20, + 0x49, 0xb8, 0x20, 0x9b, 0xbc, 0xa2, 0x01, 0xa5, + 0x5d, 0x18, 0x69, 0x0a, 0x30, 0x09, 0xc9, 0x0b, + 0xb0, 0x06, 0x69, 0xff, 0xaa, 0xa9, 0x02, 0x38, + 0xe9, 0x02, 0x85, 0x5e, 0x86, 0x5d, 0x8a, 0xf0, + 0x02, 0x10, 0x13, 0xa4, 0x71, 0xa9, 0x2e, 0xc8, + 0x99, 0xff, 0x00, 0x8a, 0xf0, 0x06, 0xa9, 0x30, + 0xc8, 0x99, 0xff, 0x00, 0x84, 0x71, 0xa0, 0x00, + 0xa2, 0x80, 0xa5, 0x65, 0x18, 0x79, 0x19, 0xbf, + 0x85, 0x65, 0xa5, 0x64, 0x79, 0x18, 0xbf, 0x85, + 0x64, 0xa5, 0x63, 0x79, 0x17, 0xbf, 0x85, 0x63, + 0xa5, 0x62, 0x79, 0x16, 0xbf, 0x85, 0x62, 0xe8, + 0xb0, 0x04, 0x10, 0xde, 0x30, 0x02, 0x30, 0xda, + 0x8a, 0x90, 0x04, 0x49, 0xff, 0x69, 0x0a, 0x69, + 0x2f, 0xc8, 0xc8, 0xc8, 0xc8, 0x84, 0x47, 0xa4, + 0x71, 0xc8, 0xaa, 0x29, 0x7f, 0x99, 0xff, 0x00, + 0xc6, 0x5d, 0xd0, 0x06, 0xa9, 0x2e, 0xc8, 0x99, + 0xff, 0x00, 0x84, 0x71, 0xa4, 0x47, 0x8a, 0x49, + 0xff, 0x29, 0x80, 0xaa, 0xc0, 0x24, 0xf0, 0x04, + 0xc0, 0x3c, 0xd0, 0xa6, 0xa4, 0x71, 0xb9, 0xff, + 0x00, 0x88, 0xc9, 0x30, 0xf0, 0xf8, 0xc9, 0x2e, + 0xf0, 0x01, 0xc8, 0xa9, 0x2b, 0xa6, 0x5e, 0xf0, + 0x2e, 0x10, 0x08, 0xa9, 0x00, 0x38, 0xe5, 0x5e, + 0xaa, 0xa9, 0x2d, 0x99, 0x01, 0x01, 0xa9, 0x45, + 0x99, 0x00, 0x01, 0x8a, 0xa2, 0x2f, 0x38, 0xe8, + 0xe9, 0x0a, 0xb0, 0xfb, 0x69, 0x3a, 0x99, 0x03, + 0x01, 0x8a, 0x99, 0x02, 0x01, 0xa9, 0x00, 0x99, + 0x04, 0x01, 0xf0, 0x08, 0x99, 0xff, 0x00, 0xa9, + 0x00, 0x99, 0x00, 0x01, 0xa9, 0x00, 0xa0, 0x01, + 0x60, 0x80, 0x00, 0x00, 0x00, 0x00, 0xfa, 0x0a, + 0x1f, 0x00, 0x00, 0x98, 0x96, 0x80, 0xff, 0xf0, + 0xbd, 0xc0, 0x00, 0x01, 0x86, 0xa0, 0xff, 0xff, + 0xd8, 0xf0, 0x00, 0x00, 0x03, 0xe8, 0xff, 0xff, + 0xff, 0x9c, 0x00, 0x00, 0x00, 0x0a, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xdf, 0x0a, 0x80, 0x00, 0x03, + 0x4b, 0xc0, 0xff, 0xff, 0x73, 0x60, 0x00, 0x00, + 0x0e, 0x10, 0xff, 0xff, 0xfd, 0xa8, 0x00, 0x00, + 0x00, 0x3c, 0xec, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0x20, 0x0c, 0xbc, 0xa9, 0x11, 0xa0, 0xbf, + 0x20, 0xa2, 0xbb, 0xf0, 0x70, 0xa5, 0x69, 0xd0, + 0x03, 0x4c, 0xf9, 0xb8, 0xa2, 0x4e, 0xa0, 0x00, + 0x20, 0xd4, 0xbb, 0xa5, 0x6e, 0x10, 0x0f, 0x20, + 0xcc, 0xbc, 0xa9, 0x4e, 0xa0, 0x00, 0x20, 0x5b, + 0xbc, 0xd0, 0x03, 0x98, 0xa4, 0x07, 0x20, 0xfe, + 0xbb, 0x98, 0x48, 0x20, 0xea, 0xb9, 0xa9, 0x4e, + 0xa0, 0x00, 0x20, 0x28, 0xba, 0x20, 0xed, 0xbf, + 0x68, 0x4a, 0x90, 0x0a, 0xa5, 0x61, 0xf0, 0x06, + 0xa5, 0x66, 0x49, 0xff, 0x85, 0x66, 0x60, 0x81, + 0x38, 0xaa, 0x3b, 0x29, 0x07, 0x71, 0x34, 0x58, + 0x3e, 0x56, 0x74, 0x16, 0x7e, 0xb3, 0x1b, 0x77, + 0x2f, 0xee, 0xe3, 0x85, 0x7a, 0x1d, 0x84, 0x1c, + 0x2a, 0x7c, 0x63, 0x59, 0x58, 0x0a, 0x7e, 0x75, + 0xfd, 0xe7, 0xc6, 0x80, 0x31, 0x72, 0x18, 0x10, + 0x81, 0x00, 0x00, 0x00, 0x00, 0xa9, 0xbf, 0xa0, + 0xbf, 0x20, 0x28, 0xba, 0xa5, 0x70, 0x69, 0x50, + 0x90, 0x03, 0x20, 0x23, 0xbc, 0x4c, 0x00, 0xe0 +}; diff --git a/Src/C64.cpp b/Src/C64.cpp new file mode 100644 index 0000000..62f6ae5 --- /dev/null +++ b/Src/C64.cpp @@ -0,0 +1,741 @@ +/* + * C64.cpp - Put the pieces together + * + * Frodo (C) 1994-1997,2002-2005 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "sysdeps.h" + +#include "C64.h" +#include "CPUC64.h" +#include "CPU1541.h" +#include "VIC.h" +#include "SID.h" +#include "CIA.h" +#include "REU.h" +#include "IEC.h" +#include "1541job.h" +#include "Display.h" +#include "Prefs.h" + +#if defined(__unix) && !defined(__svgalib__) +#include "CmdPipe.h" +#endif + + +#ifdef FRODO_SC +bool IsFrodoSC = true; +#else +bool IsFrodoSC = false; +#endif + + +/* + * Constructor: Allocate objects and memory + */ + +C64::C64() +{ + uint8 *p; + + // The thread is not yet running + thread_running = false; + quit_thyself = false; + have_a_break = false; + + // System-dependent things + c64_ctor1(); + + // Open display + printf("ssof1 %d:%d\n", + sizeof(C64Display), sizeof(C64)); + TheDisplay = new C64Display(this); + + // Allocate RAM/ROM memory + RAM = new uint8[C64_RAM_SIZE]; + Basic = new uint8[BASIC_ROM_SIZE]; + Kernal = new uint8[KERNAL_ROM_SIZE]; + Char = new uint8[CHAR_ROM_SIZE]; + Color = new uint8[COLOR_RAM_SIZE]; + RAM1541 = new uint8[DRIVE_RAM_SIZE]; + ROM1541 = new uint8[DRIVE_ROM_SIZE]; + + // Create the chips + TheCPU = new MOS6510(this, RAM, Basic, Kernal, Char, Color); + + TheJob1541 = new Job1541(RAM1541); + TheCPU1541 = new MOS6502_1541(this, TheJob1541, TheDisplay, RAM1541, ROM1541); + + TheVIC = TheCPU->TheVIC = new MOS6569(this, TheDisplay, TheCPU, RAM, Char, Color); + TheSID = TheCPU->TheSID = new MOS6581(this); + TheCIA1 = TheCPU->TheCIA1 = new MOS6526_1(TheCPU, TheVIC); + TheCIA2 = TheCPU->TheCIA2 = TheCPU1541->TheCIA2 = new MOS6526_2(TheCPU, TheVIC, TheCPU1541); + TheIEC = TheCPU->TheIEC = new IEC(TheDisplay); + TheREU = TheCPU->TheREU = new REU(TheCPU); + + // Initialize RAM with powerup pattern + p = RAM; + for (unsigned i=0; i<512; i++) { + for (unsigned j=0; j<64; j++) + *p++ = 0; + for (unsigned j=0; j<64; j++) + *p++ = 0xff; + } + + // Initialize color RAM with random values + p = Color; + for (unsigned i=0; iAsyncReset(); + TheCPU1541->AsyncReset(); + TheSID->Reset(); + TheCIA1->Reset(); + TheCIA2->Reset(); + TheIEC->Reset(); +} + + +/* + * NMI C64 + */ + +void C64::NMI(void) +{ + TheCPU->AsyncNMI(); +} + + +/* + * The preferences have changed. prefs is a pointer to the new + * preferences, ThePrefs still holds the previous ones. + * The emulation must be in the paused state! + */ + +void C64::NewPrefs(Prefs *prefs) +{ + open_close_joysticks(ThePrefs.Joystick1Port, ThePrefs.Joystick2Port, prefs->Joystick1Port, prefs->Joystick2Port); + PatchKernal(prefs->FastReset, prefs->Emul1541Proc); + + TheDisplay->NewPrefs(prefs); + +#ifdef __riscos__ + // Changed order of calls. If 1541 mode hasn't changed the order is insignificant. + if (prefs->Emul1541Proc) { + // New prefs have 1541 enabled ==> if old prefs had disabled free drives FIRST + TheIEC->NewPrefs(prefs); + TheJob1541->NewPrefs(prefs); + } else { + // New prefs has 1541 disabled ==> if old prefs had enabled free job FIRST + TheJob1541->NewPrefs(prefs); + TheIEC->NewPrefs(prefs); + } +#else + TheIEC->NewPrefs(prefs); + TheJob1541->NewPrefs(prefs); +#endif + + TheREU->NewPrefs(prefs); + TheSID->NewPrefs(prefs); + + // Reset 1541 processor if turned on + if (!ThePrefs.Emul1541Proc && prefs->Emul1541Proc) + TheCPU1541->AsyncReset(); +} + + +/* + * Patch kernal IEC routines + */ + +void C64::PatchKernal(bool fast_reset, bool emul_1541_proc) +{ + if (fast_reset) { + Kernal[0x1d84] = 0xa0; + Kernal[0x1d85] = 0x00; + } else { + Kernal[0x1d84] = orig_kernal_1d84; + Kernal[0x1d85] = orig_kernal_1d85; + } + + if (emul_1541_proc) { + Kernal[0x0d40] = 0x78; + Kernal[0x0d41] = 0x20; + Kernal[0x0d23] = 0x78; + Kernal[0x0d24] = 0x20; + Kernal[0x0d36] = 0x78; + Kernal[0x0d37] = 0x20; + Kernal[0x0e13] = 0x78; + Kernal[0x0e14] = 0xa9; + Kernal[0x0def] = 0x78; + Kernal[0x0df0] = 0x20; + Kernal[0x0dbe] = 0xad; + Kernal[0x0dbf] = 0x00; + Kernal[0x0dcc] = 0x78; + Kernal[0x0dcd] = 0x20; + Kernal[0x0e03] = 0x20; + Kernal[0x0e04] = 0xbe; + } else { + Kernal[0x0d40] = 0xf2; // IECOut + Kernal[0x0d41] = 0x00; + Kernal[0x0d23] = 0xf2; // IECOutATN + Kernal[0x0d24] = 0x01; + Kernal[0x0d36] = 0xf2; // IECOutSec + Kernal[0x0d37] = 0x02; + Kernal[0x0e13] = 0xf2; // IECIn + Kernal[0x0e14] = 0x03; + Kernal[0x0def] = 0xf2; // IECSetATN + Kernal[0x0df0] = 0x04; + Kernal[0x0dbe] = 0xf2; // IECRelATN + Kernal[0x0dbf] = 0x05; + Kernal[0x0dcc] = 0xf2; // IECTurnaround + Kernal[0x0dcd] = 0x06; + Kernal[0x0e03] = 0xf2; // IECRelease + Kernal[0x0e04] = 0x07; + } + + // 1541 + ROM1541[0x2ae4] = 0xea; // Don't check ROM checksum + ROM1541[0x2ae5] = 0xea; + ROM1541[0x2ae8] = 0xea; + ROM1541[0x2ae9] = 0xea; + ROM1541[0x2c9b] = 0xf2; // DOS idle loop + ROM1541[0x2c9c] = 0x00; + ROM1541[0x3594] = 0x20; // Write sector + ROM1541[0x3595] = 0xf2; + ROM1541[0x3596] = 0xf5; + ROM1541[0x3597] = 0xf2; + ROM1541[0x3598] = 0x01; + ROM1541[0x3b0c] = 0xf2; // Format track + ROM1541[0x3b0d] = 0x02; +} + + +/* + * Save RAM contents + */ + +void C64::SaveRAM(char *filename) +{ + FILE *f; + + if ((f = fopen(filename, "wb")) == NULL) + ShowRequester("RAM save failed.", "OK", NULL); + else { + fwrite((void*)RAM, 1, 0x10000, f); + fwrite((void*)Color, 1, 0x400, f); + if (ThePrefs.Emul1541Proc) + fwrite((void*)RAM1541, 1, 0x800, f); + fclose(f); + } +} + + +/* + * Save CPU state to snapshot + * + * 0: Error + * 1: OK + * -1: Instruction not completed + */ + +int C64::SaveCPUState(FILE *f) +{ + MOS6510State state; + TheCPU->GetState(&state); + + if (!state.instruction_complete) + return -1; + + int i = fwrite(RAM, 0x10000, 1, f); + i += fwrite(Color, 0x400, 1, f); + i += fwrite((void*)&state, sizeof(state), 1, f); + + return i == 3; +} + + +/* + * Load CPU state from snapshot + */ + +bool C64::LoadCPUState(FILE *f) +{ + MOS6510State state; + + int i = fread(RAM, 0x10000, 1, f); + i += fread(Color, 0x400, 1, f); + i += fread((void*)&state, sizeof(state), 1, f); + + if (i == 3) { + TheCPU->SetState(&state); + return true; + } else + return false; +} + + +/* + * Save 1541 state to snapshot + * + * 0: Error + * 1: OK + * -1: Instruction not completed + */ + +int C64::Save1541State(FILE *f) +{ + MOS6502State state; + TheCPU1541->GetState(&state); + + if (!state.idle && !state.instruction_complete) + return -1; + + int i = fwrite(RAM1541, 0x800, 1, f); + i += fwrite((void*)&state, sizeof(state), 1, f); + + return i == 2; +} + + +/* + * Load 1541 state from snapshot + */ + +bool C64::Load1541State(FILE *f) +{ + MOS6502State state; + + int i = fread(RAM1541, 0x800, 1, f); + i += fread((void*)&state, sizeof(state), 1, f); + + if (i == 2) { + TheCPU1541->SetState(&state); + return true; + } else + return false; +} + + +/* + * Save VIC state to snapshot + */ + +bool C64::SaveVICState(FILE *f) +{ + MOS6569State state; + TheVIC->GetState(&state); + return fwrite((void*)&state, sizeof(state), 1, f) == 1; +} + + +/* + * Load VIC state from snapshot + */ + +bool C64::LoadVICState(FILE *f) +{ + MOS6569State state; + + if (fread((void*)&state, sizeof(state), 1, f) == 1) { + TheVIC->SetState(&state); + return true; + } else + return false; +} + + +/* + * Save SID state to snapshot + */ + +bool C64::SaveSIDState(FILE *f) +{ + MOS6581State state; + TheSID->GetState(&state); + return fwrite((void*)&state, sizeof(state), 1, f) == 1; +} + + +/* + * Load SID state from snapshot + */ + +bool C64::LoadSIDState(FILE *f) +{ + MOS6581State state; + + if (fread((void*)&state, sizeof(state), 1, f) == 1) { + TheSID->SetState(&state); + return true; + } else + return false; +} + + +/* + * Save CIA states to snapshot + */ + +bool C64::SaveCIAState(FILE *f) +{ + MOS6526State state; + TheCIA1->GetState(&state); + + if (fwrite((void*)&state, sizeof(state), 1, f) == 1) { + TheCIA2->GetState(&state); + return fwrite((void*)&state, sizeof(state), 1, f) == 1; + } else + return false; +} + + +/* + * Load CIA states from snapshot + */ + +bool C64::LoadCIAState(FILE *f) +{ + MOS6526State state; + + if (fread((void*)&state, sizeof(state), 1, f) == 1) { + TheCIA1->SetState(&state); + if (fread((void*)&state, sizeof(state), 1, f) == 1) { + TheCIA2->SetState(&state); + return true; + } else + return false; + } else + return false; +} + + +/* + * Save 1541 GCR state to snapshot + */ + +bool C64::Save1541JobState(FILE *f) +{ + Job1541State state; + TheJob1541->GetState(&state); + return fwrite((void*)&state, sizeof(state), 1, f) == 1; +} + + +/* + * Load 1541 GCR state from snapshot + */ + +bool C64::Load1541JobState(FILE *f) +{ + Job1541State state; + + if (fread((void*)&state, sizeof(state), 1, f) == 1) { + TheJob1541->SetState(&state); + return true; + } else + return false; +} + + +#define SNAPSHOT_HEADER "FrodoSnapshot" +#define SNAPSHOT_1541 1 + +#define ADVANCE_CYCLES \ + TheVIC->EmulateCycle(); \ + TheCIA1->EmulateCycle(); \ + TheCIA2->EmulateCycle(); \ + TheCPU->EmulateCycle(); \ + if (ThePrefs.Emul1541Proc) { \ + TheCPU1541->CountVIATimers(1); \ + if (!TheCPU1541->Idle) \ + TheCPU1541->EmulateCycle(); \ + } + + +/* + * Save snapshot (emulation must be paused and in VBlank) + * + * To be able to use SC snapshots with SL, SC snapshots are made thus that no + * partially dealt with instructions are saved. Instead all devices are advanced + * cycle by cycle until the current instruction has been finished. The number of + * cycles this takes is saved in the snapshot and will be reconstructed if the + * snapshot is loaded into FrodoSC again. + */ + +void C64::SaveSnapshot(char *filename) +{ + FILE *f; + uint8 flags; + uint8 delay; + int stat; + + if ((f = fopen(filename, "wb")) == NULL) { + ShowRequester("Unable to open snapshot file", "OK", NULL); + return; + } + + fprintf(f, "%s%c", SNAPSHOT_HEADER, 10); + fputc(0, f); // Version number 0 + flags = 0; + if (ThePrefs.Emul1541Proc) + flags |= SNAPSHOT_1541; + fputc(flags, f); + SaveVICState(f); + SaveSIDState(f); + SaveCIAState(f); + +#ifdef FRODO_SC + delay = 0; + do { + if ((stat = SaveCPUState(f)) == -1) { // -1 -> Instruction not finished yet + ADVANCE_CYCLES; // Advance everything by one cycle + delay++; + } + } while (stat == -1); + fputc(delay, f); // Number of cycles the saved CPUC64 lags behind the previous chips +#else + SaveCPUState(f); + fputc(0, f); // No delay +#endif + + if (ThePrefs.Emul1541Proc) { + fwrite(ThePrefs.DrivePath[0], 256, 1, f); +#ifdef FRODO_SC + delay = 0; + do { + if ((stat = Save1541State(f)) == -1) { + ADVANCE_CYCLES; + delay++; + } + } while (stat == -1); + fputc(delay, f); +#else + Save1541State(f); + fputc(0, f); // No delay +#endif + Save1541JobState(f); + } + fclose(f); + +#ifdef __riscos__ + TheWIMP->SnapshotSaved(true); +#endif +} + + +/* + * Load snapshot (emulation must be paused and in VBlank) + */ + +bool C64::LoadSnapshot(char *filename) +{ + FILE *f; + + if ((f = fopen(filename, "rb")) != NULL) { + char Header[] = SNAPSHOT_HEADER; + char *b = Header, c = 0; + uint8 delay, i; + + // For some reason memcmp()/strcmp() and so forth utterly fail here. + while (*b > 32) { + if ((c = fgetc(f)) != *b++) { + b = NULL; + break; + } + } + if (b != NULL) { + uint8 flags; + bool error = false; +#ifndef FRODO_SC + long vicptr; // File offset of VIC data +#endif + + while (c != 10) + c = fgetc(f); // Shouldn't be necessary + if (fgetc(f) != 0) { + ShowRequester("Unknown snapshot format", "OK", NULL); + fclose(f); + return false; + } + flags = fgetc(f); +#ifndef FRODO_SC + vicptr = ftell(f); +#endif + + error |= !LoadVICState(f); + error |= !LoadSIDState(f); + error |= !LoadCIAState(f); + error |= !LoadCPUState(f); + + delay = fgetc(f); // Number of cycles the 6510 is ahead of the previous chips +#ifdef FRODO_SC + // Make the other chips "catch up" with the 6510 + for (i=0; iEmulateCycle(); + TheCIA1->EmulateCycle(); + TheCIA2->EmulateCycle(); + } +#endif + if ((flags & SNAPSHOT_1541) != 0) { + Prefs *prefs = new Prefs(ThePrefs); + + // First switch on emulation + error |= (fread(prefs->DrivePath[0], 256, 1, f) != 1); + prefs->Emul1541Proc = true; + NewPrefs(prefs); + ThePrefs = *prefs; + delete prefs; + + // Then read the context + error |= !Load1541State(f); + + delay = fgetc(f); // Number of cycles the 6502 is ahead of the previous chips +#ifdef FRODO_SC + // Make the other chips "catch up" with the 6502 + for (i=0; iEmulateCycle(); + TheCIA1->EmulateCycle(); + TheCIA2->EmulateCycle(); + TheCPU->EmulateCycle(); + } +#endif + Load1541JobState(f); +#ifdef __riscos__ + TheWIMP->ThePrefsToWindow(); +#endif + } else if (ThePrefs.Emul1541Proc) { // No emulation in snapshot, but currently active? + Prefs *prefs = new Prefs(ThePrefs); + prefs->Emul1541Proc = false; + NewPrefs(prefs); + ThePrefs = *prefs; + delete prefs; +#ifdef __riscos__ + TheWIMP->ThePrefsToWindow(); +#endif + } + +#ifndef FRODO_SC + fseek(f, vicptr, SEEK_SET); + LoadVICState(f); // Load VIC data twice in SL (is REALLY necessary sometimes!) +#endif + fclose(f); + + if (error) { + ShowRequester("Error reading snapshot file", "OK", NULL); + Reset(); + return false; + } else + return true; + } else { + fclose(f); + ShowRequester("Not a Frodo snapshot file", "OK", NULL); + return false; + } + } else { + ShowRequester("Can't open snapshot file", "OK", NULL); + return false; + } +} + + +#ifdef __BEOS__ +#include "C64_Be.h" +#endif + +#ifdef AMIGA +#include "C64_Amiga.h" +#endif + +#ifdef __unix +# if defined(QTOPIA) or defined(MAEMO) +# include "C64_Embedded.h" +# elif defined(HAVE_SDL) +# include "C64_SDL.h" +# else +# include "C64_x.h" +# endif +#endif + +#ifdef GEKKO +# include "C64_SDL.h" +#endif + +#ifdef __mac__ +#include "C64_mac.h" +#endif + +#ifdef WIN32 +#include "C64_WIN32.h" +#endif + +#ifdef __riscos__ +#include "C64_Acorn.h" +#endif diff --git a/Src/C64.h b/Src/C64.h new file mode 100644 index 0000000..896874d --- /dev/null +++ b/Src/C64.h @@ -0,0 +1,255 @@ +/* + * C64.h - Put the pieces together + * + * Frodo (C) 1994-1997,2002-2005 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef _C64_H +#define _C64_H + +#if defined(HAVE_SDL) +/* SDL menu */ +#include "menu.h" +#endif + +#ifdef __BEOS__ +#include +#endif + +#ifdef AMIGA +#include +#include +#include +#endif + +#ifdef __riscos__ +#include "ROlib.h" +#endif + + +// Sizes of memory areas +const int C64_RAM_SIZE = 0x10000; +const int COLOR_RAM_SIZE = 0x400; +const int BASIC_ROM_SIZE = 0x2000; +const int KERNAL_ROM_SIZE = 0x2000; +const int CHAR_ROM_SIZE = 0x1000; +const int DRIVE_RAM_SIZE = 0x800; +const int DRIVE_ROM_SIZE = 0x4000; + + +// false: Frodo, true: FrodoSC +extern bool IsFrodoSC; + +#ifdef GEKKO +#define PREFS_PATH "/apps/frodo/frodorc" +#elif defined(HAVE_SDL) +#define PREFS_PATH "/home/ska/.frodorc" +#endif + +class Prefs; +class C64Display; +class MOS6510; +class MOS6569; +class MOS6581; +class MOS6526_1; +class MOS6526_2; +class IEC; +class REU; +class MOS6502_1541; +class Job1541; +class CmdPipe; + +class C64 { +public: + C64(); + ~C64(); + + void Run(void); + void Quit(void); + void Pause(void); + void Resume(void); + void Reset(void); + void NMI(void); + void VBlank(bool draw_frame); + void NewPrefs(Prefs *prefs); + void PatchKernal(bool fast_reset, bool emul_1541_proc); + void SaveRAM(char *filename); + void SaveSnapshot(char *filename); + bool LoadSnapshot(char *filename); + int SaveCPUState(FILE *f); + int Save1541State(FILE *f); + bool Save1541JobState(FILE *f); + bool SaveVICState(FILE *f); + bool SaveSIDState(FILE *f); + bool SaveCIAState(FILE *f); + bool LoadCPUState(FILE *f); + bool Load1541State(FILE *f); + bool Load1541JobState(FILE *f); + bool LoadVICState(FILE *f); + bool LoadSIDState(FILE *f); + bool LoadCIAState(FILE *f); + + uint8 *RAM, *Basic, *Kernal, + *Char, *Color; // C64 + uint8 *RAM1541, *ROM1541; // 1541 + + C64Display *TheDisplay; + + MOS6510 *TheCPU; // C64 + MOS6569 *TheVIC; + MOS6581 *TheSID; + MOS6526_1 *TheCIA1; + MOS6526_2 *TheCIA2; + IEC *TheIEC; + REU *TheREU; + + MOS6502_1541 *TheCPU1541; // 1541 + Job1541 *TheJob1541; + +#ifdef FRODO_SC + uint32 CycleCounter; +#endif + void enter_menu() { + this->have_a_break = true; + } + +private: + void c64_ctor1(void); + void c64_ctor2(void); + void c64_dtor(void); + void open_close_joysticks(int oldjoy1, int oldjoy2, int newjoy1, int newjoy2); + uint8 poll_joystick(int port); + void thread_func(void); + + bool thread_running; // Emulation thread is running + bool quit_thyself; // Emulation thread shall quit + bool have_a_break; // Emulation thread shall pause + + int joy_minx[2], joy_maxx[2], joy_miny[2], joy_maxy[2]; // For dynamic joystick calibration + uint8 joykey; // Joystick keyboard emulation mask value + + uint8 orig_kernal_1d84, // Original contents of kernal locations $1d84 and $1d85 + orig_kernal_1d85; // (for undoing the Fast Reset patch) + +#ifdef __BEOS__ +public: + void SoundSync(void); + +private: + static long thread_invoc(void *obj); + void open_close_joystick(int port, int oldjoy, int newjoy); + + void *joy[2]; // Joystick objects (BJoystick or BDigitalPort) + bool joy_geek_port[2]; // Flag: joystick on GeekPort? + thread_id the_thread; + sem_id pause_sem; + sem_id sound_sync_sem; + bigtime_t start_time; +#endif + +#ifdef AMIGA + struct MsgPort *timer_port; // For speed limiter + struct timerequest *timer_io; + struct timeval start_time; + struct MsgPort *game_port; // For joystick + struct IOStdReq *game_io; + struct GamePortTrigger game_trigger; + struct InputEvent game_event; + UBYTE joy_state; // Current state of joystick + bool game_open, port_allocated; // Flags: gameport.device opened, game port allocated +#endif + +#ifdef __unix + void open_close_joystick(int port, int oldjoy, int newjoy); + double speed_index; +public: + CmdPipe *gui; +#elif defined(GEKKO) + void open_close_joystick(int port, int oldjoy, int newjoy); + double speed_index; +#endif +#ifdef HAVE_SDL + menu_t main_menu; + TTF_Font *menu_font; + + bool fake_key_sequence; + int fake_key_type; + int fake_key_index; + int fake_key_keytime; + + bool prefs_changed; + char save_game_name[256]; + + void select_disc(Prefs *np); + void bind_key(Prefs *np); + void other_options(Prefs *np); + void save_load_state(Prefs *np); +#endif + +#ifdef WIN32 +private: + void CheckTimerChange(); + void StartTimer(); + void StopTimer(); + static void CALLBACK StaticTimeProc(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2); + void TimeProc(UINT id); +#ifdef FRODO_SC + void EmulateCyclesWith1541(); + void EmulateCyclesWithout1541(); +#endif + + DWORD ref_time; // when frame count was reset + int skipped_frames; // number of skipped frames + int timer_every; // frequency of timer in frames + HANDLE timer_semaphore; // Timer semaphore for synch + MMRESULT timer_id; // Timer identifier + int frame; // current frame number + uint8 joy_state; // Current state of joystick + bool state_change; +#endif + +#ifdef __riscos__ +public: + void RequestSnapshot(void); + bool LoadOldSnapshot(FILE *f); + void LoadSystemConfig(const char *filename); // loads timing vals and keyboard joys + void SaveSystemConfig(const char *filename); // saves timing vals and keyboard joys + void ReadTimings(int *poll_after, int *speed_after, int *sound_after); + void WriteTimings(int poll_after, int speed_after, int sound_after); + + WIMP *TheWIMP; + int PollAfter; // centiseconds before polling + int SpeedAfter; // centiseconds before updating speedometer + int PollSoundAfter; // *rasterlines* after which DigitalRenderer is polled + int HostVolume; // sound volume of host machine + +private: + bool make_a_snapshot; + + uint8 joykey2; // two keyboard joysticks possible here + + uint8 joystate[2]; // Joystick state + bool Poll; // TRUE if polling should take place + int LastPoll, LastFrame, LastSpeed; // time of last poll / last frame / speedom (cs) + int FramesSince; + int laststate; // last keyboard state (-> scroll lock) + int lastptr; // last mouse pointer shape + bool SingleTasking; +#endif +}; + + +#endif diff --git a/Src/C64_Acorn.h b/Src/C64_Acorn.h new file mode 100644 index 0000000..e3c705a --- /dev/null +++ b/Src/C64_Acorn.h @@ -0,0 +1,421 @@ +/* + * C64_Acorn.h - Put the pieces together, RISC OS specific stuff + * + * Frodo (C) 1994-1997,2002-2005 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Prefs.h" +#include "ROlib.h" +#include "AcornGUI.h" + + +void C64::LoadSystemConfig(const char *filename) +{ + FILE *fp; + + if ((fp = fopen(filename, "r")) != NULL) + { + int i; + Joy_Keys *jk; + int args[10]; + char line[256]; + + while (fgets(line, 255, fp) != 0) + { + char *b = line; + register char c; + + do {c = *b++;} while (c > 32); + if (c == 32) // keyword mustn't contain spaces + { + *(b-1) = '\0'; + do {c = *b++;} while ((c >= 32) && (c != '=')); + if (c == '=') // read in keyword's arguments + { + int i=0; + + while ((*b != '\0') && (i < 10)) + { + args[i++] = strtol(b, &b, 10); + } + if (strcmp(line, "PollAfter") == 0) {PollAfter = args[0];} + else if (strcmp(line, "SpeedAfter") == 0) {SpeedAfter = args[0];} + else if (strcmp(line, "PollSoundAfter") == 0) {PollSoundAfter = args[0];} + else if (strcmp(line, "JoystickKeys1") == 0) + { + jk = &(TheDisplay->JoystickKeys[0]); + jk->up = args[0]; jk->down = args[1]; jk->left = args[2]; jk->right = args[3]; + jk->fire = args[4]; + } + else if (strcmp(line, "JoystickKeys2") == 0) + { + jk = &(TheDisplay->JoystickKeys[1]); + jk->up = args[0]; jk->down = args[1]; jk->left = args[2]; jk->right = args[3]; + jk->fire = args[4]; + } + else + { + _kernel_oserror err; + + err.errnum = 0; + sprintf(err.errmess,"Bad keyword <%s> in system configure file!",line); + Wimp_ReportError(&err,1,TASKNAME); + } + } + } + } + fclose(fp); + } +} + + +void C64::SaveSystemConfig(const char *filename) +{ + FILE *fp; + + if ((fp = fopen(filename, "w")) != NULL) + { + int i; + Joy_Keys *jk; + + fprintf(fp,"PollAfter = %d\n", PollAfter); + fprintf(fp,"SpeedAfter = %d\n", SpeedAfter); + fprintf(fp,"PollSoundAfter = %d\n", PollSoundAfter); + for (i=0; i<2; i++) + { + jk = &(TheDisplay->JoystickKeys[i]); + fprintf(fp,"JoystickKeys%d",i+1); + fprintf(fp," = %d %d %d %d %d\n", jk->up, jk->down, jk->left, jk->right, jk->fire); + } + fclose(fp); + } +} + + +void C64::ReadTimings(int *poll_after, int *speed_after, int *sound_after) +{ + *poll_after = PollAfter; *speed_after = SpeedAfter; *sound_after = PollSoundAfter; +} + + +void C64::WriteTimings(int poll_after, int speed_after, int sound_after) +{ + PollAfter = poll_after; SpeedAfter = speed_after; PollSoundAfter = sound_after; +} + + +void C64::RequestSnapshot(void) +{ + // Snapshots are only possible if the emulation progresses to the next vsync + if (have_a_break) Resume(); + make_a_snapshot = true; +} + + +void C64::c64_ctor1(void) +{ + TheWIMP = new WIMP(this); + PollAfter = 20; // poll every 20 centiseconds + SpeedAfter = 200; // update speedometer every 2 seconds + PollSoundAfter = 50; // poll DigitalRenderer every 50 lines + HostVolume = Sound_Volume(0); + // Just a precaution + if (HostVolume < 0) {HostVolume = 0;} + if (HostVolume > MaximumVolume) {HostVolume = MaximumVolume;} + Poll = false; + make_a_snapshot = false; +} + + +void C64::c64_ctor2(void) +{ + LoadSystemConfig(DEFAULT_SYSCONF); + // was started from multitasking so pretend ScrollLock OFF no matter what + laststate = (ReadKeyboardStatus() & ~2); SingleTasking = false; + lastptr = 1; +} + + +void C64::c64_dtor(void) +{ + delete TheWIMP; +} + + +void C64::open_close_joysticks(int oldjoy1, int oldjoy2, int newjoy1, int newjoy2) +{ + // Check if the Joystick module is loaded. If not then write an illegal value to + // the joystick state. + if (Joystick_Read(0) == -2) {joystate[0] = 0;} else {joystate[0] = 0xff;} + if (Joystick_Read(1) == -2) {joystate[1] = 0;} else {joystate[1] = 0xff;} +} + + +uint8 C64::poll_joystick(int port) +{ + register int state; + uint8 joy; + + if ((state = Joystick_Read(port)) != -2) // module present + { + if (state == -1) {joy = joystate[port];} // use old value + else + { + joy = 0xff; + if ((state & (JoyButton1 + JoyButton2)) != 0) {joy &= 0xef;} // fire + if ((state & 0x80) == 0) // positive direction #1 + { + if ((state & 0xff) >= JoyDir_Thresh) {joy &= 0xfe;} // up + } + else + { + if ((256 - (state & 0xff)) >= JoyDir_Thresh) {joy &= 0xfd;} // down + } + if ((state & 0x8000) == 0) // positive direction #2 + { + if ((state & 0xff00) >= JoyDir_Thresh<<8) {joy &= 0xf7;} // right + } + else + { + if ((0x10000 - (state & 0xff00)) >= JoyDir_Thresh<<8) {joy &= 0xfb;} // left + } + } + joystate[port] = joy; return(joy); + } + else + { + joystate[port] = 0; return(0xff); + } +} + + +void C64::VBlank(bool draw_frame) +{ + int Now, KeyState; + bool InputFocus; + + // Poll keyboard if the window has the input focus. + InputFocus = TheWIMP->EmuWindow->HaveInput(); + if (InputFocus) + { + TheDisplay->PollKeyboard(TheCIA1->KeyMatrix, TheCIA1->RevMatrix, &joykey, &joykey2); + } + + // Poll Joysticks + TheCIA1->Joystick1 = (ThePrefs.Joystick1On) ? poll_joystick(0) : 0xff; + TheCIA1->Joystick2 = (ThePrefs.Joystick2On) ? poll_joystick(1) : 0xff; + + // Swap joysticks? + if (ThePrefs.JoystickSwap) + { + register uint8 h; + + h = TheCIA1->Joystick1; TheCIA1->Joystick1 = TheCIA1->Joystick2; TheCIA1->Joystick2 = h; + } + + // Read keyboard state directly since we'll also need ScrollLock later! + KeyState = ReadKeyboardStatus(); + if (InputFocus) + { + // Keyboard emulates which joystick? (NumLock ==> Port 2, else Port 1) + if ((KeyState & 4) == 0) + { + TheCIA1->Joystick2 &= joykey; + } + else // joykey2 only mapped if numLOCK is off. + { + TheCIA1->Joystick1 &= joykey; TheCIA1->Joystick2 &= joykey2; + } + } + + if (draw_frame) + { + TheDisplay->Update(); + } + + // Make snapshot? + if (make_a_snapshot) + { + SaveSnapshot((TheWIMP->SnapFile)+44); + make_a_snapshot = false; + } + + Now = OS_ReadMonotonicTime(); + + // Limit speed? (hahaha.... ah well...) + if (ThePrefs.LimitSpeed) + { + int Now; + + while ((Now - LastFrame) < 2) // 2cs per frame = 50fps (original speed) + { + Now = OS_ReadMonotonicTime(); + } + LastFrame = Now; + } + FramesSince++; + + // Update speedometer (update, not force redraw!)? + if ((Now - LastSpeed) >= SpeedAfter) + { + char b[16]; + + if ((Now - LastSpeed) <= 0) {Now = LastSpeed+1;} + // Speed: 100% equals 50fps (round result) + sprintf(b,"%d%%\0",((400*FramesSince)/(Now - LastSpeed) + 1) >> 1); + TheWIMP->EmuPane->WriteIconTextU(Icon_Pane_Speed,b); + LastSpeed = Now; FramesSince = 0; + } + + if (InputFocus) + { + // Scroll lock state changed? + if (((KeyState ^ laststate) & 2) != 0) + { + // change to single tasking: turn off mouse, else restore previous pointer + if ((KeyState & 2) != 0) {lastptr = SetMousePointer(0); SingleTasking = true;} + else {SetMousePointer(lastptr); OS_FlushBuffer(9); SingleTasking = false;} + } + if ((KeyState & 2) != 0) {lastptr = SetMousePointer(0);} + else {SetMousePointer(lastptr); OS_FlushBuffer(9);} + } + + // Poll? ScrollLock forces single tasking, i.e. overrides timings. + if (!SingleTasking) + { + if ((Now - LastPoll) >= PollAfter) + { + Poll = true; + } + } + laststate = KeyState; +} + + +void C64::Run(void) +{ + // Resetting chips + TheCPU->Reset(); + TheSID->Reset(); + TheCIA1->Reset(); + TheCIA2->Reset(); + TheCPU1541->Reset(); + + // Patch kernel IEC routines (copied from C64_Amiga.i + orig_kernal_1d84 = Kernal[0x1d84]; + orig_kernal_1d85 = Kernal[0x1d85]; + PatchKernal(ThePrefs.FastReset, ThePrefs.Emul1541Proc); + + // Start the emulation + thread_running = true; quit_thyself = false; have_a_break = false; + thread_func(); +} + + +void C64::Quit(void) +{ + if (thread_running) + { + quit_thyself = true; thread_running = false; + } +} + + +void C64::Pause(void) +{ + have_a_break = true; TheSID->PauseSound(); +} + + +void C64::Resume(void) +{ + have_a_break = false; TheSID->ResumeSound(); +} + + +void C64::thread_func(void) +{ + LastPoll = LastFrame = LastSpeed = OS_ReadMonotonicTime(); FramesSince = 0; + + while (!quit_thyself) + { +#ifdef FRODO_SC + if (TheVIC->EmulateCycle()) {TheSID->EmulateLine();} + TheCIA1->EmulateCycle(); + TheCIA2->EmulateCycle(); + TheCPU->EmulateCycle(); + + if (ThePrefs.Emul1541Proc) + { + TheCPU1541->CountVIATimers(1); + if (!TheCPU1541->Idle) {TheCPU1541->EmulateCycle();} + } + CycleCounter++; + +#else + // Emulate each device one rasterline. Order is important! + int cycles = TheVIC->EmulateLine(); + TheSID->EmulateLine(); +#if !PRECISE_CIA_CYCLES + TheCIA1->EmulateLine(ThePrefs.CIACycles); + TheCIA2->EmulateLine(ThePrefs.CIACycles); +#endif + + if (ThePrefs.Emul1541Proc) + { + int cycles_1541 = ThePrefs.FloppyCycles; + TheCPU1541->CountVIATimers(cycles_1541); + if (!TheCPU1541->Idle) + { + while ((cycles >= 0) || (cycles_1541 >= 0)) + { + if (cycles > cycles_1541) {cycles -= TheCPU->EmulateLine(1);} + else {cycles_1541 -= TheCPU1541->EmulateLine(1);} + } + } + else {TheCPU->EmulateLine(cycles);} + } + else + { + TheCPU->EmulateLine(cycles); + } +#endif + + // Single-tasking: busy-wait 'til unpause + while (SingleTasking && have_a_break) + { + int KeyState; + + TheDisplay->CheckForUnpause(true); // unpause? + KeyState = ReadKeyboardStatus(); + if ((KeyState & 2) == 0) // leave single tasking? + { + SetMousePointer(lastptr); OS_FlushBuffer(9); SingleTasking = false; + } + laststate = KeyState; + } + if (!SingleTasking) + { + // The system-specific part of this function + if (Poll || have_a_break) + { + TheWIMP->Poll(have_a_break); + LastPoll = LastFrame = OS_ReadMonotonicTime(); Poll = false; + } + } + } +} diff --git a/Src/C64_Amiga.h b/Src/C64_Amiga.h new file mode 100644 index 0000000..02f4fad --- /dev/null +++ b/Src/C64_Amiga.h @@ -0,0 +1,407 @@ +/* + * C64_Amiga.h - Put the pieces together, Amiga specific stuff + * + * Frodo (C) 1994-1997,2002-2005 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include + + +// Library bases +struct Device *TimerBase; + + +/* + * Constructor, system-dependent things + */ + +void C64::c64_ctor1(void) +{ + // Open game_io + game_port = CreateMsgPort(); + game_io = (struct IOStdReq *)CreateIORequest(game_port, sizeof(IOStdReq)); + game_io->io_Message.mn_Node.ln_Type = NT_UNKNOWN; + game_open = port_allocated = false; + if (!OpenDevice("gameport.device", 1, (struct IORequest *)game_io, 0)) + game_open = true; +} + +void C64::c64_ctor2(void) +{ + // Initialize joystick variables + joy_state = 0xff; + + // Open timer_io + timer_port = CreateMsgPort(); + timer_io = (struct timerequest *)CreateIORequest(timer_port, sizeof(struct timerequest)); + OpenDevice(TIMERNAME, UNIT_MICROHZ, (struct IORequest *)timer_io, 0); + + // Get timer base + TimerBase = timer_io->tr_node.io_Device; + + // Preset speedometer start time + GetSysTime(&start_time); +} + + +/* + * Destructor, system-dependent things + */ + +void C64::c64_dtor(void) +{ + // Stop and delete timer_io + if (timer_io != NULL) { + if (!CheckIO((struct IORequest *)timer_io)) + WaitIO((struct IORequest *)timer_io); + CloseDevice((struct IORequest *)timer_io); + DeleteIORequest((struct IORequest *)timer_io); + } + + if (timer_port != NULL) + DeleteMsgPort(timer_port); + + if (game_open) { + if (!CheckIO((struct IORequest *)game_io)) { + AbortIO((struct IORequest *)game_io); + WaitIO((struct IORequest *)game_io); + } + CloseDevice((struct IORequest *)game_io); + } + + if (game_io != NULL) + DeleteIORequest((struct IORequest *)game_io); + + if (game_port != NULL) + DeleteMsgPort(game_port); +} + + +/* + * Start emulation + */ + +void C64::Run(void) +{ + // Reset chips + TheCPU->Reset(); + TheSID->Reset(); + TheCIA1->Reset(); + TheCIA2->Reset(); + TheCPU1541->Reset(); + + // Patch kernal IEC routines + orig_kernal_1d84 = Kernal[0x1d84]; + orig_kernal_1d85 = Kernal[0x1d85]; + PatchKernal(ThePrefs.FastReset, ThePrefs.Emul1541Proc); + + // Start timer_io + timer_io->tr_node.io_Command = TR_ADDREQUEST; + timer_io->tr_time.tv_secs = 0; + timer_io->tr_time.tv_micro = ThePrefs.SkipFrames * 20000; // 20ms per frame + SendIO((struct IORequest *)timer_io); + + // Start the CPU thread + thread_running = true; + quit_thyself = false; + have_a_break = false; + thread_func(); +} + + +/* + * Stop emulation + */ + +void C64::Quit(void) +{ + // Ask the thread to quit itself if it is running + if (thread_running) { + quit_thyself = true; + thread_running = false; + } +} + + +/* + * Pause emulation + */ + +void C64::Pause(void) +{ + TheSID->PauseSound(); +} + + +/* + * Resume emulation + */ + +void C64::Resume(void) +{ + TheSID->ResumeSound(); +} + + +/* + * Vertical blank: Poll keyboard and joysticks, update window + */ + +void C64::VBlank(bool draw_frame) +{ + struct timeval end_time; + long speed_index; + + // Poll keyboard + TheDisplay->PollKeyboard(TheCIA1->KeyMatrix, TheCIA1->RevMatrix, &joykey); + + // Poll joysticks + TheCIA1->Joystick1 = poll_joystick(0); + TheCIA1->Joystick2 = poll_joystick(1); + + if (ThePrefs.JoystickSwap) { + uint8 tmp = TheCIA1->Joystick1; + TheCIA1->Joystick1 = TheCIA1->Joystick2; + TheCIA1->Joystick2 = tmp; + } + + // Joystick keyboard emulation + if (TheDisplay->NumLock()) + TheCIA1->Joystick1 &= joykey; + else + TheCIA1->Joystick2 &= joykey; + + // Count TOD clocks + TheCIA1->CountTOD(); + TheCIA2->CountTOD(); + + // Update window if needed + if (draw_frame) { + TheDisplay->Update(); + + // Calculate time between VBlanks, display speedometer + GetSysTime(&end_time); + SubTime(&end_time, &start_time); + speed_index = 20000 * 100 * ThePrefs.SkipFrames / (end_time.tv_micro + 1); + + // Abort timer_io if speed limiter is off + if (!ThePrefs.LimitSpeed) { + if (!CheckIO((struct IORequest *)timer_io)) + AbortIO((struct IORequest *)timer_io); + } else if (speed_index > 100) + speed_index = 100; + + // Wait for timer_io (limit speed) + WaitIO((struct IORequest *)timer_io); + + // Restart timer_io + timer_io->tr_node.io_Command = TR_ADDREQUEST; + timer_io->tr_time.tv_secs = 0; + timer_io->tr_time.tv_micro = ThePrefs.SkipFrames * 20000; // 20ms per frame + SendIO((struct IORequest *)timer_io); + + GetSysTime(&start_time); + + TheDisplay->Speedometer(speed_index); + } +} + + +/* + * Open/close joystick drivers given old and new state of + * joystick preferences + */ + +void C64::open_close_joysticks(int oldjoy1, int oldjoy2, int newjoy1, int newjoy2) +{ + if (game_open && (oldjoy2 != newjoy2)) + + if (newjoy2) { // Open joystick + joy_state = 0xff; + port_allocated = false; + + // Allocate game port + BYTE ctype; + Forbid(); + game_io->io_Command = GPD_ASKCTYPE; + game_io->io_Data = &ctype; + game_io->io_Length = 1; + DoIO((struct IORequest *)game_io); + + if (ctype != GPCT_NOCONTROLLER) + Permit(); + else { + ctype = GPCT_ABSJOYSTICK; + game_io->io_Command = GPD_SETCTYPE; + game_io->io_Data = &ctype; + game_io->io_Length = 1; + DoIO((struct IORequest *)game_io); + Permit(); + + port_allocated = true; + + // Set trigger conditions + game_trigger.gpt_Keys = GPTF_UPKEYS | GPTF_DOWNKEYS; + game_trigger.gpt_Timeout = 65535; + game_trigger.gpt_XDelta = 1; + game_trigger.gpt_YDelta = 1; + game_io->io_Command = GPD_SETTRIGGER; + game_io->io_Data = &game_trigger; + game_io->io_Length = sizeof(struct GamePortTrigger); + DoIO((struct IORequest *)game_io); + + // Flush device buffer + game_io->io_Command = CMD_CLEAR; + DoIO((struct IORequest *)game_io); + + // Start reading joystick events + game_io->io_Command = GPD_READEVENT; + game_io->io_Data = &game_event; + game_io->io_Length = sizeof(struct InputEvent); + SendIO((struct IORequest *)game_io); + } + + } else { // Close joystick + + // Abort game_io + if (!CheckIO((struct IORequest *)game_io)) { + AbortIO((struct IORequest *)game_io); + WaitIO((struct IORequest *)game_io); + } + + // Free game port + if (port_allocated) { + BYTE ctype = GPCT_NOCONTROLLER; + game_io->io_Command = GPD_SETCTYPE; + game_io->io_Data = &ctype; + game_io->io_Length = 1; + DoIO((struct IORequest *)game_io); + + port_allocated = false; + } + } +} + + +/* + * Poll joystick port, return CIA mask + */ + +uint8 C64::poll_joystick(int port) +{ + if (port == 0) + return 0xff; + + if (game_open && port_allocated) { + + // Joystick event arrived? + while (GetMsg(game_port) != NULL) { + + // Yes, analyze event + switch (game_event.ie_Code) { + case IECODE_LBUTTON: // Button pressed + joy_state &= 0xef; + break; + + case IECODE_LBUTTON | IECODE_UP_PREFIX: // Button released + joy_state |= 0x10; + break; + + case IECODE_NOBUTTON: // Joystick moved + if (game_event.ie_X == 1) + joy_state &= 0xf7; // Right + if (game_event.ie_X == -1) + joy_state &= 0xfb; // Left + if (game_event.ie_X == 0) + joy_state |= 0x0c; + if (game_event.ie_Y == 1) + joy_state &= 0xfd; // Down + if (game_event.ie_Y == -1) + joy_state &= 0xfe; // Up + if (game_event.ie_Y == 0) + joy_state |= 0x03; + break; + } + + // Start reading the next event + game_io->io_Command = GPD_READEVENT; + game_io->io_Data = &game_event; + game_io->io_Length = sizeof(struct InputEvent); + SendIO((struct IORequest *)game_io); + } + return joy_state; + + } else + return 0xff; +} + + +/* + * The emulation's main loop + */ + +void C64::thread_func(void) +{ + while (!quit_thyself) { + +#ifdef FRODO_SC + // The order of calls is important here + if (TheVIC->EmulateCycle()) + TheSID->EmulateLine(); + TheCIA1->CheckIRQs(); + TheCIA2->CheckIRQs(); + TheCIA1->EmulateCycle(); + TheCIA2->EmulateCycle(); + TheCPU->EmulateCycle(); + + if (ThePrefs.Emul1541Proc) { + TheCPU1541->CountVIATimers(1); + if (!TheCPU1541->Idle) + TheCPU1541->EmulateCycle(); + } + CycleCounter++; +#else + // The order of calls is important here + int cycles = TheVIC->EmulateLine(); + TheSID->EmulateLine(); +#if !PRECISE_CIA_CYCLES + TheCIA1->EmulateLine(ThePrefs.CIACycles); + TheCIA2->EmulateLine(ThePrefs.CIACycles); +#endif + + if (ThePrefs.Emul1541Proc) { + int cycles_1541 = ThePrefs.FloppyCycles; + TheCPU1541->CountVIATimers(cycles_1541); + + if (!TheCPU1541->Idle) { + // 1541 processor active, alternately execute + // 6502 and 6510 instructions until both have + // used up their cycles + while (cycles >= 0 || cycles_1541 >= 0) + if (cycles > cycles_1541) + cycles -= TheCPU->EmulateLine(1); + else + cycles_1541 -= TheCPU1541->EmulateLine(1); + } else + TheCPU->EmulateLine(cycles); + } else + // 1541 processor disabled, only emulate 6510 + TheCPU->EmulateLine(cycles); +#endif + } +} diff --git a/Src/C64_Be.h b/Src/C64_Be.h new file mode 100644 index 0000000..aeccb99 --- /dev/null +++ b/Src/C64_Be.h @@ -0,0 +1,418 @@ +/* + * C64_Be.h - Put the pieces together, Be specific stuff + * + * Frodo (C) 1994-1997,2002-2005 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include + +#undef PROFILING + + +/* + * Constructor, system-dependent things + */ + +void C64::c64_ctor1(void) +{ + joy[0] = joy[1] = NULL; + joy_geek_port[0] = joy_geek_port[1] = false; +} + +void C64::c64_ctor2(void) +{ + // Initialize joystick variables + joy_minx = joy_miny = 32767; + joy_maxx = joy_maxy = 0; + + // Initialize semaphores (initially acquired) + pause_sem = create_sem(0, "Frodo Pause Semaphore"); + sound_sync_sem = create_sem(0, "Frodo Sound Sync Semaphore"); + + // Preset speedometer start time + start_time = system_time(); +} + + +/* + * Destructor, system-dependent things + */ + +void C64::c64_dtor(void) +{ + delete_sem(pause_sem); + delete_sem(sound_sync_sem); +} + + +/* + * Start main emulation thread + */ + +void C64::Run(void) +{ + // Reset chips + TheCPU->Reset(); + TheSID->Reset(); + TheCIA1->Reset(); + TheCIA2->Reset(); + TheCPU1541->Reset(); + + // Patch kernal IEC routines + orig_kernal_1d84 = Kernal[0x1d84]; + orig_kernal_1d85 = Kernal[0x1d85]; + PatchKernal(ThePrefs.FastReset, ThePrefs.Emul1541Proc); + + // Start the CPU thread + the_thread = spawn_thread(thread_invoc, "Frodo 6510", B_URGENT_DISPLAY_PRIORITY, this); + thread_running = true; + quit_thyself = false; + have_a_break = false; + resume_thread(the_thread); +} + + +/* + * Stop main emulation thread + */ + +void C64::Quit(void) +{ + long ret; + + // Ask the thread to quit itself if it is running + if (thread_running) { + if (have_a_break) + Resume(); + quit_thyself = true; + wait_for_thread(the_thread, &ret); + thread_running = false; + } +} + + +/* + * Pause main emulation thread + */ + +void C64::Pause(void) +{ + // Ask the thread to pause and wait for acknowledge + if (thread_running && !have_a_break) { + have_a_break = true; + acquire_sem(pause_sem); + TheSID->PauseSound(); + } +} + + +/* + * Resume main emulation thread + */ + +void C64::Resume(void) +{ + if (thread_running && have_a_break) { + have_a_break = false; + release_sem(pause_sem); + TheSID->ResumeSound(); + } +} + + +/* + * Vertical blank: Poll keyboard and joysticks, update window + */ + +void C64::VBlank(bool draw_frame) +{ + bigtime_t elapsed_time; + long speed_index; + + // To avoid deadlocks on quitting + if (quit_thyself) return; + + // Pause requested? + if (have_a_break) { + release_sem(pause_sem); // Acknowledge pause + acquire_sem(pause_sem); // Wait for resume + } + + // Poll keyboard + TheDisplay->PollKeyboard(TheCIA1->KeyMatrix, TheCIA1->RevMatrix, &joykey); + + // Poll joysticks + TheCIA1->Joystick1 = poll_joystick(0); + TheCIA1->Joystick2 = poll_joystick(1); + + if (ThePrefs.JoystickSwap) { + uint8 tmp = TheCIA1->Joystick1; + TheCIA1->Joystick1 = TheCIA1->Joystick2; + TheCIA1->Joystick2 = tmp; + } + + // Joystick keyboard emulation + if (TheDisplay->NumLock()) + TheCIA1->Joystick1 &= joykey; + else + TheCIA1->Joystick2 &= joykey; + + // Count TOD clocks + TheCIA1->CountTOD(); + TheCIA2->CountTOD(); + + // Update window if needed + if (draw_frame) { + TheDisplay->Update(); + + // Calculate time between VBlanks, display speedometer + elapsed_time = system_time() - start_time; + speed_index = 20000 * 100 * ThePrefs.SkipFrames / (elapsed_time + 1); + + // Limit speed to 100% if desired (20ms/frame) + // If the SID emulation is on and no frames are skipped, synchronize to the SID + if (ThePrefs.LimitSpeed && speed_index > 100) { + if (ThePrefs.SIDType == SIDTYPE_DIGITAL && ThePrefs.SkipFrames == 1) { + long l; + get_sem_count(sound_sync_sem, &l); + if (l > 0) // Avoid C64 lagging behind + acquire_sem_etc(sound_sync_sem, l+1, 0, 0); + else + acquire_sem(sound_sync_sem); + } else + snooze(ThePrefs.SkipFrames * 20000 - elapsed_time); + speed_index = 100; + } + + start_time = system_time(); + + TheDisplay->Speedometer(speed_index); + } +} + + +/* + * Called by SID after playing 1/50 sec of sound + */ + +void C64::SoundSync(void) +{ + release_sem(sound_sync_sem); +} + + +/* + * Open/close joystick drivers given old and new state of + * joystick preferences + */ + +void C64::open_close_joystick(int port, int oldjoy, int newjoy) +{ + if (oldjoy != newjoy) { + joy_minx = joy_miny = 32767; // Reset calibration + joy_maxx = joy_maxy = 0; + if (joy[port]) { + if (joy_geek_port[port]) { + ((BDigitalPort *)joy[port])->Close(); + delete (BDigitalPort *)joy[port]; + } else { + ((BJoystick *)joy[port])->Close(); + delete (BJoystick *)joy[port]; + } + joy[port] = NULL; + } + switch (newjoy) { + case 1: + joy[port] = new BJoystick; + ((BJoystick *)joy[port])->Open("joystick1"); + joy_geek_port[port] = false; + break; + case 2: + joy[port] = new BJoystick; + ((BJoystick *)joy[port])->Open("joystick2"); + joy_geek_port[port] = false; + break; + case 3: + joy[port] = new BDigitalPort; + ((BDigitalPort *)joy[port])->Open("DigitalA"); + joy_geek_port[port] = true; + break; + case 4: + joy[port] = new BDigitalPort; + ((BDigitalPort *)joy[port])->Open("DigitalB"); + joy_geek_port[port] = true; + break; + } + } +} + +void C64::open_close_joysticks(int oldjoy1, int oldjoy2, int newjoy1, int newjoy2) +{ + open_close_joystick(0, oldjoy1, newjoy1); + open_close_joystick(1, oldjoy2, newjoy2); +} + + +/* + * Poll joystick port, return CIA mask + */ + +uint8 C64::poll_joystick(int port) +{ + uint8 j = 0xff; + + if (joy[port] == NULL) + return j; + + if (joy_geek_port[port]) { + + // GeekPort + uint8 val; + if (((BDigitalPort *)joy[port])->Read(&val) == 1) + j = val | 0xe0; + + } else { + + // Joystick port + BJoystick *p = (BJoystick *)joy[port]; + if (p->Update() != B_ERROR) { + if (p->horizontal > joy_maxx) + joy_maxx = p->horizontal; + if (p->horizontal < joy_minx) + joy_minx = p->horizontal; + if (p->vertical > joy_maxy) + joy_maxy = p->vertical; + if (p->vertical < joy_miny) + joy_miny = p->vertical; + + if (!p->button1) + j &= 0xef; // Button + + if (joy_maxx-joy_minx < 100 || joy_maxy-joy_miny < 100) + return j; + + if (p->horizontal < (joy_minx + (joy_maxx-joy_minx)/3)) + j &= 0xf7; // Right + else if (p->horizontal > (joy_minx + 2*(joy_maxx-joy_minx)/3)) + j &= 0xfb; // Left + + if (p->vertical < (joy_miny + (joy_maxy-joy_miny)/3)) + j &= 0xfd; // Down + else if (p->vertical > (joy_miny + 2*(joy_maxy-joy_miny)/3)) + j &= 0xfe; // Up + } + } + return j; +} + + +/* + * The emulation's main loop + */ + +long C64::thread_invoc(void *obj) +{ + ((C64 *)obj)->thread_func(); + return 0; +} + +void C64::thread_func(void) +{ +#ifdef PROFILING +static bigtime_t vic_time_acc = 0; +static bigtime_t sid_time_acc = 0; +static bigtime_t cia_time_acc = 0; +static bigtime_t cpu_time_acc = 0; +#endif +#ifdef FRODO_SC + while (!quit_thyself) { + // The order of calls is important here + if (TheVIC->EmulateCycle()) + TheSID->EmulateLine(); + TheCIA1->CheckIRQs(); + TheCIA2->CheckIRQs(); + TheCIA1->EmulateCycle(); + TheCIA2->EmulateCycle(); + TheCPU->EmulateCycle(); + + if (ThePrefs.Emul1541Proc) { + TheCPU1541->CountVIATimers(1); + if (!TheCPU1541->Idle) + TheCPU1541->EmulateCycle(); + } + CycleCounter++; +#else + while (!quit_thyself) { + // The order of calls is important here +#ifdef PROFILING +bigtime_t start_time = system_time(); +#endif + int cycles = TheVIC->EmulateLine(); +#ifdef PROFILING +bigtime_t vic_time = system_time(); +#endif + TheSID->EmulateLine(); +#ifdef PROFILING +bigtime_t sid_time = system_time(); +#endif +#if !PRECISE_CIA_CYCLES + TheCIA1->EmulateLine(ThePrefs.CIACycles); + TheCIA2->EmulateLine(ThePrefs.CIACycles); +#endif +#ifdef PROFILING +bigtime_t cia_time = system_time(); +#endif + + if (ThePrefs.Emul1541Proc) { + int cycles_1541 = ThePrefs.FloppyCycles; + TheCPU1541->CountVIATimers(cycles_1541); + + if (!TheCPU1541->Idle) { + // 1541 processor active, alternately execute + // 6502 and 6510 instructions until both have + // used up their cycles + while (cycles >= 0 || cycles_1541 >= 0) + if (cycles > cycles_1541) + cycles -= TheCPU->EmulateLine(1); + else + cycles_1541 -= TheCPU1541->EmulateLine(1); + } else + TheCPU->EmulateLine(cycles); + } else + // 1541 processor disabled, only emulate 6510 + TheCPU->EmulateLine(cycles); +#ifdef PROFILING +bigtime_t cpu_time = system_time(); +vic_time_acc += vic_time - start_time; +sid_time_acc += sid_time - vic_time; +cia_time_acc += cia_time - sid_time; +cpu_time_acc += cpu_time - cia_time; +#endif + +#endif + } + +#ifdef PROFILING +bigtime_t total_time = vic_time_acc + sid_time_acc + cia_time_acc + cpu_time_acc; +printf("VIC: %Ld\n", vic_time_acc * 100 / total_time); +printf("SID: %Ld\n", sid_time_acc * 100 / total_time); +printf("CIA: %Ld\n", cia_time_acc * 100 / total_time); +printf("CPU: %Ld\n", cpu_time_acc * 100 / total_time); +#endif +} diff --git a/Src/C64_Embedded.h b/Src/C64_Embedded.h new file mode 100644 index 0000000..66b1869 --- /dev/null +++ b/Src/C64_Embedded.h @@ -0,0 +1,320 @@ +/* + * C64_x.h - Put the pieces together, X specific stuff + * + * Frodo (C) 1994-1997,2002-2004 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "main.h" + + +static struct timeval tv_start; + +#ifndef HAVE_USLEEP +/* + * NAME: + * usleep -- This is the precision timer for Test Set + * Automation. It uses the select(2) system + * call to delay for the desired number of + * micro-seconds. This call returns ZERO + * (which is usually ignored) on successful + * completion, -1 otherwise. + * + * ALGORITHM: + * 1) We range check the passed in microseconds and log a + * warning message if appropriate. We then return without + * delay, flagging an error. + * 2) Load the Seconds and micro-seconds portion of the + * interval timer structure. + * 3) Call select(2) with no file descriptors set, just the + * timer, this results in either delaying the proper + * ammount of time or being interupted early by a signal. + * + * HISTORY: + * Added when the need for a subsecond timer was evident. + * + * AUTHOR: + * Michael J. Dyer Telephone: AT&T 414.647.4044 + * General Electric Medical Systems GE DialComm 8 *767.4044 + * P.O. Box 414 Mail Stop 12-27 Sect'y AT&T 414.647.4584 + * Milwaukee, Wisconsin USA 53201 8 *767.4584 + * internet: mike@sherlock.med.ge.com GEMS WIZARD e-mail: DYER + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +int usleep(unsigned long int microSeconds) +{ + unsigned int Seconds, uSec; + int nfds, readfds, writefds, exceptfds; + struct timeval Timer; + + nfds = readfds = writefds = exceptfds = 0; + + if( (microSeconds == (unsigned long) 0) + || microSeconds > (unsigned long) 4000000 ) + { + errno = ERANGE; /* value out of range */ + perror( "usleep time out of range ( 0 -> 4000000 ) " ); + return -1; + } + + Seconds = microSeconds / (unsigned long) 1000000; + uSec = microSeconds % (unsigned long) 1000000; + + Timer.tv_sec = Seconds; + Timer.tv_usec = uSec; + + if( select( nfds, &readfds, &writefds, &exceptfds, &Timer ) < 0 ) + { + perror( "usleep (select) failed" ); + return -1; + } + + return 0; +} +#endif + +#ifdef __linux__ +// select() timing is much more accurate under Linux +static void Delay_usec(unsigned long usec) +{ + int was_error; + struct timeval tv; + + tv.tv_sec = 0; + tv.tv_usec = usec; + do { + errno = 0; + was_error = select(0, NULL, NULL, NULL, &tv); + } while (was_error && (errno == EINTR)); +} +#else +static void Delay_usec(unsigned long usec) +{ + usleep(usec); +} +#endif + +/* + * Constructor, system-dependent things + */ + +void C64::c64_ctor1(void) +{ + // Initialize joystick variables + joy_minx[0] = joy_miny[0] = 32767; + joy_maxx[0] = joy_maxy[0] = -32768; + joy_minx[1] = joy_miny[1] = 32767; + joy_maxx[1] = joy_maxy[1] = -32768; +} + +void C64::c64_ctor2(void) +{ + gettimeofday(&tv_start, NULL); +} + + +/* + * Destructor, system-dependent things + */ + +void C64::c64_dtor(void) +{ +} + + +/* + * Start main emulation thread + */ + +void C64::Run(void) +{ + // Reset chips + TheCPU->Reset(); + TheSID->Reset(); + TheCIA1->Reset(); + TheCIA2->Reset(); + TheCPU1541->Reset(); + + // Patch kernal IEC routines + orig_kernal_1d84 = Kernal[0x1d84]; + orig_kernal_1d85 = Kernal[0x1d85]; + PatchKernal(ThePrefs.FastReset, ThePrefs.Emul1541Proc); + + quit_thyself = false; + thread_func(); +} + + +/* + * Vertical blank: Poll keyboard and joysticks, update window + */ + +void C64::VBlank(bool draw_frame) +{ + // Poll keyboard + TheDisplay->PollKeyboard(TheCIA1->KeyMatrix, TheCIA1->RevMatrix, &joykey); + if (TheDisplay->quit_requested) + quit_thyself = true; + + // Poll joysticks + TheCIA1->Joystick1 = poll_joystick(0); + TheCIA1->Joystick2 = poll_joystick(1); + + if (ThePrefs.JoystickSwap) { + uint8 tmp = TheCIA1->Joystick1; + TheCIA1->Joystick1 = TheCIA1->Joystick2; + TheCIA1->Joystick2 = tmp; + } + + // Joystick keyboard emulation + if (TheDisplay->NumLock()) + TheCIA1->Joystick1 &= joykey; + else + TheCIA1->Joystick2 &= joykey; + + // Count TOD clocks + TheCIA1->CountTOD(); + TheCIA2->CountTOD(); + + // Update window if needed + if (draw_frame) { + TheDisplay->Update(); + + // Calculate time between VBlanks, display speedometer + struct timeval tv; + gettimeofday(&tv, NULL); + if ((tv.tv_usec -= tv_start.tv_usec) < 0) { + tv.tv_usec += 1000000; + tv.tv_sec -= 1; + } + tv.tv_sec -= tv_start.tv_sec; + double elapsed_time = (double)tv.tv_sec * 1000000 + tv.tv_usec; + speed_index = 20000 / (elapsed_time + 1) * ThePrefs.SkipFrames * 100; + + // Limit speed to 100% if desired + if ((speed_index > 100) && ThePrefs.LimitSpeed) { + Delay_usec((unsigned long)(ThePrefs.SkipFrames * 20000 - elapsed_time)); + speed_index = 100; + } + + gettimeofday(&tv_start, NULL); + + TheDisplay->Speedometer((int)speed_index); + } +} + + +/* + * The emulation's main loop + */ + +void C64::thread_func(void) +{ + int linecnt = 0; + +#ifdef FRODO_SC + while (!quit_thyself) { + + // The order of calls is important here + if (TheVIC->EmulateCycle()) + TheSID->EmulateLine(); + TheCIA1->CheckIRQs(); + TheCIA2->CheckIRQs(); + TheCIA1->EmulateCycle(); + TheCIA2->EmulateCycle(); + TheCPU->EmulateCycle(); + + if (ThePrefs.Emul1541Proc) { + TheCPU1541->CountVIATimers(1); + if (!TheCPU1541->Idle) + TheCPU1541->EmulateCycle(); + } + CycleCounter++; +#else + while (!quit_thyself) { + + // The order of calls is important here + int cycles = TheVIC->EmulateLine(); + TheSID->EmulateLine(); +#if !PRECISE_CIA_CYCLES + TheCIA1->EmulateLine(ThePrefs.CIACycles); + TheCIA2->EmulateLine(ThePrefs.CIACycles); +#endif + + if (ThePrefs.Emul1541Proc) { + int cycles_1541 = ThePrefs.FloppyCycles; + TheCPU1541->CountVIATimers(cycles_1541); + + if (!TheCPU1541->Idle) { + // 1541 processor active, alternately execute + // 6502 and 6510 instructions until both have + // used up their cycles + while (cycles >= 0 || cycles_1541 >= 0) + if (cycles > cycles_1541) + cycles -= TheCPU->EmulateLine(1); + else + cycles_1541 -= TheCPU1541->EmulateLine(1); + } else + TheCPU->EmulateLine(cycles); + } else + // 1541 processor disabled, only emulate 6510 + TheCPU->EmulateLine(cycles); +#endif + linecnt++; +#if !defined(__svgalib__) + if ((linecnt & 0xfff) == 0 && gui) { + + // check for command from GUI process + // fprintf(stderr,":"); + while (gui->probe()) { + char c; + if (gui->eread(&c, 1) != 1) { + delete gui; + gui = 0; + fprintf(stderr,"Oops, GUI process died...\n"); + } else { + // fprintf(stderr,"%c",c); + switch (c) { + case 'q': + quit_thyself = true; + break; + case 'r': + Reset(); + break; + case 'p':{ + Prefs *np = Frodo::reload_prefs(); + NewPrefs(np); + ThePrefs = *np; + break; + } + default: + break; + } + } + } + } +#endif + } +} diff --git a/Src/C64_SC.cpp b/Src/C64_SC.cpp new file mode 100644 index 0000000..29f9549 --- /dev/null +++ b/Src/C64_SC.cpp @@ -0,0 +1,28 @@ +/* + * C64_SC.cpp - Put the pieces together (Frodo SC) + * + * Frodo (C) 1994-1997,2002-2005 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +// Same as C64.cpp (but object files are different, and this gives it the +// name "C64_SC.o" instead of "C64.o" which is needed under some build +// environments) +#ifdef __riscos__ +#include "C64.cc" +#else +#include "C64.cpp" +#endif diff --git a/Src/C64_SDL.h b/Src/C64_SDL.h new file mode 100644 index 0000000..8ef350c --- /dev/null +++ b/Src/C64_SDL.h @@ -0,0 +1,674 @@ +/* + * C64_x.i - Put the pieces together, X specific stuff + * + * Frodo (C) 1994-1997,2002 Christian Bauer + * Unix stuff by Bernd Schmidt/Lutz Vieweg + */ + +#include "main.h" + +#include +#include + +#if defined(GEKKO) +#include +#include +#define FONT_PATH "/apps/frodo/FreeMono.ttf" +#define SAVES_PATH "/apps/frodo/saves" +#define IMAGE_PATH "/apps/frodo/images" +#define TMP_PATH "/apps/frodo/tmp" +#else +#define FONT_PATH "FreeMono.ttf" +#define SAVES_PATH "saves" +#define IMAGE_PATH "images" +#define TMP_PATH "tmp" +#endif + +static struct timeval tv_start; +static int MENU_SIZE_X, MENU_SIZE_Y; +static const char *main_menu_messages[] = { + "Insert disc or tape", /* 0 */ + "Load disc or tape", /* 1 */ + "Reset C64", /* 2 */ + "Bind key to joystick",/* 3 */ + "Other options", /* 4 */ + "Controller 1 joystick port", /* 5 */ + "^|1|2", + "Save/Load state", /* 7 */ + " ", + "Quit", /* 9 */ + NULL, +}; + +static const char *other_options_messages[] = { + "Display resolution", /* 0 */ + "^|double-center|stretched", + "Speed (approx)", /* 2 */ + "^|95|100|110", + NULL, +}; + +static const char *save_load_state_messages[] = { + "Load saved state", /* 0 */ + "Save current state", /* 1 */ + "Delete state", /* 2 */ + NULL, +}; + +/* + * Constructor, system-dependent things + */ +void C64::c64_ctor1(void) +{ + // Initialize joystick variables +#ifdef HAVE_LINUX_JOYSTICK_H + joyfd[0] = joyfd[1] = -1; + joy_minx = joy_miny = 32767; + joy_maxx = joy_maxy = -32768; +#endif + + this->fake_key_sequence = false; + this->fake_key_index = 0; + this->fake_key_keytime = 5; + this->fake_key_type = 0; + + this->prefs_changed = false; + memset(this->save_game_name, 0, sizeof(this->save_game_name)); + strcpy(this->save_game_name, "unknown"); + + MENU_SIZE_X = FULL_DISPLAY_X - FULL_DISPLAY_X / 4; + MENU_SIZE_Y = FULL_DISPLAY_Y - FULL_DISPLAY_Y / 4; + + SDL_RWops *rw; + + Uint8 *data = (Uint8*)malloc(1 * 1024*1024); + FILE *fp = fopen(FONT_PATH, "r"); + if (!fp) { + fprintf(stderr, "Could not open font\n"); + exit(1); + } + fread(data, 1, 1 * 1024 * 1024, fp); + rw = SDL_RWFromMem(data, 1 * 1024 * 1024); + if (!rw) { + fprintf(stderr, "Could not create RW: %s\n", SDL_GetError()); + exit(1); + } + + this->menu_font = TTF_OpenFontRW(rw, 1, 20); + if (!this->menu_font) + { + fprintf(stderr, "Unable to open font\n" ); + exit(1); + } + menu_init(&this->main_menu, this->menu_font, main_menu_messages, + 32, 32, MENU_SIZE_X, MENU_SIZE_Y); +} + +void C64::c64_ctor2(void) +{ + gettimeofday(&tv_start, NULL); +} + + +/* + * Destructor, system-dependent things + */ + +void C64::c64_dtor(void) +{ + menu_fini(&this->main_menu); +} + +static int cmpstringp(const void *p1, const void *p2) +{ + return strcmp(* (char * const *) p1, * (char * const *) p2); +} + +static const char **get_file_list(const char *base_dir) +{ + DIR *d = opendir(base_dir); + const char **file_list; + int cur = 0; + struct dirent *de; + int cnt = 16; + + if (!d) + return NULL; + + file_list = (const char**)malloc(cnt * sizeof(char*)); + file_list[cur++] = strdup("None"); + file_list[cur] = NULL; + + for (de = readdir(d); + de; + de = readdir(d)) + { + if (strstr(de->d_name, ".d64") || strstr(de->d_name, ".D64") || + strstr(de->d_name, ".prg") || strstr(de->d_name, ".PRG") || + strstr(de->d_name, ".p00") || strstr(de->d_name, ".P00") || + strstr(de->d_name, ".s00") || strstr(de->d_name, ".S00") || + strstr(de->d_name, ".t64") || strstr(de->d_name, ".T64") || + strstr(de->d_name, ".sav")) + { + char *p; + + p = strdup(de->d_name); + file_list[cur++] = p; + file_list[cur] = NULL; + if (cur > cnt - 2) + { + cnt = cnt + 32; + file_list = (const char**)realloc(file_list, cnt * sizeof(char*)); + if (!file_list) + return NULL; + } + } + } + closedir(d); + qsort(&file_list[1], cur-1, sizeof(const char *), cmpstringp); + + return file_list; +} + +void C64::select_disc(Prefs *np) +{ + const char **file_list = get_file_list(IMAGE_PATH); + menu_t select_disc_menu; + + if (file_list == NULL) + return; + + menu_init(&select_disc_menu, this->menu_font, file_list, + 32, 32, MENU_SIZE_X, MENU_SIZE_Y); + int opt = menu_select(real_screen, &select_disc_menu, NULL); + if (opt >= 0) + { + const char *name = file_list[opt]; + + if (strcmp(file_list[opt], "None") == 0) + { + strcpy(np->DrivePath[0], "\0"); + strcpy(this->save_game_name, "unknown"); + } + else + { + snprintf(np->DrivePath[0], 255, "%s/%s", + IMAGE_PATH, name); + strncpy(this->save_game_name, name, 255); + if (strstr(name, ".prg") || strstr(name, ".PRG") || + strstr(name, ".p00") || strstr(name, ".P00") || + strstr(name, ".s00") || strstr(name, ".S00")) { + FILE *src, *dst; + + /* Clean temp dir first (we only want one file) */ + unlink(TMP_PATH"/a"); + + src = fopen(np->DrivePath[0], "r"); + if (src != NULL) + { + snprintf(np->DrivePath[0], 255, "%s", TMP_PATH); + + /* Special handling of .prg: Copy to TMP_PATH and + * load that as a dir */ + dst = fopen(TMP_PATH"/a", "w"); + if (dst) + { + Uint8 buf[1024]; + size_t v; + + do { + v = fread(buf, 1, 1024, src); + fwrite(buf, 1, v, dst); + } while (v > 0); + fclose(dst); + } + fclose(src); + } + } + + NewPrefs(np); + ThePrefs = *np; + } + this->prefs_changed = true; + } + menu_fini(&select_disc_menu); + + /* Cleanup everything */ + for ( int i = 0; file_list[i]; i++ ) + free((void*)file_list[i]); + free(file_list); +} + +/* + C64 keyboard matrix: + + Bit 7 6 5 4 3 2 1 0 + 0 CUD F5 F3 F1 F7 CLR RET DEL + 1 SHL E S Z 4 A W 3 + 2 X T F C 6 D R 5 + 3 V U H B 8 G Y 7 + 4 N O K M 0 J I 9 + 5 , @ : . - L P + + 6 / ^ = SHR HOM ; * � + 7 R/S Q C= SPC 2 CTL <- 1 +*/ +#define MATRIX(a,b) (((a) << 3) | (b)) + +void C64::bind_key(Prefs *np) +{ + const char **bind_key_messages; + bool has_classic_controller = false; + menu_t bind_key_menu; + menu_t key_menu; + static const char *keys[] = { "None", "space", "Run/Stop", "return", "F1", "F3", "F5", "F7", + "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", + "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", + "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", + "ctrl", "del", "home,", "shl", "shr", "clr", "C=", "<-", + NULL }; + int kcs[] = { 0, MATRIX(7, 4), MATRIX(7, 7), MATRIX(0, 1), /* space, R/S, return */ + MATRIX(0, 4), MATRIX(0, 5), MATRIX(0, 6), MATRIX(0, 3), MATRIX(4, 3), MATRIX(7, 0), + MATRIX(7, 3), MATRIX(1, 0), MATRIX(1, 3), MATRIX(2, 0), MATRIX(2, 3), MATRIX(3, 0), + MATRIX(3, 3), MATRIX(4, 0), MATRIX(1, 2), MATRIX(3, 4), MATRIX(2, 4), MATRIX(2, 2), + MATRIX(1, 6), MATRIX(2, 5), MATRIX(3, 2), MATRIX(3, 5), MATRIX(4, 1), MATRIX(4, 2), + MATRIX(4, 5), MATRIX(5, 2), MATRIX(4, 4), MATRIX(4, 7), MATRIX(4, 6), MATRIX(5, 1), + MATRIX(7, 6), MATRIX(2, 1), MATRIX(1, 5), MATRIX(2, 6), MATRIX(3, 6), MATRIX(3, 7), + MATRIX(1, 1), MATRIX(2, 7), MATRIX(3, 1), MATRIX(1, 4), /* ... Z */ + MATRIX(7, 3), MATRIX(0, 0), MATRIX(6, 4), MATRIX(1, 7), MATRIX(6, 4), + MATRIX(0, 2), MATRIX(7, 5), MATRIX(7, 1), + }; + +#if defined(GEKKO) + WPADData *wpad, *wpad_other; + + wpad = WPAD_Data(0); + wpad_other = WPAD_Data(1); + + if (wpad->exp.type == WPAD_EXP_CLASSIC || + wpad_other->exp.type == WPAD_EXP_CLASSIC) + has_classic_controller = true; +#endif + + bind_key_messages = (const char **)malloc( sizeof(const char*) * (N_WIIMOTE_BINDINGS + 1)); + assert(bind_key_messages); + memset(bind_key_messages, 0, sizeof(const char*) * (N_WIIMOTE_BINDINGS + 1)); + + bind_key_messages[WIIMOTE_A] = "Bind to A"; + bind_key_messages[WIIMOTE_B] = "Bind to B"; + bind_key_messages[WIIMOTE_PLUS] = "Bind to Plus"; + bind_key_messages[WIIMOTE_MINUS] = "Bind to Minus"; + bind_key_messages[WIIMOTE_1] = "Bind to 1"; + + if (has_classic_controller) + { + bind_key_messages[WIIMOTE_PLUS] = "Bind to wiimote/classic Plus"; + bind_key_messages[WIIMOTE_MINUS] = "Bind to wiimote/classic Minus"; + + bind_key_messages[CLASSIC_X] = "Bind to classic X"; + bind_key_messages[CLASSIC_Y] = "Bind to classic Y"; + bind_key_messages[CLASSIC_B] = "Bind to classic B"; + bind_key_messages[CLASSIC_L] = "Bind to classic L"; + bind_key_messages[CLASSIC_R] = "Bind to classic R"; + bind_key_messages[CLASSIC_ZL] = "Bind to classic ZL"; + bind_key_messages[CLASSIC_ZR] = "Bind to classic ZR"; + } + + menu_init(&bind_key_menu, this->menu_font, bind_key_messages, + 32, 32, MENU_SIZE_X, MENU_SIZE_Y); + int opt = menu_select(real_screen, &bind_key_menu, NULL); + if (opt >= 0) + { + menu_init(&key_menu, this->menu_font, keys, + 32, 32, MENU_SIZE_X, MENU_SIZE_Y); + int key = menu_select(real_screen, &key_menu, NULL); + + /* Assume prefs are changed */ + this->prefs_changed = true; + if (key > 0) + np->JoystickKeyBinding[opt] = kcs[key]; + else if (key == 0) + np->JoystickKeyBinding[opt] = -1; + else + this->prefs_changed = false; + menu_fini(&key_menu); + } + menu_fini(&bind_key_menu); + free(bind_key_messages); +} + +void C64::other_options(Prefs *np) +{ + menu_t display_menu; + int submenus[2] = { np->DisplayOption}; + +#define SPEED_95 40 +#define SPEED_110 34 +#define SPEED_100 38 + + switch (np->MsPerFrame) + { + case SPEED_95: + submenus[1] = 0; break; + case SPEED_110: + submenus[1] = 2; break; + default: + /* If it has some other value... */ + submenus[1] = 1; break; + } + menu_init(&display_menu, this->menu_font, other_options_messages, + 32, 32, MENU_SIZE_X, MENU_SIZE_Y); + int opt = menu_select(real_screen, &display_menu, submenus); + if (opt >= 0) + { + np->DisplayOption = submenus[0]; + switch(submenus[1]) + { + case 0: + np->MsPerFrame = SPEED_95; break; + case 1: + np->MsPerFrame = SPEED_100; break; + case 2: + default: + np->MsPerFrame = SPEED_110; break; + } + this->prefs_changed = true; + } + menu_fini(&display_menu); +} + +void C64::save_load_state(Prefs *np) +{ + menu_t save_load_menu; + menu_t select_saves_menu; + + menu_init(&save_load_menu, this->menu_font, save_load_state_messages, + 32, 32, MENU_SIZE_X, MENU_SIZE_Y); + int opt = menu_select(real_screen, &save_load_menu, NULL); + switch(opt) + { + case 1: /* save */ + { + char buf[255]; + + snprintf(buf, 255, "%s/%s.sav", SAVES_PATH, + this->save_game_name); + + this->SaveSnapshot(buf); + } break; + case 0: /* load/delete */ + case 2: + { + const char **file_list = get_file_list(SAVES_PATH); + + if (file_list == NULL) + break; + menu_init(&select_saves_menu, this->menu_font, file_list, + 32, 32, MENU_SIZE_X, MENU_SIZE_Y); + int save = menu_select(real_screen, &select_saves_menu, NULL); + if (save >= 0) + { + char buf[255]; + + snprintf(buf, 255, "%s/%s", SAVES_PATH, file_list[save]); + if (opt == 2) + unlink(buf); + else /* Load the snapshot */ + this->LoadSnapshot(buf); + } + menu_fini(&select_saves_menu); + + /* Cleanup everything */ + for ( int i = 0; file_list[i]; i++ ) + free((void*)file_list[i]); + free(file_list); + } break; + default: + break; + } + menu_fini(&save_load_menu); +} + +/* + * Start main emulation thread + */ + +void C64::Run(void) +{ + // Reset chips + TheCPU->Reset(); + TheSID->Reset(); + TheCIA1->Reset(); + TheCIA2->Reset(); + TheCPU1541->Reset(); + + // Patch kernal IEC routines + orig_kernal_1d84 = Kernal[0x1d84]; + orig_kernal_1d85 = Kernal[0x1d85]; + PatchKernal(ThePrefs.FastReset, ThePrefs.Emul1541Proc); + + quit_thyself = false; + thread_func(); +} + +/* From dreamcast port */ +static const char *auto_seq[4] = +{ + "\nLOAD \"*\",8,1\nRUN\n", + "\nLOAD \"*\",9,1\nRUN\n", + "\nLOAD \"*\",10,1\nRUN\n", + "\nLOAD \"*\",11,1\nRUN\n", +}; +extern "C" int get_kc_from_char(char c_in, int *shifted); + +/* + * Vertical blank: Poll keyboard and joysticks, update window + */ + +void C64::VBlank(bool draw_frame) +{ +#if defined(GEKKO) + WPAD_ScanPads(); +#endif + + // Poll joysticks + TheCIA1->Joystick1 = poll_joystick(0); + TheCIA1->Joystick2 = poll_joystick(1); + + // Poll keyboard + TheDisplay->PollKeyboard(TheCIA1->KeyMatrix, TheCIA1->RevMatrix, &joykey); + if (TheDisplay->quit_requested) + quit_thyself = true; + + if (this->fake_key_sequence) + { + int shifted; + int kc = get_kc_from_char(auto_seq[this->fake_key_type][this->fake_key_index], &shifted); + + TheDisplay->FakeKeyPress(kc, shifted, TheCIA1->KeyMatrix, + TheCIA1->RevMatrix); + + this->fake_key_keytime --; + if (this->fake_key_keytime == 0) + { + this->fake_key_keytime = 1; + this->fake_key_index ++; + + if (auto_seq[this->fake_key_type][this->fake_key_index] == '\0') + { + this->fake_key_sequence = false; + this->fake_key_index = 0; + this->fake_key_keytime = 5; + } + } + } +#ifndef GEKKO + // Joystick keyboard emulation + if (TheDisplay->NumLock()) + TheCIA1->Joystick1 &= joykey; + else + TheCIA1->Joystick2 &= joykey; +#endif + + // Count TOD clocks + TheCIA1->CountTOD(); + TheCIA2->CountTOD(); + + // Update window if needed + if (draw_frame) { + TheDisplay->Update(); +#if 0 + // Calculate time between VBlanks, display speedometer + struct timeval tv; + gettimeofday(&tv, NULL); + if ((tv.tv_usec -= tv_start.tv_usec) < 0) { + tv.tv_usec += 1000000; + tv.tv_sec -= 1; + } + tv.tv_sec -= tv_start.tv_sec; + double elapsed_time = (double)tv.tv_sec * 1000000 + tv.tv_usec; + speed_index = 20000 / (elapsed_time + 1) * 100; + + // Limit speed to 100% if desired + if ((speed_index > 100)) { + usleep((unsigned long)(20000 - elapsed_time)); + speed_index = 100; + } + + gettimeofday(&tv_start, NULL); + + TheDisplay->Speedometer((int)speed_index); +#endif + } + if (this->have_a_break) { + int submenus[1]; + int opt; + int old_swap = ThePrefs.JoystickSwap == true ? 1 : 0; + + Prefs np = ThePrefs; + this->prefs_changed = false; + + TheSID->PauseSound(); + submenus[0] = old_swap; + opt = menu_select(real_screen, &this->main_menu, submenus); + + switch(opt) + { + case 0: /* Insert disc/tape */ + this->select_disc(&np); + break; + case 1: /* Load disc/tape */ + this->fake_key_sequence = true; + break; + case 2: /* Reset */ + Reset(); + break; + case 3: /* Bind keys to joystick */ + this->bind_key(&np); + break; + case 4: /* Other options */ + this->other_options(&np); + break; + case 5: /* Swap joysticks */ + break; + case 7: /* Save / load game */ + this->save_load_state(&np); + break; + case 9: /* Quit */ + quit_thyself = true; + break; + case -1: + default: + break; + } + if (submenus[0] == 0) + np.JoystickSwap = false; + else + np.JoystickSwap = true; + if (submenus[0] != old_swap) + this->prefs_changed = true; + + if (this->prefs_changed) + { + this->NewPrefs(&np); + ThePrefs = np; + } + TheDisplay->FakeKeyPress(-1, false, TheCIA1->KeyMatrix, + TheCIA1->RevMatrix); + + this->have_a_break = false; + if (this->quit_thyself) + ThePrefs.Save(PREFS_PATH); + } + /* From Acorn port */ + static uint64_t lastFrame; +#if defined(GEKKO) + uint32_t now = ticks_to_millisecs(gettime()); +#else + uint32_t now = SDL_GetTicks(); +#endif + + if ( (now - lastFrame) < ThePrefs.MsPerFrame) { + usleep( (ThePrefs.MsPerFrame - (now - lastFrame)) * 1000); + } + lastFrame = now; +} + +/* + * The emulation's main loop + */ + +void C64::thread_func(void) +{ + int linecnt = 0; + +#ifdef FRODO_SC + while (!quit_thyself) { + + // The order of calls is important here + if (TheVIC->EmulateCycle()) + TheSID->EmulateLine(); + TheCIA1->CheckIRQs(); + TheCIA2->CheckIRQs(); + TheCIA1->EmulateCycle(); + TheCIA2->EmulateCycle(); + TheCPU->EmulateCycle(); + + if (ThePrefs.Emul1541Proc) { + TheCPU1541->CountVIATimers(1); + if (!TheCPU1541->Idle) + TheCPU1541->EmulateCycle(); + } + CycleCounter++; +#else + while (!quit_thyself) { + + // The order of calls is important here + int cycles = TheVIC->EmulateLine(); + TheSID->EmulateLine(); +#if !PRECISE_CIA_CYCLES + TheCIA1->EmulateLine(ThePrefs.CIACycles); + TheCIA2->EmulateLine(ThePrefs.CIACycles); +#endif + + if (ThePrefs.Emul1541Proc) { + int cycles_1541 = ThePrefs.FloppyCycles; + TheCPU1541->CountVIATimers(cycles_1541); + + if (!TheCPU1541->Idle) { + // 1541 processor active, alternately execute + // 6502 and 6510 instructions until both have + // used up their cycles + while (cycles >= 0 || cycles_1541 >= 0) + if (cycles > cycles_1541) + cycles -= TheCPU->EmulateLine(1); + else + cycles_1541 -= TheCPU1541->EmulateLine(1); + } else + TheCPU->EmulateLine(cycles); + } else + // 1541 processor disabled, only emulate 6510 + TheCPU->EmulateLine(cycles); +#endif + linecnt++; + } +} diff --git a/Src/C64_WIN32.h b/Src/C64_WIN32.h new file mode 100644 index 0000000..6aac403 --- /dev/null +++ b/Src/C64_WIN32.h @@ -0,0 +1,452 @@ +/* + * C64_WIN32.h - Put the pieces together, WIN32 specific stuff + * + * Frodo (C) 1994-1997,2002-2005 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include "main.h" + +#define FRAME_INTERVAL (1000/SCREEN_FREQ) // in milliseconds +#ifdef FRODO_SC +#define SPEEDOMETER_INTERVAL 4000 // in milliseconds +#else +#define SPEEDOMETER_INTERVAL 1000 // in milliseconds +#endif +#define JOYSTICK_SENSITIVITY 40 // % of live range +#define JOYSTICK_MIN 0x0000 // min value of range +#define JOYSTICK_MAX 0xffff // max value of range +#define JOYSTICK_RANGE (JOYSTICK_MAX - JOYSTICK_MIN) + +static BOOL high_resolution_timer = FALSE; + +/* + * Constructor, system-dependent things + */ + +void C64::c64_ctor1() +{ + Debug("C64::c64_ctor1\n"); + + // Initialize joystick variables. + joy_state = 0xff; + + // No need to check for state change. + state_change = FALSE; + + // Start the synchronization timer. + timer_semaphore = NULL; + timer_id = NULL; + StartTimer(); +} + +void C64::c64_ctor2() +{ + Debug("C64::c64_ctor2\n"); +} + + +/* + * Destructor, system-dependent things + */ + +void C64::c64_dtor() +{ + Debug("C64::c64_dtor\n"); + + StopTimer(); +} + + +/* + * Start emulation + */ + +void C64::Run() +{ + // Reset chips + TheCPU->Reset(); + TheSID->Reset(); + TheCIA1->Reset(); + TheCIA2->Reset(); + TheCPU1541->Reset(); + + // Patch kernal IEC routines + orig_kernal_1d84 = Kernal[0x1d84]; + orig_kernal_1d85 = Kernal[0x1d85]; + patch_kernal(ThePrefs.FastReset, ThePrefs.Emul1541Proc); + + // Start the CPU thread + thread_func(); +} + + +/* + * Stop emulation + */ + +void C64::Quit() +{ + // Ask the thread to quit itself if it is running + quit_thyself = TRUE; + state_change = TRUE; +} + + +/* + * Pause emulation + */ + +void C64::Pause() +{ + StopTimer(); + TheSID->PauseSound(); + have_a_break = TRUE; + state_change = TRUE; +} + + +/* + * Resume emulation + */ + +void C64::Resume() +{ + StartTimer(); + TheSID->ResumeSound(); + have_a_break = FALSE; +} + + +/* + * Vertical blank: Poll keyboard and joysticks, update window + */ + +void C64::VBlank(bool draw_frame) +{ + //Debug("C64::VBlank\n"); + + // Poll the keyboard. + TheDisplay->PollKeyboard(TheCIA1->KeyMatrix, TheCIA1->RevMatrix, &joykey); + // Poll the joysticks. + TheCIA1->Joystick1 = poll_joystick(0); + TheCIA1->Joystick2 = poll_joystick(1); + + if (ThePrefs.JoystickSwap) { + uint8 tmp = TheCIA1->Joystick1; + TheCIA1->Joystick1 = TheCIA1->Joystick2; + TheCIA1->Joystick2 = tmp; + } + + // Joystick keyboard emulation. + if (TheDisplay->NumLock()) + TheCIA1->Joystick1 &= joykey; + else + TheCIA1->Joystick2 &= joykey; + + // Count TOD clocks. + TheCIA1->CountTOD(); + TheCIA2->CountTOD(); + +#if 1 + // Output a frag. + TheSID->VBlank(); +#endif + + if (have_a_break) + return; + + // Update the window if needed. + frame++; + if (draw_frame) { + + // Synchronize to the timer if limiting the speed. + if (ThePrefs.LimitSpeed) { + if (skipped_frames == 0) { + // There is a tiny race condtion here that + // could cause a full extra delay cycle. + WaitForSingleObject(timer_semaphore, INFINITE); + } + else { + Debug("*** Skipped a frame! ***\n"); + skipped_frames = 0; + } + } + + // Perform the actual screen update exactly at the + // beginning of an interval for the smoothest video. + TheDisplay->Update(); + + // Compute the speed index and show it in the speedometer. + DWORD now = timeGetTime(); + int elapsed_time = now - ref_time; + if (now - ref_time >= SPEEDOMETER_INTERVAL) { + double speed_index = double(frame * FRAME_INTERVAL * 100 + elapsed_time/2) / elapsed_time; + TheDisplay->Speedometer((int)speed_index); + ref_time = now; + frame = 0; + } + + // Make sure our timer is set correctly. + CheckTimerChange(); + } +} + + +void C64::CheckTimerChange() +{ + // Make sure the timer interval matches the preferences. + if (!ThePrefs.LimitSpeed && timer_every == 0) + return; + if (ThePrefs.LimitSpeed && ThePrefs.SkipFrames == timer_every) + return; + StopTimer(); + StartTimer(); +} + +/* + * Open/close joystick drivers given old and new state of + * joystick preferences + */ + +BOOL joystick_open[2]; + +void C64::open_close_joysticks(int oldjoy1, int oldjoy2, int newjoy1, int newjoy2) +{ + if (oldjoy1 != newjoy1) { + joystick_open[0] = FALSE; + if (newjoy1) { + JOYINFO joyinfo; + if (joyGetPos(0, &joyinfo) == JOYERR_NOERROR) + joystick_open[0] = TRUE; + } + } + + if (oldjoy2 != newjoy2) { + joystick_open[1] = FALSE; + if (newjoy1) { + JOYINFO joyinfo; + if (joyGetPos(1, &joyinfo) == JOYERR_NOERROR) + joystick_open[1] = TRUE; + } + } + + // XXX: Should have our own new prefs! + state_change = TRUE; +} + + +/* + * Poll joystick port, return CIA mask + */ + +uint8 C64::poll_joystick(int port) +{ + uint8 j = 0xff; + + if (joystick_open[port]) { + JOYINFO joyinfo; + if (joyGetPos(port, &joyinfo) == JOYERR_NOERROR) { + int x = joyinfo.wXpos; + int y = joyinfo.wYpos; + int buttons = joyinfo.wButtons; + int s1 = JOYSTICK_SENSITIVITY; + int s2 = 100 - JOYSTICK_SENSITIVITY; + if (x < JOYSTICK_MIN + s1*JOYSTICK_RANGE/100) + j &= 0xfb; // Left + else if (x > JOYSTICK_MIN + s2*JOYSTICK_RANGE/100) + j &= 0xf7; // Right + if (y < JOYSTICK_MIN + s1*JOYSTICK_RANGE/100) + j &= 0xfe; // Up + else if (y > JOYSTICK_MIN + s2*JOYSTICK_RANGE/100) + j &= 0xfd; // Down + if (buttons & 1) + j &= 0xef; // Button + if (buttons & 2) { + Pause(); + while (joyGetPos(port, &joyinfo) == JOYERR_NOERROR && (joyinfo.wButtons & 2)) + Sleep(100); + Resume(); + } + } + } + + return j; +} + +void C64::StartTimer() +{ + ref_time = timeGetTime(); + skipped_frames = 0; + frame = 0; + + if (!ThePrefs.LimitSpeed) { + timer_every = 0; + StopTimer(); + return; + } + timer_every = ThePrefs.SkipFrames; + + if (!timer_semaphore) { + timer_semaphore = CreateSemaphore(NULL, 0, 1, NULL); + if (!timer_semaphore) + Debug("CreateSemaphore failed\n"); + } + + if (!timer_id) { + + // Turn on high-resolution times and delays. + int resolution = FRAME_INTERVAL; + if (high_resolution_timer) { + timeBeginPeriod(1); + resolution = 0; + } + + timer_id = timeSetEvent(timer_every*FRAME_INTERVAL, resolution, StaticTimeProc, (DWORD) this, TIME_PERIODIC); + if (!timer_id) + Debug("timeSetEvent failed\n"); + } +} + +void C64::StopTimer() +{ + if (timer_semaphore) { + CloseHandle(timer_semaphore); + timer_semaphore = NULL; + } + if (timer_id) { + timeKillEvent(timer_id); + timer_id = NULL; + + // Turn off high-resolution delays. + if (high_resolution_timer) + timeEndPeriod(1); + } + +} + +void CALLBACK C64::StaticTimeProc(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2) +{ + C64* TheC64 = (C64 *) dwUser; + TheC64->TimeProc(uID); +} + +void C64::TimeProc(UINT id) +{ + if (id != timer_id) { + Debug("TimeProc called for wrong timer id!\n"); + timeKillEvent(id); + return; + } + + if (!ReleaseSemaphore(timer_semaphore, 1, NULL)) + skipped_frames++; +} + + +/* + * The emulation's main loop + */ + +void C64::thread_func() +{ + Debug("C64::thread_func\n"); + + thread_running = TRUE; + + while (!quit_thyself) { + + if (have_a_break) + TheDisplay->WaitUntilActive(); + +#ifdef FRODO_SC + if (ThePrefs.Emul1541Proc) + EmulateCyclesWith1541(); + else + EmulateCyclesWithout1541(); + state_change = FALSE; +#else + // The order of calls is important here + int cycles = TheVIC->EmulateLine(); + TheSID->EmulateLine(); +#if !PRECISE_CIA_CYCLES + TheCIA1->EmulateLine(ThePrefs.CIACycles); + TheCIA2->EmulateLine(ThePrefs.CIACycles); +#endif + if (ThePrefs.Emul1541Proc) { + int cycles_1541 = ThePrefs.FloppyCycles; + TheCPU1541->CountVIATimers(cycles_1541); + + if (!TheCPU1541->Idle) { + // 1541 processor active, alternately execute + // 6502 and 6510 instructions until both have + // used up their cycles + while (cycles >= 0 || cycles_1541 >= 0) + if (cycles > cycles_1541) + cycles -= TheCPU->EmulateLine(1); + else + cycles_1541 -= TheCPU1541->EmulateLine(1); + } else + TheCPU->EmulateLine(cycles); + } else + // 1541 processor disabled, only emulate 6510 + TheCPU->EmulateLine(cycles); +#endif + } + + thread_running = FALSE; + +} + +#ifdef FRODO_SC + +void C64::EmulateCyclesWith1541() +{ + thread_running = TRUE; + while (!state_change) { + // The order of calls is important here + if (TheVIC->EmulateCycle()) + TheSID->EmulateLine(); +#ifndef BATCH_CIA_CYCLES + TheCIA1->EmulateCycle(); + TheCIA2->EmulateCycle(); +#endif + TheCPU->EmulateCycle(); + TheCPU1541->CountVIATimers(1); + if (!TheCPU1541->Idle) + TheCPU1541->EmulateCycle(); + CycleCounter++; + } +} + +void C64::EmulateCyclesWithout1541() +{ + thread_running = TRUE; + while (!state_change) { + // The order of calls is important here + if (TheVIC->EmulateCycle()) + TheSID->EmulateLine(); +#ifndef BATCH_CIA_CYCLES + TheCIA1->EmulateCycle(); + TheCIA2->EmulateCycle(); +#endif + TheCPU->EmulateCycle(); + CycleCounter++; + } +} + +#endif diff --git a/Src/C64_x.h b/Src/C64_x.h new file mode 100644 index 0000000..b15378a --- /dev/null +++ b/Src/C64_x.h @@ -0,0 +1,378 @@ +/* + * C64_x.h - Put the pieces together, X specific stuff + * + * Frodo (C) 1994-1997,2002-2005 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "main.h" + + +static struct timeval tv_start; + +#ifndef HAVE_USLEEP +/* + * NAME: + * usleep -- This is the precision timer for Test Set + * Automation. It uses the select(2) system + * call to delay for the desired number of + * micro-seconds. This call returns ZERO + * (which is usually ignored) on successful + * completion, -1 otherwise. + * + * ALGORITHM: + * 1) We range check the passed in microseconds and log a + * warning message if appropriate. We then return without + * delay, flagging an error. + * 2) Load the Seconds and micro-seconds portion of the + * interval timer structure. + * 3) Call select(2) with no file descriptors set, just the + * timer, this results in either delaying the proper + * ammount of time or being interupted early by a signal. + * + * HISTORY: + * Added when the need for a subsecond timer was evident. + * + * AUTHOR: + * Michael J. Dyer Telephone: AT&T 414.647.4044 + * General Electric Medical Systems GE DialComm 8 *767.4044 + * P.O. Box 414 Mail Stop 12-27 Sect'y AT&T 414.647.4584 + * Milwaukee, Wisconsin USA 53201 8 *767.4584 + * internet: mike@sherlock.med.ge.com GEMS WIZARD e-mail: DYER + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +int usleep(unsigned long int microSeconds) +{ + unsigned int Seconds, uSec; + int nfds, readfds, writefds, exceptfds; + struct timeval Timer; + + nfds = readfds = writefds = exceptfds = 0; + + if( (microSeconds == (unsigned long) 0) + || microSeconds > (unsigned long) 4000000 ) + { + errno = ERANGE; /* value out of range */ + perror( "usleep time out of range ( 0 -> 4000000 ) " ); + return -1; + } + + Seconds = microSeconds / (unsigned long) 1000000; + uSec = microSeconds % (unsigned long) 1000000; + + Timer.tv_sec = Seconds; + Timer.tv_usec = uSec; + + if( select( nfds, &readfds, &writefds, &exceptfds, &Timer ) < 0 ) + { + perror( "usleep (select) failed" ); + return -1; + } + + return 0; +} +#endif + +#ifdef __linux__ +// select() timing is much more accurate under Linux +static void Delay_usec(unsigned long usec) +{ + int was_error; + struct timeval tv; + + tv.tv_sec = 0; + tv.tv_usec = usec; + do { + errno = 0; + was_error = select(0, NULL, NULL, NULL, &tv); + } while (was_error && (errno == EINTR)); +} +#else +static void Delay_usec(unsigned long usec) +{ + usleep(usec); +} +#endif + + +/* + * Constructor, system-dependent things + */ + +void C64::c64_ctor1(void) +{ + // Initialize joystick variables + joy_minx[0] = joy_miny[0] = 32767; + joy_maxx[0] = joy_maxy[0] = -32768; + joy_minx[1] = joy_miny[1] = 32767; + joy_maxx[1] = joy_maxy[1] = -32768; + + // we need to create a potential GUI subprocess here, because we don't want + // it to inherit file-descriptors (such as for the audio-device and alike..) +#if defined(__svgalib__) + gui = 0; +#else + // try to start up Tk gui. + gui = new CmdPipe("wish", BINDIR "Frodo_GUI.tcl"); + if (gui) { + if (gui->fail) { + delete gui; gui = 0; + } + } + // wait until the GUI process responds (if it does...) + if (gui) { + if (5 != gui->ewrite("ping\n",5)) { + delete gui; gui = 0; + } else { + char c; + fd_set set; + FD_ZERO(&set); + FD_SET(gui->get_read_fd(), &set); + struct timeval tv; + tv.tv_usec = 0; + tv.tv_sec = 5; +// Use the following commented line for HP-UX < 10.20 +// if (select(FD_SETSIZE, (int *)&set, (int *)NULL, (int *)NULL, &tv) <= 0) { + if (select(FD_SETSIZE, &set, NULL, NULL, &tv) <= 0) { + delete gui; gui = 0; + } else { + if (1 != gui->eread(&c, 1)) { + delete gui; gui = 0; + } else { + if (c != 'o') { + delete gui; gui = 0; + } + } + } + } + } +#endif // __svgalib__ +} + +void C64::c64_ctor2(void) +{ +#ifndef __svgalib__ + if (!gui) { + fprintf(stderr,"Alas, master, no preferences window will be available.\n" + "If you wish to see one, make sure the 'wish' interpreter\n" + "(Tk version >= 4.1) is installed in your path.\n" + "You can still use Frodo, though. Use F10 to quit, \n" + "F11 to cause an NMI and F12 to reset the C64.\n" + "You can change the preferences by editing ~/.frodorc\n"); + } +#endif // SVGAlib + + gettimeofday(&tv_start, NULL); +} + + +/* + * Destructor, system-dependent things + */ + +void C64::c64_dtor(void) +{ +} + + +/* + * Start main emulation thread + */ + +void C64::Run(void) +{ + // Reset chips + TheCPU->Reset(); + TheSID->Reset(); + TheCIA1->Reset(); + TheCIA2->Reset(); + TheCPU1541->Reset(); + + // Patch kernal IEC routines + orig_kernal_1d84 = Kernal[0x1d84]; + orig_kernal_1d85 = Kernal[0x1d85]; + PatchKernal(ThePrefs.FastReset, ThePrefs.Emul1541Proc); + + quit_thyself = false; + thread_func(); +} + + +/* + * Vertical blank: Poll keyboard and joysticks, update window + */ + +void C64::VBlank(bool draw_frame) +{ + // Poll keyboard + TheDisplay->PollKeyboard(TheCIA1->KeyMatrix, TheCIA1->RevMatrix, &joykey); + if (TheDisplay->quit_requested) + quit_thyself = true; + + // Poll joysticks + TheCIA1->Joystick1 = poll_joystick(0); + TheCIA1->Joystick2 = poll_joystick(1); + + if (ThePrefs.JoystickSwap) { + uint8 tmp = TheCIA1->Joystick1; + TheCIA1->Joystick1 = TheCIA1->Joystick2; + TheCIA1->Joystick2 = tmp; + } + + // Joystick keyboard emulation + if (TheDisplay->NumLock()) + TheCIA1->Joystick1 &= joykey; + else + TheCIA1->Joystick2 &= joykey; + + // Count TOD clocks + TheCIA1->CountTOD(); + TheCIA2->CountTOD(); + + // Update window if needed + if (draw_frame) { + TheDisplay->Update(); + + // Calculate time between VBlanks, display speedometer + struct timeval tv; + gettimeofday(&tv, NULL); + if ((tv.tv_usec -= tv_start.tv_usec) < 0) { + tv.tv_usec += 1000000; + tv.tv_sec -= 1; + } + tv.tv_sec -= tv_start.tv_sec; + double elapsed_time = (double)tv.tv_sec * 1000000 + tv.tv_usec; + speed_index = 20000 / (elapsed_time + 1) * ThePrefs.SkipFrames * 100; + + // Limit speed to 100% if desired + if ((speed_index > 100) && ThePrefs.LimitSpeed) { + Delay_usec((unsigned long)(ThePrefs.SkipFrames * 20000 - elapsed_time)); + speed_index = 100; + } + + gettimeofday(&tv_start, NULL); + + TheDisplay->Speedometer((int)speed_index); + } +} + + +/* + * The emulation's main loop + */ + +void C64::thread_func(void) +{ + int linecnt = 0; + +#ifdef FRODO_SC + while (!quit_thyself) { + + // The order of calls is important here + if (TheVIC->EmulateCycle()) + TheSID->EmulateLine(); + TheCIA1->CheckIRQs(); + TheCIA2->CheckIRQs(); + TheCIA1->EmulateCycle(); + TheCIA2->EmulateCycle(); + TheCPU->EmulateCycle(); + + if (ThePrefs.Emul1541Proc) { + TheCPU1541->CountVIATimers(1); + if (!TheCPU1541->Idle) + TheCPU1541->EmulateCycle(); + } + CycleCounter++; +#else + while (!quit_thyself) { + + // The order of calls is important here + int cycles = TheVIC->EmulateLine(); + TheSID->EmulateLine(); +#if !PRECISE_CIA_CYCLES + TheCIA1->EmulateLine(ThePrefs.CIACycles); + TheCIA2->EmulateLine(ThePrefs.CIACycles); +#endif + + if (ThePrefs.Emul1541Proc) { + int cycles_1541 = ThePrefs.FloppyCycles; + TheCPU1541->CountVIATimers(cycles_1541); + + if (!TheCPU1541->Idle) { + // 1541 processor active, alternately execute + // 6502 and 6510 instructions until both have + // used up their cycles + while (cycles >= 0 || cycles_1541 >= 0) + if (cycles > cycles_1541) + cycles -= TheCPU->EmulateLine(1); + else + cycles_1541 -= TheCPU1541->EmulateLine(1); + } else + TheCPU->EmulateLine(cycles); + } else + // 1541 processor disabled, only emulate 6510 + TheCPU->EmulateLine(cycles); +#endif + linecnt++; +#if !defined(__svgalib__) + if ((linecnt & 0xfff) == 0 && gui) { + + // check for command from GUI process + // fprintf(stderr,":"); + while (gui->probe()) { + char c; + if (gui->eread(&c, 1) != 1) { + delete gui; + gui = 0; + fprintf(stderr,"Oops, GUI process died...\n"); + } else { + // fprintf(stderr,"%c",c); + switch (c) { + case 'q': + quit_thyself = true; + break; + case 'r': + Reset(); + break; + case 'p':{ + Prefs *np = Frodo::reload_prefs(); + NewPrefs(np); + ThePrefs = *np; + break; + } + default: + break; + } + } + } + } +#endif + } +#if !defined(__svgalib__) + if (gui) { + gui->ewrite("quit\n",5); + } +#endif +} diff --git a/Src/CIA.cpp b/Src/CIA.cpp new file mode 100644 index 0000000..2b79ec5 --- /dev/null +++ b/Src/CIA.cpp @@ -0,0 +1,570 @@ +/* + * CIA.cpp - 6526 emulation + * + * Frodo (C) 1994-1997,2002-2005 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * Notes: + * ------ + * + * - The EmulateLine() function is called for every emulated raster + * line. It counts down the timers and triggers interrupts if + * necessary. + * - The TOD clocks are counted by CountTOD() during the VBlank, so + * the input frequency is 50Hz + * - The fields KeyMatrix and RevMatrix contain one bit for each + * key on the C64 keyboard (0: key pressed, 1: key released). + * KeyMatrix is used for normal keyboard polling (PRA->PRB), + * RevMatrix for reversed polling (PRB->PRA). + * + * Incompatibilities: + * ------------------ + * + * - The TOD clock should not be stopped on a read access, but + * latched + * - The SDR interrupt is faked + */ + +#include "sysdeps.h" + +#include "CIA.h" +#include "CPUC64.h" +#include "CPU1541.h" +#include "VIC.h" +#include "Prefs.h" + + +/* + * Constructors + */ + +MOS6526::MOS6526(MOS6510 *CPU) : the_cpu(CPU) {} +MOS6526_1::MOS6526_1(MOS6510 *CPU, MOS6569 *VIC) : MOS6526(CPU), the_vic(VIC) {} +MOS6526_2::MOS6526_2(MOS6510 *CPU, MOS6569 *VIC, MOS6502_1541 *CPU1541) : MOS6526(CPU), the_vic(VIC), the_cpu_1541(CPU1541) {} + + +/* + * Reset the CIA + */ + +void MOS6526::Reset(void) +{ + pra = prb = ddra = ddrb = 0; + + ta = tb = 0xffff; + latcha = latchb = 1; + + tod_10ths = tod_sec = tod_min = tod_hr = 0; + alm_10ths = alm_sec = alm_min = alm_hr = 0; + + sdr = icr = cra = crb = int_mask = 0; + + tod_halt = ta_cnt_phi2 = tb_cnt_phi2 = tb_cnt_ta = false; + tod_divider = 0; +} + +void MOS6526_1::Reset(void) +{ + MOS6526::Reset(); + + // Clear keyboard matrix and joystick states + for (int i=0; i<8; i++) + KeyMatrix[i] = RevMatrix[i] = 0xff; + + Joystick1 = Joystick2 = 0xff; + prev_lp = 0x10; +} + +void MOS6526_2::Reset(void) +{ + MOS6526::Reset(); + + // VA14/15 = 0 + the_vic->ChangedVA(0); + + // IEC + IECLines = 0xd0; +} + + +/* + * Get CIA state + */ + +void MOS6526::GetState(MOS6526State *cs) +{ + cs->pra = pra; + cs->prb = prb; + cs->ddra = ddra; + cs->ddrb = ddrb; + + cs->ta_lo = ta & 0xff; + cs->ta_hi = ta >> 8; + cs->tb_lo = tb & 0xff; + cs->tb_hi = tb >> 8; + cs->latcha = latcha; + cs->latchb = latchb; + cs->cra = cra; + cs->crb = crb; + + cs->tod_10ths = tod_10ths; + cs->tod_sec = tod_sec; + cs->tod_min = tod_min; + cs->tod_hr = tod_hr; + cs->alm_10ths = alm_10ths; + cs->alm_sec = alm_sec; + cs->alm_min = alm_min; + cs->alm_hr = alm_hr; + + cs->sdr = sdr; + + cs->int_data = icr; + cs->int_mask = int_mask; +} + + +/* + * Restore CIA state + */ + +void MOS6526::SetState(MOS6526State *cs) +{ + pra = cs->pra; + prb = cs->prb; + ddra = cs->ddra; + ddrb = cs->ddrb; + + ta = (cs->ta_hi << 8) | cs->ta_lo; + tb = (cs->tb_hi << 8) | cs->tb_lo; + latcha = cs->latcha; + latchb = cs->latchb; + cra = cs->cra; + crb = cs->crb; + + tod_10ths = cs->tod_10ths; + tod_sec = cs->tod_sec; + tod_min = cs->tod_min; + tod_hr = cs->tod_hr; + alm_10ths = cs->alm_10ths; + alm_sec = cs->alm_sec; + alm_min = cs->alm_min; + alm_hr = cs->alm_hr; + + sdr = cs->sdr; + + icr = cs->int_data; + int_mask = cs->int_mask; + + tod_halt = false; + ta_cnt_phi2 = ((cra & 0x21) == 0x01); + tb_cnt_phi2 = ((crb & 0x61) == 0x01); + tb_cnt_ta = ((crb & 0x61) == 0x41); +} + + +/* + * Read from register (CIA 1) + */ + +uint8 MOS6526_1::ReadRegister(uint16 adr) +{ + switch (adr) { + case 0x00: { + uint8 ret = pra | ~ddra, tst = (prb | ~ddrb) & Joystick1; + if (!(tst & 0x01)) ret &= RevMatrix[0]; // AND all active columns + if (!(tst & 0x02)) ret &= RevMatrix[1]; + if (!(tst & 0x04)) ret &= RevMatrix[2]; + if (!(tst & 0x08)) ret &= RevMatrix[3]; + if (!(tst & 0x10)) ret &= RevMatrix[4]; + if (!(tst & 0x20)) ret &= RevMatrix[5]; + if (!(tst & 0x40)) ret &= RevMatrix[6]; + if (!(tst & 0x80)) ret &= RevMatrix[7]; + return ret & Joystick2; + } + case 0x01: { + uint8 ret = ~ddrb, tst = (pra | ~ddra) & Joystick2; + if (!(tst & 0x01)) ret &= KeyMatrix[0]; // AND all active rows + if (!(tst & 0x02)) ret &= KeyMatrix[1]; + if (!(tst & 0x04)) ret &= KeyMatrix[2]; + if (!(tst & 0x08)) ret &= KeyMatrix[3]; + if (!(tst & 0x10)) ret &= KeyMatrix[4]; + if (!(tst & 0x20)) ret &= KeyMatrix[5]; + if (!(tst & 0x40)) ret &= KeyMatrix[6]; + if (!(tst & 0x80)) ret &= KeyMatrix[7]; + return (ret | (prb & ddrb)) & Joystick1; + } + case 0x02: return ddra; + case 0x03: return ddrb; + case 0x04: return ta; + case 0x05: return ta >> 8; + case 0x06: return tb; + case 0x07: return tb >> 8; + case 0x08: tod_halt = false; return tod_10ths; + case 0x09: return tod_sec; + case 0x0a: return tod_min; + case 0x0b: tod_halt = true; return tod_hr; + case 0x0c: return sdr; + case 0x0d: { + uint8 ret = icr; // Read and clear ICR + icr = 0; + the_cpu->ClearCIAIRQ(); // Clear IRQ + return ret; + } + case 0x0e: return cra; + case 0x0f: return crb; + } + return 0; // Can't happen +} + + +/* + * Read from register (CIA 2) + */ + +uint8 MOS6526_2::ReadRegister(uint16 adr) +{ + switch (adr) { + case 0x00: + return (pra | ~ddra) & 0x3f + | IECLines & the_cpu_1541->IECLines; + case 0x01: return prb | ~ddrb; + case 0x02: return ddra; + case 0x03: return ddrb; + case 0x04: return ta; + case 0x05: return ta >> 8; + case 0x06: return tb; + case 0x07: return tb >> 8; + case 0x08: tod_halt = false; return tod_10ths; + case 0x09: return tod_sec; + case 0x0a: return tod_min; + case 0x0b: tod_halt = true; return tod_hr; + case 0x0c: return sdr; + case 0x0d: { + uint8 ret = icr; // Read and clear ICR + icr = 0; + the_cpu->ClearNMI(); // Clear NMI + return ret; + } + case 0x0e: return cra; + case 0x0f: return crb; + } + return 0; // Can't happen +} + + +/* + * Write to register (CIA 1) + */ + +// Write to port B, check for lightpen interrupt +inline void MOS6526_1::check_lp(void) +{ + if ((prb | ~ddrb) & 0x10 != prev_lp) + the_vic->TriggerLightpen(); + prev_lp = (prb | ~ddrb) & 0x10; +} + +void MOS6526_1::WriteRegister(uint16 adr, uint8 byte) +{ + switch (adr) { + case 0x0: pra = byte; break; + case 0x1: + prb = byte; + check_lp(); + break; + case 0x2: ddra = byte; break; + case 0x3: + ddrb = byte; + check_lp(); + break; + + case 0x4: latcha = (latcha & 0xff00) | byte; break; + case 0x5: + latcha = (latcha & 0xff) | (byte << 8); + if (!(cra & 1)) // Reload timer if stopped + ta = latcha; + break; + + case 0x6: latchb = (latchb & 0xff00) | byte; break; + case 0x7: + latchb = (latchb & 0xff) | (byte << 8); + if (!(crb & 1)) // Reload timer if stopped + tb = latchb; + break; + + case 0x8: + if (crb & 0x80) + alm_10ths = byte & 0x0f; + else + tod_10ths = byte & 0x0f; + break; + case 0x9: + if (crb & 0x80) + alm_sec = byte & 0x7f; + else + tod_sec = byte & 0x7f; + break; + case 0xa: + if (crb & 0x80) + alm_min = byte & 0x7f; + else + tod_min = byte & 0x7f; + break; + case 0xb: + if (crb & 0x80) + alm_hr = byte & 0x9f; + else + tod_hr = byte & 0x9f; + break; + + case 0xc: + sdr = byte; + TriggerInterrupt(8); // Fake SDR interrupt for programs that need it + break; + + case 0xd: + if (ThePrefs.CIAIRQHack) // Hack for addressing modes that read from the address + icr = 0; + if (byte & 0x80) { + int_mask |= byte & 0x7f; + if (icr & int_mask & 0x1f) { // Trigger IRQ if pending + icr |= 0x80; + the_cpu->TriggerCIAIRQ(); + } + } else + int_mask &= ~byte; + break; + + case 0xe: + cra = byte & 0xef; + if (byte & 0x10) // Force load + ta = latcha; + ta_cnt_phi2 = ((byte & 0x21) == 0x01); + break; + + case 0xf: + crb = byte & 0xef; + if (byte & 0x10) // Force load + tb = latchb; + tb_cnt_phi2 = ((byte & 0x61) == 0x01); + tb_cnt_ta = ((byte & 0x61) == 0x41); + break; + } +} + + +/* + * Write to register (CIA 2) + */ + +void MOS6526_2::WriteRegister(uint16 adr, uint8 byte) +{ + switch (adr) { + case 0x0:{ + pra = byte; + byte = ~pra & ddra; + the_vic->ChangedVA(byte & 3); + uint8 old_lines = IECLines; + IECLines = (byte << 2) & 0x80 // DATA + | (byte << 2) & 0x40 // CLK + | (byte << 1) & 0x10; // ATN + if ((IECLines ^ old_lines) & 0x10) { // ATN changed + the_cpu_1541->NewATNState(); + if (old_lines & 0x10) // ATN 1->0 + the_cpu_1541->IECInterrupt(); + } + break; + } + case 0x1: prb = byte; break; + + case 0x2: + ddra = byte; + the_vic->ChangedVA(~(pra | ~ddra) & 3); + break; + case 0x3: ddrb = byte; break; + + case 0x4: latcha = (latcha & 0xff00) | byte; break; + case 0x5: + latcha = (latcha & 0xff) | (byte << 8); + if (!(cra & 1)) // Reload timer if stopped + ta = latcha; + break; + + case 0x6: latchb = (latchb & 0xff00) | byte; break; + case 0x7: + latchb = (latchb & 0xff) | (byte << 8); + if (!(crb & 1)) // Reload timer if stopped + tb = latchb; + break; + + case 0x8: + if (crb & 0x80) + alm_10ths = byte & 0x0f; + else + tod_10ths = byte & 0x0f; + break; + case 0x9: + if (crb & 0x80) + alm_sec = byte & 0x7f; + else + tod_sec = byte & 0x7f; + break; + case 0xa: + if (crb & 0x80) + alm_min = byte & 0x7f; + else + tod_min = byte & 0x7f; + break; + case 0xb: + if (crb & 0x80) + alm_hr = byte & 0x9f; + else + tod_hr = byte & 0x9f; + break; + + case 0xc: + sdr = byte; + TriggerInterrupt(8); // Fake SDR interrupt for programs that need it + break; + + case 0xd: + if (ThePrefs.CIAIRQHack) + icr = 0; + if (byte & 0x80) { + int_mask |= byte & 0x7f; + if (icr & int_mask & 0x1f) { // Trigger NMI if pending + icr |= 0x80; + the_cpu->TriggerNMI(); + } + } else + int_mask &= ~byte; + break; + + case 0xe: + cra = byte & 0xef; + if (byte & 0x10) // Force load + ta = latcha; + ta_cnt_phi2 = ((byte & 0x21) == 0x01); + break; + + case 0xf: + crb = byte & 0xef; + if (byte & 0x10) // Force load + tb = latchb; + tb_cnt_phi2 = ((byte & 0x61) == 0x01); + tb_cnt_ta = ((byte & 0x61) == 0x41); + break; + } +} + + +/* + * Count CIA TOD clock (called during VBlank) + */ + +void MOS6526::CountTOD(void) +{ + uint8 lo, hi; + + // Decrement frequency divider + if (tod_divider) + tod_divider--; + else { + + // Reload divider according to 50/60 Hz flag + if (cra & 0x80) + tod_divider = 4; + else + tod_divider = 5; + + // 1/10 seconds + tod_10ths++; + if (tod_10ths > 9) { + tod_10ths = 0; + + // Seconds + lo = (tod_sec & 0x0f) + 1; + hi = tod_sec >> 4; + if (lo > 9) { + lo = 0; + hi++; + } + if (hi > 5) { + tod_sec = 0; + + // Minutes + lo = (tod_min & 0x0f) + 1; + hi = tod_min >> 4; + if (lo > 9) { + lo = 0; + hi++; + } + if (hi > 5) { + tod_min = 0; + + // Hours + lo = (tod_hr & 0x0f) + 1; + hi = (tod_hr >> 4) & 1; + tod_hr &= 0x80; // Keep AM/PM flag + if (lo > 9) { + lo = 0; + hi++; + } + tod_hr |= (hi << 4) | lo; + if ((tod_hr & 0x1f) > 0x11) + tod_hr = tod_hr & 0x80 ^ 0x80; + } else + tod_min = (hi << 4) | lo; + } else + tod_sec = (hi << 4) | lo; + } + + // Alarm time reached? Trigger interrupt if enabled + if (tod_10ths == alm_10ths && tod_sec == alm_sec && + tod_min == alm_min && tod_hr == alm_hr) + TriggerInterrupt(4); + } +} + + +/* + * Trigger IRQ (CIA 1) + */ + +void MOS6526_1::TriggerInterrupt(int bit) +{ + icr |= bit; + if (int_mask & bit) { + icr |= 0x80; + the_cpu->TriggerCIAIRQ(); + } +} + + +/* + * Trigger NMI (CIA 2) + */ + +void MOS6526_2::TriggerInterrupt(int bit) +{ + icr |= bit; + if (int_mask & bit) { + icr |= 0x80; + the_cpu->TriggerNMI(); + } +} diff --git a/Src/CIA.h b/Src/CIA.h new file mode 100644 index 0000000..b8d2902 --- /dev/null +++ b/Src/CIA.h @@ -0,0 +1,210 @@ +/* + * CIA.h - 6526 emulation + * + * Frodo (C) 1994-1997,2002-2005 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _CIA_H +#define _CIA_H + +#include "Prefs.h" + + +class MOS6510; +class MOS6502_1541; +class MOS6569; +struct MOS6526State; + + +class MOS6526 { +public: + MOS6526(MOS6510 *CPU); + + void Reset(void); + void GetState(MOS6526State *cs); + void SetState(MOS6526State *cs); +#ifdef FRODO_SC + void CheckIRQs(void); + void EmulateCycle(void); +#else + void EmulateLine(int cycles); +#endif + void CountTOD(void); + virtual void TriggerInterrupt(int bit)=0; + +protected: + MOS6510 *the_cpu; // Pointer to 6510 + + uint8 pra, prb, ddra, ddrb; + + uint16 ta, tb, latcha, latchb; + + uint8 tod_10ths, tod_sec, tod_min, tod_hr; + uint8 alm_10ths, alm_sec, alm_min, alm_hr; + + uint8 sdr, icr, cra, crb; + uint8 int_mask; + + int tod_divider; // TOD frequency divider + + bool tod_halt, // Flag: TOD halted + ta_cnt_phi2, // Flag: Timer A is counting Phi 2 + tb_cnt_phi2, // Flag: Timer B is counting Phi 2 + tb_cnt_ta; // Flag: Timer B is counting underflows of Timer A + +#ifdef FRODO_SC + bool ta_irq_next_cycle, // Flag: Trigger TA IRQ in next cycle + tb_irq_next_cycle, // Flag: Trigger TB IRQ in next cycle + has_new_cra, // Flag: New value for CRA pending + has_new_crb; // Flag: New value for CRB pending + char ta_state, tb_state; // Timer A/B states + uint8 new_cra, new_crb; // New values for CRA/CRB +#endif +}; + + +class MOS6526_1 : public MOS6526 { +public: + MOS6526_1(MOS6510 *CPU, MOS6569 *VIC); + + void Reset(void); + uint8 ReadRegister(uint16 adr); + void WriteRegister(uint16 adr, uint8 byte); + virtual void TriggerInterrupt(int bit); + + uint8 KeyMatrix[8]; // C64 keyboard matrix, 1 bit/key (0: key down, 1: key up) + uint8 RevMatrix[8]; // Reversed keyboard matrix + + uint8 Joystick1; // Joystick 1 AND value + uint8 Joystick2; // Joystick 2 AND value + +private: + void check_lp(void); + + MOS6569 *the_vic; + + uint8 prev_lp; // Previous state of LP line (bit 4) +}; + + +class MOS6526_2 : public MOS6526{ +public: + MOS6526_2(MOS6510 *CPU, MOS6569 *VIC, MOS6502_1541 *CPU1541); + + void Reset(void); + uint8 ReadRegister(uint16 adr); + void WriteRegister(uint16 adr, uint8 byte); + virtual void TriggerInterrupt(int bit); + + uint8 IECLines; // State of IEC lines (bit 7 - DATA, bit 6 - CLK, bit 4 - ATN) + +private: + MOS6569 *the_vic; + MOS6502_1541 *the_cpu_1541; +}; + + +// CIA state +struct MOS6526State { + uint8 pra; + uint8 ddra; + uint8 prb; + uint8 ddrb; + uint8 ta_lo; + uint8 ta_hi; + uint8 tb_lo; + uint8 tb_hi; + uint8 tod_10ths; + uint8 tod_sec; + uint8 tod_min; + uint8 tod_hr; + uint8 sdr; + uint8 int_data; // Pending interrupts + uint8 cra; + uint8 crb; + // Additional registers + uint16 latcha; // Timer latches + uint16 latchb; + uint8 alm_10ths; // Alarm time + uint8 alm_sec; + uint8 alm_min; + uint8 alm_hr; + uint8 int_mask; // Enabled interrupts +}; + + +/* + * Emulate CIA for one cycle/raster line + */ + +#ifdef FRODO_SC +inline void MOS6526::CheckIRQs(void) +{ + // Trigger pending interrupts + if (ta_irq_next_cycle) { + ta_irq_next_cycle = false; + TriggerInterrupt(1); + } + if (tb_irq_next_cycle) { + tb_irq_next_cycle = false; + TriggerInterrupt(2); + } +} +#else +inline void MOS6526::EmulateLine(int cycles) +{ + unsigned long tmp; + + // Timer A + if (ta_cnt_phi2) { + ta = tmp = ta - cycles; // Decrement timer + + if (tmp > 0xffff) { // Underflow? + ta = latcha; // Reload timer + + if (cra & 8) { // One-shot? + cra &= 0xfe; + ta_cnt_phi2 = false; + } + TriggerInterrupt(1); + if (tb_cnt_ta) { // Timer B counting underflows of Timer A? + tb = tmp = tb - 1; // tmp = --tb doesn't work + if (tmp > 0xffff) goto tb_underflow; + } + } + } + + // Timer B + if (tb_cnt_phi2) { + tb = tmp = tb - cycles; // Decrement timer + + if (tmp > 0xffff) { // Underflow? +tb_underflow: + tb = latchb; + + if (crb & 8) { // One-shot? + crb &= 0xfe; + tb_cnt_phi2 = false; + tb_cnt_ta = false; + } + TriggerInterrupt(2); + } + } +} +#endif + +#endif diff --git a/Src/CIA_SC.cpp b/Src/CIA_SC.cpp new file mode 100644 index 0000000..556da04 --- /dev/null +++ b/Src/CIA_SC.cpp @@ -0,0 +1,777 @@ +/* + * CIA_SC.cpp - Single-cycle 6526 emulation + * + * Frodo (C) 1994-1997,2002-2009 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * Notes: + * ------ + * + * - The Emulate() function is called for every emulated Phi2 + * clock cycle. It counts down the timers and triggers + * interrupts if necessary. + * - The TOD clocks are counted by CountTOD() during the VBlank, so + * the input frequency is 50Hz + * - The fields KeyMatrix and RevMatrix contain one bit for each + * key on the C64 keyboard (0: key pressed, 1: key released). + * KeyMatrix is used for normal keyboard polling (PRA->PRB), + * RevMatrix for reversed polling (PRB->PRA). + * + * Incompatibilities: + * ------------------ + * + * - The TOD clock should not be stopped on a read access, but be + * latched + * - The SDR interrupt is faked + * - Some small incompatibilities with the timers + */ + +#include "sysdeps.h" + +#include "CIA.h" +#include "CPUC64.h" +#include "CPU1541.h" +#include "VIC.h" +#include "Prefs.h" + + +// Timer states +enum { + T_STOP, + T_WAIT_THEN_COUNT, + T_LOAD_THEN_STOP, + T_LOAD_THEN_COUNT, + T_LOAD_THEN_WAIT_THEN_COUNT, + T_COUNT, + T_COUNT_THEN_STOP +}; + + +/* + * Constructors + */ + +MOS6526::MOS6526(MOS6510 *CPU) : the_cpu(CPU) {} +MOS6526_1::MOS6526_1(MOS6510 *CPU, MOS6569 *VIC) : MOS6526(CPU), the_vic(VIC) {} +MOS6526_2::MOS6526_2(MOS6510 *CPU, MOS6569 *VIC, MOS6502_1541 *CPU1541) : MOS6526(CPU), the_vic(VIC), the_cpu_1541(CPU1541) {} + + +/* + * Reset the CIA + */ + +void MOS6526::Reset(void) +{ + pra = prb = ddra = ddrb = 0; + + ta = tb = 0xffff; + latcha = latchb = 1; + + tod_10ths = tod_sec = tod_min = tod_hr = 0; + alm_10ths = alm_sec = alm_min = alm_hr = 0; + + sdr = icr = cra = crb = int_mask = 0; + + tod_halt = false; + tod_divider = 0; + + ta_cnt_phi2 = tb_cnt_phi2 = tb_cnt_ta = false; + + ta_irq_next_cycle = tb_irq_next_cycle = false; + ta_state = tb_state = T_STOP; +} + +void MOS6526_1::Reset(void) +{ + MOS6526::Reset(); + + // Clear keyboard matrix and joystick states + for (int i=0; i<8; i++) + KeyMatrix[i] = RevMatrix[i] = 0xff; + + Joystick1 = Joystick2 = 0xff; + prev_lp = 0x10; +} + +void MOS6526_2::Reset(void) +{ + MOS6526::Reset(); + + // VA14/15 = 0 + the_vic->ChangedVA(0); + + // IEC + IECLines = 0xd0; +} + + +/* + * Get CIA state + */ + +void MOS6526::GetState(MOS6526State *cs) +{ + cs->pra = pra; + cs->prb = prb; + cs->ddra = ddra; + cs->ddrb = ddrb; + + cs->ta_lo = ta & 0xff; + cs->ta_hi = ta >> 8; + cs->tb_lo = tb & 0xff; + cs->tb_hi = tb >> 8; + cs->latcha = latcha; + cs->latchb = latchb; + cs->cra = cra; + cs->crb = crb; + + cs->tod_10ths = tod_10ths; + cs->tod_sec = tod_sec; + cs->tod_min = tod_min; + cs->tod_hr = tod_hr; + cs->alm_10ths = alm_10ths; + cs->alm_sec = alm_sec; + cs->alm_min = alm_min; + cs->alm_hr = alm_hr; + + cs->sdr = sdr; + + cs->int_data = icr; + cs->int_mask = int_mask; +} + + +/* + * Restore CIA state + */ + +void MOS6526::SetState(MOS6526State *cs) +{ + pra = cs->pra; + prb = cs->prb; + ddra = cs->ddra; + ddrb = cs->ddrb; + + ta = (cs->ta_hi << 8) | cs->ta_lo; + tb = (cs->tb_hi << 8) | cs->tb_lo; + latcha = cs->latcha; + latchb = cs->latchb; + cra = cs->cra; + crb = cs->crb; + + tod_10ths = cs->tod_10ths; + tod_sec = cs->tod_sec; + tod_min = cs->tod_min; + tod_hr = cs->tod_hr; + alm_10ths = cs->alm_10ths; + alm_sec = cs->alm_sec; + alm_min = cs->alm_min; + alm_hr = cs->alm_hr; + + sdr = cs->sdr; + + icr = cs->int_data; + int_mask = cs->int_mask; + + tod_halt = false; + ta_cnt_phi2 = ((cra & 0x20) == 0x00); + tb_cnt_phi2 = ((crb & 0x60) == 0x00); + tb_cnt_ta = ((crb & 0x60) == 0x40); + + ta_state = (cra & 1) ? T_COUNT : T_STOP; + tb_state = (crb & 1) ? T_COUNT : T_STOP; +} + + +/* + * Read from register (CIA 1) + */ + +uint8 MOS6526_1::ReadRegister(uint16 adr) +{ + switch (adr) { + case 0x00: { + uint8 ret = pra | ~ddra, tst = (prb | ~ddrb) & Joystick1; + if (!(tst & 0x01)) ret &= RevMatrix[0]; // AND all active columns + if (!(tst & 0x02)) ret &= RevMatrix[1]; + if (!(tst & 0x04)) ret &= RevMatrix[2]; + if (!(tst & 0x08)) ret &= RevMatrix[3]; + if (!(tst & 0x10)) ret &= RevMatrix[4]; + if (!(tst & 0x20)) ret &= RevMatrix[5]; + if (!(tst & 0x40)) ret &= RevMatrix[6]; + if (!(tst & 0x80)) ret &= RevMatrix[7]; + return ret & Joystick2; + } + case 0x01: { + uint8 ret = ~ddrb, tst = (pra | ~ddra) & Joystick2; + if (!(tst & 0x01)) ret &= KeyMatrix[0]; // AND all active rows + if (!(tst & 0x02)) ret &= KeyMatrix[1]; + if (!(tst & 0x04)) ret &= KeyMatrix[2]; + if (!(tst & 0x08)) ret &= KeyMatrix[3]; + if (!(tst & 0x10)) ret &= KeyMatrix[4]; + if (!(tst & 0x20)) ret &= KeyMatrix[5]; + if (!(tst & 0x40)) ret &= KeyMatrix[6]; + if (!(tst & 0x80)) ret &= KeyMatrix[7]; + return (ret | (prb & ddrb)) & Joystick1; + } + case 0x02: return ddra; + case 0x03: return ddrb; + case 0x04: return ta; + case 0x05: return ta >> 8; + case 0x06: return tb; + case 0x07: return tb >> 8; + case 0x08: tod_halt = false; return tod_10ths; + case 0x09: return tod_sec; + case 0x0a: return tod_min; + case 0x0b: tod_halt = true; return tod_hr; + case 0x0c: return sdr; + case 0x0d: { + uint8 ret = icr; // Read and clear ICR + icr = 0; + the_cpu->ClearCIAIRQ(); // Clear IRQ + return ret; + } + case 0x0e: return cra; + case 0x0f: return crb; + } + return 0; // Can't happen +} + + +/* + * Read from register (CIA 2) + */ + +uint8 MOS6526_2::ReadRegister(uint16 adr) +{ + switch (adr) { + case 0x00: + return (pra | ~ddra) & 0x3f + | IECLines & the_cpu_1541->IECLines; + case 0x01: return prb | ~ddrb; + case 0x02: return ddra; + case 0x03: return ddrb; + case 0x04: return ta; + case 0x05: return ta >> 8; + case 0x06: return tb; + case 0x07: return tb >> 8; + case 0x08: tod_halt = false; return tod_10ths; + case 0x09: return tod_sec; + case 0x0a: return tod_min; + case 0x0b: tod_halt = true; return tod_hr; + case 0x0c: return sdr; + case 0x0d: { + uint8 ret = icr; // Read and clear ICR + icr = 0; + the_cpu->ClearNMI(); + return ret; + } + case 0x0e: return cra; + case 0x0f: return crb; + } + return 0; // Can't happen +} + + +/* + * Write to register (CIA 1) + */ + +// Write to port B, check for lightpen interrupt +inline void MOS6526_1::check_lp(void) +{ + if ((prb | ~ddrb) & 0x10 != prev_lp) + the_vic->TriggerLightpen(); + prev_lp = (prb | ~ddrb) & 0x10; +} + +void MOS6526_1::WriteRegister(uint16 adr, uint8 byte) +{ + switch (adr) { + case 0x0: pra = byte; break; + case 0x1: + prb = byte; + check_lp(); + break; + case 0x2: ddra = byte; break; + case 0x3: + ddrb = byte; + check_lp(); + break; + + case 0x4: latcha = (latcha & 0xff00) | byte; break; + case 0x5: + latcha = (latcha & 0xff) | (byte << 8); + if (!(cra & 1)) // Reload timer if stopped + ta = latcha; + break; + + case 0x6: latchb = (latchb & 0xff00) | byte; break; + case 0x7: + latchb = (latchb & 0xff) | (byte << 8); + if (!(crb & 1)) // Reload timer if stopped + tb = latchb; + break; + + case 0x8: + if (crb & 0x80) + alm_10ths = byte & 0x0f; + else + tod_10ths = byte & 0x0f; + break; + case 0x9: + if (crb & 0x80) + alm_sec = byte & 0x7f; + else + tod_sec = byte & 0x7f; + break; + case 0xa: + if (crb & 0x80) + alm_min = byte & 0x7f; + else + tod_min = byte & 0x7f; + break; + case 0xb: + if (crb & 0x80) + alm_hr = byte & 0x9f; + else + tod_hr = byte & 0x9f; + break; + + case 0xc: + sdr = byte; + TriggerInterrupt(8); // Fake SDR interrupt for programs that need it + break; + + case 0xd: + if (byte & 0x80) + int_mask |= byte & 0x7f; + else + int_mask &= ~byte; + if (icr & int_mask & 0x1f) { // Trigger IRQ if pending + icr |= 0x80; + the_cpu->TriggerCIAIRQ(); + } + break; + + case 0xe: + has_new_cra = true; // Delay write by 1 cycle + new_cra = byte; + ta_cnt_phi2 = ((byte & 0x20) == 0x00); + break; + + case 0xf: + has_new_crb = true; // Delay write by 1 cycle + new_crb = byte; + tb_cnt_phi2 = ((byte & 0x60) == 0x00); + tb_cnt_ta = ((byte & 0x60) == 0x40); + break; + } +} + + +/* + * Write to register (CIA 2) + */ + +void MOS6526_2::WriteRegister(uint16 adr, uint8 byte) +{ + switch (adr) { + case 0x0:{ + pra = byte; + the_vic->ChangedVA(~(pra | ~ddra) & 3); + uint8 old_lines = IECLines; + IECLines = (~byte << 2) & 0x80 // DATA + | (~byte << 2) & 0x40 // CLK + | (~byte << 1) & 0x10; // ATN + if ((IECLines ^ old_lines) & 0x10) { // ATN changed + the_cpu_1541->NewATNState(); + if (old_lines & 0x10) // ATN 1->0 + the_cpu_1541->IECInterrupt(); + } + break; + } + case 0x1: prb = byte; break; + + case 0x2: + ddra = byte; + the_vic->ChangedVA(~(pra | ~ddra) & 3); + break; + case 0x3: ddrb = byte; break; + + case 0x4: latcha = (latcha & 0xff00) | byte; break; + case 0x5: + latcha = (latcha & 0xff) | (byte << 8); + if (!(cra & 1)) // Reload timer if stopped + ta = latcha; + break; + + case 0x6: latchb = (latchb & 0xff00) | byte; break; + case 0x7: + latchb = (latchb & 0xff) | (byte << 8); + if (!(crb & 1)) // Reload timer if stopped + tb = latchb; + break; + + case 0x8: + if (crb & 0x80) + alm_10ths = byte & 0x0f; + else + tod_10ths = byte & 0x0f; + break; + case 0x9: + if (crb & 0x80) + alm_sec = byte & 0x7f; + else + tod_sec = byte & 0x7f; + break; + case 0xa: + if (crb & 0x80) + alm_min = byte & 0x7f; + else + tod_min = byte & 0x7f; + break; + case 0xb: + if (crb & 0x80) + alm_hr = byte & 0x9f; + else + tod_hr = byte & 0x9f; + break; + + case 0xc: + sdr = byte; + TriggerInterrupt(8); // Fake SDR interrupt for programs that need it + break; + + case 0xd: + if (byte & 0x80) + int_mask |= byte & 0x7f; + else + int_mask &= ~byte; + if (icr & int_mask & 0x1f) { // Trigger NMI if pending + icr |= 0x80; + the_cpu->TriggerNMI(); + } + break; + + case 0xe: + has_new_cra = true; // Delay write by 1 cycle + new_cra = byte; + ta_cnt_phi2 = ((byte & 0x20) == 0x00); + break; + + case 0xf: + has_new_crb = true; // Delay write by 1 cycle + new_crb = byte; + tb_cnt_phi2 = ((byte & 0x60) == 0x00); + tb_cnt_ta = ((byte & 0x60) == 0x40); + break; + } +} + + +/* + * Emulate CIA for one cycle/raster line + */ + +void MOS6526::EmulateCycle(void) +{ + bool ta_underflow = false; + + // Timer A state machine + switch (ta_state) { + case T_WAIT_THEN_COUNT: + ta_state = T_COUNT; // fall through + case T_STOP: + goto ta_idle; + case T_LOAD_THEN_STOP: + ta_state = T_STOP; + ta = latcha; // Reload timer + goto ta_idle; + case T_LOAD_THEN_COUNT: + ta_state = T_COUNT; + ta = latcha; // Reload timer + goto ta_idle; + case T_LOAD_THEN_WAIT_THEN_COUNT: + ta_state = T_WAIT_THEN_COUNT; + if (ta == 1) + goto ta_interrupt; // Interrupt if timer == 1 + else { + ta = latcha; // Reload timer + goto ta_idle; + } + case T_COUNT: + goto ta_count; + case T_COUNT_THEN_STOP: + ta_state = T_STOP; + goto ta_count; + } + + // Count timer A +ta_count: + if (ta_cnt_phi2) + if (!ta || !--ta) { // Decrement timer, underflow? + if (ta_state != T_STOP) { +ta_interrupt: + ta = latcha; // Reload timer + ta_irq_next_cycle = true; // Trigger interrupt in next cycle + icr |= 1; // But set ICR bit now + + if (cra & 8) { // One-shot? + cra &= 0xfe; // Yes, stop timer + new_cra &= 0xfe; + ta_state = T_LOAD_THEN_STOP; // Reload in next cycle + } else + ta_state = T_LOAD_THEN_COUNT; // No, delay one cycle (and reload) + } + ta_underflow = true; + } + + // Delayed write to CRA? +ta_idle: + if (has_new_cra) { + switch (ta_state) { + case T_STOP: + case T_LOAD_THEN_STOP: + if (new_cra & 1) { // Timer started, wasn't running + if (new_cra & 0x10) // Force load + ta_state = T_LOAD_THEN_WAIT_THEN_COUNT; + else // No force load + ta_state = T_WAIT_THEN_COUNT; + } else { // Timer stopped, was already stopped + if (new_cra & 0x10) // Force load + ta_state = T_LOAD_THEN_STOP; + } + break; + case T_COUNT: + if (new_cra & 1) { // Timer started, was already running + if (new_cra & 0x10) // Force load + ta_state = T_LOAD_THEN_WAIT_THEN_COUNT; + } else { // Timer stopped, was running + if (new_cra & 0x10) // Force load + ta_state = T_LOAD_THEN_STOP; + else // No force load + ta_state = T_COUNT_THEN_STOP; + } + break; + case T_LOAD_THEN_COUNT: + case T_WAIT_THEN_COUNT: + if (new_cra & 1) { + if (new_cra & 8) { // One-shot? + new_cra &= 0xfe; // Yes, stop timer + ta_state = T_STOP; + } else if (new_cra & 0x10) // Force load + ta_state = T_LOAD_THEN_WAIT_THEN_COUNT; + } else { + ta_state = T_STOP; + } + break; + } + cra = new_cra & 0xef; + has_new_cra = false; + } + + // Timer B state machine + switch (tb_state) { + case T_WAIT_THEN_COUNT: + tb_state = T_COUNT; // fall through + case T_STOP: + goto tb_idle; + case T_LOAD_THEN_STOP: + tb_state = T_STOP; + tb = latchb; // Reload timer + goto tb_idle; + case T_LOAD_THEN_COUNT: + tb_state = T_COUNT; + tb = latchb; // Reload timer + goto tb_idle; + case T_LOAD_THEN_WAIT_THEN_COUNT: + tb_state = T_WAIT_THEN_COUNT; + if (tb == 1) + goto tb_interrupt; // Interrupt if timer == 1 + else { + tb = latchb; // Reload timer + goto tb_idle; + } + case T_COUNT: + goto tb_count; + case T_COUNT_THEN_STOP: + tb_state = T_STOP; + goto tb_count; + } + + // Count timer B +tb_count: + if (tb_cnt_phi2 || (tb_cnt_ta && ta_underflow)) + if (!tb || !--tb) { // Decrement timer, underflow? + if (tb_state != T_STOP) { +tb_interrupt: + tb = latchb; // Reload timer + tb_irq_next_cycle = true; // Trigger interrupt in next cycle + icr |= 2; // But set ICR bit now + + if (crb & 8) { // One-shot? + crb &= 0xfe; // Yes, stop timer + new_crb &= 0xfe; + tb_state = T_LOAD_THEN_STOP; // Reload in next cycle + } else + tb_state = T_LOAD_THEN_COUNT; // No, delay one cycle (and reload) + } + } + + // Delayed write to CRB? +tb_idle: + if (has_new_crb) { + switch (tb_state) { + case T_STOP: + case T_LOAD_THEN_STOP: + if (new_crb & 1) { // Timer started, wasn't running + if (new_crb & 0x10) // Force load + tb_state = T_LOAD_THEN_WAIT_THEN_COUNT; + else // No force load + tb_state = T_WAIT_THEN_COUNT; + } else { // Timer stopped, was already stopped + if (new_crb & 0x10) // Force load + tb_state = T_LOAD_THEN_STOP; + } + break; + case T_COUNT: + if (new_crb & 1) { // Timer started, was already running + if (new_crb & 0x10) // Force load + tb_state = T_LOAD_THEN_WAIT_THEN_COUNT; + } else { // Timer stopped, was running + if (new_crb & 0x10) // Force load + tb_state = T_LOAD_THEN_STOP; + else // No force load + tb_state = T_COUNT_THEN_STOP; + } + break; + case T_LOAD_THEN_COUNT: + case T_WAIT_THEN_COUNT: + if (new_crb & 1) { + if (new_crb & 8) { // One-shot? + new_crb &= 0xfe; // Yes, stop timer + tb_state = T_STOP; + } else if (new_crb & 0x10) // Force load + tb_state = T_LOAD_THEN_WAIT_THEN_COUNT; + } else { + tb_state = T_STOP; + } + break; + } + crb = new_crb & 0xef; + has_new_crb = false; + } +} + + +/* + * Count CIA TOD clock (called during VBlank) + */ + +void MOS6526::CountTOD(void) +{ + uint8 lo, hi; + + // Decrement frequency divider + if (tod_divider) + tod_divider--; + else { + + // Reload divider according to 50/60 Hz flag + if (cra & 0x80) + tod_divider = 4; + else + tod_divider = 5; + + // 1/10 seconds + tod_10ths++; + if (tod_10ths > 9) { + tod_10ths = 0; + + // Seconds + lo = (tod_sec & 0x0f) + 1; + hi = tod_sec >> 4; + if (lo > 9) { + lo = 0; + hi++; + } + if (hi > 5) { + tod_sec = 0; + + // Minutes + lo = (tod_min & 0x0f) + 1; + hi = tod_min >> 4; + if (lo > 9) { + lo = 0; + hi++; + } + if (hi > 5) { + tod_min = 0; + + // Hours + lo = (tod_hr & 0x0f) + 1; + hi = (tod_hr >> 4) & 1; + tod_hr &= 0x80; // Keep AM/PM flag + if (lo > 9) { + lo = 0; + hi++; + } + tod_hr |= (hi << 4) | lo; + if ((tod_hr & 0x1f) > 0x11) + tod_hr = tod_hr & 0x80 ^ 0x80; + } else + tod_min = (hi << 4) | lo; + } else + tod_sec = (hi << 4) | lo; + } + + // Alarm time reached? Trigger interrupt if enabled + if (tod_10ths == alm_10ths && tod_sec == alm_sec && + tod_min == alm_min && tod_hr == alm_hr) + TriggerInterrupt(4); + } +} + + +/* + * Trigger IRQ (CIA 1) + */ + +void MOS6526_1::TriggerInterrupt(int bit) +{ + icr |= bit; + if (int_mask & bit) { + icr |= 0x80; + the_cpu->TriggerCIAIRQ(); + } +} + + +/* + * Trigger NMI (CIA 2) + */ + +void MOS6526_2::TriggerInterrupt(int bit) +{ + icr |= bit; + if (int_mask & bit) { + icr |= 0x80; + the_cpu->TriggerNMI(); + } +} diff --git a/Src/CPU1541.cpp b/Src/CPU1541.cpp new file mode 100644 index 0000000..8827a69 --- /dev/null +++ b/Src/CPU1541.cpp @@ -0,0 +1,786 @@ +/* + * CPU1541.cpp - 6502 (1541) emulation (line based) + * + * Frodo (C) 1994-1997,2002-2005 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * Notes: + * ------ + * + * - The EmulateLine() function is called for every emulated + * raster line. It has a cycle counter that is decremented + * by every executed opcode and if the counter goes below + * zero, the function returns. + * - Memory map (1541C, the 1541 and 1541-II are a bit different): + * $0000-$07ff RAM (2K) + * $0800-$0fff RAM mirror + * $1000-$17ff free + * $1800-$1bff VIA 1 + * $1c00-$1fff VIA 2 + * $2000-$bfff free + * $c000-$ffff ROM (16K) + * - All memory accesses are done with the read_byte() and + * write_byte() functions which also do the memory address + * decoding. The read_zp() and write_zp() functions allow + * faster access to the zero page, the pop_byte() and + * push_byte() macros for the stack. + * - The PC is either emulated with a 16 bit address or a + * direct memory pointer (for faster access), depending on + * the PC_IS_POINTER #define. In the latter case, a second + * pointer, pc_base, is kept to allow recalculating the + * 16 bit 6502 PC if it has to be pushed on the stack. + * - The possible interrupt sources are: + * INT_VIA1IRQ: I flag is checked, jump to ($fffe) (unused) + * INT_VIA2IRQ: I flag is checked, jump to ($fffe) (unused) + * INT_IECIRQ: I flag is checked, jump to ($fffe) (unused) + * INT_RESET: Jump to ($fffc) + * - Interrupts are not checked before every opcode but only + * at certain times: + * On entering EmulateLine() + * On CLI + * On PLP if the I flag was cleared + * On RTI if the I flag was cleared + * - The z_flag variable has the inverse meaning of the + * 6502 Z flag + * - Only the highest bit of the n_flag variable is used + * - The $f2 opcode that would normally crash the 6502 is + * used to implement emulator-specific functions + * - The 1541 6502 emulation also includes a very simple VIA + * emulation (enough to make the IEC bus and GCR loading work). + * It's too small to move it to a source file of its own. + * + * Incompatibilities: + * ------------------ + * + * - If PC_IS_POINTER is set, neither branches accross memory + * areas nor jumps to I/O space are possible + * - Extra cycles for crossing page boundaries are not + * accounted for + */ + +#include "sysdeps.h" + +#include "CPU1541.h" +#include "1541job.h" +#include "C64.h" +#include "CIA.h" +#include "Display.h" + + +enum { + INT_RESET = 3 +}; + + +/* + * 6502 constructor: Initialize registers + */ + +MOS6502_1541::MOS6502_1541(C64 *c64, Job1541 *job, C64Display *disp, uint8 *Ram, uint8 *Rom) + : ram(Ram), rom(Rom), the_c64(c64), the_display(disp), the_job(job) +{ + a = x = y = 0; + sp = 0xff; + n_flag = z_flag = 0; + v_flag = d_flag = c_flag = false; + i_flag = true; + + borrowed_cycles = 0; + + via1_t1c = via1_t1l = via1_t2c = via1_t2l = 0; + via1_sr = 0; + via2_t1c = via2_t1l = via2_t2c = via2_t2l = 0; + via2_sr = 0; + + Idle = false; +} + + +/* + * Reset CPU asynchronously + */ + +void MOS6502_1541::AsyncReset(void) +{ + interrupt.intr[INT_RESET] = true; + Idle = false; +} + + +/* + * Read a byte from I/O space + */ + +inline uint8 MOS6502_1541::read_byte_io(uint16 adr) +{ + if ((adr & 0xfc00) == 0x1800) // VIA 1 + switch (adr & 0xf) { + case 0: + return (via1_prb & 0x1a + | ((IECLines & TheCIA2->IECLines) >> 7) // DATA + | ((IECLines & TheCIA2->IECLines) >> 4) & 0x04 // CLK + | (TheCIA2->IECLines << 3) & 0x80) ^ 0x85; // ATN + case 1: + case 15: + return 0xff; // Keep 1541C ROMs happy (track 0 sensor) + case 2: + return via1_ddrb; + case 3: + return via1_ddra; + case 4: + via1_ifr &= 0xbf; + return via1_t1c; + case 5: + return via1_t1c >> 8; + case 6: + return via1_t1l; + case 7: + return via1_t1l >> 8; + case 8: + via1_ifr &= 0xdf; + return via1_t2c; + case 9: + return via1_t2c >> 8; + case 10: + return via1_sr; + case 11: + return via1_acr; + case 12: + return via1_pcr; + case 13: + return via1_ifr | (via1_ifr & via1_ier ? 0x80 : 0); + case 14: + return via1_ier | 0x80; + default: // Can't happen + return 0; + } + + else if ((adr & 0xfc00) == 0x1c00) // VIA 2 + switch (adr & 0xf) { + case 0: + if (the_job->SyncFound()) + return via2_prb & 0x7f | the_job->WPState(); + else + return via2_prb | 0x80 | the_job->WPState(); + case 1: + case 15: + return the_job->ReadGCRByte(); + case 2: + return via2_ddrb; + case 3: + return via2_ddra; + case 4: + via2_ifr &= 0xbf; + interrupt.intr[INT_VIA2IRQ] = false; // Clear job IRQ + return via2_t1c; + case 5: + return via2_t1c >> 8; + case 6: + return via2_t1l; + case 7: + return via2_t1l >> 8; + case 8: + via2_ifr &= 0xdf; + return via2_t2c; + case 9: + return via2_t2c >> 8; + case 10: + return via2_sr; + case 11: + return via2_acr; + case 12: + return via2_pcr; + case 13: + return via2_ifr | (via2_ifr & via2_ier ? 0x80 : 0); + case 14: + return via2_ier | 0x80; + default: // Can't happen + return 0; + } + + else + return adr >> 8; +} + + +/* + * Read a byte from the CPU's address space + */ + +uint8 MOS6502_1541::read_byte(uint16 adr) +{ + if (adr >= 0xc000) + return rom[adr & 0x3fff]; + else if (adr < 0x1000) + return ram[adr & 0x07ff]; + else + return read_byte_io(adr); +} + + +/* + * Read a word (little-endian) from the CPU's address space + */ + +inline uint16 MOS6502_1541::read_word(uint16 adr) +{ + return read_byte(adr) | (read_byte(adr+1) << 8); +} + + +/* + * Write a byte to I/O space + */ + +void MOS6502_1541::write_byte_io(uint16 adr, uint8 byte) +{ + if ((adr & 0xfc00) == 0x1800) // VIA 1 + switch (adr & 0xf) { + case 0: + via1_prb = byte; + byte = ~via1_prb & via1_ddrb; + IECLines = (byte << 6) & ((~byte ^ TheCIA2->IECLines) << 3) & 0x80 + | (byte << 3) & 0x40; + break; + case 1: + case 15: + via1_pra = byte; + break; + case 2: + via1_ddrb = byte; + byte &= ~via1_prb; + IECLines = (byte << 6) & ((~byte ^ TheCIA2->IECLines) << 3) & 0x80 + | (byte << 3) & 0x40; + break; + case 3: + via1_ddra = byte; + break; + case 4: + case 6: + via1_t1l = via1_t1l & 0xff00 | byte; + break; + case 5: + via1_t1l = via1_t1l & 0xff | (byte << 8); + via1_ifr &= 0xbf; + via1_t1c = via1_t1l; + break; + case 7: + via1_t1l = via1_t1l & 0xff | (byte << 8); + break; + case 8: + via1_t2l = via1_t2l & 0xff00 | byte; + break; + case 9: + via1_t2l = via1_t2l & 0xff | (byte << 8); + via1_ifr &= 0xdf; + via1_t2c = via1_t2l; + break; + case 10: + via1_sr = byte; + break; + case 11: + via1_acr = byte; + break; + case 12: + via1_pcr = byte; + break; + case 13: + via1_ifr &= ~byte; + break; + case 14: + if (byte & 0x80) + via1_ier |= byte & 0x7f; + else + via1_ier &= ~byte; + break; + } + + else if ((adr & 0xfc00) == 0x1c00) // VIA 2 + switch (adr & 0xf) { + case 0: + if ((via2_prb ^ byte) & 8) // Bit 3: Drive LED + the_display->UpdateLEDs(byte & 8 ? 1 : 0, 0, 0, 0); + if ((via2_prb ^ byte) & 3) // Bits 0/1: Stepper motor + if ((via2_prb & 3) == ((byte+1) & 3)) + the_job->MoveHeadOut(); + else if ((via2_prb & 3) == ((byte-1) & 3)) + the_job->MoveHeadIn(); + via2_prb = byte & 0xef; + break; + case 1: + case 15: + via2_pra = byte; + break; + case 2: + via2_ddrb = byte; + break; + case 3: + via2_ddra = byte; + break; + case 4: + case 6: + via2_t1l = via2_t1l & 0xff00 | byte; + break; + case 5: + via2_t1l = via2_t1l & 0xff | (byte << 8); + via2_ifr &= 0xbf; + via2_t1c = via2_t1l; + break; + case 7: + via2_t1l = via2_t1l & 0xff | (byte << 8); + break; + case 8: + via2_t2l = via2_t2l & 0xff00 | byte; + break; + case 9: + via2_t2l = via2_t2l & 0xff | (byte << 8); + via2_ifr &= 0xdf; + via2_t2c = via2_t2l; + break; + case 10: + via2_sr = byte; + break; + case 11: + via2_acr = byte; + break; + case 12: + via2_pcr = byte; + break; + case 13: + via2_ifr &= ~byte; + break; + case 14: + if (byte & 0x80) + via2_ier |= byte & 0x7f; + else + via2_ier &= ~byte; + break; + } +} + + +/* + * Write a byte to the CPU's address space + */ + +inline void MOS6502_1541::write_byte(uint16 adr, uint8 byte) +{ + if (adr < 0x1000) + ram[adr & 0x7ff] = byte; + else + write_byte_io(adr, byte); +} + + +/* + * Read a byte from the zeropage + */ + +inline uint8 MOS6502_1541::read_zp(uint16 adr) +{ + return ram[adr]; +} + + +/* + * Read a word (little-endian) from the zeropage + */ + +inline uint16 MOS6502_1541::read_zp_word(uint16 adr) +{ + return ram[adr & 0xff] | (ram[(adr+1) & 0xff] << 8); +} + + +/* + * Write a byte to the zeropage + */ + +inline void MOS6502_1541::write_zp(uint16 adr, uint8 byte) +{ + ram[adr] = byte; +} + + +/* + * Read byte from 6502/1541 address space (used by SAM) + */ + +uint8 MOS6502_1541::ExtReadByte(uint16 adr) +{ + return read_byte(adr); +} + + +/* + * Write byte to 6502/1541 address space (used by SAM) + */ + +void MOS6502_1541::ExtWriteByte(uint16 adr, uint8 byte) +{ + write_byte(adr, byte); +} + + +/* + * Jump to address + */ + +#if PC_IS_POINTER +void MOS6502_1541::jump(uint16 adr) +{ + if (adr >= 0xc000) { + pc = rom + (adr & 0x3fff); + pc_base = rom - 0xc000; + } else if (adr < 0x800) { + pc = ram + adr; + pc_base = ram; + } else + illegal_jump(pc-pc_base, adr); +} +#else +inline void MOS6502_1541::jump(uint16 adr) +{ + pc = adr; +} +#endif + + +/* + * Adc instruction + */ + +void MOS6502_1541::do_adc(uint8 byte) +{ + if (!d_flag) { + uint16 tmp; + + // Binary mode + tmp = a + byte + (c_flag ? 1 : 0); + c_flag = tmp > 0xff; + v_flag = !((a ^ byte) & 0x80) && ((a ^ tmp) & 0x80); + z_flag = n_flag = a = tmp; + + } else { + uint16 al, ah; + + // Decimal mode + al = (a & 0x0f) + (byte & 0x0f) + (c_flag ? 1 : 0); // Calculate lower nybble + if (al > 9) al += 6; // BCD fixup for lower nybble + + ah = (a >> 4) + (byte >> 4); // Calculate upper nybble + if (al > 0x0f) ah++; + + z_flag = a + byte + (c_flag ? 1 : 0); // Set flags + n_flag = ah << 4; // Only highest bit used + v_flag = (((ah << 4) ^ a) & 0x80) && !((a ^ byte) & 0x80); + + if (ah > 9) ah += 6; // BCD fixup for upper nybble + c_flag = ah > 0x0f; // Set carry flag + a = (ah << 4) | (al & 0x0f); // Compose result + } +} + + +/* + * Sbc instruction + */ + +void MOS6502_1541::do_sbc(uint8 byte) +{ + uint16 tmp = a - byte - (c_flag ? 0 : 1); + + if (!d_flag) { + + // Binary mode + c_flag = tmp < 0x100; + v_flag = ((a ^ tmp) & 0x80) && ((a ^ byte) & 0x80); + z_flag = n_flag = a = tmp; + + } else { + uint16 al, ah; + + // Decimal mode + al = (a & 0x0f) - (byte & 0x0f) - (c_flag ? 0 : 1); // Calculate lower nybble + ah = (a >> 4) - (byte >> 4); // Calculate upper nybble + if (al & 0x10) { + al -= 6; // BCD fixup for lower nybble + ah--; + } + if (ah & 0x10) ah -= 6; // BCD fixup for upper nybble + + c_flag = tmp < 0x100; // Set flags + v_flag = ((a ^ tmp) & 0x80) && ((a ^ byte) & 0x80); + z_flag = n_flag = tmp; + + a = (ah << 4) | (al & 0x0f); // Compose result + } +} + + +/* + * Get 6502 register state + */ + +void MOS6502_1541::GetState(MOS6502State *s) +{ + s->a = a; + s->x = x; + s->y = y; + + s->p = 0x20 | (n_flag & 0x80); + if (v_flag) s->p |= 0x40; + if (d_flag) s->p |= 0x08; + if (i_flag) s->p |= 0x04; + if (!z_flag) s->p |= 0x02; + if (c_flag) s->p |= 0x01; + +#if PC_IS_POINTER + s->pc = pc - pc_base; +#else + s->pc = pc; +#endif + s->sp = sp | 0x0100; + + s->intr[INT_VIA1IRQ] = interrupt.intr[INT_VIA1IRQ]; + s->intr[INT_VIA2IRQ] = interrupt.intr[INT_VIA2IRQ]; + s->intr[INT_IECIRQ] = interrupt.intr[INT_IECIRQ]; + s->intr[INT_RESET] = interrupt.intr[INT_RESET]; + s->instruction_complete = true; + s->idle = Idle; + + s->via1_pra = via1_pra; s->via1_ddra = via1_ddra; + s->via1_prb = via1_prb; s->via1_ddrb = via1_ddrb; + s->via1_t1c = via1_t1c; s->via1_t1l = via1_t1l; + s->via1_t2c = via1_t2c; s->via1_t2l = via1_t2l; + s->via1_sr = via1_sr; + s->via1_acr = via1_acr; s->via1_pcr = via1_pcr; + s->via1_ifr = via1_ifr; s->via1_ier = via1_ier; + + s->via2_pra = via2_pra; s->via2_ddra = via2_ddra; + s->via2_prb = via2_prb; s->via2_ddrb = via2_ddrb; + s->via2_t1c = via2_t1c; s->via2_t1l = via2_t1l; + s->via2_t2c = via2_t2c; s->via2_t2l = via2_t2l; + s->via2_sr = via2_sr; + s->via2_acr = via2_acr; s->via2_pcr = via2_pcr; + s->via2_ifr = via2_ifr; s->via2_ier = via2_ier; +} + + +/* + * Restore 6502 state + */ + +void MOS6502_1541::SetState(MOS6502State *s) +{ + a = s->a; + x = s->x; + y = s->y; + + n_flag = s->p; + v_flag = s->p & 0x40; + d_flag = s->p & 0x08; + i_flag = s->p & 0x04; + z_flag = !(s->p & 0x02); + c_flag = s->p & 0x01; + + jump(s->pc); + sp = s->sp & 0xff; + + interrupt.intr[INT_VIA1IRQ] = s->intr[INT_VIA1IRQ]; + interrupt.intr[INT_VIA2IRQ] = s->intr[INT_VIA2IRQ]; + interrupt.intr[INT_IECIRQ] = s->intr[INT_IECIRQ]; + interrupt.intr[INT_RESET] = s->intr[INT_RESET]; + Idle = s->idle; + + via1_pra = s->via1_pra; via1_ddra = s->via1_ddra; + via1_prb = s->via1_prb; via1_ddrb = s->via1_ddrb; + via1_t1c = s->via1_t1c; via1_t1l = s->via1_t1l; + via1_t2c = s->via1_t2c; via1_t2l = s->via1_t2l; + via1_sr = s->via1_sr; + via1_acr = s->via1_acr; via1_pcr = s->via1_pcr; + via1_ifr = s->via1_ifr; via1_ier = s->via1_ier; + + via2_pra = s->via2_pra; via2_ddra = s->via2_ddra; + via2_prb = s->via2_prb; via2_ddrb = s->via2_ddrb; + via2_t1c = s->via2_t1c; via2_t1l = s->via2_t1l; + via2_t2c = s->via2_t2c; via2_t2l = s->via2_t2l; + via2_sr = s->via2_sr; + via2_acr = s->via2_acr; via2_pcr = s->via2_pcr; + via2_ifr = s->via2_ifr; via2_ier = s->via2_ier; +} + + +/* + * Reset CPU + */ + +void MOS6502_1541::Reset(void) +{ + // IEC lines and VIA registers + IECLines = 0xc0; + + via1_pra = via1_ddra = via1_prb = via1_ddrb = 0; + via1_acr = via1_pcr = 0; + via1_ifr = via1_ier = 0; + via2_pra = via2_ddra = via2_prb = via2_ddrb = 0; + via2_acr = via2_pcr = 0; + via2_ifr = via2_ier = 0; + + // Clear all interrupt lines + interrupt.intr_any = 0; + + // Read reset vector + jump(read_word(0xfffc)); + + // Wake up 1541 + Idle = false; +} + + +/* + * Illegal opcode encountered + */ + +void MOS6502_1541::illegal_op(uint8 op, uint16 at) +{ + char illop_msg[80]; + + sprintf(illop_msg, "1541: Illegal opcode %02x at %04x.", op, at); + if (ShowRequester(illop_msg, "Reset 1541", "Reset C64")) + the_c64->Reset(); + Reset(); +} + + +/* + * Jump to illegal address space (PC_IS_POINTER only) + */ + +void MOS6502_1541::illegal_jump(uint16 at, uint16 to) +{ + char illop_msg[80]; + + sprintf(illop_msg, "1541: Jump to I/O space at %04x to %04x.", at, to); + if (ShowRequester(illop_msg, "Reset 1541", "Reset C64")) + the_c64->Reset(); + Reset(); +} + + +/* + * Stack macros + */ + +// Pop a byte from the stack +#define pop_byte() ram[(++sp) | 0x0100] + +// Push a byte onto the stack +#define push_byte(byte) (ram[(sp--) & 0xff | 0x0100] = (byte)) + +// Pop processor flags from the stack +#define pop_flags() \ + n_flag = tmp = pop_byte(); \ + v_flag = tmp & 0x40; \ + d_flag = tmp & 0x08; \ + i_flag = tmp & 0x04; \ + z_flag = !(tmp & 0x02); \ + c_flag = tmp & 0x01; + +// Push processor flags onto the stack +#define push_flags(b_flag) \ + tmp = 0x20 | (n_flag & 0x80); \ + if (v_flag) tmp |= 0x40; \ + if (b_flag) tmp |= 0x10; \ + if (d_flag) tmp |= 0x08; \ + if (i_flag) tmp |= 0x04; \ + if (!z_flag) tmp |= 0x02; \ + if (c_flag) tmp |= 0x01; \ + push_byte(tmp); + + +/* + * Emulate cycles_left worth of 6502 instructions + * Returns number of cycles of last instruction + */ + +int MOS6502_1541::EmulateLine(int cycles_left) +{ + uint8 tmp, tmp2; + uint16 adr; + int last_cycles = 0; + + // Any pending interrupts? + if (interrupt.intr_any) { +handle_int: + if (interrupt.intr[INT_RESET]) + Reset(); + + else if ((interrupt.intr[INT_VIA1IRQ] || interrupt.intr[INT_VIA2IRQ] || interrupt.intr[INT_IECIRQ]) && !i_flag) { +#if PC_IS_POINTER + push_byte((pc-pc_base) >> 8); push_byte(pc-pc_base); +#else + push_byte(pc >> 8); push_byte(pc); +#endif + push_flags(false); + i_flag = true; + jump(read_word(0xfffe)); + last_cycles = 7; + } + } + +#define IS_CPU_1541 +#include "CPU_emulline.h" + + // Extension opcode + case 0xf2: +#if PC_IS_POINTER + if ((pc-pc_base) < 0xc000) { + illegal_op(0xf2, pc-pc_base-1); +#else + if (pc < 0xc000) { + illegal_op(0xf2, pc-1); +#endif + break; + } + switch (read_byte_imm()) { + case 0x00: // Go to sleep in DOS idle loop if error flag is clear and no command received + Idle = !(ram[0x26c] | ram[0x7c]); + jump(0xebff); + break; + case 0x01: // Write sector + the_job->WriteSector(); + jump(0xf5dc); + break; + case 0x02: // Format track + the_job->FormatTrack(); + jump(0xfd8b); + break; + default: +#if PC_IS_POINTER + illegal_op(0xf2, pc-pc_base-1); +#else + illegal_op(0xf2, pc-1); +#endif + break; + } + break; + } + } + return last_cycles; +} diff --git a/Src/CPU1541.h b/Src/CPU1541.h new file mode 100644 index 0000000..d70f3e5 --- /dev/null +++ b/Src/CPU1541.h @@ -0,0 +1,295 @@ +/* + * CPU1541.h - 6502 (1541) emulation (line based) + * + * Frodo (C) 1994-1997,2002-2005 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _CPU_1541_H +#define _CPU_1541_H + +#include "CIA.h" +#include "C64.h" + + +// Set this to 1 if the 6502 PC should be represented by a real pointer +#ifndef FRODO_SC +#ifndef PC_IS_POINTER +#define PC_IS_POINTER 1 +#endif +#endif + +// Set this to 1 for more precise CPU cycle calculation +#ifndef PRECISE_CPU_CYCLES +#define PRECISE_CPU_CYCLES 0 +#endif + + +// Interrupt types +enum { + INT_VIA1IRQ, + INT_VIA2IRQ, + INT_IECIRQ + // INT_RESET (private) +}; + + +class C64; +class Job1541; +class C64Display; +struct MOS6502State; + + +// 6502 emulation (1541) +class MOS6502_1541 { +public: + MOS6502_1541(C64 *c64, Job1541 *job, C64Display *disp, uint8 *Ram, uint8 *Rom); + +#ifdef FRODO_SC + void EmulateCycle(void); // Emulate one clock cycle +#else + int EmulateLine(int cycles_left); // Emulate until cycles_left underflows +#endif + void Reset(void); + void AsyncReset(void); // Reset the CPU asynchronously + void GetState(MOS6502State *s); + void SetState(MOS6502State *s); + uint8 ExtReadByte(uint16 adr); + void ExtWriteByte(uint16 adr, uint8 byte); + void CountVIATimers(int cycles); + void NewATNState(void); + void IECInterrupt(void); + void TriggerJobIRQ(void); + bool InterruptEnabled(void); + + MOS6526_2 *TheCIA2; // Pointer to C64 CIA 2 + + uint8 IECLines; // State of IEC lines (bit 7 - DATA, bit 6 - CLK) + bool Idle; // true: 1541 is idle + +private: + uint8 read_byte(uint16 adr); + uint8 read_byte_io(uint16 adr); + uint16 read_word(uint16 adr); + void write_byte(uint16 adr, uint8 byte); + void write_byte_io(uint16 adr, uint8 byte); + + uint8 read_zp(uint16 adr); + uint16 read_zp_word(uint16 adr); + void write_zp(uint16 adr, uint8 byte); + + void jump(uint16 adr); + void illegal_op(uint8 op, uint16 at); + void illegal_jump(uint16 at, uint16 to); + + void do_adc(uint8 byte); + void do_sbc(uint8 byte); + + uint8 *ram; // Pointer to main RAM + uint8 *rom; // Pointer to ROM + C64 *the_c64; // Pointer to C64 object + C64Display *the_display; // Pointer to C64 display object + Job1541 *the_job; // Pointer to 1541 job object + + union { // Pending interrupts + uint8 intr[4]; // Index: See definitions above + unsigned long intr_any; + } interrupt; + + uint8 n_flag, z_flag; + bool v_flag, d_flag, i_flag, c_flag; + uint8 a, x, y, sp; +#if PC_IS_POINTER + uint8 *pc, *pc_base; +#else + uint16 pc; +#endif + +#ifdef FRODO_SC + uint32 first_irq_cycle; + + uint8 state, op; // Current state and opcode + uint16 ar, ar2; // Address registers + uint8 rdbuf; // Data buffer for RMW instructions + uint8 ddr, pr; // Processor port +#else + int borrowed_cycles; // Borrowed cycles from next line +#endif + + uint8 via1_pra; // PRA of VIA 1 + uint8 via1_ddra; // DDRA of VIA 1 + uint8 via1_prb; // PRB of VIA 1 + uint8 via1_ddrb; // DDRB of VIA 1 + uint16 via1_t1c; // T1 Counter of VIA 1 + uint16 via1_t1l; // T1 Latch of VIA 1 + uint16 via1_t2c; // T2 Counter of VIA 1 + uint16 via1_t2l; // T2 Latch of VIA 1 + uint8 via1_sr; // SR of VIA 1 + uint8 via1_acr; // ACR of VIA 1 + uint8 via1_pcr; // PCR of VIA 1 + uint8 via1_ifr; // IFR of VIA 1 + uint8 via1_ier; // IER of VIA 1 + + uint8 via2_pra; // PRA of VIA 2 + uint8 via2_ddra; // DDRA of VIA 2 + uint8 via2_prb; // PRB of VIA 2 + uint8 via2_ddrb; // DDRB of VIA 2 + uint16 via2_t1c; // T1 Counter of VIA 2 + uint16 via2_t1l; // T1 Latch of VIA 2 + uint16 via2_t2c; // T2 Counter of VIA 2 + uint16 via2_t2l; // T2 Latch of VIA 2 + uint8 via2_sr; // SR of VIA 2 + uint8 via2_acr; // ACR of VIA 2 + uint8 via2_pcr; // PCR of VIA 2 + uint8 via2_ifr; // IFR of VIA 2 + uint8 via2_ier; // IER of VIA 2 +}; + +// 6502 state +struct MOS6502State { + uint8 a, x, y; + uint8 p; // Processor flags + uint16 pc, sp; + + uint8 intr[4]; // Interrupt state + bool instruction_complete; + bool idle; + + uint8 via1_pra; // VIA 1 + uint8 via1_ddra; + uint8 via1_prb; + uint8 via1_ddrb; + uint16 via1_t1c; + uint16 via1_t1l; + uint16 via1_t2c; + uint16 via1_t2l; + uint8 via1_sr; + uint8 via1_acr; + uint8 via1_pcr; + uint8 via1_ifr; + uint8 via1_ier; + + uint8 via2_pra; // VIA 2 + uint8 via2_ddra; + uint8 via2_prb; + uint8 via2_ddrb; + uint16 via2_t1c; + uint16 via2_t1l; + uint16 via2_t2c; + uint16 via2_t2l; + uint8 via2_sr; + uint8 via2_acr; + uint8 via2_pcr; + uint8 via2_ifr; + uint8 via2_ier; +}; + + + +/* + * Trigger job loop IRQ + */ + +#ifdef FRODO_SC +inline void MOS6502_1541::TriggerJobIRQ(void) +{ + if (!(interrupt.intr[INT_VIA2IRQ])) + first_irq_cycle = the_c64->CycleCounter; + interrupt.intr[INT_VIA2IRQ] = true; + Idle = false; +} +#else +inline void MOS6502_1541::TriggerJobIRQ(void) +{ + interrupt.intr[INT_VIA2IRQ] = true; + Idle = false; +} +#endif + + +/* + * Count VIA timers + */ + +inline void MOS6502_1541::CountVIATimers(int cycles) +{ + unsigned long tmp; + + via1_t1c = tmp = via1_t1c - cycles; + if (tmp > 0xffff) { + if (via1_acr & 0x40) // Reload from latch in free-run mode + via1_t1c = via1_t1l; + via1_ifr |= 0x40; + } + + if (!(via1_acr & 0x20)) { // Only count in one-shot mode + via1_t2c = tmp = via1_t2c - cycles; + if (tmp > 0xffff) + via1_ifr |= 0x20; + } + + via2_t1c = tmp = via2_t1c - cycles; + if (tmp > 0xffff) { + if (via2_acr & 0x40) // Reload from latch in free-run mode + via2_t1c = via2_t1l; + via2_ifr |= 0x40; + if (via2_ier & 0x40) + TriggerJobIRQ(); + } + + if (!(via2_acr & 0x20)) { // Only count in one-shot mode + via2_t2c = tmp = via2_t2c - cycles; + if (tmp > 0xffff) + via2_ifr |= 0x20; + } +} + + +/* + * ATN line probably changed state, recalc IECLines + */ + +inline void MOS6502_1541::NewATNState(void) +{ + uint8 byte = ~via1_prb & via1_ddrb; + IECLines = (byte << 6) & ((~byte ^ TheCIA2->IECLines) << 3) & 0x80 // DATA (incl. ATN acknowledge) + | (byte << 3) & 0x40; // CLK +} + + +/* + * Interrupt by negative edge of ATN on IEC bus + */ + +inline void MOS6502_1541::IECInterrupt(void) +{ + ram[0x7c] = 1; + + // Wake up 1541 + Idle = false; +} + + +/* + * Test if interrupts are enabled (for job loop) + */ + +inline bool MOS6502_1541::InterruptEnabled(void) +{ + return !i_flag; +} + +#endif diff --git a/Src/CPU1541_SC.cpp b/Src/CPU1541_SC.cpp new file mode 100644 index 0000000..5cdaa90 --- /dev/null +++ b/Src/CPU1541_SC.cpp @@ -0,0 +1,661 @@ +/* + * CPU1541_SC.cpp - Single-cycle 6502 (1541) emulation + * + * Frodo (C) 1994-1997,2002-2005 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * Notes: + * ------ + * + * Opcode execution: + * - All opcodes are resolved into single clock cycles. There is one + * switch case for each cycle. + * - The "state" variable specifies the routine to be executed in the + * next cycle. Its upper 8 bits contain the current opcode, its lower + * 8 bits contain the cycle number (0..7) within the opcode. + * - Opcodes are fetched in cycle 0 (state = 0) + * - The states 0x0010..0x0027 are used for interrupts + * - There is exactly one memory access in each clock cycle + * + * Memory map (1541C, the 1541 and 1541-II are a bit different): + * + * $0000-$07ff RAM (2K) + * $0800-$0fff RAM mirror + * $1000-$17ff free + * $1800-$1bff VIA 1 + * $1c00-$1fff VIA 2 + * $2000-$bfff free + * $c000-$ffff ROM (16K) + * + * - All memory accesses are done with the read_byte() and + * write_byte() functions which also do the memory address + * decoding. + * - The possible interrupt sources are: + * INT_VIA1IRQ: I flag is checked, jump to ($fffe) (unused) + * INT_VIA2IRQ: I flag is checked, jump to ($fffe) (unused) + * INT_IECIRQ: I flag is checked, jump to ($fffe) (unused) + * INT_RESET: Jump to ($fffc) + * - The z_flag variable has the inverse meaning of the + * 6502 Z flag + * - Only the highest bit of the n_flag variable is used + * - The $f2 opcode that would normally crash the 6502 is + * used to implement emulator-specific functions + * - The 1541 6502 emulation also includes a very simple VIA + * emulation (enough to make the IEC bus and GCR loading work). + * It's too small to move it to a source file of its own. + * + * Incompatibilities: + * ------------------ + * + * - VIA emulation incomplete + */ + +#include "sysdeps.h" + +#include "CPU1541.h" +#include "CPU_common.h" +#include "1541job.h" +#include "C64.h" +#include "CIA.h" +#include "Display.h" + + +enum { + INT_RESET = 3 +}; + + +/* + * 6502 constructor: Initialize registers + */ + +MOS6502_1541::MOS6502_1541(C64 *c64, Job1541 *job, C64Display *disp, uint8 *Ram, uint8 *Rom) + : ram(Ram), rom(Rom), the_c64(c64), the_display(disp), the_job(job) +{ + a = x = y = 0; + sp = 0xff; + n_flag = z_flag = 0; + v_flag = d_flag = c_flag = false; + i_flag = true; + + via1_t1c = via1_t1l = via1_t2c = via1_t2l = 0; + via1_sr = 0; + via2_t1c = via2_t1l = via2_t2c = via2_t2l = 0; + via2_sr = 0; + + first_irq_cycle = 0; + Idle = false; +} + + +/* + * Reset CPU asynchronously + */ + +void MOS6502_1541::AsyncReset(void) +{ + interrupt.intr[INT_RESET] = true; + Idle = false; +} + + +/* + * Get 6502 register state + */ + +void MOS6502_1541::GetState(MOS6502State *s) +{ + s->a = a; + s->x = x; + s->y = y; + + s->p = 0x20 | (n_flag & 0x80); + if (v_flag) s->p |= 0x40; + if (d_flag) s->p |= 0x08; + if (i_flag) s->p |= 0x04; + if (!z_flag) s->p |= 0x02; + if (c_flag) s->p |= 0x01; + + s->pc = pc; + s->sp = sp | 0x0100; + + s->intr[INT_VIA1IRQ] = interrupt.intr[INT_VIA1IRQ]; + s->intr[INT_VIA2IRQ] = interrupt.intr[INT_VIA2IRQ]; + s->intr[INT_IECIRQ] = interrupt.intr[INT_IECIRQ]; + s->intr[INT_RESET] = interrupt.intr[INT_RESET]; + s->idle = Idle; + + s->via1_pra = via1_pra; s->via1_ddra = via1_ddra; + s->via1_prb = via1_prb; s->via1_ddrb = via1_ddrb; + s->via1_t1c = via1_t1c; s->via1_t1l = via1_t1l; + s->via1_t2c = via1_t2c; s->via1_t2l = via1_t2l; + s->via1_sr = via1_sr; + s->via1_acr = via1_acr; s->via1_pcr = via1_pcr; + s->via1_ifr = via1_ifr; s->via1_ier = via1_ier; + + s->via2_pra = via2_pra; s->via2_ddra = via2_ddra; + s->via2_prb = via2_prb; s->via2_ddrb = via2_ddrb; + s->via2_t1c = via2_t1c; s->via2_t1l = via2_t1l; + s->via2_t2c = via2_t2c; s->via2_t2l = via2_t2l; + s->via2_sr = via2_sr; + s->via2_acr = via2_acr; s->via2_pcr = via2_pcr; + s->via2_ifr = via2_ifr; s->via2_ier = via2_ier; +} + + +/* + * Restore 6502 state + */ + +void MOS6502_1541::SetState(MOS6502State *s) +{ + a = s->a; + x = s->x; + y = s->y; + + n_flag = s->p; + v_flag = s->p & 0x40; + d_flag = s->p & 0x08; + i_flag = s->p & 0x04; + z_flag = !(s->p & 0x02); + c_flag = s->p & 0x01; + + pc = s->pc; + sp = s->sp & 0xff; + + interrupt.intr[INT_VIA1IRQ] = s->intr[INT_VIA1IRQ]; + interrupt.intr[INT_VIA2IRQ] = s->intr[INT_VIA2IRQ]; + interrupt.intr[INT_IECIRQ] = s->intr[INT_IECIRQ]; + interrupt.intr[INT_RESET] = s->intr[INT_RESET]; + Idle = s->idle; + + via1_pra = s->via1_pra; via1_ddra = s->via1_ddra; + via1_prb = s->via1_prb; via1_ddrb = s->via1_ddrb; + via1_t1c = s->via1_t1c; via1_t1l = s->via1_t1l; + via1_t2c = s->via1_t2c; via1_t2l = s->via1_t2l; + via1_sr = s->via1_sr; + via1_acr = s->via1_acr; via1_pcr = s->via1_pcr; + via1_ifr = s->via1_ifr; via1_ier = s->via1_ier; + + via2_pra = s->via2_pra; via2_ddra = s->via2_ddra; + via2_prb = s->via2_prb; via2_ddrb = s->via2_ddrb; + via2_t1c = s->via2_t1c; via2_t1l = s->via2_t1l; + via2_t2c = s->via2_t2c; via2_t2l = s->via2_t2l; + via2_sr = s->via2_sr; + via2_acr = s->via2_acr; via2_pcr = s->via2_pcr; + via2_ifr = s->via2_ifr; via2_ier = s->via2_ier; +} + + +/* + * Read a byte from I/O space + */ + +inline uint8 MOS6502_1541::read_byte_io(uint16 adr) +{ + if ((adr & 0xfc00) == 0x1800) // VIA 1 + switch (adr & 0xf) { + case 0: + return (via1_prb & 0x1a + | ((IECLines & TheCIA2->IECLines) >> 7) // DATA + | ((IECLines & TheCIA2->IECLines) >> 4) & 0x04 // CLK + | (TheCIA2->IECLines << 3) & 0x80) ^ 0x85; // ATN + case 1: + case 15: + return 0xff; // Keep 1541C ROMs happy (track 0 sensor) + case 2: + return via1_ddrb; + case 3: + return via1_ddra; + case 4: + via1_ifr &= 0xbf; + return via1_t1c; + case 5: + return via1_t1c >> 8; + case 6: + return via1_t1l; + case 7: + return via1_t1l >> 8; + case 8: + via1_ifr &= 0xdf; + return via1_t2c; + case 9: + return via1_t2c >> 8; + case 10: + return via1_sr; + case 11: + return via1_acr; + case 12: + return via1_pcr; + case 13: + return via1_ifr | (via1_ifr & via1_ier ? 0x80 : 0); + case 14: + return via1_ier | 0x80; + default: // Can't happen + return 0; + } + + else if ((adr & 0xfc00) == 0x1c00) // VIA 2 + switch (adr & 0xf) { + case 0: + if (the_job->SyncFound()) + return via2_prb & 0x7f | the_job->WPState(); + else + return via2_prb | 0x80 | the_job->WPState(); + case 1: + case 15: + return the_job->ReadGCRByte(); + case 2: + return via2_ddrb; + case 3: + return via2_ddra; + case 4: + via2_ifr &= 0xbf; + interrupt.intr[INT_VIA2IRQ] = false; // Clear job IRQ + return via2_t1c; + case 5: + return via2_t1c >> 8; + case 6: + return via2_t1l; + case 7: + return via2_t1l >> 8; + case 8: + via2_ifr &= 0xdf; + return via2_t2c; + case 9: + return via2_t2c >> 8; + case 10: + return via2_sr; + case 11: + return via2_acr; + case 12: + return via2_pcr; + case 13: + return via2_ifr | (via2_ifr & via2_ier ? 0x80 : 0); + case 14: + return via2_ier | 0x80; + default: // Can't happen + return 0; + } + + else + return adr >> 8; +} + + +/* + * Read a byte from the CPU's address space + */ + +uint8 MOS6502_1541::read_byte(uint16 adr) +{ + if (adr >= 0xc000) + return rom[adr & 0x3fff]; + else if (adr < 0x1000) + return ram[adr & 0x07ff]; + else + return read_byte_io(adr); +} + + +/* + * Read a word (little-endian) from the CPU's address space + */ + +inline uint16 MOS6502_1541::read_word(uint16 adr) +{ + return read_byte(adr) | (read_byte(adr+1) << 8); +} + + +/* + * Write a byte to I/O space + */ + +void MOS6502_1541::write_byte_io(uint16 adr, uint8 byte) +{ + if ((adr & 0xfc00) == 0x1800) // VIA 1 + switch (adr & 0xf) { + case 0: + via1_prb = byte; + byte = ~via1_prb & via1_ddrb; + IECLines = (byte << 6) & ((~byte ^ TheCIA2->IECLines) << 3) & 0x80 + | (byte << 3) & 0x40; + break; + case 1: + case 15: + via1_pra = byte; + break; + case 2: + via1_ddrb = byte; + byte &= ~via1_prb; + IECLines = (byte << 6) & ((~byte ^ TheCIA2->IECLines) << 3) & 0x80 + | (byte << 3) & 0x40; + break; + case 3: + via1_ddra = byte; + break; + case 4: + case 6: + via1_t1l = via1_t1l & 0xff00 | byte; + break; + case 5: + via1_t1l = via1_t1l & 0xff | (byte << 8); + via1_ifr &= 0xbf; + via1_t1c = via1_t1l; + break; + case 7: + via1_t1l = via1_t1l & 0xff | (byte << 8); + break; + case 8: + via1_t2l = via1_t2l & 0xff00 | byte; + break; + case 9: + via1_t2l = via1_t2l & 0xff | (byte << 8); + via1_ifr &= 0xdf; + via1_t2c = via1_t2l; + break; + case 10: + via1_sr = byte; + break; + case 11: + via1_acr = byte; + break; + case 12: + via1_pcr = byte; + break; + case 13: + via1_ifr &= ~byte; + break; + case 14: + if (byte & 0x80) + via1_ier |= byte & 0x7f; + else + via1_ier &= ~byte; + break; + } + + else if ((adr & 0xfc00) == 0x1c00) + switch (adr & 0xf) { + case 0: + if ((via2_prb ^ byte) & 8) // Bit 3: Drive LED + the_display->UpdateLEDs(byte & 8 ? 1 : 0, 0, 0, 0); + if ((via2_prb ^ byte) & 3) // Bits 0/1: Stepper motor + if ((via2_prb & 3) == ((byte+1) & 3)) + the_job->MoveHeadOut(); + else if ((via2_prb & 3) == ((byte-1) & 3)) + the_job->MoveHeadIn(); + via2_prb = byte & 0xef; + break; + case 1: + case 15: + via2_pra = byte; + break; + case 2: + via2_ddrb = byte; + break; + case 3: + via2_ddra = byte; + break; + case 4: + case 6: + via2_t1l = via2_t1l & 0xff00 | byte; + break; + case 5: + via2_t1l = via2_t1l & 0xff | (byte << 8); + via2_ifr &= 0xbf; + via2_t1c = via2_t1l; + break; + case 7: + via2_t1l = via2_t1l & 0xff | (byte << 8); + break; + case 8: + via2_t2l = via2_t2l & 0xff00 | byte; + break; + case 9: + via2_t2l = via2_t2l & 0xff | (byte << 8); + via2_ifr &= 0xdf; + via2_t2c = via2_t2l; + break; + case 10: + via2_sr = byte; + break; + case 11: + via2_acr = byte; + break; + case 12: + via2_pcr = byte; + break; + case 13: + via2_ifr &= ~byte; + break; + case 14: + if (byte & 0x80) + via2_ier |= byte & 0x7f; + else + via2_ier &= ~byte; + break; + } +} + + +/* + * Write a byte to the CPU's address space + */ + +inline void MOS6502_1541::write_byte(uint16 adr, uint8 byte) +{ + if (adr < 0x1000) + ram[adr & 0x7ff] = byte; + else + write_byte_io(adr, byte); +} + + +/* + * Read byte from 6502/1541 address space (used by SAM) + */ + +uint8 MOS6502_1541::ExtReadByte(uint16 adr) +{ + return read_byte(adr); +} + + +/* + * Write byte to 6502/1541 address space (used by SAM) + */ + +void MOS6502_1541::ExtWriteByte(uint16 adr, uint8 byte) +{ + write_byte(adr, byte); +} + + +/* + * Adc instruction + */ + +inline void MOS6502_1541::do_adc(uint8 byte) +{ + if (!d_flag) { + uint16 tmp; + + // Binary mode + tmp = a + byte + (c_flag ? 1 : 0); + c_flag = tmp > 0xff; + v_flag = !((a ^ byte) & 0x80) && ((a ^ tmp) & 0x80); + z_flag = n_flag = a = tmp; + + } else { + uint16 al, ah; + + // Decimal mode + al = (a & 0x0f) + (byte & 0x0f) + (c_flag ? 1 : 0); // Calculate lower nybble + if (al > 9) al += 6; // BCD fixup for lower nybble + + ah = (a >> 4) + (byte >> 4); // Calculate upper nybble + if (al > 0x0f) ah++; + + z_flag = a + byte + (c_flag ? 1 : 0); // Set flags + n_flag = ah << 4; // Only highest bit used + v_flag = (((ah << 4) ^ a) & 0x80) && !((a ^ byte) & 0x80); + + if (ah > 9) ah += 6; // BCD fixup for upper nybble + c_flag = ah > 0x0f; // Set carry flag + a = (ah << 4) | (al & 0x0f); // Compose result + } +} + + +/* + * Sbc instruction + */ + +inline void MOS6502_1541::do_sbc(uint8 byte) +{ + uint16 tmp = a - byte - (c_flag ? 0 : 1); + + if (!d_flag) { + + // Binary mode + c_flag = tmp < 0x100; + v_flag = ((a ^ tmp) & 0x80) && ((a ^ byte) & 0x80); + z_flag = n_flag = a = tmp; + + } else { + uint16 al, ah; + + // Decimal mode + al = (a & 0x0f) - (byte & 0x0f) - (c_flag ? 0 : 1); // Calculate lower nybble + ah = (a >> 4) - (byte >> 4); // Calculate upper nybble + if (al & 0x10) { + al -= 6; // BCD fixup for lower nybble + ah--; + } + if (ah & 0x10) ah -= 6; // BCD fixup for upper nybble + + c_flag = tmp < 0x100; // Set flags + v_flag = ((a ^ tmp) & 0x80) && ((a ^ byte) & 0x80); + z_flag = n_flag = tmp; + + a = (ah << 4) | (al & 0x0f); // Compose result + } +} + + +/* + * Reset CPU + */ + +void MOS6502_1541::Reset(void) +{ + // IEC lines and VIA registers + IECLines = 0xc0; + + via1_pra = via1_ddra = via1_prb = via1_ddrb = 0; + via1_acr = via1_pcr = 0; + via1_ifr = via1_ier = 0; + via2_pra = via2_ddra = via2_prb = via2_ddrb = 0; + via2_acr = via2_pcr = 0; + via2_ifr = via2_ier = 0; + + // Clear all interrupt lines + interrupt.intr_any = 0; + + // Read reset vector + pc = read_word(0xfffc); + state = 0; + + // Wake up 1541 + Idle = false; +} + + +/* + * Illegal opcode encountered + */ + +void MOS6502_1541::illegal_op(uint8 op, uint16 at) +{ + char illop_msg[80]; + + sprintf(illop_msg, "1541: Illegal opcode %02x at %04x.", op, at); + if (ShowRequester(illop_msg, "Reset 1541", "Reset C64")) + the_c64->Reset(); + Reset(); +} + + +/* + * Emulate one 6502 clock cycle + */ + +// Read byte from memory +#define read_to(adr, to) \ + to = read_byte(adr); + +// Read byte from memory, throw away result +#define read_idle(adr) \ + read_byte(adr); + +void MOS6502_1541::EmulateCycle(void) +{ + uint8 data, tmp; + + // Any pending interrupts in state 0 (opcode fetch)? + if (!state && interrupt.intr_any) { + if (interrupt.intr[INT_RESET]) + Reset(); + else if ((interrupt.intr[INT_VIA1IRQ] || interrupt.intr[INT_VIA2IRQ] || interrupt.intr[INT_IECIRQ]) && (the_c64->CycleCounter-first_irq_cycle >= 2) && !i_flag) + state = 0x0008; + } + +#define IS_CPU_1541 +#include "CPU_emulcycle.h" + + // Extension opcode + case O_EXT: + if (pc < 0xc000) { + illegal_op(0xf2, pc-1); + break; + } + switch (read_byte(pc++)) { + case 0x00: // Go to sleep in DOS idle loop if error flag is clear and no command received + Idle = !(ram[0x26c] | ram[0x7c]); + pc = 0xebff; + Last; + case 0x01: // Write sector + the_job->WriteSector(); + pc = 0xf5dc; + Last; + case 0x02: // Format track + the_job->FormatTrack(); + pc = 0xfd8b; + Last; + default: + illegal_op(0xf2, pc-1); + break; + } + break; + + default: + illegal_op(op, pc-1); + break; + } +} diff --git a/Src/CPUC64.cpp b/Src/CPUC64.cpp new file mode 100644 index 0000000..b6b3bb4 --- /dev/null +++ b/Src/CPUC64.cpp @@ -0,0 +1,847 @@ +/* + * CPUC64.cpp - 6510 (C64) emulation (line based) + * + * Frodo (C) 1994-1997,2002-2005 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * Notes: + * ------ + * + * - The EmulateLine() function is called for every emulated + * raster line. It has a cycle counter that is decremented + * by every executed opcode and if the counter goes below + * zero, the function returns. + * - Memory configurations: + * $01 $a000-$bfff $d000-$dfff $e000-$ffff + * ----------------------------------------------- + * 0 RAM RAM RAM + * 1 RAM Char ROM RAM + * 2 RAM Char ROM Kernal ROM + * 3 Basic ROM Char ROM Kernal ROM + * 4 RAM RAM RAM + * 5 RAM I/O RAM + * 6 RAM I/O Kernal ROM + * 7 Basic ROM I/O Kernal ROM + * - All memory accesses are done with the read_byte() and + * write_byte() functions which also do the memory address + * decoding. The read_zp() and write_zp() functions allow + * faster access to the zero page, the pop_byte() and + * push_byte() macros for the stack. + * - If a write occurs to addresses 0 or 1, new_config is + * called to check whether the memory configuration has + * changed + * - The PC is either emulated with a 16 bit address or a + * direct memory pointer (for faster access), depending on + * the PC_IS_POINTER #define. In the latter case, a second + * pointer, pc_base, is kept to allow recalculating the + * 16 bit 6510 PC if it has to be pushed on the stack. + * - The possible interrupt sources are: + * INT_VICIRQ: I flag is checked, jump to ($fffe) + * INT_CIAIRQ: I flag is checked, jump to ($fffe) + * INT_NMI: Jump to ($fffa) + * INT_RESET: Jump to ($fffc) + * - Interrupts are not checked before every opcode but only + * at certain times: + * On entering EmulateLine() + * On CLI + * On PLP if the I flag was cleared + * On RTI if the I flag was cleared + * - The z_flag variable has the inverse meaning of the + * 6510 Z flag + * - Only the highest bit of the n_flag variable is used + * - The $f2 opcode that would normally crash the 6510 is + * used to implement emulator-specific functions, mainly + * those for the IEC routines + * + * Incompatibilities: + * ------------------ + * + * - If PC_IS_POINTER is set, neither branches accross memory + * areas nor jumps to I/O space are possible + * - Extra cycles for crossing page boundaries are not + * accounted for + * - The cassette sense line is always closed + */ + +#include "sysdeps.h" + +#include "CPUC64.h" +#include "C64.h" +#include "VIC.h" +#include "SID.h" +#include "CIA.h" +#include "REU.h" +#include "IEC.h" +#include "Display.h" +#include "Version.h" + + +enum { + INT_RESET = 3 +}; + + +/* + * 6510 constructor: Initialize registers + */ + +MOS6510::MOS6510(C64 *c64, uint8 *Ram, uint8 *Basic, uint8 *Kernal, uint8 *Char, uint8 *Color) + : the_c64(c64), ram(Ram), basic_rom(Basic), kernal_rom(Kernal), char_rom(Char), color_ram(Color) +{ + a = x = y = 0; + sp = 0xff; + n_flag = z_flag = 0; + v_flag = d_flag = c_flag = false; + i_flag = true; + dfff_byte = 0x55; + borrowed_cycles = 0; +} + + +/* + * Reset CPU asynchronously + */ + +void MOS6510::AsyncReset(void) +{ + interrupt.intr[INT_RESET] = true; +} + + +/* + * Raise NMI asynchronously (Restore key) + */ + +void MOS6510::AsyncNMI(void) +{ + if (!nmi_state) + interrupt.intr[INT_NMI] = true; +} + + +/* + * Memory configuration has probably changed + */ + +void MOS6510::new_config(void) +{ + uint8 port = ~ram[0] | ram[1]; + + basic_in = (port & 3) == 3; + kernal_in = port & 2; + char_in = (port & 3) && !(port & 4); + io_in = (port & 3) && (port & 4); +} + + +/* + * Read a byte from I/O / ROM space + */ + +inline uint8 MOS6510::read_byte_io(uint16 adr) +{ + switch (adr >> 12) { + case 0xa: + case 0xb: + if (basic_in) + return basic_rom[adr & 0x1fff]; + else + return ram[adr]; + case 0xc: + return ram[adr]; + case 0xd: + if (io_in) + switch ((adr >> 8) & 0x0f) { + case 0x0: // VIC + case 0x1: + case 0x2: + case 0x3: + return TheVIC->ReadRegister(adr & 0x3f); + case 0x4: // SID + case 0x5: + case 0x6: + case 0x7: + return TheSID->ReadRegister(adr & 0x1f); + case 0x8: // Color RAM + case 0x9: + case 0xa: + case 0xb: + return color_ram[adr & 0x03ff] | rand() & 0xf0; + case 0xc: // CIA 1 + return TheCIA1->ReadRegister(adr & 0x0f); + case 0xd: // CIA 2 + return TheCIA2->ReadRegister(adr & 0x0f); + case 0xe: // REU/Open I/O + case 0xf: + if ((adr & 0xfff0) == 0xdf00) + return TheREU->ReadRegister(adr & 0x0f); + else if (adr < 0xdfa0) + return rand(); + else + return read_emulator_id(adr & 0x7f); + } + else if (char_in) + return char_rom[adr & 0x0fff]; + else + return ram[adr]; + case 0xe: + case 0xf: + if (kernal_in) + return kernal_rom[adr & 0x1fff]; + else + return ram[adr]; + default: // Can't happen + return 0; + } +} + + +/* + * Read a byte from the CPU's address space + */ + +uint8 MOS6510::read_byte(uint16 adr) +{ + if (adr < 0xa000) + return ram[adr]; + else + return read_byte_io(adr); +} + + +/* + * $dfa0-$dfff: Emulator identification + */ + +const char frodo_id[0x5c] = "FRODO\r(C) 1994-1997 CHRISTIAN BAUER"; + +uint8 MOS6510::read_emulator_id(uint16 adr) +{ + switch (adr) { + case 0x7c: // $dffc: revision + return FRODO_REVISION << 4; + case 0x7d: // $dffd: version + return FRODO_VERSION; + case 0x7e: // $dffe returns 'F' (Frodo ID) + return 'F'; + case 0x7f: // $dfff alternates between $55 and $aa + dfff_byte = ~dfff_byte; + return dfff_byte; + default: + return frodo_id[adr - 0x20]; + } +} + + +/* + * Read a word (little-endian) from the CPU's address space + */ + +#if LITTLE_ENDIAN_UNALIGNED + +inline uint16 MOS6510::read_word(uint16 adr) +{ + switch (adr >> 12) { + case 0x0: + case 0x1: + case 0x2: + case 0x3: + case 0x4: + case 0x5: + case 0x6: + case 0x7: + case 0x8: + case 0x9: + return *(uint16*)&ram[adr]; + break; + case 0xa: + case 0xb: + if (basic_in) + return *(uint16*)&basic_rom[adr & 0x1fff]; + else + return *(uint16*)&ram[adr]; + case 0xc: + return *(uint16*)&ram[adr]; + case 0xd: + if (io_in) + return read_byte(adr) | (read_byte(adr+1) << 8); + else if (char_in) + return *(uint16*)&char_rom[adr & 0x0fff]; + else + return *(uint16*)&ram[adr]; + case 0xe: + case 0xf: + if (kernal_in) + return *(uint16*)&kernal_rom[adr & 0x1fff]; + else + return *(uint16*)&ram[adr]; + default: // Can't happen + return 0; + } +} + +#else + +inline uint16 MOS6510::read_word(uint16 adr) +{ + return read_byte(adr) | (read_byte(adr+1) << 8); +} + +#endif + + +/* + * Write byte to I/O space + */ + +void MOS6510::write_byte_io(uint16 adr, uint8 byte) +{ + if (adr >= 0xe000) { + ram[adr] = byte; + if (adr == 0xff00) + TheREU->FF00Trigger(); + } else if (io_in) + switch ((adr >> 8) & 0x0f) { + case 0x0: // VIC + case 0x1: + case 0x2: + case 0x3: + TheVIC->WriteRegister(adr & 0x3f, byte); + return; + case 0x4: // SID + case 0x5: + case 0x6: + case 0x7: + TheSID->WriteRegister(adr & 0x1f, byte); + return; + case 0x8: // Color RAM + case 0x9: + case 0xa: + case 0xb: + color_ram[adr & 0x03ff] = byte & 0x0f; + return; + case 0xc: // CIA 1 + TheCIA1->WriteRegister(adr & 0x0f, byte); + return; + case 0xd: // CIA 2 + TheCIA2->WriteRegister(adr & 0x0f, byte); + return; + case 0xe: // REU/Open I/O + case 0xf: + if ((adr & 0xfff0) == 0xdf00) + TheREU->WriteRegister(adr & 0x0f, byte); + return; + } + else + ram[adr] = byte; +} + + +/* + * Write a byte to the CPU's address space + */ + +inline void MOS6510::write_byte(uint16 adr, uint8 byte) +{ + if (adr < 0xd000) { + ram[adr] = byte; + if (adr < 2) + new_config(); + } else + write_byte_io(adr, byte); +} + + +/* + * Read a byte from the zeropage + */ + +inline uint8 MOS6510::read_zp(uint16 adr) +{ + return ram[adr]; +} + + +/* + * Read a word (little-endian) from the zeropage + */ + +inline uint16 MOS6510::read_zp_word(uint16 adr) +{ +// !! zeropage word addressing wraps around !! +#if LITTLE_ENDIAN_UNALIGNED + return *(uint16 *)&ram[adr & 0xff]; +#else + return ram[adr & 0xff] | (ram[(adr+1) & 0xff] << 8); +#endif +} + + +/* + * Write a byte to the zeropage + */ + +inline void MOS6510::write_zp(uint16 adr, uint8 byte) +{ + ram[adr] = byte; + + // Check if memory configuration may have changed. + if (adr < 2) + new_config(); +} + + +/* + * Read byte from 6510 address space with special memory config (used by SAM) + */ + +uint8 MOS6510::ExtReadByte(uint16 adr) +{ + // Save old memory configuration + bool bi = basic_in, ki = kernal_in, ci = char_in, ii = io_in; + + // Set new configuration + basic_in = (ExtConfig & 3) == 3; + kernal_in = ExtConfig & 2; + char_in = (ExtConfig & 3) && ~(ExtConfig & 4); + io_in = (ExtConfig & 3) && (ExtConfig & 4); + + // Read byte + uint8 byte = read_byte(adr); + + // Restore old configuration + basic_in = bi; kernal_in = ki; char_in = ci; io_in = ii; + + return byte; +} + + +/* + * Write byte to 6510 address space with special memory config (used by SAM) + */ + +void MOS6510::ExtWriteByte(uint16 adr, uint8 byte) +{ + // Save old memory configuration + bool bi = basic_in, ki = kernal_in, ci = char_in, ii = io_in; + + // Set new configuration + basic_in = (ExtConfig & 3) == 3; + kernal_in = ExtConfig & 2; + char_in = (ExtConfig & 3) && ~(ExtConfig & 4); + io_in = (ExtConfig & 3) && (ExtConfig & 4); + + // Write byte + write_byte(adr, byte); + + // Restore old configuration + basic_in = bi; kernal_in = ki; char_in = ci; io_in = ii; +} + + +/* + * Read byte from 6510 address space with current memory config (used by REU) + */ + +uint8 MOS6510::REUReadByte(uint16 adr) +{ + return read_byte(adr); +} + + +/* + * Write byte to 6510 address space with current memory config (used by REU) + */ + +void MOS6510::REUWriteByte(uint16 adr, uint8 byte) +{ + write_byte(adr, byte); +} + + +/* + * Jump to address + */ + +#if PC_IS_POINTER +#define jump(adr) \ + if ((adr) < 0xa000) { \ + pc = ram + (adr); \ + pc_base = ram; \ + } else { \ + switch ((adr) >> 12) { \ + case 0xa: \ + case 0xb: \ + if (basic_in) { \ + pc = basic_rom + ((adr) & 0x1fff); \ + pc_base = basic_rom - 0xa000; \ + } else { \ + pc = ram + (adr); \ + pc_base = ram; \ + } \ + break; \ + case 0xc: \ + pc = ram + (adr); \ + pc_base = ram; \ + break; \ + case 0xd: \ + if (io_in) { \ + illegal_jump(pc-pc_base, (adr)); \ + } else if (char_in) { \ + pc = char_rom + ((adr) & 0x0fff); \ + pc_base = char_rom - 0xd000; \ + } else { \ + pc = ram + (adr); \ + pc_base = ram; \ + } \ + break; \ + case 0xe: \ + case 0xf: \ + if (kernal_in) { \ + pc = kernal_rom + ((adr) & 0x1fff); \ + pc_base = kernal_rom - 0xe000; \ + } else { \ + pc = ram + (adr); \ + pc_base = ram; \ + } \ + break; \ + } \ + } +#else +#define jump(adr) pc = (adr) +#endif + + +/* + * Adc instruction + */ + +void MOS6510::do_adc(uint8 byte) +{ + if (!d_flag) { + uint16 tmp = a + (byte) + (c_flag ? 1 : 0); + c_flag = tmp > 0xff; + v_flag = !((a ^ (byte)) & 0x80) && ((a ^ tmp) & 0x80); + z_flag = n_flag = a = tmp; + } else { + uint16 al, ah; + al = (a & 0x0f) + ((byte) & 0x0f) + (c_flag ? 1 : 0); + if (al > 9) al += 6; + ah = (a >> 4) + ((byte) >> 4); + if (al > 0x0f) ah++; + z_flag = a + (byte) + (c_flag ? 1 : 0); + n_flag = ah << 4; + v_flag = (((ah << 4) ^ a) & 0x80) && !((a ^ (byte)) & 0x80); + if (ah > 9) ah += 6; + c_flag = ah > 0x0f; + a = (ah << 4) | (al & 0x0f); + } +} + + +/* + * Sbc instruction + */ + +void MOS6510::do_sbc(uint8 byte) +{ + uint16 tmp = a - (byte) - (c_flag ? 0 : 1); + if (!d_flag) { + c_flag = tmp < 0x100; + v_flag = ((a ^ tmp) & 0x80) && ((a ^ (byte)) & 0x80); + z_flag = n_flag = a = tmp; + } else { + uint16 al, ah; + al = (a & 0x0f) - ((byte) & 0x0f) - (c_flag ? 0 : 1); + ah = (a >> 4) - ((byte) >> 4); + if (al & 0x10) { + al -= 6; + ah--; + } + if (ah & 0x10) ah -= 6; + c_flag = tmp < 0x100; + v_flag = ((a ^ tmp) & 0x80) && ((a ^ (byte)) & 0x80); + z_flag = n_flag = tmp; + a = (ah << 4) | (al & 0x0f); + } +} + + +/* + * Get 6510 register state + */ + +void MOS6510::GetState(MOS6510State *s) +{ + s->a = a; + s->x = x; + s->y = y; + + s->p = 0x20 | (n_flag & 0x80); + if (v_flag) s->p |= 0x40; + if (d_flag) s->p |= 0x08; + if (i_flag) s->p |= 0x04; + if (!z_flag) s->p |= 0x02; + if (c_flag) s->p |= 0x01; + + s->ddr = ram[0]; + s->pr = ram[1] & 0x3f; + +#if PC_IS_POINTER + s->pc = pc - pc_base; +#else + s->pc = pc; +#endif + s->sp = sp | 0x0100; + + s->intr[INT_VICIRQ] = interrupt.intr[INT_VICIRQ]; + s->intr[INT_CIAIRQ] = interrupt.intr[INT_CIAIRQ]; + s->intr[INT_NMI] = interrupt.intr[INT_NMI]; + s->intr[INT_RESET] = interrupt.intr[INT_RESET]; + s->nmi_state = nmi_state; + s->dfff_byte = dfff_byte; + s->instruction_complete = true; +} + + +/* + * Restore 6510 state + */ + +void MOS6510::SetState(MOS6510State *s) +{ + a = s->a; + x = s->x; + y = s->y; + + n_flag = s->p; + v_flag = s->p & 0x40; + d_flag = s->p & 0x08; + i_flag = s->p & 0x04; + z_flag = !(s->p & 0x02); + c_flag = s->p & 0x01; + + ram[0] = s->ddr; + ram[1] = s->pr; + new_config(); + + jump(s->pc); + sp = s->sp & 0xff; + + interrupt.intr[INT_VICIRQ] = s->intr[INT_VICIRQ]; + interrupt.intr[INT_CIAIRQ] = s->intr[INT_CIAIRQ]; + interrupt.intr[INT_NMI] = s->intr[INT_NMI]; + interrupt.intr[INT_RESET] = s->intr[INT_RESET]; + nmi_state = s->nmi_state; + dfff_byte = s->dfff_byte; +} + + +/* + * Reset CPU + */ + +void MOS6510::Reset(void) +{ + // Delete 'CBM80' if present + if (ram[0x8004] == 0xc3 && ram[0x8005] == 0xc2 && ram[0x8006] == 0xcd + && ram[0x8007] == 0x38 && ram[0x8008] == 0x30) + ram[0x8004] = 0; + + // Initialize extra 6510 registers and memory configuration + ram[0] = ram[1] = 0; + new_config(); + + // Clear all interrupt lines + interrupt.intr_any = 0; + nmi_state = false; + + // Read reset vector + jump(read_word(0xfffc)); +} + + +/* + * Illegal opcode encountered + */ + +void MOS6510::illegal_op(uint8 op, uint16 at) +{ + char illop_msg[80]; + + sprintf(illop_msg, "Illegal opcode %02x at %04x.", op, at); + ShowRequester(illop_msg, "Reset"); + the_c64->Reset(); + Reset(); +} + + +/* + * Jump to illegal address space (PC_IS_POINTER only) + */ + +void MOS6510::illegal_jump(uint16 at, uint16 to) +{ + char illop_msg[80]; + + sprintf(illop_msg, "Jump to I/O space at %04x to %04x.", at, to); + ShowRequester(illop_msg, "Reset"); + the_c64->Reset(); + Reset(); +} + + +/* + * Stack macros + */ + +// Pop a byte from the stack +#define pop_byte() ram[(++sp) | 0x0100] + +// Push a byte onto the stack +#define push_byte(byte) (ram[(sp--) & 0xff | 0x0100] = (byte)) + +// Pop processor flags from the stack +#define pop_flags() \ + n_flag = tmp = pop_byte(); \ + v_flag = tmp & 0x40; \ + d_flag = tmp & 0x08; \ + i_flag = tmp & 0x04; \ + z_flag = !(tmp & 0x02); \ + c_flag = tmp & 0x01; + +// Push processor flags onto the stack +#define push_flags(b_flag) \ + tmp = 0x20 | (n_flag & 0x80); \ + if (v_flag) tmp |= 0x40; \ + if (b_flag) tmp |= 0x10; \ + if (d_flag) tmp |= 0x08; \ + if (i_flag) tmp |= 0x04; \ + if (!z_flag) tmp |= 0x02; \ + if (c_flag) tmp |= 0x01; \ + push_byte(tmp); + + +/* + * Emulate cycles_left worth of 6510 instructions + * Returns number of cycles of last instruction + */ + +int MOS6510::EmulateLine(int cycles_left) +{ + uint8 tmp, tmp2; + uint16 adr; // Used by read_adr_abs()! + int last_cycles = 0; + + // Any pending interrupts? + if (interrupt.intr_any) { +handle_int: + if (interrupt.intr[INT_RESET]) { + Reset(); + + } else if (interrupt.intr[INT_NMI]) { + interrupt.intr[INT_NMI] = false; // Simulate an edge-triggered input +#if PC_IS_POINTER + push_byte((pc-pc_base) >> 8); push_byte(pc-pc_base); +#else + push_byte(pc >> 8); push_byte(pc); +#endif + push_flags(false); + i_flag = true; + adr = read_word(0xfffa); + jump(adr); + last_cycles = 7; + + } else if ((interrupt.intr[INT_VICIRQ] || interrupt.intr[INT_CIAIRQ]) && !i_flag) { +#if PC_IS_POINTER + push_byte((pc-pc_base) >> 8); push_byte(pc-pc_base); +#else + push_byte(pc >> 8); push_byte(pc); +#endif + push_flags(false); + i_flag = true; + adr = read_word(0xfffe); + jump(adr); + last_cycles = 7; + } + } + +#include "CPU_emulline.h" + + // Extension opcode + case 0xf2: +#if PC_IS_POINTER + if ((pc-pc_base) < 0xe000) { + illegal_op(0xf2, pc-pc_base-1); +#else + if (pc < 0xe000) { + illegal_op(0xf2, pc-1); +#endif + break; + } + switch (read_byte_imm()) { + case 0x00: + ram[0x90] |= TheIEC->Out(ram[0x95], ram[0xa3] & 0x80); + c_flag = false; + jump(0xedac); + break; + case 0x01: + ram[0x90] |= TheIEC->OutATN(ram[0x95]); + c_flag = false; + jump(0xedac); + break; + case 0x02: + ram[0x90] |= TheIEC->OutSec(ram[0x95]); + c_flag = false; + jump(0xedac); + break; + case 0x03: + ram[0x90] |= TheIEC->In(a); + set_nz(a); + c_flag = false; + jump(0xedac); + break; + case 0x04: + TheIEC->SetATN(); + jump(0xedfb); + break; + case 0x05: + TheIEC->RelATN(); + jump(0xedac); + break; + case 0x06: + TheIEC->Turnaround(); + jump(0xedac); + break; + case 0x07: + TheIEC->Release(); + jump(0xedac); + break; + default: +#if PC_IS_POINTER + illegal_op(0xf2, pc-pc_base-1); +#else + illegal_op(0xf2, pc-1); +#endif + break; + } + break; + } + } + return last_cycles; +} diff --git a/Src/CPUC64.h b/Src/CPUC64.h new file mode 100644 index 0000000..ba6ab1f --- /dev/null +++ b/Src/CPUC64.h @@ -0,0 +1,230 @@ +/* + * CPUC64.h - 6510 (C64) emulation (line based) + * + * Frodo (C) 1994-1997,2002-2009 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _CPU_C64_H +#define _CPU_C64_H + +#include "C64.h" + + +// Set this to 1 if the 6502 PC should be represented by a real pointer +#ifndef FRODO_SC +#ifndef PC_IS_POINTER +#define PC_IS_POINTER 1 +#endif +#endif + +// Set this to 1 for more precise CPU cycle calculation +#ifndef PRECISE_CPU_CYCLES +#define PRECISE_CPU_CYCLES 0 +#endif + +// Set this to 1 for instruction-aligned CIA emulation +#ifndef PRECISE_CIA_CYCLES +#define PRECISE_CIA_CYCLES 0 +#endif + + +// Interrupt types +enum { + INT_VICIRQ, + INT_CIAIRQ, + INT_NMI + // INT_RESET (private) +}; + + +class MOS6569; +class MOS6581; +class MOS6526_1; +class MOS6526_2; +class REU; +class IEC; +struct MOS6510State; + + +// 6510 emulation (C64) +class MOS6510 { +public: + MOS6510(C64 *c64, uint8 *Ram, uint8 *Basic, uint8 *Kernal, uint8 *Char, uint8 *Color); + +#ifdef FRODO_SC + void EmulateCycle(void); // Emulate one clock cycle +#else + int EmulateLine(int cycles_left); // Emulate until cycles_left underflows +#endif + void Reset(void); + void AsyncReset(void); // Reset the CPU asynchronously + void AsyncNMI(void); // Raise NMI asynchronously (NMI pulse) + void GetState(MOS6510State *s); + void SetState(MOS6510State *s); + uint8 ExtReadByte(uint16 adr); + void ExtWriteByte(uint16 adr, uint8 byte); + uint8 REUReadByte(uint16 adr); + void REUWriteByte(uint16 adr, uint8 byte); + + void TriggerVICIRQ(void); + void ClearVICIRQ(void); + void TriggerCIAIRQ(void); + void ClearCIAIRQ(void); + void TriggerNMI(void); + void ClearNMI(void); + + int ExtConfig; // Memory configuration for ExtRead/WriteByte (0..7) + + MOS6569 *TheVIC; // Pointer to VIC + MOS6581 *TheSID; // Pointer to SID + MOS6526_1 *TheCIA1; // Pointer to CIA 1 + MOS6526_2 *TheCIA2; // Pointer to CIA 2 + REU *TheREU; // Pointer to REU + IEC *TheIEC; // Pointer to drive array + +#ifdef FRODO_SC + bool BALow; // BA line for Frodo SC +#endif + +private: + uint8 read_byte(uint16 adr); + uint8 read_byte_io(uint16 adr); + uint16 read_word(uint16 adr); + void write_byte(uint16 adr, uint8 byte); + void write_byte_io(uint16 adr, uint8 byte); + + uint8 read_zp(uint16 adr); + uint16 read_zp_word(uint16 adr); + void write_zp(uint16 adr, uint8 byte); + + void new_config(void); + void illegal_op(uint8 op, uint16 at); + void illegal_jump(uint16 at, uint16 to); + + void do_adc(uint8 byte); + void do_sbc(uint8 byte); + + uint8 read_emulator_id(uint16 adr); + + C64 *the_c64; // Pointer to C64 object + + uint8 *ram; // Pointer to main RAM + uint8 *basic_rom, *kernal_rom, *char_rom, *color_ram; // Pointers to ROMs and color RAM + + union { // Pending interrupts + uint8 intr[4]; // Index: See definitions above + unsigned long intr_any; + } interrupt; + bool nmi_state; // State of NMI line + + uint8 n_flag, z_flag; + bool v_flag, d_flag, i_flag, c_flag; + uint8 a, x, y, sp; + +#if PC_IS_POINTER + uint8 *pc, *pc_base; +#else + uint16 pc; +#endif + +#ifdef FRODO_SC + uint32 first_irq_cycle, first_nmi_cycle; + + uint8 state, op; // Current state and opcode + uint16 ar, ar2; // Address registers + uint8 rdbuf; // Data buffer for RMW instructions + uint8 ddr, pr, pr_out; // Processor port +#else + int borrowed_cycles; // Borrowed cycles from next line +#endif + + bool basic_in, kernal_in, char_in, io_in; + uint8 dfff_byte; +}; + +// 6510 state +struct MOS6510State { + uint8 a, x, y; + uint8 p; // Processor flags + uint8 ddr, pr; // Port + uint16 pc, sp; + uint8 intr[4]; // Interrupt state + bool nmi_state; + uint8 dfff_byte; + bool instruction_complete; +}; + + +// Interrupt functions +#ifdef FRODO_SC +inline void MOS6510::TriggerVICIRQ(void) +{ + if (!(interrupt.intr[INT_VICIRQ] || interrupt.intr[INT_CIAIRQ])) + first_irq_cycle = the_c64->CycleCounter; + interrupt.intr[INT_VICIRQ] = true; +} + +inline void MOS6510::TriggerCIAIRQ(void) +{ + if (!(interrupt.intr[INT_VICIRQ] || interrupt.intr[INT_CIAIRQ])) + first_irq_cycle = the_c64->CycleCounter; + interrupt.intr[INT_CIAIRQ] = true; +} + +inline void MOS6510::TriggerNMI(void) +{ + if (!nmi_state) { + nmi_state = true; + interrupt.intr[INT_NMI] = true; + first_nmi_cycle = the_c64->CycleCounter; + } +} +#else +inline void MOS6510::TriggerVICIRQ(void) +{ + interrupt.intr[INT_VICIRQ] = true; +} + +inline void MOS6510::TriggerCIAIRQ(void) +{ + interrupt.intr[INT_CIAIRQ] = true; +} + +inline void MOS6510::TriggerNMI(void) +{ + if (!nmi_state) { + nmi_state = true; + interrupt.intr[INT_NMI] = true; + } +} +#endif +inline void MOS6510::ClearVICIRQ(void) +{ + interrupt.intr[INT_VICIRQ] = false; +} + +inline void MOS6510::ClearCIAIRQ(void) +{ + interrupt.intr[INT_CIAIRQ] = false; +} + +inline void MOS6510::ClearNMI(void) +{ + nmi_state = false; +} + +#endif diff --git a/Src/CPUC64_SC.cpp b/Src/CPUC64_SC.cpp new file mode 100644 index 0000000..c51580c --- /dev/null +++ b/Src/CPUC64_SC.cpp @@ -0,0 +1,670 @@ +/* + * CPUC64_SC.cpp - Single-cycle 6510 (C64) emulation + * + * Frodo (C) 1994-1997,2002-2009 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * Notes: + * ------ + * + * Opcode execution: + * - All opcodes are resolved into single clock cycles. There is one + * switch case for each cycle. + * - The "state" variable specifies the routine to be executed in the + * next cycle. Its upper 8 bits contain the current opcode, its lower + * 8 bits contain the cycle number (0..7) within the opcode. + * - Opcodes are fetched in cycle 0 (state = 0) + * - The states 0x0010..0x0027 are used for interrupts + * - There is exactly one memory access in each clock cycle + * + * Memory configurations: + * + * $01 $a000-$bfff $d000-$dfff $e000-$ffff + * ----------------------------------------------- + * 0 RAM RAM RAM + * 1 RAM Char ROM RAM + * 2 RAM Char ROM Kernal ROM + * 3 Basic ROM Char ROM Kernal ROM + * 4 RAM RAM RAM + * 5 RAM I/O RAM + * 6 RAM I/O Kernal ROM + * 7 Basic ROM I/O Kernal ROM + * + * - All memory accesses are done with the read_byte() and + * write_byte() functions which also do the memory address + * decoding. + * - If a write occurs to addresses 0 or 1, new_config is + * called to check whether the memory configuration has + * changed + * - The possible interrupt sources are: + * INT_VICIRQ: I flag is checked, jump to ($fffe) + * INT_CIAIRQ: I flag is checked, jump to ($fffe) + * INT_NMI: Jump to ($fffa) + * INT_RESET: Jump to ($fffc) + * - The z_flag variable has the inverse meaning of the + * 6510 Z flag + * - Only the highest bit of the n_flag variable is used + * - The $f2 opcode that would normally crash the 6510 is + * used to implement emulator-specific functions, mainly + * those for the IEC routines + * + * Incompatibilities: + * ------------------ + * + * - If BA is low and AEC is high, read accesses should occur + */ + +#include "sysdeps.h" + +#include "CPUC64.h" +#include "CPU_common.h" +#include "C64.h" +#include "VIC.h" +#include "SID.h" +#include "CIA.h" +#include "REU.h" +#include "IEC.h" +#include "Display.h" +#include "Version.h" + + +enum { + INT_RESET = 3 +}; + + +/* + * 6510 constructor: Initialize registers + */ + +MOS6510::MOS6510(C64 *c64, uint8 *Ram, uint8 *Basic, uint8 *Kernal, uint8 *Char, uint8 *Color) + : the_c64(c64), ram(Ram), basic_rom(Basic), kernal_rom(Kernal), char_rom(Char), color_ram(Color) +{ + a = x = y = 0; + sp = 0xff; + n_flag = z_flag = 0; + v_flag = d_flag = c_flag = false; + i_flag = true; + dfff_byte = 0x55; + BALow = false; + first_irq_cycle = first_nmi_cycle = 0; +} + + +/* + * Reset CPU asynchronously + */ + +void MOS6510::AsyncReset(void) +{ + interrupt.intr[INT_RESET] = true; +} + + +/* + * Raise NMI asynchronously (Restore key) + */ + +void MOS6510::AsyncNMI(void) +{ + if (!nmi_state) + interrupt.intr[INT_NMI] = true; +} + + +/* + * Get 6510 register state + */ + +void MOS6510::GetState(MOS6510State *s) +{ + s->a = a; + s->x = x; + s->y = y; + + s->p = 0x20 | (n_flag & 0x80); + if (v_flag) s->p |= 0x40; + if (d_flag) s->p |= 0x08; + if (i_flag) s->p |= 0x04; + if (!z_flag) s->p |= 0x02; + if (c_flag) s->p |= 0x01; + + s->ddr = ddr; + s->pr = pr; + + s->pc = pc; + s->sp = sp | 0x0100; + + s->intr[INT_VICIRQ] = interrupt.intr[INT_VICIRQ]; + s->intr[INT_CIAIRQ] = interrupt.intr[INT_CIAIRQ]; + s->intr[INT_NMI] = interrupt.intr[INT_NMI]; + s->intr[INT_RESET] = interrupt.intr[INT_RESET]; + s->nmi_state = nmi_state; + s->dfff_byte = dfff_byte; + s->instruction_complete = (state == 0); +} + + +/* + * Restore 6510 state + */ + +void MOS6510::SetState(MOS6510State *s) +{ + a = s->a; + x = s->x; + y = s->y; + + n_flag = s->p; + v_flag = s->p & 0x40; + d_flag = s->p & 0x08; + i_flag = s->p & 0x04; + z_flag = !(s->p & 0x02); + c_flag = s->p & 0x01; + + ddr = s->ddr; + pr = s->pr; + pr_out = 0; // FIXME: should be saved in MOS6510State + new_config(); + + pc = s->pc; + sp = s->sp & 0xff; + + interrupt.intr[INT_VICIRQ] = s->intr[INT_VICIRQ]; + interrupt.intr[INT_CIAIRQ] = s->intr[INT_CIAIRQ]; + interrupt.intr[INT_NMI] = s->intr[INT_NMI]; + interrupt.intr[INT_RESET] = s->intr[INT_RESET]; + nmi_state = s->nmi_state; + dfff_byte = s->dfff_byte; + if (s->instruction_complete) + state = 0; +} + + +/* + * Memory configuration has probably changed + */ + +void MOS6510::new_config(void) +{ + pr_out = (pr_out & ~ddr) | (pr & ddr); + uint8 port = pr | ~ddr; + + basic_in = (port & 3) == 3; + kernal_in = port & 2; + char_in = (port & 3) && !(port & 4); + io_in = (port & 3) && (port & 4); +} + + +/* + * Read a byte from I/O / ROM space + */ + +inline uint8 MOS6510::read_byte_io(uint16 adr) +{ + switch (adr >> 12) { + case 0xa: + case 0xb: + if (basic_in) + return basic_rom[adr & 0x1fff]; + else + return ram[adr]; + case 0xc: + return ram[adr]; + case 0xd: + if (io_in) + switch ((adr >> 8) & 0x0f) { + case 0x0: // VIC + case 0x1: + case 0x2: + case 0x3: + return TheVIC->ReadRegister(adr & 0x3f); + case 0x4: // SID + case 0x5: + case 0x6: + case 0x7: + return TheSID->ReadRegister(adr & 0x1f); + case 0x8: // Color RAM + case 0x9: + case 0xa: + case 0xb: + return color_ram[adr & 0x03ff] & 0x0f | TheVIC->LastVICByte & 0xf0; + case 0xc: // CIA 1 + return TheCIA1->ReadRegister(adr & 0x0f); + case 0xd: // CIA 2 + return TheCIA2->ReadRegister(adr & 0x0f); + case 0xe: // REU/Open I/O + case 0xf: + if ((adr & 0xfff0) == 0xdf00) + return TheREU->ReadRegister(adr & 0x0f); + else if (adr < 0xdfa0) + return TheVIC->LastVICByte; + else + return read_emulator_id(adr & 0x7f); + } + else if (char_in) + return char_rom[adr & 0x0fff]; + else + return ram[adr]; + case 0xe: + case 0xf: + if (kernal_in) + return kernal_rom[adr & 0x1fff]; + else + return ram[adr]; + default: // Can't happen + return 0; + } +} + + +/* + * Read a byte from the CPU's address space + */ + +uint8 MOS6510::read_byte(uint16 adr) +{ + if (adr < 0xa000) { + if (adr >= 2) { + return ram[adr]; + } else if (adr == 0) { + return ddr; + } else { + uint8 byte = (pr | ~ddr) & (pr_out | 0x17); + if (!(ddr & 0x20)) + byte &= 0xdf; + return byte; + } + } else + return read_byte_io(adr); +} + + +/* + * $dfa0-$dfff: Emulator identification + */ + +const char frodo_id[0x5c] = "FRODO\r(C) 1994-1997 CHRISTIAN BAUER"; + +uint8 MOS6510::read_emulator_id(uint16 adr) +{ + switch (adr) { + case 0x7c: // $dffc: revision + return FRODO_REVISION << 4; + case 0x7d: // $dffd: version + return FRODO_VERSION; + case 0x7e: // $dffe returns 'F' (Frodo ID) + return 'F'; + case 0x7f: // $dfff alternates between $55 and $aa + dfff_byte = ~dfff_byte; + return dfff_byte; + default: + return frodo_id[adr - 0x20]; + } +} + + +/* + * Read a word (little-endian) from the CPU's address space + */ + +inline uint16 MOS6510::read_word(uint16 adr) +{ + return read_byte(adr) | (read_byte(adr+1) << 8); +} + + +/* + * Write a byte to I/O space + */ + +inline void MOS6510::write_byte_io(uint16 adr, uint8 byte) +{ + if (adr >= 0xe000) { + ram[adr] = byte; + if (adr == 0xff00) + TheREU->FF00Trigger(); + } else if (io_in) + switch ((adr >> 8) & 0x0f) { + case 0x0: // VIC + case 0x1: + case 0x2: + case 0x3: + TheVIC->WriteRegister(adr & 0x3f, byte); + return; + case 0x4: // SID + case 0x5: + case 0x6: + case 0x7: + TheSID->WriteRegister(adr & 0x1f, byte); + return; + case 0x8: // Color RAM + case 0x9: + case 0xa: + case 0xb: + color_ram[adr & 0x03ff] = byte & 0x0f; + return; + case 0xc: // CIA 1 + TheCIA1->WriteRegister(adr & 0x0f, byte); + return; + case 0xd: // CIA 2 + TheCIA2->WriteRegister(adr & 0x0f, byte); + return; + case 0xe: // REU/Open I/O + case 0xf: + if ((adr & 0xfff0) == 0xdf00) + TheREU->WriteRegister(adr & 0x0f, byte); + return; + } + else + ram[adr] = byte; +} + + +/* + * Write a byte to the CPU's address space + */ + +void MOS6510::write_byte(uint16 adr, uint8 byte) +{ + if (adr < 0xd000) { + if (adr >= 2) + ram[adr] = byte; + else if (adr == 0) { + ddr = byte; + ram[0] = TheVIC->LastVICByte; + new_config(); + } else { + pr = byte; + ram[1] = TheVIC->LastVICByte; + new_config(); + } + } else + write_byte_io(adr, byte); +} + + +/* + * Read byte from 6510 address space with special memory config (used by SAM) + */ + +uint8 MOS6510::ExtReadByte(uint16 adr) +{ + // Save old memory configuration + bool bi = basic_in, ki = kernal_in, ci = char_in, ii = io_in; + + // Set new configuration + basic_in = (ExtConfig & 3) == 3; + kernal_in = ExtConfig & 2; + char_in = (ExtConfig & 3) && ~(ExtConfig & 4); + io_in = (ExtConfig & 3) && (ExtConfig & 4); + + // Read byte + uint8 byte = read_byte(adr); + + // Restore old configuration + basic_in = bi; kernal_in = ki; char_in = ci; io_in = ii; + + return byte; +} + + +/* + * Write byte to 6510 address space with special memory config (used by SAM) + */ + +void MOS6510::ExtWriteByte(uint16 adr, uint8 byte) +{ + // Save old memory configuration + bool bi = basic_in, ki = kernal_in, ci = char_in, ii = io_in; + + // Set new configuration + basic_in = (ExtConfig & 3) == 3; + kernal_in = ExtConfig & 2; + char_in = (ExtConfig & 3) && ~(ExtConfig & 4); + io_in = (ExtConfig & 3) && (ExtConfig & 4); + + // Write byte + write_byte(adr, byte); + + // Restore old configuration + basic_in = bi; kernal_in = ki; char_in = ci; io_in = ii; +} + + +/* + * Read byte from 6510 address space with current memory config (used by REU) + */ + +uint8 MOS6510::REUReadByte(uint16 adr) +{ + return read_byte(adr); +} + + +/* + * Write byte to 6510 address space with current memory config (used by REU) + */ + +void MOS6510::REUWriteByte(uint16 adr, uint8 byte) +{ + write_byte(adr, byte); +} + + +/* + * Adc instruction + */ + +inline void MOS6510::do_adc(uint8 byte) +{ + if (!d_flag) { + uint16 tmp; + + // Binary mode + tmp = a + byte + (c_flag ? 1 : 0); + c_flag = tmp > 0xff; + v_flag = !((a ^ byte) & 0x80) && ((a ^ tmp) & 0x80); + z_flag = n_flag = a = tmp; + + } else { + uint16 al, ah; + + // Decimal mode + al = (a & 0x0f) + (byte & 0x0f) + (c_flag ? 1 : 0); // Calculate lower nybble + if (al > 9) al += 6; // BCD fixup for lower nybble + + ah = (a >> 4) + (byte >> 4); // Calculate upper nybble + if (al > 0x0f) ah++; + + z_flag = a + byte + (c_flag ? 1 : 0); // Set flags + n_flag = ah << 4; // Only highest bit used + v_flag = (((ah << 4) ^ a) & 0x80) && !((a ^ byte) & 0x80); + + if (ah > 9) ah += 6; // BCD fixup for upper nybble + c_flag = ah > 0x0f; // Set carry flag + a = (ah << 4) | (al & 0x0f); // Compose result + } +} + + +/* + * Sbc instruction + */ + +inline void MOS6510::do_sbc(uint8 byte) +{ + uint16 tmp = a - byte - (c_flag ? 0 : 1); + + if (!d_flag) { + + // Binary mode + c_flag = tmp < 0x100; + v_flag = ((a ^ tmp) & 0x80) && ((a ^ byte) & 0x80); + z_flag = n_flag = a = tmp; + + } else { + uint16 al, ah; + + // Decimal mode + al = (a & 0x0f) - (byte & 0x0f) - (c_flag ? 0 : 1); // Calculate lower nybble + ah = (a >> 4) - (byte >> 4); // Calculate upper nybble + if (al & 0x10) { + al -= 6; // BCD fixup for lower nybble + ah--; + } + if (ah & 0x10) ah -= 6; // BCD fixup for upper nybble + + c_flag = tmp < 0x100; // Set flags + v_flag = ((a ^ tmp) & 0x80) && ((a ^ byte) & 0x80); + z_flag = n_flag = tmp; + + a = (ah << 4) | (al & 0x0f); // Compose result + } +} + + +/* + * Reset CPU + */ + +void MOS6510::Reset(void) +{ + // Delete 'CBM80' if present + if (ram[0x8004] == 0xc3 && ram[0x8005] == 0xc2 && ram[0x8006] == 0xcd + && ram[0x8007] == 0x38 && ram[0x8008] == 0x30) + ram[0x8004] = 0; + + // Initialize extra 6510 registers and memory configuration + ddr = pr = pr_out = 0; + new_config(); + + // Clear all interrupt lines + interrupt.intr_any = 0; + nmi_state = false; + + // Read reset vector + pc = read_word(0xfffc); + state = 0; +} + + +/* + * Illegal opcode encountered + */ + +void MOS6510::illegal_op(uint8 op, uint16 at) +{ + char illop_msg[80]; + + sprintf(illop_msg, "Illegal opcode %02x at %04x.", op, at); + ShowRequester(illop_msg, "Reset"); + the_c64->Reset(); + Reset(); +} + + +/* + * Emulate one 6510 clock cycle + */ + +// Read byte from memory +#define read_to(adr, to) \ + if (BALow) \ + return; \ + to = read_byte(adr); + +// Read byte from memory, throw away result +#define read_idle(adr) \ + if (BALow) \ + return; \ + read_byte(adr); + +void MOS6510::EmulateCycle(void) +{ + uint8 data, tmp; + + // Any pending interrupts in state 0 (opcode fetch)? + if (!state && interrupt.intr_any) { + if (interrupt.intr[INT_RESET]) + Reset(); + else if (interrupt.intr[INT_NMI] && (the_c64->CycleCounter-first_nmi_cycle >= 2)) { + interrupt.intr[INT_NMI] = false; // Simulate an edge-triggered input + state = 0x0010; + } else if ((interrupt.intr[INT_VICIRQ] || interrupt.intr[INT_CIAIRQ]) && (the_c64->CycleCounter-first_irq_cycle >= 2) && !i_flag) + state = 0x0008; + } + +#include "CPU_emulcycle.h" + + // Extension opcode + case O_EXT: + if (pc < 0xe000) { + illegal_op(0xf2, pc-1); + break; + } + switch (read_byte(pc++)) { + case 0x00: + ram[0x90] |= TheIEC->Out(ram[0x95], ram[0xa3] & 0x80); + c_flag = false; + pc = 0xedac; + Last; + case 0x01: + ram[0x90] |= TheIEC->OutATN(ram[0x95]); + c_flag = false; + pc = 0xedac; + Last; + case 0x02: + ram[0x90] |= TheIEC->OutSec(ram[0x95]); + c_flag = false; + pc = 0xedac; + Last; + case 0x03: + ram[0x90] |= TheIEC->In(a); + set_nz(a); + c_flag = false; + pc = 0xedac; + Last; + case 0x04: + TheIEC->SetATN(); + pc = 0xedfb; + Last; + case 0x05: + TheIEC->RelATN(); + pc = 0xedac; + Last; + case 0x06: + TheIEC->Turnaround(); + pc = 0xedac; + Last; + case 0x07: + TheIEC->Release(); + pc = 0xedac; + Last; + default: + illegal_op(0xf2, pc-1); + break; + } + break; + + default: + illegal_op(op, pc-1); + break; + } +} diff --git a/Src/CPU_common.cpp b/Src/CPU_common.cpp new file mode 100644 index 0000000..44527ab --- /dev/null +++ b/Src/CPU_common.cpp @@ -0,0 +1,97 @@ +/* + * CPU_common.cpp - Definitions common to 6502/6510 SC emulation + * + * Frodo (C) 1994-1997,2002-2005 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "sysdeps.h" + +#include "CPU_common.h" + + +// Addressing mode for each opcode (first part of execution) (Frodo SC) +const uint8 ModeTab[256] = { + O_BRK, A_INDX, 1, M_INDX, A_ZERO, A_ZERO, M_ZERO, M_ZERO, // 00 + O_PHP, O_ORA_I,O_ASL_A,O_ANC_I,A_ABS, A_ABS, M_ABS, M_ABS, + O_BPL, AE_INDY,1, M_INDY, A_ZEROX,A_ZEROX,M_ZEROX,M_ZEROX,// 10 + O_CLC, AE_ABSY,O_NOP, M_ABSY, AE_ABSX,AE_ABSX,M_ABSX, M_ABSX, + O_JSR, A_INDX, 1, M_INDX, A_ZERO, A_ZERO, M_ZERO, M_ZERO, // 20 + O_PLP, O_AND_I,O_ROL_A,O_ANC_I,A_ABS, A_ABS, M_ABS, M_ABS, + O_BMI, AE_INDY,1, M_INDY, A_ZEROX,A_ZEROX,M_ZEROX,M_ZEROX,// 30 + O_SEC, AE_ABSY,O_NOP, M_ABSY, AE_ABSX,AE_ABSX,M_ABSX, M_ABSX, + O_RTI, A_INDX, 1, M_INDX, A_ZERO, A_ZERO, M_ZERO, M_ZERO, // 40 + O_PHA, O_EOR_I,O_LSR_A,O_ASR_I,O_JMP, A_ABS, M_ABS, M_ABS, + O_BVC, AE_INDY,1, M_INDY, A_ZEROX,A_ZEROX,M_ZEROX,M_ZEROX,// 50 + O_CLI, AE_ABSY,O_NOP, M_ABSY, AE_ABSX,AE_ABSX,M_ABSX, M_ABSX, + O_RTS, A_INDX, 1, M_INDX, A_ZERO, A_ZERO, M_ZERO, M_ZERO, // 60 + O_PLA, O_ADC_I,O_ROR_A,O_ARR_I,A_ABS, A_ABS, M_ABS, M_ABS, + O_BVS, AE_INDY,1, M_INDY, A_ZEROX,A_ZEROX,M_ZEROX,M_ZEROX,// 70 + O_SEI, AE_ABSY,O_NOP, M_ABSY, AE_ABSX,AE_ABSX,M_ABSX, M_ABSX, + O_NOP_I,A_INDX, O_NOP_I,A_INDX, A_ZERO, A_ZERO, A_ZERO, A_ZERO, // 80 + O_DEY, O_NOP_I,O_TXA, O_ANE_I,A_ABS, A_ABS, A_ABS, A_ABS, + O_BCC, A_INDY, 1, A_INDY, A_ZEROX,A_ZEROX,A_ZEROY,A_ZEROY,// 90 + O_TYA, A_ABSY, O_TXS, A_ABSY, A_ABSX, A_ABSX, A_ABSY, A_ABSY, + O_LDY_I,A_INDX, O_LDX_I,A_INDX, A_ZERO, A_ZERO, A_ZERO, A_ZERO, // a0 + O_TAY, O_LDA_I,O_TAX, O_LXA_I,A_ABS, A_ABS, A_ABS, A_ABS, + O_BCS, AE_INDY,1, AE_INDY,A_ZEROX,A_ZEROX,A_ZEROY,A_ZEROY,// b0 + O_CLV, AE_ABSY,O_TSX, AE_ABSY,AE_ABSX,AE_ABSX,AE_ABSY,AE_ABSY, + O_CPY_I,A_INDX, O_NOP_I,M_INDX, A_ZERO, A_ZERO, M_ZERO, M_ZERO, // c0 + O_INY, O_CMP_I,O_DEX, O_SBX_I,A_ABS, A_ABS, M_ABS, M_ABS, + O_BNE, AE_INDY,1, M_INDY, A_ZEROX,A_ZEROX,M_ZEROX,M_ZEROX,// d0 + O_CLD, AE_ABSY,O_NOP, M_ABSY, AE_ABSX,AE_ABSX,M_ABSX, M_ABSX, + O_CPX_I,A_INDX, O_NOP_I,M_INDX, A_ZERO, A_ZERO, M_ZERO, M_ZERO, // e0 + O_INX, O_SBC_I,O_NOP, O_SBC_I,A_ABS, A_ABS, M_ABS, M_ABS, + O_BEQ, AE_INDY,O_EXT, M_INDY, A_ZEROX,A_ZEROX,M_ZEROX,M_ZEROX,// f0 + O_SED, AE_ABSY,O_NOP, M_ABSY, AE_ABSX,AE_ABSX,M_ABSX, M_ABSX +}; + + +// Operation for each opcode (second part of execution) (Frodo SC) +const uint8 OpTab[256] = { + 1, O_ORA, 1, O_SLO, O_NOP_A,O_ORA, O_ASL, O_SLO, // 00 + 1, 1, 1, 1, O_NOP_A,O_ORA, O_ASL, O_SLO, + 1, O_ORA, 1, O_SLO, O_NOP_A,O_ORA, O_ASL, O_SLO, // 10 + 1, O_ORA, 1, O_SLO, O_NOP_A,O_ORA, O_ASL, O_SLO, + 1, O_AND, 1, O_RLA, O_BIT, O_AND, O_ROL, O_RLA, // 20 + 1, 1, 1, 1, O_BIT, O_AND, O_ROL, O_RLA, + 1, O_AND, 1, O_RLA, O_NOP_A,O_AND, O_ROL, O_RLA, // 30 + 1, O_AND, 1, O_RLA, O_NOP_A,O_AND, O_ROL, O_RLA, + 1, O_EOR, 1, O_SRE, O_NOP_A,O_EOR, O_LSR, O_SRE, // 40 + 1, 1, 1, 1, 1, O_EOR, O_LSR, O_SRE, + 1, O_EOR, 1, O_SRE, O_NOP_A,O_EOR, O_LSR, O_SRE, // 50 + 1, O_EOR, 1, O_SRE, O_NOP_A,O_EOR, O_LSR, O_SRE, + 1, O_ADC, 1, O_RRA, O_NOP_A,O_ADC, O_ROR, O_RRA, // 60 + 1, 1, 1, 1, O_JMP_I,O_ADC, O_ROR, O_RRA, + 1, O_ADC, 1, O_RRA, O_NOP_A,O_ADC, O_ROR, O_RRA, // 70 + 1, O_ADC, 1, O_RRA, O_NOP_A,O_ADC, O_ROR, O_RRA, + 1, O_STA, 1, O_SAX, O_STY, O_STA, O_STX, O_SAX, // 80 + 1, 1, 1, 1, O_STY, O_STA, O_STX, O_SAX, + 1, O_STA, 1, O_SHA, O_STY, O_STA, O_STX, O_SAX, // 90 + 1, O_STA, 1, O_SHS, O_SHY, O_STA, O_SHX, O_SHA, + 1, O_LDA, 1, O_LAX, O_LDY, O_LDA, O_LDX, O_LAX, // a0 + 1, 1, 1, 1, O_LDY, O_LDA, O_LDX, O_LAX, + 1, O_LDA, 1, O_LAX, O_LDY, O_LDA, O_LDX, O_LAX, // b0 + 1, O_LDA, 1, O_LAS, O_LDY, O_LDA, O_LDX, O_LAX, + 1, O_CMP, 1, O_DCP, O_CPY, O_CMP, O_DEC, O_DCP, // c0 + 1, 1, 1, 1, O_CPY, O_CMP, O_DEC, O_DCP, + 1, O_CMP, 1, O_DCP, O_NOP_A,O_CMP, O_DEC, O_DCP, // d0 + 1, O_CMP, 1, O_DCP, O_NOP_A,O_CMP, O_DEC, O_DCP, + 1, O_SBC, 1, O_ISB, O_CPX, O_SBC, O_INC, O_ISB, // e0 + 1, 1, 1, 1, O_CPX, O_SBC, O_INC, O_ISB, + 1, O_SBC, 1, O_ISB, O_NOP_A,O_SBC, O_INC, O_ISB, // f0 + 1, O_SBC, 1, O_ISB, O_NOP_A,O_SBC, O_INC, O_ISB +}; diff --git a/Src/CPU_common.h b/Src/CPU_common.h new file mode 100644 index 0000000..3a55847 --- /dev/null +++ b/Src/CPU_common.h @@ -0,0 +1,90 @@ +/* + * CPU_common.h - Definitions common to 6502/6510 SC emulation + * + * Frodo (C) 1994-1997,2002-2005 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _CPU_COMMON_H_ +#define _CPU_COMMON_H_ + + +// States for addressing modes/operations (Frodo SC) +enum { + // Read effective address, no extra cycles + A_ZERO=0x18, + A_ZEROX, A_ZEROX1, + A_ZEROY, A_ZEROY1, + A_ABS, A_ABS1, + A_ABSX, A_ABSX1, A_ABSX2, A_ABSX3, + A_ABSY, A_ABSY1, A_ABSY2, A_ABSY3, + A_INDX, A_INDX1, A_INDX2, A_INDX3, + A_INDY, A_INDY1, A_INDY2, A_INDY3, A_INDY4, + + // Read effective address, extra cycle on page crossing + AE_ABSX, AE_ABSX1, AE_ABSX2, + AE_ABSY, AE_ABSY1, AE_ABSY2, + AE_INDY, AE_INDY1, AE_INDY2, AE_INDY3, + + // Read operand and write it back (for RMW instructions), no extra cycles + M_ZERO, + M_ZEROX, M_ZEROX1, + M_ZEROY, M_ZEROY1, + M_ABS, M_ABS1, + M_ABSX, M_ABSX1, M_ABSX2, M_ABSX3, + M_ABSY, M_ABSY1, M_ABSY2, M_ABSY3, + M_INDX, M_INDX1, M_INDX2, M_INDX3, + M_INDY, M_INDY1, M_INDY2, M_INDY3, M_INDY4, + RMW_DO_IT, RMW_DO_IT1, + + // Operations (_I = Immediate/Indirect, _A = Accumulator) + O_LDA, O_LDA_I, O_LDX, O_LDX_I, O_LDY, O_LDY_I, + O_STA, O_STX, O_STY, + O_TAX, O_TXA, O_TAY, O_TYA, O_TSX, O_TXS, + O_ADC, O_ADC_I, O_SBC, O_SBC_I, + O_INX, O_DEX, O_INY, O_DEY, O_INC, O_DEC, + O_AND, O_AND_I, O_ORA, O_ORA_I, O_EOR, O_EOR_I, + O_CMP, O_CMP_I, O_CPX, O_CPX_I, O_CPY, O_CPY_I, + O_BIT, + O_ASL, O_ASL_A, O_LSR, O_LSR_A, O_ROL, O_ROL_A, O_ROR, O_ROR_A, + O_PHA, O_PHA1, O_PLA, O_PLA1, O_PLA2, + O_PHP, O_PHP1, O_PLP, O_PLP1, O_PLP2, + O_JMP, O_JMP1, O_JMP_I, O_JMP_I1, + O_JSR, O_JSR1, O_JSR2, O_JSR3, O_JSR4, + O_RTS, O_RTS1, O_RTS2, O_RTS3, O_RTS4, + O_RTI, O_RTI1, O_RTI2, O_RTI3, O_RTI4, + O_BRK, O_BRK1, O_BRK2, O_BRK3, O_BRK4, O_BRK5, O_BRK5NMI, + O_BCS, O_BCC, O_BEQ, O_BNE, O_BVS, O_BVC, O_BMI, O_BPL, + O_BRANCH_NP, O_BRANCH_BP, O_BRANCH_BP1, O_BRANCH_FP, O_BRANCH_FP1, + O_SEC, O_CLC, O_SED, O_CLD, O_SEI, O_CLI, O_CLV, + O_NOP, + + O_NOP_I, O_NOP_A, + O_LAX, O_SAX, + O_SLO, O_RLA, O_SRE, O_RRA, O_DCP, O_ISB, + O_ANC_I, O_ASR_I, O_ARR_I, O_ANE_I, O_LXA_I, O_SBX_I, + O_LAS, O_SHS, O_SHY, O_SHX, O_SHA, + O_EXT +}; + + +// Addressing mode for each opcode (first part of execution) (Frodo SC) +extern const uint8 ModeTab[256]; + +// Operation for each opcode (second part of execution) (Frodo SC) +extern const uint8 OpTab[256]; + +#endif diff --git a/Src/CPU_emulcycle.h b/Src/CPU_emulcycle.h new file mode 100644 index 0000000..d43e83f --- /dev/null +++ b/Src/CPU_emulcycle.h @@ -0,0 +1,1114 @@ +/* + * CPU_emulcycle.h - SC 6510/6502 emulation core (body of + * EmulateCycle() function, the same for + * both 6510 and 6502) + * + * Frodo (C) 1994-1997,2002-2005 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +/* + * Stack macros + */ + +// Pop processor flags from the stack +#define pop_flags() \ + read_to(sp | 0x100, data); \ + n_flag = data; \ + v_flag = data & 0x40; \ + d_flag = data & 0x08; \ + i_flag = data & 0x04; \ + z_flag = !(data & 0x02); \ + c_flag = data & 0x01; + +// Push processor flags onto the stack +#define push_flags(b_flag) \ + data = 0x20 | (n_flag & 0x80); \ + if (v_flag) data |= 0x40; \ + if (b_flag) data |= 0x10; \ + if (d_flag) data |= 0x08; \ + if (i_flag) data |= 0x04; \ + if (!z_flag) data |= 0x02; \ + if (c_flag) data |= 0x01; \ + write_byte(sp-- | 0x100, data); + + +/* + * Other macros + */ + +// Branch (cycle 1) +#define Branch(flag) \ + read_to(pc++, data); \ + if (flag) { \ + ar = pc + (int8)data; \ + if ((ar >> 8) != (pc >> 8)) { \ + if (data & 0x80) \ + state = O_BRANCH_BP; \ + else \ + state = O_BRANCH_FP; \ + } else \ + state = O_BRANCH_NP; \ + } else \ + state = 0; \ + break; + +// Set N and Z flags according to byte +#define set_nz(x) (z_flag = n_flag = (x)) + +// Address fetch of RMW instruction done, now read and write operand +#define DoRMW state = RMW_DO_IT; break; + +// Operand fetch done, now execute opcode +#define Execute state = OpTab[op]; break; + +// Last cycle of opcode +#define Last state = 0; break; + + +/* + * EmulCycle() function + */ + + switch (state) { + + + // Opcode fetch (cycle 0) + case 0: + read_to(pc++, op); + state = ModeTab[op]; + break; + + + // IRQ + case 0x0008: + read_idle(pc); + state = 0x0009; + break; + case 0x0009: + read_idle(pc); + state = 0x000a; + break; + case 0x000a: + write_byte(sp-- | 0x100, pc >> 8); + state = 0x000b; + break; + case 0x000b: + write_byte(sp-- | 0x100, pc); + state = 0x000c; + break; + case 0x000c: + push_flags(false); + i_flag = true; + state = 0x000d; + break; + case 0x000d: + read_to(0xfffe, pc); + state = 0x000e; + break; + case 0x000e: + read_to(0xffff, data); + pc |= data << 8; + Last; + + + // NMI + case 0x0010: + read_idle(pc); + state = 0x0011; + break; + case 0x0011: + read_idle(pc); + state = 0x0012; + break; + case 0x0012: + write_byte(sp-- | 0x100, pc >> 8); + state = 0x0013; + break; + case 0x0013: + write_byte(sp-- | 0x100, pc); + state = 0x0014; + break; + case 0x0014: + push_flags(false); + i_flag = true; + state = 0x0015; + break; + case 0x0015: + read_to(0xfffa, pc); + state = 0x0016; + break; + case 0x0016: + read_to(0xfffb, data); + pc |= data << 8; + Last; + + + // Addressing modes: Fetch effective address, no extra cycles (-> ar) + case A_ZERO: + read_to(pc++, ar); + Execute; + + case A_ZEROX: + read_to(pc++, ar); + state = A_ZEROX1; + break; + case A_ZEROX1: + read_idle(ar); + ar = (ar + x) & 0xff; + Execute; + + case A_ZEROY: + read_to(pc++, ar); + state = A_ZEROY1; + break; + case A_ZEROY1: + read_idle(ar); + ar = (ar + y) & 0xff; + Execute; + + case A_ABS: + read_to(pc++, ar); + state = A_ABS1; + break; + case A_ABS1: + read_to(pc++, data); + ar = ar | (data << 8); + Execute; + + case A_ABSX: + read_to(pc++, ar); + state = A_ABSX1; + break; + case A_ABSX1: + read_to(pc++, ar2); // Note: Some undocumented opcodes rely on the value of ar2 + if (ar+x < 0x100) + state = A_ABSX2; + else + state = A_ABSX3; + ar = (ar + x) & 0xff | (ar2 << 8); + break; + case A_ABSX2: // No page crossed + read_idle(ar); + Execute; + case A_ABSX3: // Page crossed + read_idle(ar); + ar += 0x100; + Execute; + + case A_ABSY: + read_to(pc++, ar); + state = A_ABSY1; + break; + case A_ABSY1: + read_to(pc++, ar2); // Note: Some undocumented opcodes rely on the value of ar2 + if (ar+y < 0x100) + state = A_ABSY2; + else + state = A_ABSY3; + ar = (ar + y) & 0xff | (ar2 << 8); + break; + case A_ABSY2: // No page crossed + read_idle(ar); + Execute; + case A_ABSY3: // Page crossed + read_idle(ar); + ar += 0x100; + Execute; + + case A_INDX: + read_to(pc++, ar2); + state = A_INDX1; + break; + case A_INDX1: + read_idle(ar2); + ar2 = (ar2 + x) & 0xff; + state = A_INDX2; + break; + case A_INDX2: + read_to(ar2, ar); + state = A_INDX3; + break; + case A_INDX3: + read_to((ar2 + 1) & 0xff, data); + ar = ar | (data << 8); + Execute; + + case A_INDY: + read_to(pc++, ar2); + state = A_INDY1; + break; + case A_INDY1: + read_to(ar2, ar); + state = A_INDY2; + break; + case A_INDY2: + read_to((ar2 + 1) & 0xff, ar2); // Note: Some undocumented opcodes rely on the value of ar2 + if (ar+y < 0x100) + state = A_INDY3; + else + state = A_INDY4; + ar = (ar + y) & 0xff | (ar2 << 8); + break; + case A_INDY3: // No page crossed + read_idle(ar); + Execute; + case A_INDY4: // Page crossed + read_idle(ar); + ar += 0x100; + Execute; + + + // Addressing modes: Fetch effective address, extra cycle on page crossing (-> ar) + case AE_ABSX: + read_to(pc++, ar); + state = AE_ABSX1; + break; + case AE_ABSX1: + read_to(pc++, data); + if (ar+x < 0x100) { + ar = (ar + x) & 0xff | (data << 8); + Execute; + } else { + ar = (ar + x) & 0xff | (data << 8); + state = AE_ABSX2; + } + break; + case AE_ABSX2: // Page crossed + read_idle(ar); + ar += 0x100; + Execute; + + case AE_ABSY: + read_to(pc++, ar); + state = AE_ABSY1; + break; + case AE_ABSY1: + read_to(pc++, data); + if (ar+y < 0x100) { + ar = (ar + y) & 0xff | (data << 8); + Execute; + } else { + ar = (ar + y) & 0xff | (data << 8); + state = AE_ABSY2; + } + break; + case AE_ABSY2: // Page crossed + read_idle(ar); + ar += 0x100; + Execute; + + case AE_INDY: + read_to(pc++, ar2); + state = AE_INDY1; + break; + case AE_INDY1: + read_to(ar2, ar); + state = AE_INDY2; + break; + case AE_INDY2: + read_to((ar2 + 1) & 0xff, data); + if (ar+y < 0x100) { + ar = (ar + y) & 0xff | (data << 8); + Execute; + } else { + ar = (ar + y) & 0xff | (data << 8); + state = AE_INDY3; + } + break; + case AE_INDY3: // Page crossed + read_idle(ar); + ar += 0x100; + Execute; + + + // Addressing modes: Read operand, write it back, no extra cycles (-> ar, rdbuf) + case M_ZERO: + read_to(pc++, ar); + DoRMW; + + case M_ZEROX: + read_to(pc++, ar); + state = M_ZEROX1; + break; + case M_ZEROX1: + read_idle(ar); + ar = (ar + x) & 0xff; + DoRMW; + + case M_ZEROY: + read_to(pc++, ar); + state = M_ZEROY1; + break; + case M_ZEROY1: + read_idle(ar); + ar = (ar + y) & 0xff; + DoRMW; + + case M_ABS: + read_to(pc++, ar); + state = M_ABS1; + break; + case M_ABS1: + read_to(pc++, data); + ar = ar | (data << 8); + DoRMW; + + case M_ABSX: + read_to(pc++, ar); + state = M_ABSX1; + break; + case M_ABSX1: + read_to(pc++, data); + if (ar+x < 0x100) + state = M_ABSX2; + else + state = M_ABSX3; + ar = (ar + x) & 0xff | (data << 8); + break; + case M_ABSX2: // No page crossed + read_idle(ar); + DoRMW; + case M_ABSX3: // Page crossed + read_idle(ar); + ar += 0x100; + DoRMW; + + case M_ABSY: + read_to(pc++, ar); + state = M_ABSY1; + break; + case M_ABSY1: + read_to(pc++, data); + if (ar+y < 0x100) + state = M_ABSY2; + else + state = M_ABSY3; + ar = (ar + y) & 0xff | (data << 8); + break; + case M_ABSY2: // No page crossed + read_idle(ar); + DoRMW; + case M_ABSY3: // Page crossed + read_idle(ar); + ar += 0x100; + DoRMW; + + case M_INDX: + read_to(pc++, ar2); + state = M_INDX1; + break; + case M_INDX1: + read_idle(ar2); + ar2 = (ar2 + x) & 0xff; + state = M_INDX2; + break; + case M_INDX2: + read_to(ar2, ar); + state = M_INDX3; + break; + case M_INDX3: + read_to((ar2 + 1) & 0xff, data); + ar = ar | (data << 8); + DoRMW; + + case M_INDY: + read_to(pc++, ar2); + state = M_INDY1; + break; + case M_INDY1: + read_to(ar2, ar); + state = M_INDY2; + break; + case M_INDY2: + read_to((ar2 + 1) & 0xff, data); + if (ar+y < 0x100) + state = M_INDY3; + else + state = M_INDY4; + ar = (ar + y) & 0xff | (data << 8); + break; + case M_INDY3: // No page crossed + read_idle(ar); + DoRMW; + case M_INDY4: // Page crossed + read_idle(ar); + ar += 0x100; + DoRMW; + + case RMW_DO_IT: + read_to(ar, rdbuf); + state = RMW_DO_IT1; + break; + case RMW_DO_IT1: + write_byte(ar, rdbuf); + Execute; + + + // Load group + case O_LDA: + read_to(ar, data); + set_nz(a = data); + Last; + case O_LDA_I: + read_to(pc++, data); + set_nz(a = data); + Last; + + case O_LDX: + read_to(ar, data); + set_nz(x = data); + Last; + case O_LDX_I: + read_to(pc++, data); + set_nz(x = data); + Last; + + case O_LDY: + read_to(ar, data); + set_nz(y = data); + Last; + case O_LDY_I: + read_to(pc++, data); + set_nz(y = data); + Last; + + + // Store group + case O_STA: + write_byte(ar, a); + Last; + + case O_STX: + write_byte(ar, x); + Last; + + case O_STY: + write_byte(ar, y); + Last; + + + // Transfer group + case O_TAX: + read_idle(pc); + set_nz(x = a); + Last; + + case O_TXA: + read_idle(pc); + set_nz(a = x); + Last; + + case O_TAY: + read_idle(pc); + set_nz(y = a); + Last; + + case O_TYA: + read_idle(pc); + set_nz(a = y); + Last; + + case O_TSX: + read_idle(pc); + set_nz(x = sp); + Last; + + case O_TXS: + read_idle(pc); + sp = x; + Last; + + + // Arithmetic group + case O_ADC: + read_to(ar, data); + do_adc(data); + Last; + case O_ADC_I: + read_to(pc++, data); + do_adc(data); + Last; + + case O_SBC: + read_to(ar, data); + do_sbc(data); + Last; + case O_SBC_I: + read_to(pc++, data); + do_sbc(data); + Last; + + + // Increment/decrement group + case O_INX: + read_idle(pc); + set_nz(++x); + Last; + + case O_DEX: + read_idle(pc); + set_nz(--x); + Last; + + case O_INY: + read_idle(pc); + set_nz(++y); + Last; + + case O_DEY: + read_idle(pc); + set_nz(--y); + Last; + + case O_INC: + write_byte(ar, set_nz(rdbuf + 1)); + Last; + + case O_DEC: + write_byte(ar, set_nz(rdbuf - 1)); + Last; + + + // Logic group + case O_AND: + read_to(ar, data); + set_nz(a &= data); + Last; + case O_AND_I: + read_to(pc++, data); + set_nz(a &= data); + Last; + + case O_ORA: + read_to(ar, data); + set_nz(a |= data); + Last; + case O_ORA_I: + read_to(pc++, data); + set_nz(a |= data); + Last; + + case O_EOR: + read_to(ar, data); + set_nz(a ^= data); + Last; + case O_EOR_I: + read_to(pc++, data); + set_nz(a ^= data); + Last; + + // Compare group + case O_CMP: + read_to(ar, data); + set_nz(ar = a - data); + c_flag = ar < 0x100; + Last; + case O_CMP_I: + read_to(pc++, data); + set_nz(ar = a - data); + c_flag = ar < 0x100; + Last; + + case O_CPX: + read_to(ar, data); + set_nz(ar = x - data); + c_flag = ar < 0x100; + Last; + case O_CPX_I: + read_to(pc++, data); + set_nz(ar = x - data); + c_flag = ar < 0x100; + Last; + + case O_CPY: + read_to(ar, data); + set_nz(ar = y - data); + c_flag = ar < 0x100; + Last; + case O_CPY_I: + read_to(pc++, data); + set_nz(ar = y - data); + c_flag = ar < 0x100; + Last; + + + // Bit-test group + case O_BIT: + read_to(ar, data); + z_flag = a & data; + n_flag = data; + v_flag = data & 0x40; + Last; + + + // Shift/rotate group + case O_ASL: + c_flag = rdbuf & 0x80; + write_byte(ar, set_nz(rdbuf << 1)); + Last; + case O_ASL_A: + read_idle(pc); + c_flag = a & 0x80; + set_nz(a <<= 1); + Last; + + case O_LSR: + c_flag = rdbuf & 0x01; + write_byte(ar, set_nz(rdbuf >> 1)); + Last; + case O_LSR_A: + read_idle(pc); + c_flag = a & 0x01; + set_nz(a >>= 1); + Last; + + case O_ROL: + write_byte(ar, set_nz(c_flag ? (rdbuf << 1) | 0x01 : rdbuf << 1)); + c_flag = rdbuf & 0x80; + Last; + case O_ROL_A: + read_idle(pc); + data = a & 0x80; + set_nz(a = c_flag ? (a << 1) | 0x01 : a << 1); + c_flag = data; + Last; + + case O_ROR: + write_byte(ar, set_nz(c_flag ? (rdbuf >> 1) | 0x80 : rdbuf >> 1)); + c_flag = rdbuf & 0x01; + Last; + case O_ROR_A: + read_idle(pc); + data = a & 0x01; + set_nz(a = (c_flag ? (a >> 1) | 0x80 : a >> 1)); + c_flag = data; + Last; + + + // Stack group + case O_PHA: + read_idle(pc); + state = O_PHA1; + break; + case O_PHA1: + write_byte(sp-- | 0x100, a); + Last; + + case O_PLA: + read_idle(pc); + state = O_PLA1; + break; + case O_PLA1: + read_idle(sp++ | 0x100); + state = O_PLA2; + break; + case O_PLA2: + read_to(sp | 0x100, data); + set_nz(a = data); + Last; + + case O_PHP: + read_idle(pc); + state = O_PHP1; + break; + case O_PHP1: + push_flags(true); + Last; + + case O_PLP: + read_idle(pc); + state = O_PLP1; + break; + case O_PLP1: + read_idle(sp++ | 0x100); + state = O_PLP2; + break; + case O_PLP2: + pop_flags(); + Last; + + + // Jump/branch group + case O_JMP: + read_to(pc++, ar); + state = O_JMP1; + break; + case O_JMP1: + read_to(pc, data); + pc = (data << 8) | ar; + Last; + + case O_JMP_I: + read_to(ar, pc); + state = O_JMP_I1; + break; + case O_JMP_I1: + read_to((ar + 1) & 0xff | ar & 0xff00, data); + pc |= data << 8; + Last; + + case O_JSR: + read_to(pc++, ar); + state = O_JSR1; + break; + case O_JSR1: + read_idle(sp | 0x100); + state = O_JSR2; + break; + case O_JSR2: + write_byte(sp-- | 0x100, pc >> 8); + state = O_JSR3; + break; + case O_JSR3: + write_byte(sp-- | 0x100, pc); + state = O_JSR4; + break; + case O_JSR4: + read_to(pc++, data); + pc = ar | (data << 8); + Last; + + case O_RTS: + read_idle(pc); + state = O_RTS1; + break; + case O_RTS1: + read_idle(sp++ | 0x100); + state = O_RTS2; + break; + case O_RTS2: + read_to(sp++ | 0x100, pc); + state = O_RTS3; + break; + case O_RTS3: + read_to(sp | 0x100, data); + pc |= data << 8; + state = O_RTS4; + break; + case O_RTS4: + read_idle(pc++); + Last; + + case O_RTI: + read_idle(pc); + state = O_RTI1; + break; + case O_RTI1: + read_idle(sp++ | 0x100); + state = O_RTI2; + break; + case O_RTI2: + pop_flags(); + sp++; + state = O_RTI3; + break; + case O_RTI3: + read_to(sp++ | 0x100, pc); + state = O_RTI4; + break; + case O_RTI4: + read_to(sp | 0x100, data); + pc |= data << 8; + Last; + + case O_BRK: + read_idle(pc++); + state = O_BRK1; + break; + case O_BRK1: + write_byte(sp-- | 0x100, pc >> 8); + state = O_BRK2; + break; + case O_BRK2: + write_byte(sp-- | 0x100, pc); + state = O_BRK3; + break; + case O_BRK3: + push_flags(true); + i_flag = true; +#ifndef IS_CPU_1541 + if (interrupt.intr[INT_NMI]) { // BRK interrupted by NMI? + interrupt.intr[INT_NMI] = false; // Simulate an edge-triggered input + state = 0x0015; // Jump to NMI sequence + break; + } +#endif + state = O_BRK4; + break; + case O_BRK4: +#ifndef IS_CPU_1541 + first_nmi_cycle++; // Delay NMI +#endif + read_to(0xfffe, pc); + state = O_BRK5; + break; + case O_BRK5: + read_to(0xffff, data); + pc |= data << 8; + Last; + + case O_BCS: + Branch(c_flag); + + case O_BCC: + Branch(!c_flag); + + case O_BEQ: + Branch(!z_flag); + + case O_BNE: + Branch(z_flag); + + case O_BVS: +#ifndef IS_CPU_1541 + Branch(v_flag); +#else + Branch((via2_pcr & 0x0e) == 0x0e ? 1 : v_flag); // GCR byte ready flag +#endif + + case O_BVC: +#ifndef IS_CPU_1541 + Branch(!v_flag); +#else + Branch(!((via2_pcr & 0x0e) == 0x0e) ? 0 : v_flag); // GCR byte ready flag +#endif + + case O_BMI: + Branch(n_flag & 0x80); + + case O_BPL: + Branch(!(n_flag & 0x80)); + + case O_BRANCH_NP: // No page crossed + first_irq_cycle++; // Delay IRQ +#ifndef IS_CPU_1541 + first_nmi_cycle++; // Delay NMI +#endif + read_idle(pc); + pc = ar; + Last; + case O_BRANCH_BP: // Page crossed, branch backwards + read_idle(pc); + pc = ar; + state = O_BRANCH_BP1; + break; + case O_BRANCH_BP1: + read_idle(pc + 0x100); + Last; + case O_BRANCH_FP: // Page crossed, branch forwards + read_idle(pc); + pc = ar; + state = O_BRANCH_FP1; + break; + case O_BRANCH_FP1: + read_idle(pc - 0x100); + Last; + + + // Flag group + case O_SEC: + read_idle(pc); + c_flag = true; + Last; + + case O_CLC: + read_idle(pc); + c_flag = false; + Last; + + case O_SED: + read_idle(pc); + d_flag = true; + Last; + + case O_CLD: + read_idle(pc); + d_flag = false; + Last; + + case O_SEI: + read_idle(pc); + i_flag = true; + Last; + + case O_CLI: + read_idle(pc); + i_flag = false; + Last; + + case O_CLV: + read_idle(pc); + v_flag = false; + Last; + + + // NOP group + case O_NOP: + read_idle(pc); + Last; + + +/* + * Undocumented opcodes start here + */ + + // NOP group + case O_NOP_I: + read_idle(pc++); + Last; + + case O_NOP_A: + read_idle(ar); + Last; + + + // Load A/X group + case O_LAX: + read_to(ar, data); + set_nz(a = x = data); + Last; + + + // Store A/X group + case O_SAX: + write_byte(ar, a & x); + Last; + + + // ASL/ORA group + case O_SLO: + c_flag = rdbuf & 0x80; + rdbuf <<= 1; + write_byte(ar, rdbuf); + set_nz(a |= rdbuf); + Last; + + + // ROL/AND group + case O_RLA: + tmp = rdbuf & 0x80; + rdbuf = c_flag ? (rdbuf << 1) | 0x01 : rdbuf << 1; + c_flag = tmp; + write_byte(ar, rdbuf); + set_nz(a &= rdbuf); + Last; + + + // LSR/EOR group + case O_SRE: + c_flag = rdbuf & 0x01; + rdbuf >>= 1; + write_byte(ar, rdbuf); + set_nz(a ^= rdbuf); + Last; + + + // ROR/ADC group + case O_RRA: + tmp = rdbuf & 0x01; + rdbuf = c_flag ? (rdbuf >> 1) | 0x80 : rdbuf >> 1; + c_flag = tmp; + write_byte(ar, rdbuf); + do_adc(rdbuf); + Last; + + + // DEC/CMP group + case O_DCP: + write_byte(ar, --rdbuf); + set_nz(ar = a - rdbuf); + c_flag = ar < 0x100; + Last; + + + // INC/SBC group + case O_ISB: + write_byte(ar, ++rdbuf); + do_sbc(rdbuf); + Last; + + + // Complex functions + case O_ANC_I: + read_to(pc++, data); + set_nz(a &= data); + c_flag = n_flag & 0x80; + Last; + + case O_ASR_I: + read_to(pc++, data); + a &= data; + c_flag = a & 0x01; + set_nz(a >>= 1); + Last; + + case O_ARR_I: + read_to(pc++, data); + data &= a; + a = (c_flag ? (data >> 1) | 0x80 : data >> 1); + if (!d_flag) { + set_nz(a); + c_flag = a & 0x40; + v_flag = (a & 0x40) ^ ((a & 0x20) << 1); + } else { + n_flag = c_flag ? 0x80 : 0; + z_flag = a; + v_flag = (data ^ a) & 0x40; + if ((data & 0x0f) + (data & 0x01) > 5) + a = a & 0xf0 | (a + 6) & 0x0f; + if ((c_flag = ((data + (data & 0x10)) & 0x1f0) > 0x50) != 0) + a += 0x60; + } + Last; + + case O_ANE_I: + read_to(pc++, data); + set_nz(a = (a | 0xee) & x & data); + Last; + + case O_LXA_I: + read_to(pc++, data); + set_nz(a = x = (a | 0xee) & data); + Last; + + case O_SBX_I: + read_to(pc++, data); + set_nz(x = ar = (x & a) - data); + c_flag = ar < 0x100; + Last; + + case O_LAS: + read_to(ar, data); + set_nz(a = x = sp = data & sp); + Last; + + case O_SHS: // ar2 contains the high byte of the operand address + write_byte(ar, (ar2+1) & (sp = a & x)); + Last; + + case O_SHY: // ar2 contains the high byte of the operand address + write_byte(ar, y & (ar2+1)); + Last; + + case O_SHX: // ar2 contains the high byte of the operand address + write_byte(ar, x & (ar2+1)); + Last; + + case O_SHA: // ar2 contains the high byte of the operand address + write_byte(ar, a & x & (ar2+1)); + Last; diff --git a/Src/CPU_emulline.h b/Src/CPU_emulline.h new file mode 100644 index 0000000..66f0d10 --- /dev/null +++ b/Src/CPU_emulline.h @@ -0,0 +1,1418 @@ +/* + * CPU_emulline.h - 6510/6502 emulation core (body of + * EmulateLine() function, the same for + * both 6510 and 6502) + * + * Frodo (C) 1994-1997,2002-2005 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +/* + * Addressing mode macros + */ + +// Read immediate operand +#if PC_IS_POINTER +#define read_byte_imm() (*pc++) +#else +#define read_byte_imm() read_byte(pc++) +#endif + +// Read zeropage operand address +#define read_adr_zero() ((uint16)read_byte_imm()) + +// Read zeropage x-indexed operand address +#define read_adr_zero_x() ((read_byte_imm() + x) & 0xff) + +// Read zeropage y-indexed operand address +#define read_adr_zero_y() ((read_byte_imm() + y) & 0xff) + +// Read absolute operand address (uses adr!) +#if PC_IS_POINTER +#if LITTLE_ENDIAN_UNALIGNED +#define read_adr_abs() (adr = *(UWORD *)pc, pc+=2, adr) +#else +#define read_adr_abs() (adr = ((*(pc+1)) << 8) | *pc, pc+=2, adr) +#endif +#else +#define read_adr_abs() (adr = read_word(pc), pc+=2, adr) +#endif + +// Read absolute x-indexed operand address +#define read_adr_abs_x() (read_adr_abs() + x) + +// Read absolute y-indexed operand address +#define read_adr_abs_y() (read_adr_abs() + y) + +// Read indexed indirect operand address +#define read_adr_ind_x() (read_zp_word(read_byte_imm() + x)) + +// Read indirect indexed operand address +#define read_adr_ind_y() (read_zp_word(read_byte_imm()) + y) + +// Read zeropage operand +#define read_byte_zero() read_zp(read_adr_zero()) + +// Read zeropage x-indexed operand +#define read_byte_zero_x() read_zp(read_adr_zero_x()) + +// Read zeropage y-indexed operand +#define read_byte_zero_y() read_zp(read_adr_zero_y()) + +// Read absolute operand +#define read_byte_abs() read_byte(read_adr_abs()) + +#if PRECISE_CPU_CYCLES +// Acount for cyles due to crossing page boundaries +#define page_plus(exp, reg) \ + (adr = exp, page_cycles = (adr & 0xff) + reg >= 0x100, adr + reg) + +// Read absolute x-indexed operand +#define read_byte_abs_x() read_byte(page_plus(read_adr_abs(), x)) + +// Read absolute x-indexed operand +#define read_byte_abs_y() read_byte(page_plus(read_adr_abs(), y)) + +// Read indirect y-indexed operand +#define read_byte_ind_y() read_byte(page_plus(read_zp_word(read_byte_imm()), y)) + +#else + +// Read absolute x-indexed operand +#define read_byte_abs_x() read_byte(read_adr_abs_x()) + +// Read absolute x-indexed operand +#define read_byte_abs_y() read_byte(read_adr_abs_y()) + +// Read indirect y-indexed operand +#define read_byte_ind_y() read_byte(read_adr_ind_y()) +#endif + +// Read indexed indirect operand +#define read_byte_ind_x() read_byte(read_adr_ind_x()) + + +/* + * Set N and Z flags according to byte + */ + +#define set_nz(x) (z_flag = n_flag = (x)) + + +/* + * End of opcode, decrement cycles left + */ + +#define ENDOP(cyc) last_cycles = cyc; break; + + + // Main opcode fetch/execute loop +#if PRECISE_CPU_CYCLES + if (cycles_left != 1) + cycles_left -= borrowed_cycles; + int page_cycles = 0; + for (;;) { + if (last_cycles) { + last_cycles += page_cycles; + page_cycles = 0; +#if PRECISE_CIA_CYCLES && !defined(IS_CPU_1541) + TheCIA1->EmulateLine(last_cycles); + TheCIA2->EmulateLine(last_cycles); +#endif + } + if ((cycles_left -= last_cycles) < 0) { + borrowed_cycles = -cycles_left; + break; + } +#else + while ((cycles_left -= last_cycles) >= 0) { +#endif + + switch (read_byte_imm()) { + + + // Load group + case 0xa9: // LDA #imm + set_nz(a = read_byte_imm()); + ENDOP(2); + + case 0xa5: // LDA zero + set_nz(a = read_byte_zero()); + ENDOP(3); + + case 0xb5: // LDA zero,X + set_nz(a = read_byte_zero_x()); + ENDOP(4); + + case 0xad: // LDA abs + set_nz(a = read_byte_abs()); + ENDOP(4); + + case 0xbd: // LDA abs,X + set_nz(a = read_byte_abs_x()); + ENDOP(4); + + case 0xb9: // LDA abs,Y + set_nz(a = read_byte_abs_y()); + ENDOP(4); + + case 0xa1: // LDA (ind,X) + set_nz(a = read_byte_ind_x()); + ENDOP(6); + + case 0xb1: // LDA (ind),Y + set_nz(a = read_byte_ind_y()); + ENDOP(5); + + case 0xa2: // LDX #imm + set_nz(x = read_byte_imm()); + ENDOP(2); + + case 0xa6: // LDX zero + set_nz(x = read_byte_zero()); + ENDOP(3); + + case 0xb6: // LDX zero,Y + set_nz(x = read_byte_zero_y()); + ENDOP(4); + + case 0xae: // LDX abs + set_nz(x = read_byte_abs()); + ENDOP(4); + + case 0xbe: // LDX abs,Y + set_nz(x = read_byte_abs_y()); + ENDOP(4); + + case 0xa0: // LDY #imm + set_nz(y = read_byte_imm()); + ENDOP(2); + + case 0xa4: // LDY zero + set_nz(y = read_byte_zero()); + ENDOP(3); + + case 0xb4: // LDY zero,X + set_nz(y = read_byte_zero_x()); + ENDOP(4); + + case 0xac: // LDY abs + set_nz(y = read_byte_abs()); + ENDOP(4); + + case 0xbc: // LDY abs,X + set_nz(y = read_byte_abs_x()); + ENDOP(4); + + + // Store group + case 0x85: // STA zero + write_byte(read_adr_zero(), a); + ENDOP(3); + + case 0x95: // STA zero,X + write_byte(read_adr_zero_x(), a); + ENDOP(4); + + case 0x8d: // STA abs + write_byte(read_adr_abs(), a); + ENDOP(4); + + case 0x9d: // STA abs,X + write_byte(read_adr_abs_x(), a); + ENDOP(5); + + case 0x99: // STA abs,Y + write_byte(read_adr_abs_y(), a); + ENDOP(5); + + case 0x81: // STA (ind,X) + write_byte(read_adr_ind_x(), a); + ENDOP(6); + + case 0x91: // STA (ind),Y + write_byte(read_adr_ind_y(), a); + ENDOP(6); + + case 0x86: // STX zero + write_byte(read_adr_zero(), x); + ENDOP(3); + + case 0x96: // STX zero,Y + write_byte(read_adr_zero_y(), x); + ENDOP(4); + + case 0x8e: // STX abs + write_byte(read_adr_abs(), x); + ENDOP(4); + + case 0x84: // STY zero + write_byte(read_adr_zero(), y); + ENDOP(3); + + case 0x94: // STY zero,X + write_byte(read_adr_zero_x(), y); + ENDOP(4); + + case 0x8c: // STY abs + write_byte(read_adr_abs(), y); + ENDOP(4); + + + // Transfer group + case 0xaa: // TAX + set_nz(x = a); + ENDOP(2); + + case 0x8a: // TXA + set_nz(a = x); + ENDOP(2); + + case 0xa8: // TAY + set_nz(y = a); + ENDOP(2); + + case 0x98: // TYA + set_nz(a = y); + ENDOP(2); + + case 0xba: // TSX + set_nz(x = sp); + ENDOP(2); + + case 0x9a: // TXS + sp = x; + ENDOP(2); + + + // Arithmetic group + case 0x69: // ADC #imm + do_adc(read_byte_imm()); + ENDOP(2); + + case 0x65: // ADC zero + do_adc(read_byte_zero()); + ENDOP(3); + + case 0x75: // ADC zero,X + do_adc(read_byte_zero_x()); + ENDOP(4); + + case 0x6d: // ADC abs + do_adc(read_byte_abs()); + ENDOP(4); + + case 0x7d: // ADC abs,X + do_adc(read_byte_abs_x()); + ENDOP(4); + + case 0x79: // ADC abs,Y + do_adc(read_byte_abs_y()); + ENDOP(4); + + case 0x61: // ADC (ind,X) + do_adc(read_byte_ind_x()); + ENDOP(6); + + case 0x71: // ADC (ind),Y + do_adc(read_byte_ind_y()); + ENDOP(5); + + case 0xe9: // SBC #imm + case 0xeb: // Undocumented opcode + do_sbc(read_byte_imm()); + ENDOP(2); + + case 0xe5: // SBC zero + do_sbc(read_byte_zero()); + ENDOP(3); + + case 0xf5: // SBC zero,X + do_sbc(read_byte_zero_x()); + ENDOP(4); + + case 0xed: // SBC abs + do_sbc(read_byte_abs()); + ENDOP(4); + + case 0xfd: // SBC abs,X + do_sbc(read_byte_abs_x()); + ENDOP(4); + + case 0xf9: // SBC abs,Y + do_sbc(read_byte_abs_y()); + ENDOP(4); + + case 0xe1: // SBC (ind,X) + do_sbc(read_byte_ind_x()); + ENDOP(6); + + case 0xf1: // SBC (ind),Y + do_sbc(read_byte_ind_y()); + ENDOP(5); + + + // Increment/decrement group + case 0xe8: // INX + set_nz(++x); + ENDOP(2); + + case 0xca: // DEX + set_nz(--x); + ENDOP(2); + + case 0xc8: // INY + set_nz(++y); + ENDOP(2); + + case 0x88: // DEY + set_nz(--y); + ENDOP(2); + + case 0xe6: // INC zero + adr = read_adr_zero(); + write_zp(adr, set_nz(read_zp(adr) + 1)); + ENDOP(5); + + case 0xf6: // INC zero,X + adr = read_adr_zero_x(); + write_zp(adr, set_nz(read_zp(adr) + 1)); + ENDOP(6); + + case 0xee: // INC abs + adr = read_adr_abs(); + write_byte(adr, set_nz(read_byte(adr) + 1)); + ENDOP(6); + + case 0xfe: // INC abs,X + adr = read_adr_abs_x(); + write_byte(adr, set_nz(read_byte(adr) + 1)); + ENDOP(7); + + case 0xc6: // DEC zero + adr = read_adr_zero(); + write_zp(adr, set_nz(read_zp(adr) - 1)); + ENDOP(5); + + case 0xd6: // DEC zero,X + adr = read_adr_zero_x(); + write_zp(adr, set_nz(read_zp(adr) - 1)); + ENDOP(6); + + case 0xce: // DEC abs + adr = read_adr_abs(); + write_byte(adr, set_nz(read_byte(adr) - 1)); + ENDOP(6); + + case 0xde: // DEC abs,X + adr = read_adr_abs_x(); + write_byte(adr, set_nz(read_byte(adr) - 1)); + ENDOP(7); + + + // Logic group + case 0x29: // AND #imm + set_nz(a &= read_byte_imm()); + ENDOP(2); + + case 0x25: // AND zero + set_nz(a &= read_byte_zero()); + ENDOP(3); + + case 0x35: // AND zero,X + set_nz(a &= read_byte_zero_x()); + ENDOP(4); + + case 0x2d: // AND abs + set_nz(a &= read_byte_abs()); + ENDOP(4); + + case 0x3d: // AND abs,X + set_nz(a &= read_byte_abs_x()); + ENDOP(4); + + case 0x39: // AND abs,Y + set_nz(a &= read_byte_abs_y()); + ENDOP(4); + + case 0x21: // AND (ind,X) + set_nz(a &= read_byte_ind_x()); + ENDOP(6); + + case 0x31: // AND (ind),Y + set_nz(a &= read_byte_ind_y()); + ENDOP(5); + + case 0x09: // ORA #imm + set_nz(a |= read_byte_imm()); + ENDOP(2); + + case 0x05: // ORA zero + set_nz(a |= read_byte_zero()); + ENDOP(3); + + case 0x15: // ORA zero,X + set_nz(a |= read_byte_zero_x()); + ENDOP(4); + + case 0x0d: // ORA abs + set_nz(a |= read_byte_abs()); + ENDOP(4); + + case 0x1d: // ORA abs,X + set_nz(a |= read_byte_abs_x()); + ENDOP(4); + + case 0x19: // ORA abs,Y + set_nz(a |= read_byte_abs_y()); + ENDOP(4); + + case 0x01: // ORA (ind,X) + set_nz(a |= read_byte_ind_x()); + ENDOP(6); + + case 0x11: // ORA (ind),Y + set_nz(a |= read_byte_ind_y()); + ENDOP(5); + + case 0x49: // EOR #imm + set_nz(a ^= read_byte_imm()); + ENDOP(2); + + case 0x45: // EOR zero + set_nz(a ^= read_byte_zero()); + ENDOP(3); + + case 0x55: // EOR zero,X + set_nz(a ^= read_byte_zero_x()); + ENDOP(4); + + case 0x4d: // EOR abs + set_nz(a ^= read_byte_abs()); + ENDOP(4); + + case 0x5d: // EOR abs,X + set_nz(a ^= read_byte_abs_x()); + ENDOP(4); + + case 0x59: // EOR abs,Y + set_nz(a ^= read_byte_abs_y()); + ENDOP(4); + + case 0x41: // EOR (ind,X) + set_nz(a ^= read_byte_ind_x()); + ENDOP(6); + + case 0x51: // EOR (ind),Y + set_nz(a ^= read_byte_ind_y()); + ENDOP(5); + + + // Compare group + case 0xc9: // CMP #imm + set_nz(adr = a - read_byte_imm()); + c_flag = adr < 0x100; + ENDOP(2); + + case 0xc5: // CMP zero + set_nz(adr = a - read_byte_zero()); + c_flag = adr < 0x100; + ENDOP(3); + + case 0xd5: // CMP zero,X + set_nz(adr = a - read_byte_zero_x()); + c_flag = adr < 0x100; + ENDOP(4); + + case 0xcd: // CMP abs + set_nz(adr = a - read_byte_abs()); + c_flag = adr < 0x100; + ENDOP(4); + + case 0xdd: // CMP abs,X + set_nz(adr = a - read_byte_abs_x()); + c_flag = adr < 0x100; + ENDOP(4); + + case 0xd9: // CMP abs,Y + set_nz(adr = a - read_byte_abs_y()); + c_flag = adr < 0x100; + ENDOP(4); + + case 0xc1: // CMP (ind,X) + set_nz(adr = a - read_byte_ind_x()); + c_flag = adr < 0x100; + ENDOP(6); + + case 0xd1: // CMP (ind),Y + set_nz(adr = a - read_byte_ind_y()); + c_flag = adr < 0x100; + ENDOP(5); + + case 0xe0: // CPX #imm + set_nz(adr = x - read_byte_imm()); + c_flag = adr < 0x100; + ENDOP(2); + + case 0xe4: // CPX zero + set_nz(adr = x - read_byte_zero()); + c_flag = adr < 0x100; + ENDOP(3); + + case 0xec: // CPX abs + set_nz(adr = x - read_byte_abs()); + c_flag = adr < 0x100; + ENDOP(4); + + case 0xc0: // CPY #imm + set_nz(adr = y - read_byte_imm()); + c_flag = adr < 0x100; + ENDOP(2); + + case 0xc4: // CPY zero + set_nz(adr = y - read_byte_zero()); + c_flag = adr < 0x100; + ENDOP(3); + + case 0xcc: // CPY abs + set_nz(adr = y - read_byte_abs()); + c_flag = adr < 0x100; + ENDOP(4); + + + // Bit-test group + case 0x24: // BIT zero + z_flag = a & (tmp = read_byte_zero()); + n_flag = tmp; + v_flag = tmp & 0x40; + ENDOP(3); + + case 0x2c: // BIT abs + z_flag = a & (tmp = read_byte_abs()); + n_flag = tmp; + v_flag = tmp & 0x40; + ENDOP(4); + + + // Shift/rotate group + case 0x0a: // ASL A + c_flag = a & 0x80; + set_nz(a <<= 1); + ENDOP(2); + + case 0x06: // ASL zero + tmp = read_zp(adr = read_adr_zero()); + c_flag = tmp & 0x80; + write_zp(adr, set_nz(tmp << 1)); + ENDOP(5); + + case 0x16: // ASL zero,X + tmp = read_zp(adr = read_adr_zero_x()); + c_flag = tmp & 0x80; + write_zp(adr, set_nz(tmp << 1)); + ENDOP(6); + + case 0x0e: // ASL abs + tmp = read_byte(adr = read_adr_abs()); + c_flag = tmp & 0x80; + write_byte(adr, set_nz(tmp << 1)); + ENDOP(6); + + case 0x1e: // ASL abs,X + tmp = read_byte(adr = read_adr_abs_x()); + c_flag = tmp & 0x80; + write_byte(adr, set_nz(tmp << 1)); + ENDOP(7); + + case 0x4a: // LSR A + c_flag = a & 0x01; + set_nz(a >>= 1); + ENDOP(2); + + case 0x46: // LSR zero + tmp = read_zp(adr = read_adr_zero()); + c_flag = tmp & 0x01; + write_zp(adr, set_nz(tmp >> 1)); + ENDOP(5); + + case 0x56: // LSR zero,X + tmp = read_zp(adr = read_adr_zero_x()); + c_flag = tmp & 0x01; + write_zp(adr, set_nz(tmp >> 1)); + ENDOP(6); + + case 0x4e: // LSR abs + tmp = read_byte(adr = read_adr_abs()); + c_flag = tmp & 0x01; + write_byte(adr, set_nz(tmp >> 1)); + ENDOP(6); + + case 0x5e: // LSR abs,X + tmp = read_byte(adr = read_adr_abs_x()); + c_flag = tmp & 0x01; + write_byte(adr, set_nz(tmp >> 1)); + ENDOP(7); + + case 0x2a: // ROL A + tmp2 = a & 0x80; + set_nz(a = c_flag ? (a << 1) | 0x01 : a << 1); + c_flag = tmp2; + ENDOP(2); + + case 0x26: // ROL zero + tmp = read_zp(adr = read_adr_zero()); + tmp2 = tmp & 0x80; + write_zp(adr, set_nz(c_flag ? (tmp << 1) | 0x01 : tmp << 1)); + c_flag = tmp2; + ENDOP(5); + + case 0x36: // ROL zero,X + tmp = read_zp(adr = read_adr_zero_x()); + tmp2 = tmp & 0x80; + write_zp(adr, set_nz(c_flag ? (tmp << 1) | 0x01 : tmp << 1)); + c_flag = tmp2; + ENDOP(6); + + case 0x2e: // ROL abs + tmp = read_byte(adr = read_adr_abs()); + tmp2 = tmp & 0x80; + write_byte(adr, set_nz(c_flag ? (tmp << 1) | 0x01 : tmp << 1)); + c_flag = tmp2; + ENDOP(6); + + case 0x3e: // ROL abs,X + tmp = read_byte(adr = read_adr_abs_x()); + tmp2 = tmp & 0x80; + write_byte(adr, set_nz(c_flag ? (tmp << 1) | 0x01 : tmp << 1)); + c_flag = tmp2; + ENDOP(7); + + case 0x6a: // ROR A + tmp2 = a & 0x01; + set_nz(a = (c_flag ? (a >> 1) | 0x80 : a >> 1)); + c_flag = tmp2; + ENDOP(2); + + case 0x66: // ROR zero + tmp = read_zp(adr = read_adr_zero()); + tmp2 = tmp & 0x01; + write_zp(adr, set_nz(c_flag ? (tmp >> 1) | 0x80 : tmp >> 1)); + c_flag = tmp2; + ENDOP(5); + + case 0x76: // ROR zero,X + tmp = read_zp(adr = read_adr_zero_x()); + tmp2 = tmp & 0x01; + write_zp(adr, set_nz(c_flag ? (tmp >> 1) | 0x80 : tmp >> 1)); + c_flag = tmp2; + ENDOP(6); + + case 0x6e: // ROR abs + tmp = read_byte(adr = read_adr_abs()); + tmp2 = tmp & 0x01; + write_byte(adr, set_nz(c_flag ? (tmp >> 1) | 0x80 : tmp >> 1)); + c_flag = tmp2; + ENDOP(6); + + case 0x7e: // ROR abs,X + tmp = read_byte(adr = read_adr_abs_x()); + tmp2 = tmp & 0x01; + write_byte(adr, set_nz(c_flag ? (tmp >> 1) | 0x80 : tmp >> 1)); + c_flag = tmp2; + ENDOP(7); + + + // Stack group + case 0x48: // PHA + push_byte(a); + ENDOP(3); + + case 0x68: // PLA + set_nz(a = pop_byte()); + ENDOP(4); + + case 0x08: // PHP + push_flags(true); + ENDOP(3); + + case 0x28: // PLP + pop_flags(); + if (interrupt.intr_any && !i_flag) + goto handle_int; + ENDOP(4); + + + // Jump/branch group + case 0x4c: // JMP abs + adr = read_adr_abs(); + jump(adr); + ENDOP(3); + + case 0x6c: // JMP (ind) + adr = read_adr_abs(); + adr = read_byte(adr) | (read_byte((adr + 1) & 0xff | adr & 0xff00) << 8); + jump(adr); + ENDOP(5); + + case 0x20: // JSR abs +#if PC_IS_POINTER + push_byte((pc-pc_base+1) >> 8); push_byte(pc-pc_base+1); +#else + push_byte(pc+1 >> 8); push_byte(pc+1); +#endif + adr = read_adr_abs(); + jump(adr); + ENDOP(6); + + case 0x60: // RTS + adr = pop_byte(); // Split because of pop_byte ++sp side-effect + adr = (adr | pop_byte() << 8) + 1; + jump(adr); + ENDOP(6); + + case 0x40: // RTI + pop_flags(); + adr = pop_byte(); // Split because of pop_byte ++sp side-effect + adr = adr | pop_byte() << 8; + jump(adr); + if (interrupt.intr_any && !i_flag) + goto handle_int; + ENDOP(6); + + case 0x00: // BRK +#if PC_IS_POINTER + push_byte((pc+1-pc_base) >> 8); push_byte(pc+1-pc_base); +#else + push_byte((pc+1) >> 8); push_byte(pc+1); +#endif + push_flags(true); + i_flag = true; + adr = read_word(0xfffe); + jump(adr); + ENDOP(7); + +#if PC_IS_POINTER +#if PRECISE_CPU_CYCLES +#define Branch(flag) \ + if (flag) { \ + pc += (int8)*pc + 1; \ + if (((pc-pc_base) ^ (old_pc - pc_base)) & 0xff00) { \ + ENDOP(4); \ + } else { \ + ENDOP(3); \ + } \ + } else { \ + pc++; \ + ENDOP(2); \ + } +#else +#define Branch(flag) \ + if (flag) { \ + pc += (int8)*pc + 1; \ + ENDOP(3); \ + } else { \ + pc++; \ + ENDOP(2); \ + } +#endif +#else +#define Branch(flag) \ + if (flag) { \ + uint16 old_pc = pc; \ + pc += (int8)read_byte(pc) + 1; \ + if ((pc ^ old_pc) & 0xff00) { \ + ENDOP(4); \ + } else { \ + ENDOP(3); \ + } \ + } else { \ + pc++; \ + ENDOP(2); \ + } +#endif + + case 0xb0: // BCS rel + Branch(c_flag); + + case 0x90: // BCC rel + Branch(!c_flag); + + case 0xf0: // BEQ rel + Branch(!z_flag); + + case 0xd0: // BNE rel + Branch(z_flag); + + case 0x70: // BVS rel +#ifndef IS_CPU_1541 + Branch(v_flag); +#else + Branch((via2_pcr & 0x0e) == 0x0e ? 1 : v_flag); // GCR byte ready flag +#endif + + case 0x50: // BVC rel +#ifndef IS_CPU_1541 + Branch(!v_flag); +#else + Branch(!((via2_pcr & 0x0e) == 0x0e) ? 0 : v_flag); // GCR byte ready flag +#endif + + case 0x30: // BMI rel + Branch(n_flag & 0x80); + + case 0x10: // BPL rel + Branch(!(n_flag & 0x80)); + + + // Flags group + case 0x38: // SEC + c_flag = true; + ENDOP(2); + + case 0x18: // CLC + c_flag = false; + ENDOP(2); + + case 0xf8: // SED + d_flag = true; + ENDOP(2); + + case 0xd8: // CLD + d_flag = false; + ENDOP(2); + + case 0x78: // SEI + i_flag = true; + ENDOP(2); + + case 0x58: // CLI + i_flag = false; + if (interrupt.intr_any) + goto handle_int; + ENDOP(2); + + case 0xb8: // CLV + v_flag = false; + ENDOP(2); + + + // NOP group + case 0xea: // NOP + ENDOP(2); + + +/* + * Undocumented opcodes start here + */ + + // NOP group + case 0x1a: // NOP + case 0x3a: + case 0x5a: + case 0x7a: + case 0xda: + case 0xfa: + ENDOP(2); + + case 0x80: // NOP #imm + case 0x82: + case 0x89: + case 0xc2: + case 0xe2: + pc++; + ENDOP(2); + + case 0x04: // NOP zero + case 0x44: + case 0x64: + pc++; + ENDOP(3); + + case 0x14: // NOP zero,X + case 0x34: + case 0x54: + case 0x74: + case 0xd4: + case 0xf4: + pc++; + ENDOP(4); + + case 0x0c: // NOP abs + pc+=2; + ENDOP(4); + + case 0x1c: // NOP abs,X + case 0x3c: + case 0x5c: + case 0x7c: + case 0xdc: + case 0xfc: +#if PRECISE_CPU_CYCLES + read_byte_abs_x(); +#else + pc+=2; +#endif + ENDOP(4); + + + // Load A/X group + case 0xa7: // LAX zero + set_nz(a = x = read_byte_zero()); + ENDOP(3); + + case 0xb7: // LAX zero,Y + set_nz(a = x = read_byte_zero_y()); + ENDOP(4); + + case 0xaf: // LAX abs + set_nz(a = x = read_byte_abs()); + ENDOP(4); + + case 0xbf: // LAX abs,Y + set_nz(a = x = read_byte_abs_y()); + ENDOP(4); + + case 0xa3: // LAX (ind,X) + set_nz(a = x = read_byte_ind_x()); + ENDOP(6); + + case 0xb3: // LAX (ind),Y + set_nz(a = x = read_byte_ind_y()); + ENDOP(5); + + + // Store A/X group + case 0x87: // SAX zero + write_byte(read_adr_zero(), a & x); + ENDOP(3); + + case 0x97: // SAX zero,Y + write_byte(read_adr_zero_y(), a & x); + ENDOP(4); + + case 0x8f: // SAX abs + write_byte(read_adr_abs(), a & x); + ENDOP(4); + + case 0x83: // SAX (ind,X) + write_byte(read_adr_ind_x(), a & x); + ENDOP(6); + + + // ASL/ORA group +#define ShiftLeftOr \ + c_flag = tmp & 0x80; \ + tmp <<= 1; \ + set_nz(a |= tmp); + + case 0x07: // SLO zero + tmp = read_zp(adr = read_adr_zero()); + ShiftLeftOr; + write_zp(adr, tmp); + ENDOP(5); + + case 0x17: // SLO zero,X + tmp = read_zp(adr = read_adr_zero_x()); + ShiftLeftOr; + write_zp(adr, tmp); + ENDOP(6); + + case 0x0f: // SLO abs + tmp = read_byte(adr = read_adr_abs()); + ShiftLeftOr; + write_byte(adr, tmp); + ENDOP(6); + + case 0x1f: // SLO abs,X + tmp = read_byte(adr = read_adr_abs_x()); + ShiftLeftOr; + write_byte(adr, tmp); + ENDOP(7); + + case 0x1b: // SLO abs,Y + tmp = read_byte(adr = read_adr_abs_y()); + ShiftLeftOr; + write_byte(adr, tmp); + ENDOP(7); + + case 0x03: // SLO (ind,X) + tmp = read_byte(adr = read_adr_ind_x()); + ShiftLeftOr; + write_byte(adr, tmp); + ENDOP(8); + + case 0x13: // SLO (ind),Y + tmp = read_byte(adr = read_adr_ind_y()); + ShiftLeftOr; + write_byte(adr, tmp); + ENDOP(8); + + + // ROL/AND group +#define RoLeftAnd \ + tmp2 = tmp & 0x80; \ + tmp = c_flag ? (tmp << 1) | 0x01 : tmp << 1; \ + set_nz(a &= tmp); \ + c_flag = tmp2; + + case 0x27: // RLA zero + tmp = read_zp(adr = read_adr_zero()); + RoLeftAnd; + write_zp(adr, tmp); + ENDOP(5); + + case 0x37: // RLA zero,X + tmp = read_zp(adr = read_adr_zero_x()); + RoLeftAnd; + write_zp(adr, tmp); + ENDOP(6); + + case 0x2f: // RLA abs + tmp = read_byte(adr = read_adr_abs()); + RoLeftAnd; + write_byte(adr, tmp); + ENDOP(6); + + case 0x3f: // RLA abs,X + tmp = read_byte(adr = read_adr_abs_x()); + RoLeftAnd; + write_byte(adr, tmp); + ENDOP(7); + + case 0x3b: // RLA abs,Y + tmp = read_byte(adr = read_adr_abs_y()); + RoLeftAnd; + write_byte(adr, tmp); + ENDOP(7); + + case 0x23: // RLA (ind,X) + tmp = read_byte(adr = read_adr_ind_x()); + RoLeftAnd; + write_byte(adr, tmp); + ENDOP(8); + + case 0x33: // RLA (ind),Y + tmp = read_byte(adr = read_adr_ind_y()); + RoLeftAnd; + write_byte(adr, tmp); + ENDOP(8); + + + // LSR/EOR group +#define ShiftRightEor \ + c_flag = tmp & 0x01; \ + tmp >>= 1; \ + set_nz(a ^= tmp); + + case 0x47: // SRE zero + tmp = read_zp(adr = read_adr_zero()); + ShiftRightEor; + write_zp(adr, tmp); + ENDOP(5); + + case 0x57: // SRE zero,X + tmp = read_zp(adr = read_adr_zero_x()); + ShiftRightEor; + write_zp(adr, tmp); + ENDOP(6); + + case 0x4f: // SRE abs + tmp = read_byte(adr = read_adr_abs()); + ShiftRightEor; + write_byte(adr, tmp); + ENDOP(6); + + case 0x5f: // SRE abs,X + tmp = read_byte(adr = read_adr_abs_x()); + ShiftRightEor; + write_byte(adr, tmp); + ENDOP(7); + + case 0x5b: // SRE abs,Y + tmp = read_byte(adr = read_adr_abs_y()); + ShiftRightEor; + write_byte(adr, tmp); + ENDOP(7); + + case 0x43: // SRE (ind,X) + tmp = read_byte(adr = read_adr_ind_x()); + ShiftRightEor; + write_byte(adr, tmp); + ENDOP(8); + + case 0x53: // SRE (ind),Y + tmp = read_byte(adr = read_adr_ind_y()); + ShiftRightEor; + write_byte(adr, tmp); + ENDOP(8); + + + // ROR/ADC group +#define RoRightAdc \ + tmp2 = tmp & 0x01; \ + tmp = c_flag ? (tmp >> 1) | 0x80 : tmp >> 1; \ + c_flag = tmp2; \ + do_adc(tmp); + + case 0x67: // RRA zero + tmp = read_zp(adr = read_adr_zero()); + RoRightAdc; + write_zp(adr, tmp); + ENDOP(5); + + case 0x77: // RRA zero,X + tmp = read_zp(adr = read_adr_zero_x()); + RoRightAdc; + write_zp(adr, tmp); + ENDOP(6); + + case 0x6f: // RRA abs + tmp = read_byte(adr = read_adr_abs()); + RoRightAdc; + write_byte(adr, tmp); + ENDOP(6); + + case 0x7f: // RRA abs,X + tmp = read_byte(adr = read_adr_abs_x()); + RoRightAdc; + write_byte(adr, tmp); + ENDOP(7); + + case 0x7b: // RRA abs,Y + tmp = read_byte(adr = read_adr_abs_y()); + RoRightAdc; + write_byte(adr, tmp); + ENDOP(7); + + case 0x63: // RRA (ind,X) + tmp = read_byte(adr = read_adr_ind_x()); + RoRightAdc; + write_byte(adr, tmp); + ENDOP(8); + + case 0x73: // RRA (ind),Y + tmp = read_byte(adr = read_adr_ind_y()); + RoRightAdc; + write_byte(adr, tmp); + ENDOP(8); + + + // DEC/CMP group +#define DecCompare \ + set_nz(adr = a - tmp); \ + c_flag = adr < 0x100; + + case 0xc7: // DCP zero + tmp = read_zp(adr = read_adr_zero()) - 1; + write_zp(adr, tmp); + DecCompare; + ENDOP(5); + + case 0xd7: // DCP zero,X + tmp = read_zp(adr = read_adr_zero_x()) - 1; + write_zp(adr, tmp); + DecCompare; + ENDOP(6); + + case 0xcf: // DCP abs + tmp = read_byte(adr = read_adr_abs()) - 1; + write_byte(adr, tmp); + DecCompare; + ENDOP(6); + + case 0xdf: // DCP abs,X + tmp = read_byte(adr = read_adr_abs_x()) - 1; + write_byte(adr, tmp); + DecCompare; + ENDOP(7); + + case 0xdb: // DCP abs,Y + tmp = read_byte(adr = read_adr_abs_y()) - 1; + write_byte(adr, tmp); + DecCompare; + ENDOP(7); + + case 0xc3: // DCP (ind,X) + tmp = read_byte(adr = read_adr_ind_x()) - 1; + write_byte(adr, tmp); + DecCompare; + ENDOP(8); + + case 0xd3: // DCP (ind),Y + tmp = read_byte(adr = read_adr_ind_y()) - 1; + write_byte(adr, tmp); + DecCompare; + ENDOP(8); + + + // INC/SBC group + case 0xe7: // ISB zero + tmp = read_zp(adr = read_adr_zero()) + 1; + do_sbc(tmp); + write_zp(adr, tmp); + ENDOP(5); + + case 0xf7: // ISB zero,X + tmp = read_zp(adr = read_adr_zero_x()) + 1; + do_sbc(tmp); + write_zp(adr, tmp); + ENDOP(6); + + case 0xef: // ISB abs + tmp = read_byte(adr = read_adr_abs()) + 1; + do_sbc(tmp); + write_byte(adr, tmp); + ENDOP(6); + + case 0xff: // ISB abs,X + tmp = read_byte(adr = read_adr_abs_x()) + 1; + do_sbc(tmp); + write_byte(adr, tmp); + ENDOP(7); + + case 0xfb: // ISB abs,Y + tmp = read_byte(adr = read_adr_abs_y()) + 1; + do_sbc(tmp); + write_byte(adr, tmp); + ENDOP(7); + + case 0xe3: // ISB (ind,X) + tmp = read_byte(adr = read_adr_ind_x()) + 1; + do_sbc(tmp); + write_byte(adr, tmp); + ENDOP(8); + + case 0xf3: // ISB (ind),Y + tmp = read_byte(adr = read_adr_ind_y()) + 1; + do_sbc(tmp); + write_byte(adr, tmp); + ENDOP(8); + + + // Complex functions + case 0x0b: // ANC #imm + case 0x2b: + set_nz(a &= read_byte_imm()); + c_flag = n_flag & 0x80; + ENDOP(2); + + case 0x4b: // ASR #imm + a &= read_byte_imm(); + c_flag = a & 0x01; + set_nz(a >>= 1); + ENDOP(2); + + case 0x6b: // ARR #imm + tmp2 = read_byte_imm() & a; + a = (c_flag ? (tmp2 >> 1) | 0x80 : tmp2 >> 1); + if (!d_flag) { + set_nz(a); + c_flag = a & 0x40; + v_flag = (a & 0x40) ^ ((a & 0x20) << 1); + } else { + n_flag = c_flag ? 0x80 : 0; + z_flag = a; + v_flag = (tmp2 ^ a) & 0x40; + if ((tmp2 & 0x0f) + (tmp2 & 0x01) > 5) + a = a & 0xf0 | (a + 6) & 0x0f; + if ((c_flag = ((tmp2 + (tmp2 & 0x10)) & 0x1f0) > 0x50) != 0) + a += 0x60; + } + ENDOP(2); + + case 0x8b: // ANE #imm + set_nz(a = read_byte_imm() & x & (a | 0xee)); + ENDOP(2); + + case 0x93: // SHA (ind),Y +#if PC_IS_POINTER + tmp2 = read_zp(pc[0] + 1); +#else + tmp2 = read_zp(read_byte(pc) + 1); +#endif + write_byte(read_adr_ind_y(), a & x & (tmp2+1)); + ENDOP(6); + + case 0x9b: // SHS abs,Y +#if PC_IS_POINTER + tmp2 = pc[1]; +#else + tmp2 = read_byte(pc+1); +#endif + write_byte(read_adr_abs_y(), a & x & (tmp2+1)); + sp = a & x; + ENDOP(5); + + case 0x9c: // SHY abs,X +#if PC_IS_POINTER + tmp2 = pc[1]; +#else + tmp2 = read_byte(pc+1); +#endif + write_byte(read_adr_abs_x(), y & (tmp2+1)); + ENDOP(5); + + case 0x9e: // SHX abs,Y +#if PC_IS_POINTER + tmp2 = pc[1]; +#else + tmp2 = read_byte(pc+1); +#endif + write_byte(read_adr_abs_y(), x & (tmp2+1)); + ENDOP(5); + + case 0x9f: // SHA abs,Y +#if PC_IS_POINTER + tmp2 = pc[1]; +#else + tmp2 = read_byte(pc+1); +#endif + write_byte(read_adr_abs_y(), a & x & (tmp2+1)); + ENDOP(5); + + case 0xab: // LXA #imm + set_nz(a = x = (a | 0xee) & read_byte_imm()); + ENDOP(2); + + case 0xbb: // LAS abs,Y + set_nz(a = x = sp = read_byte_abs_y() & sp); + ENDOP(4); + + case 0xcb: // SBX #imm + x &= a; + adr = x - read_byte_imm(); + c_flag = adr < 0x100; + set_nz(x = adr); + ENDOP(2); + + case 0x02: + case 0x12: + case 0x22: + case 0x32: + case 0x42: + case 0x52: + case 0x62: + case 0x72: + case 0x92: + case 0xb2: + case 0xd2: +#if PC_IS_POINTER + illegal_op(*(pc-1), pc-pc_base-1); +#else + illegal_op(read_byte(pc-1), pc-1); +#endif + break; diff --git a/Src/Char_ROM.h b/Src/Char_ROM.h new file mode 100644 index 0000000..2d7a863 --- /dev/null +++ b/Src/Char_ROM.h @@ -0,0 +1,521 @@ +/* + * Char_ROM.h - C64 Character ROM + * + * Frodo (C) 1994-1997,2002-2005 Christian Bauer + * C64/1541 ROMs (C) Commodore Business Machines + */ + +static const uint8 builtin_char_rom[CHAR_ROM_SIZE] = { + 0x3c, 0x66, 0x6e, 0x6e, 0x60, 0x62, 0x3c, 0x00, + 0x18, 0x3c, 0x66, 0x7e, 0x66, 0x66, 0x66, 0x00, + 0x7c, 0x66, 0x66, 0x7c, 0x66, 0x66, 0x7c, 0x00, + 0x3c, 0x66, 0x60, 0x60, 0x60, 0x66, 0x3c, 0x00, + 0x78, 0x6c, 0x66, 0x66, 0x66, 0x6c, 0x78, 0x00, + 0x7e, 0x60, 0x60, 0x78, 0x60, 0x60, 0x7e, 0x00, + 0x7e, 0x60, 0x60, 0x78, 0x60, 0x60, 0x60, 0x00, + 0x3c, 0x66, 0x60, 0x6e, 0x66, 0x66, 0x3c, 0x00, + 0x66, 0x66, 0x66, 0x7e, 0x66, 0x66, 0x66, 0x00, + 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, + 0x1e, 0x0c, 0x0c, 0x0c, 0x0c, 0x6c, 0x38, 0x00, + 0x66, 0x6c, 0x78, 0x70, 0x78, 0x6c, 0x66, 0x00, + 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x7e, 0x00, + 0x63, 0x77, 0x7f, 0x6b, 0x63, 0x63, 0x63, 0x00, + 0x66, 0x76, 0x7e, 0x7e, 0x6e, 0x66, 0x66, 0x00, + 0x3c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, + 0x7c, 0x66, 0x66, 0x7c, 0x60, 0x60, 0x60, 0x00, + 0x3c, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x0e, 0x00, + 0x7c, 0x66, 0x66, 0x7c, 0x78, 0x6c, 0x66, 0x00, + 0x3c, 0x66, 0x60, 0x3c, 0x06, 0x66, 0x3c, 0x00, + 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, + 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, + 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x00, + 0x63, 0x63, 0x63, 0x6b, 0x7f, 0x77, 0x63, 0x00, + 0x66, 0x66, 0x3c, 0x18, 0x3c, 0x66, 0x66, 0x00, + 0x66, 0x66, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x00, + 0x7e, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x7e, 0x00, + 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3c, 0x00, + 0x0c, 0x12, 0x30, 0x7c, 0x30, 0x62, 0xfc, 0x00, + 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3c, 0x00, + 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, + 0x00, 0x10, 0x30, 0x7f, 0x7f, 0x30, 0x10, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x18, 0x00, + 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x66, 0x66, 0xff, 0x66, 0xff, 0x66, 0x66, 0x00, + 0x18, 0x3e, 0x60, 0x3c, 0x06, 0x7c, 0x18, 0x00, + 0x62, 0x66, 0x0c, 0x18, 0x30, 0x66, 0x46, 0x00, + 0x3c, 0x66, 0x3c, 0x38, 0x67, 0x66, 0x3f, 0x00, + 0x06, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0c, 0x18, 0x30, 0x30, 0x30, 0x18, 0x0c, 0x00, + 0x30, 0x18, 0x0c, 0x0c, 0x0c, 0x18, 0x30, 0x00, + 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00, + 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, + 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, + 0x00, 0x03, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x00, + 0x3c, 0x66, 0x6e, 0x76, 0x66, 0x66, 0x3c, 0x00, + 0x18, 0x18, 0x38, 0x18, 0x18, 0x18, 0x7e, 0x00, + 0x3c, 0x66, 0x06, 0x0c, 0x30, 0x60, 0x7e, 0x00, + 0x3c, 0x66, 0x06, 0x1c, 0x06, 0x66, 0x3c, 0x00, + 0x06, 0x0e, 0x1e, 0x66, 0x7f, 0x06, 0x06, 0x00, + 0x7e, 0x60, 0x7c, 0x06, 0x06, 0x66, 0x3c, 0x00, + 0x3c, 0x66, 0x60, 0x7c, 0x66, 0x66, 0x3c, 0x00, + 0x7e, 0x66, 0x0c, 0x18, 0x18, 0x18, 0x18, 0x00, + 0x3c, 0x66, 0x66, 0x3c, 0x66, 0x66, 0x3c, 0x00, + 0x3c, 0x66, 0x66, 0x3e, 0x06, 0x66, 0x3c, 0x00, + 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x18, 0x30, + 0x0e, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0e, 0x00, + 0x00, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x00, 0x00, + 0x70, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x70, 0x00, + 0x3c, 0x66, 0x06, 0x0c, 0x18, 0x00, 0x18, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x08, 0x1c, 0x3e, 0x7f, 0x7f, 0x1c, 0x3e, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x00, 0x00, 0x00, 0xe0, 0xf0, 0x38, 0x18, 0x18, + 0x18, 0x18, 0x1c, 0x0f, 0x07, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x38, 0xf0, 0xe0, 0x00, 0x00, 0x00, + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xff, 0xff, + 0xc0, 0xe0, 0x70, 0x38, 0x1c, 0x0e, 0x07, 0x03, + 0x03, 0x07, 0x0e, 0x1c, 0x38, 0x70, 0xe0, 0xc0, + 0xff, 0xff, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, + 0xff, 0xff, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x00, 0x3c, 0x7e, 0x7e, 0x7e, 0x7e, 0x3c, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x36, 0x7f, 0x7f, 0x7f, 0x3e, 0x1c, 0x08, 0x00, + 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, + 0x00, 0x00, 0x00, 0x07, 0x0f, 0x1c, 0x18, 0x18, + 0xc3, 0xe7, 0x7e, 0x3c, 0x3c, 0x7e, 0xe7, 0xc3, + 0x00, 0x3c, 0x7e, 0x66, 0x66, 0x7e, 0x3c, 0x00, + 0x18, 0x18, 0x66, 0x66, 0x18, 0x18, 0x3c, 0x00, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x08, 0x1c, 0x3e, 0x7f, 0x3e, 0x1c, 0x08, 0x00, + 0x18, 0x18, 0x18, 0xff, 0xff, 0x18, 0x18, 0x18, + 0xc0, 0xc0, 0x30, 0x30, 0xc0, 0xc0, 0x30, 0x30, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x03, 0x3e, 0x76, 0x36, 0x36, 0x00, + 0xff, 0x7f, 0x3f, 0x1f, 0x0f, 0x07, 0x03, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, + 0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0x33, 0x33, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0x33, 0x33, + 0xff, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x18, 0x18, 0x18, 0x1f, 0x1f, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, + 0x18, 0x18, 0x18, 0x1f, 0x1f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xf8, 0xf8, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x1f, 0x1f, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0xf8, 0xf8, 0x18, 0x18, 0x18, + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, + 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0, + 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0xf8, 0xf8, 0x00, 0x00, 0x00, + 0xf0, 0xf0, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0xf0, 0xf0, 0xf0, 0xf0, 0x0f, 0x0f, 0x0f, 0x0f, + 0xc3, 0x99, 0x91, 0x91, 0x9f, 0x99, 0xc3, 0xff, + 0xe7, 0xc3, 0x99, 0x81, 0x99, 0x99, 0x99, 0xff, + 0x83, 0x99, 0x99, 0x83, 0x99, 0x99, 0x83, 0xff, + 0xc3, 0x99, 0x9f, 0x9f, 0x9f, 0x99, 0xc3, 0xff, + 0x87, 0x93, 0x99, 0x99, 0x99, 0x93, 0x87, 0xff, + 0x81, 0x9f, 0x9f, 0x87, 0x9f, 0x9f, 0x81, 0xff, + 0x81, 0x9f, 0x9f, 0x87, 0x9f, 0x9f, 0x9f, 0xff, + 0xc3, 0x99, 0x9f, 0x91, 0x99, 0x99, 0xc3, 0xff, + 0x99, 0x99, 0x99, 0x81, 0x99, 0x99, 0x99, 0xff, + 0xc3, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xc3, 0xff, + 0xe1, 0xf3, 0xf3, 0xf3, 0xf3, 0x93, 0xc7, 0xff, + 0x99, 0x93, 0x87, 0x8f, 0x87, 0x93, 0x99, 0xff, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x81, 0xff, + 0x9c, 0x88, 0x80, 0x94, 0x9c, 0x9c, 0x9c, 0xff, + 0x99, 0x89, 0x81, 0x81, 0x91, 0x99, 0x99, 0xff, + 0xc3, 0x99, 0x99, 0x99, 0x99, 0x99, 0xc3, 0xff, + 0x83, 0x99, 0x99, 0x83, 0x9f, 0x9f, 0x9f, 0xff, + 0xc3, 0x99, 0x99, 0x99, 0x99, 0xc3, 0xf1, 0xff, + 0x83, 0x99, 0x99, 0x83, 0x87, 0x93, 0x99, 0xff, + 0xc3, 0x99, 0x9f, 0xc3, 0xf9, 0x99, 0xc3, 0xff, + 0x81, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0xc3, 0xff, + 0x99, 0x99, 0x99, 0x99, 0x99, 0xc3, 0xe7, 0xff, + 0x9c, 0x9c, 0x9c, 0x94, 0x80, 0x88, 0x9c, 0xff, + 0x99, 0x99, 0xc3, 0xe7, 0xc3, 0x99, 0x99, 0xff, + 0x99, 0x99, 0x99, 0xc3, 0xe7, 0xe7, 0xe7, 0xff, + 0x81, 0xf9, 0xf3, 0xe7, 0xcf, 0x9f, 0x81, 0xff, + 0xc3, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xc3, 0xff, + 0xf3, 0xed, 0xcf, 0x83, 0xcf, 0x9d, 0x03, 0xff, + 0xc3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xc3, 0xff, + 0xff, 0xe7, 0xc3, 0x81, 0xe7, 0xe7, 0xe7, 0xe7, + 0xff, 0xef, 0xcf, 0x80, 0x80, 0xcf, 0xef, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xff, 0xe7, 0xff, + 0x99, 0x99, 0x99, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x99, 0x99, 0x00, 0x99, 0x00, 0x99, 0x99, 0xff, + 0xe7, 0xc1, 0x9f, 0xc3, 0xf9, 0x83, 0xe7, 0xff, + 0x9d, 0x99, 0xf3, 0xe7, 0xcf, 0x99, 0xb9, 0xff, + 0xc3, 0x99, 0xc3, 0xc7, 0x98, 0x99, 0xc0, 0xff, + 0xf9, 0xf3, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf3, 0xe7, 0xcf, 0xcf, 0xcf, 0xe7, 0xf3, 0xff, + 0xcf, 0xe7, 0xf3, 0xf3, 0xf3, 0xe7, 0xcf, 0xff, + 0xff, 0x99, 0xc3, 0x00, 0xc3, 0x99, 0xff, 0xff, + 0xff, 0xe7, 0xe7, 0x81, 0xe7, 0xe7, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xe7, 0xcf, + 0xff, 0xff, 0xff, 0x81, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xe7, 0xff, + 0xff, 0xfc, 0xf9, 0xf3, 0xe7, 0xcf, 0x9f, 0xff, + 0xc3, 0x99, 0x91, 0x89, 0x99, 0x99, 0xc3, 0xff, + 0xe7, 0xe7, 0xc7, 0xe7, 0xe7, 0xe7, 0x81, 0xff, + 0xc3, 0x99, 0xf9, 0xf3, 0xcf, 0x9f, 0x81, 0xff, + 0xc3, 0x99, 0xf9, 0xe3, 0xf9, 0x99, 0xc3, 0xff, + 0xf9, 0xf1, 0xe1, 0x99, 0x80, 0xf9, 0xf9, 0xff, + 0x81, 0x9f, 0x83, 0xf9, 0xf9, 0x99, 0xc3, 0xff, + 0xc3, 0x99, 0x9f, 0x83, 0x99, 0x99, 0xc3, 0xff, + 0x81, 0x99, 0xf3, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, + 0xc3, 0x99, 0x99, 0xc3, 0x99, 0x99, 0xc3, 0xff, + 0xc3, 0x99, 0x99, 0xc1, 0xf9, 0x99, 0xc3, 0xff, + 0xff, 0xff, 0xe7, 0xff, 0xff, 0xe7, 0xff, 0xff, + 0xff, 0xff, 0xe7, 0xff, 0xff, 0xe7, 0xe7, 0xcf, + 0xf1, 0xe7, 0xcf, 0x9f, 0xcf, 0xe7, 0xf1, 0xff, + 0xff, 0xff, 0x81, 0xff, 0x81, 0xff, 0xff, 0xff, + 0x8f, 0xe7, 0xf3, 0xf9, 0xf3, 0xe7, 0x8f, 0xff, + 0xc3, 0x99, 0xf9, 0xf3, 0xe7, 0xff, 0xe7, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xf7, 0xe3, 0xc1, 0x80, 0x80, 0xe3, 0xc1, 0xff, + 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, + 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, + 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, + 0xff, 0xff, 0xff, 0x1f, 0x0f, 0xc7, 0xe7, 0xe7, + 0xe7, 0xe7, 0xe3, 0xf0, 0xf8, 0xff, 0xff, 0xff, + 0xe7, 0xe7, 0xc7, 0x0f, 0x1f, 0xff, 0xff, 0xff, + 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0x00, + 0x3f, 0x1f, 0x8f, 0xc7, 0xe3, 0xf1, 0xf8, 0xfc, + 0xfc, 0xf8, 0xf1, 0xe3, 0xc7, 0x8f, 0x1f, 0x3f, + 0x00, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, + 0x00, 0x00, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, + 0xff, 0xc3, 0x81, 0x81, 0x81, 0x81, 0xc3, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, + 0xc9, 0x80, 0x80, 0x80, 0xc1, 0xe3, 0xf7, 0xff, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, + 0xff, 0xff, 0xff, 0xf8, 0xf0, 0xe3, 0xe7, 0xe7, + 0x3c, 0x18, 0x81, 0xc3, 0xc3, 0x81, 0x18, 0x3c, + 0xff, 0xc3, 0x81, 0x99, 0x99, 0x81, 0xc3, 0xff, + 0xe7, 0xe7, 0x99, 0x99, 0xe7, 0xe7, 0xc3, 0xff, + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, + 0xf7, 0xe3, 0xc1, 0x80, 0xc1, 0xe3, 0xf7, 0xff, + 0xe7, 0xe7, 0xe7, 0x00, 0x00, 0xe7, 0xe7, 0xe7, + 0x3f, 0x3f, 0xcf, 0xcf, 0x3f, 0x3f, 0xcf, 0xcf, + 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, + 0xff, 0xff, 0xfc, 0xc1, 0x89, 0xc9, 0xc9, 0xff, + 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, + 0x33, 0x33, 0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc, + 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, + 0xff, 0xff, 0xff, 0xff, 0x33, 0x33, 0xcc, 0xcc, + 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, + 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, + 0xe7, 0xe7, 0xe7, 0xe0, 0xe0, 0xe7, 0xe7, 0xe7, + 0xff, 0xff, 0xff, 0xff, 0xf0, 0xf0, 0xf0, 0xf0, + 0xe7, 0xe7, 0xe7, 0xe0, 0xe0, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x07, 0x07, 0xe7, 0xe7, 0xe7, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xe0, 0xe0, 0xe7, 0xe7, 0xe7, + 0xe7, 0xe7, 0xe7, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0xe7, 0xe7, 0xe7, + 0xe7, 0xe7, 0xe7, 0x07, 0x07, 0xe7, 0xe7, 0xe7, + 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, + 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, + 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0x0f, 0x0f, 0x0f, 0x0f, + 0xf0, 0xf0, 0xf0, 0xf0, 0xff, 0xff, 0xff, 0xff, + 0xe7, 0xe7, 0xe7, 0x07, 0x07, 0xff, 0xff, 0xff, + 0x0f, 0x0f, 0x0f, 0x0f, 0xff, 0xff, 0xff, 0xff, + 0x0f, 0x0f, 0x0f, 0x0f, 0xf0, 0xf0, 0xf0, 0xf0, + 0x3c, 0x66, 0x6e, 0x6e, 0x60, 0x62, 0x3c, 0x00, + 0x00, 0x00, 0x3c, 0x06, 0x3e, 0x66, 0x3e, 0x00, + 0x00, 0x60, 0x60, 0x7c, 0x66, 0x66, 0x7c, 0x00, + 0x00, 0x00, 0x3c, 0x60, 0x60, 0x60, 0x3c, 0x00, + 0x00, 0x06, 0x06, 0x3e, 0x66, 0x66, 0x3e, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0x7e, 0x60, 0x3c, 0x00, + 0x00, 0x0e, 0x18, 0x3e, 0x18, 0x18, 0x18, 0x00, + 0x00, 0x00, 0x3e, 0x66, 0x66, 0x3e, 0x06, 0x7c, + 0x00, 0x60, 0x60, 0x7c, 0x66, 0x66, 0x66, 0x00, + 0x00, 0x18, 0x00, 0x38, 0x18, 0x18, 0x3c, 0x00, + 0x00, 0x06, 0x00, 0x06, 0x06, 0x06, 0x06, 0x3c, + 0x00, 0x60, 0x60, 0x6c, 0x78, 0x6c, 0x66, 0x00, + 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, + 0x00, 0x00, 0x66, 0x7f, 0x7f, 0x6b, 0x63, 0x00, + 0x00, 0x00, 0x7c, 0x66, 0x66, 0x66, 0x66, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x00, + 0x00, 0x00, 0x7c, 0x66, 0x66, 0x7c, 0x60, 0x60, + 0x00, 0x00, 0x3e, 0x66, 0x66, 0x3e, 0x06, 0x06, + 0x00, 0x00, 0x7c, 0x66, 0x60, 0x60, 0x60, 0x00, + 0x00, 0x00, 0x3e, 0x60, 0x3c, 0x06, 0x7c, 0x00, + 0x00, 0x18, 0x7e, 0x18, 0x18, 0x18, 0x0e, 0x00, + 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x00, + 0x00, 0x00, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x00, + 0x00, 0x00, 0x63, 0x6b, 0x7f, 0x3e, 0x36, 0x00, + 0x00, 0x00, 0x66, 0x3c, 0x18, 0x3c, 0x66, 0x00, + 0x00, 0x00, 0x66, 0x66, 0x66, 0x3e, 0x0c, 0x78, + 0x00, 0x00, 0x7e, 0x0c, 0x18, 0x30, 0x7e, 0x00, + 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3c, 0x00, + 0x0c, 0x12, 0x30, 0x7c, 0x30, 0x62, 0xfc, 0x00, + 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3c, 0x00, + 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, + 0x00, 0x10, 0x30, 0x7f, 0x7f, 0x30, 0x10, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x18, 0x00, + 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x66, 0x66, 0xff, 0x66, 0xff, 0x66, 0x66, 0x00, + 0x18, 0x3e, 0x60, 0x3c, 0x06, 0x7c, 0x18, 0x00, + 0x62, 0x66, 0x0c, 0x18, 0x30, 0x66, 0x46, 0x00, + 0x3c, 0x66, 0x3c, 0x38, 0x67, 0x66, 0x3f, 0x00, + 0x06, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0c, 0x18, 0x30, 0x30, 0x30, 0x18, 0x0c, 0x00, + 0x30, 0x18, 0x0c, 0x0c, 0x0c, 0x18, 0x30, 0x00, + 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00, + 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, + 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, + 0x00, 0x03, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x00, + 0x3c, 0x66, 0x6e, 0x76, 0x66, 0x66, 0x3c, 0x00, + 0x18, 0x18, 0x38, 0x18, 0x18, 0x18, 0x7e, 0x00, + 0x3c, 0x66, 0x06, 0x0c, 0x30, 0x60, 0x7e, 0x00, + 0x3c, 0x66, 0x06, 0x1c, 0x06, 0x66, 0x3c, 0x00, + 0x06, 0x0e, 0x1e, 0x66, 0x7f, 0x06, 0x06, 0x00, + 0x7e, 0x60, 0x7c, 0x06, 0x06, 0x66, 0x3c, 0x00, + 0x3c, 0x66, 0x60, 0x7c, 0x66, 0x66, 0x3c, 0x00, + 0x7e, 0x66, 0x0c, 0x18, 0x18, 0x18, 0x18, 0x00, + 0x3c, 0x66, 0x66, 0x3c, 0x66, 0x66, 0x3c, 0x00, + 0x3c, 0x66, 0x66, 0x3e, 0x06, 0x66, 0x3c, 0x00, + 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x18, 0x30, + 0x0e, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0e, 0x00, + 0x00, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x00, 0x00, + 0x70, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x70, 0x00, + 0x3c, 0x66, 0x06, 0x0c, 0x18, 0x00, 0x18, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x18, 0x3c, 0x66, 0x7e, 0x66, 0x66, 0x66, 0x00, + 0x7c, 0x66, 0x66, 0x7c, 0x66, 0x66, 0x7c, 0x00, + 0x3c, 0x66, 0x60, 0x60, 0x60, 0x66, 0x3c, 0x00, + 0x78, 0x6c, 0x66, 0x66, 0x66, 0x6c, 0x78, 0x00, + 0x7e, 0x60, 0x60, 0x78, 0x60, 0x60, 0x7e, 0x00, + 0x7e, 0x60, 0x60, 0x78, 0x60, 0x60, 0x60, 0x00, + 0x3c, 0x66, 0x60, 0x6e, 0x66, 0x66, 0x3c, 0x00, + 0x66, 0x66, 0x66, 0x7e, 0x66, 0x66, 0x66, 0x00, + 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, + 0x1e, 0x0c, 0x0c, 0x0c, 0x0c, 0x6c, 0x38, 0x00, + 0x66, 0x6c, 0x78, 0x70, 0x78, 0x6c, 0x66, 0x00, + 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x7e, 0x00, + 0x63, 0x77, 0x7f, 0x6b, 0x63, 0x63, 0x63, 0x00, + 0x66, 0x76, 0x7e, 0x7e, 0x6e, 0x66, 0x66, 0x00, + 0x3c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, + 0x7c, 0x66, 0x66, 0x7c, 0x60, 0x60, 0x60, 0x00, + 0x3c, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x0e, 0x00, + 0x7c, 0x66, 0x66, 0x7c, 0x78, 0x6c, 0x66, 0x00, + 0x3c, 0x66, 0x60, 0x3c, 0x06, 0x66, 0x3c, 0x00, + 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, + 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, + 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x00, + 0x63, 0x63, 0x63, 0x6b, 0x7f, 0x77, 0x63, 0x00, + 0x66, 0x66, 0x3c, 0x18, 0x3c, 0x66, 0x66, 0x00, + 0x66, 0x66, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x00, + 0x7e, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x7e, 0x00, + 0x18, 0x18, 0x18, 0xff, 0xff, 0x18, 0x18, 0x18, + 0xc0, 0xc0, 0x30, 0x30, 0xc0, 0xc0, 0x30, 0x30, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x33, 0x33, 0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc, + 0x33, 0x99, 0xcc, 0x66, 0x33, 0x99, 0xcc, 0x66, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, + 0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0x33, 0x33, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0x33, 0x33, + 0xcc, 0x99, 0x33, 0x66, 0xcc, 0x99, 0x33, 0x66, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x18, 0x18, 0x18, 0x1f, 0x1f, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, + 0x18, 0x18, 0x18, 0x1f, 0x1f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xf8, 0xf8, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x1f, 0x1f, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0xf8, 0xf8, 0x18, 0x18, 0x18, + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, + 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0x01, 0x03, 0x06, 0x6c, 0x78, 0x70, 0x60, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0, + 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0xf8, 0xf8, 0x00, 0x00, 0x00, + 0xf0, 0xf0, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0xf0, 0xf0, 0xf0, 0xf0, 0x0f, 0x0f, 0x0f, 0x0f, + 0xc3, 0x99, 0x91, 0x91, 0x9f, 0x99, 0xc3, 0xff, + 0xff, 0xff, 0xc3, 0xf9, 0xc1, 0x99, 0xc1, 0xff, + 0xff, 0x9f, 0x9f, 0x83, 0x99, 0x99, 0x83, 0xff, + 0xff, 0xff, 0xc3, 0x9f, 0x9f, 0x9f, 0xc3, 0xff, + 0xff, 0xf9, 0xf9, 0xc1, 0x99, 0x99, 0xc1, 0xff, + 0xff, 0xff, 0xc3, 0x99, 0x81, 0x9f, 0xc3, 0xff, + 0xff, 0xf1, 0xe7, 0xc1, 0xe7, 0xe7, 0xe7, 0xff, + 0xff, 0xff, 0xc1, 0x99, 0x99, 0xc1, 0xf9, 0x83, + 0xff, 0x9f, 0x9f, 0x83, 0x99, 0x99, 0x99, 0xff, + 0xff, 0xe7, 0xff, 0xc7, 0xe7, 0xe7, 0xc3, 0xff, + 0xff, 0xf9, 0xff, 0xf9, 0xf9, 0xf9, 0xf9, 0xc3, + 0xff, 0x9f, 0x9f, 0x93, 0x87, 0x93, 0x99, 0xff, + 0xff, 0xc7, 0xe7, 0xe7, 0xe7, 0xe7, 0xc3, 0xff, + 0xff, 0xff, 0x99, 0x80, 0x80, 0x94, 0x9c, 0xff, + 0xff, 0xff, 0x83, 0x99, 0x99, 0x99, 0x99, 0xff, + 0xff, 0xff, 0xc3, 0x99, 0x99, 0x99, 0xc3, 0xff, + 0xff, 0xff, 0x83, 0x99, 0x99, 0x83, 0x9f, 0x9f, + 0xff, 0xff, 0xc1, 0x99, 0x99, 0xc1, 0xf9, 0xf9, + 0xff, 0xff, 0x83, 0x99, 0x9f, 0x9f, 0x9f, 0xff, + 0xff, 0xff, 0xc1, 0x9f, 0xc3, 0xf9, 0x83, 0xff, + 0xff, 0xe7, 0x81, 0xe7, 0xe7, 0xe7, 0xf1, 0xff, + 0xff, 0xff, 0x99, 0x99, 0x99, 0x99, 0xc1, 0xff, + 0xff, 0xff, 0x99, 0x99, 0x99, 0xc3, 0xe7, 0xff, + 0xff, 0xff, 0x9c, 0x94, 0x80, 0xc1, 0xc9, 0xff, + 0xff, 0xff, 0x99, 0xc3, 0xe7, 0xc3, 0x99, 0xff, + 0xff, 0xff, 0x99, 0x99, 0x99, 0xc1, 0xf3, 0x87, + 0xff, 0xff, 0x81, 0xf3, 0xe7, 0xcf, 0x81, 0xff, + 0xc3, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xc3, 0xff, + 0xf3, 0xed, 0xcf, 0x83, 0xcf, 0x9d, 0x03, 0xff, + 0xc3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xc3, 0xff, + 0xff, 0xe7, 0xc3, 0x81, 0xe7, 0xe7, 0xe7, 0xe7, + 0xff, 0xef, 0xcf, 0x80, 0x80, 0xcf, 0xef, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0xff, 0xe7, 0xff, + 0x99, 0x99, 0x99, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x99, 0x99, 0x00, 0x99, 0x00, 0x99, 0x99, 0xff, + 0xe7, 0xc1, 0x9f, 0xc3, 0xf9, 0x83, 0xe7, 0xff, + 0x9d, 0x99, 0xf3, 0xe7, 0xcf, 0x99, 0xb9, 0xff, + 0xc3, 0x99, 0xc3, 0xc7, 0x98, 0x99, 0xc0, 0xff, + 0xf9, 0xf3, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf3, 0xe7, 0xcf, 0xcf, 0xcf, 0xe7, 0xf3, 0xff, + 0xcf, 0xe7, 0xf3, 0xf3, 0xf3, 0xe7, 0xcf, 0xff, + 0xff, 0x99, 0xc3, 0x00, 0xc3, 0x99, 0xff, 0xff, + 0xff, 0xe7, 0xe7, 0x81, 0xe7, 0xe7, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xe7, 0xcf, + 0xff, 0xff, 0xff, 0x81, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xe7, 0xff, + 0xff, 0xfc, 0xf9, 0xf3, 0xe7, 0xcf, 0x9f, 0xff, + 0xc3, 0x99, 0x91, 0x89, 0x99, 0x99, 0xc3, 0xff, + 0xe7, 0xe7, 0xc7, 0xe7, 0xe7, 0xe7, 0x81, 0xff, + 0xc3, 0x99, 0xf9, 0xf3, 0xcf, 0x9f, 0x81, 0xff, + 0xc3, 0x99, 0xf9, 0xe3, 0xf9, 0x99, 0xc3, 0xff, + 0xf9, 0xf1, 0xe1, 0x99, 0x80, 0xf9, 0xf9, 0xff, + 0x81, 0x9f, 0x83, 0xf9, 0xf9, 0x99, 0xc3, 0xff, + 0xc3, 0x99, 0x9f, 0x83, 0x99, 0x99, 0xc3, 0xff, + 0x81, 0x99, 0xf3, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, + 0xc3, 0x99, 0x99, 0xc3, 0x99, 0x99, 0xc3, 0xff, + 0xc3, 0x99, 0x99, 0xc1, 0xf9, 0x99, 0xc3, 0xff, + 0xff, 0xff, 0xe7, 0xff, 0xff, 0xe7, 0xff, 0xff, + 0xff, 0xff, 0xe7, 0xff, 0xff, 0xe7, 0xe7, 0xcf, + 0xf1, 0xe7, 0xcf, 0x9f, 0xcf, 0xe7, 0xf1, 0xff, + 0xff, 0xff, 0x81, 0xff, 0x81, 0xff, 0xff, 0xff, + 0x8f, 0xe7, 0xf3, 0xf9, 0xf3, 0xe7, 0x8f, 0xff, + 0xc3, 0x99, 0xf9, 0xf3, 0xe7, 0xff, 0xe7, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xe7, 0xc3, 0x99, 0x81, 0x99, 0x99, 0x99, 0xff, + 0x83, 0x99, 0x99, 0x83, 0x99, 0x99, 0x83, 0xff, + 0xc3, 0x99, 0x9f, 0x9f, 0x9f, 0x99, 0xc3, 0xff, + 0x87, 0x93, 0x99, 0x99, 0x99, 0x93, 0x87, 0xff, + 0x81, 0x9f, 0x9f, 0x87, 0x9f, 0x9f, 0x81, 0xff, + 0x81, 0x9f, 0x9f, 0x87, 0x9f, 0x9f, 0x9f, 0xff, + 0xc3, 0x99, 0x9f, 0x91, 0x99, 0x99, 0xc3, 0xff, + 0x99, 0x99, 0x99, 0x81, 0x99, 0x99, 0x99, 0xff, + 0xc3, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xc3, 0xff, + 0xe1, 0xf3, 0xf3, 0xf3, 0xf3, 0x93, 0xc7, 0xff, + 0x99, 0x93, 0x87, 0x8f, 0x87, 0x93, 0x99, 0xff, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x81, 0xff, + 0x9c, 0x88, 0x80, 0x94, 0x9c, 0x9c, 0x9c, 0xff, + 0x99, 0x89, 0x81, 0x81, 0x91, 0x99, 0x99, 0xff, + 0xc3, 0x99, 0x99, 0x99, 0x99, 0x99, 0xc3, 0xff, + 0x83, 0x99, 0x99, 0x83, 0x9f, 0x9f, 0x9f, 0xff, + 0xc3, 0x99, 0x99, 0x99, 0x99, 0xc3, 0xf1, 0xff, + 0x83, 0x99, 0x99, 0x83, 0x87, 0x93, 0x99, 0xff, + 0xc3, 0x99, 0x9f, 0xc3, 0xf9, 0x99, 0xc3, 0xff, + 0x81, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0xc3, 0xff, + 0x99, 0x99, 0x99, 0x99, 0x99, 0xc3, 0xe7, 0xff, + 0x9c, 0x9c, 0x9c, 0x94, 0x80, 0x88, 0x9c, 0xff, + 0x99, 0x99, 0xc3, 0xe7, 0xc3, 0x99, 0x99, 0xff, + 0x99, 0x99, 0x99, 0xc3, 0xe7, 0xe7, 0xe7, 0xff, + 0x81, 0xf9, 0xf3, 0xe7, 0xcf, 0x9f, 0x81, 0xff, + 0xe7, 0xe7, 0xe7, 0x00, 0x00, 0xe7, 0xe7, 0xe7, + 0x3f, 0x3f, 0xcf, 0xcf, 0x3f, 0x3f, 0xcf, 0xcf, + 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, + 0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0x33, 0x33, + 0xcc, 0x66, 0x33, 0x99, 0xcc, 0x66, 0x33, 0x99, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, + 0x33, 0x33, 0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc, + 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, + 0xff, 0xff, 0xff, 0xff, 0x33, 0x33, 0xcc, 0xcc, + 0x33, 0x66, 0xcc, 0x99, 0x33, 0x66, 0xcc, 0x99, + 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, + 0xe7, 0xe7, 0xe7, 0xe0, 0xe0, 0xe7, 0xe7, 0xe7, + 0xff, 0xff, 0xff, 0xff, 0xf0, 0xf0, 0xf0, 0xf0, + 0xe7, 0xe7, 0xe7, 0xe0, 0xe0, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x07, 0x07, 0xe7, 0xe7, 0xe7, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xe0, 0xe0, 0xe7, 0xe7, 0xe7, + 0xe7, 0xe7, 0xe7, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0xe7, 0xe7, 0xe7, + 0xe7, 0xe7, 0xe7, 0x07, 0x07, 0xe7, 0xe7, 0xe7, + 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, + 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, + 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0xfe, 0xfc, 0xf9, 0x93, 0x87, 0x8f, 0x9f, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x0f, 0x0f, 0x0f, 0x0f, + 0xf0, 0xf0, 0xf0, 0xf0, 0xff, 0xff, 0xff, 0xff, + 0xe7, 0xe7, 0xe7, 0x07, 0x07, 0xff, 0xff, 0xff, + 0x0f, 0x0f, 0x0f, 0x0f, 0xff, 0xff, 0xff, 0xff, + 0x0f, 0x0f, 0x0f, 0x0f, 0xf0, 0xf0, 0xf0, 0xf0 +}; diff --git a/Src/CmdPipe.cpp b/Src/CmdPipe.cpp new file mode 100644 index 0000000..e740e92 --- /dev/null +++ b/Src/CmdPipe.cpp @@ -0,0 +1,181 @@ +/* + * CmdPipe.cpp + * + * Frodo (C) 1994-1997,2002-2005 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "CmdPipe.h" + + +extern "C" { + #include + #include + #include + #include + #include + #include + #include + +#if defined(__alpha__) + #include +#endif + +#if defined(AIX) + #include +#else + #include +#endif + +#if defined(__linux__) + #include +#endif + + #include + #include +} + +static void kaputt(const char * c1, const char * c2) { + fprintf(stderr,"error: %s%s\n",c1,c2); + exit(20); +} + +Pipe::Pipe(void) : fail(true) { + + fds[0] = 0; + fds[1] = 1; + + if (-1 == pipe(fds)) { + kaputt("Pipe: ","unable to create pipe"); + return; + } + + fail = false; + return; +} + +Pipe::~Pipe(void) { + + if (! fail) { + close(fds[0]); + close(fds[1]); + } + return; +} + +unsigned long Pipe::ewrite(const void * buf, unsigned long len) { + + unsigned long wsum = 0; + while (len) { + long wlen; + + wlen = ::write(fds[1], buf, (long) len); + if (wlen <= 0) { + kaputt("Pipe::ewrite ","write-error"); + } + + len -= wlen; + buf = (void*) ((char*) buf + wlen); + wsum += wlen; + } + return wsum; +} + +unsigned long Pipe::eread(void * buf, unsigned long len) { + + unsigned long rsum = 0; + while (len) { + long rlen; + + rlen = ::read(fds[0], buf, (long) len); + + if (rlen <= 0) { + kaputt("Pipe::eread ","read-error"); + } + + len -= rlen; + buf = (void*) ((char*) buf + rlen); + rsum += rlen; + } + return rsum; +} + +int Pipe::probe(void) const { + + fd_set set; + FD_ZERO(&set); + FD_SET(fds[0], &set); + + struct timeval tv; + tv.tv_sec = 0; + tv.tv_usec = 0; + + int res; +// Use the following commented line for HP-UX < 10.20 +// res = select(FD_SETSIZE, (int *)&set, (int *)0, (int *)0, &tv); + res = select(FD_SETSIZE, &set, (fd_set *)0, (fd_set *)0, &tv); + + if (res > 0) return -1; + return 0; + +} + +CmdPipe::CmdPipe(const char * command, const char * arg, int nicediff) : childpid(0), fail(true) { + + if (tocmd.fail || fromcmd.fail) { + kaputt("CmdPipe: ","unable to initialize pipes"); + return; + } + + childpid = fork(); + + if (childpid == -1) { + childpid = 0; + kaputt("CmdPipe: ","unable to fork process"); + return; + } + + if (childpid == 0) { + + if (nicediff) { + if (-1 == nice(nicediff)) { + fprintf(stderr,"CmdPipe: unable to change nice-level (non-fatal)"); + } + } + + dup2(tocmd.get_read_fd(), STDIN_FILENO); + + dup2(fromcmd.get_write_fd(), STDOUT_FILENO); + execlp(command, "Frodo_GUI", arg, (char *)0); + kaputt("CmdPipe: unable to execute child process ",command); + _exit(0); // exit (and do NOT call destructors etc..) + } + + fail = false; + return; +} + +CmdPipe::~CmdPipe(void) { + + if (childpid) { + int status; + waitpid(childpid, &status, 0); + + if (status != 0) { + fprintf(stderr,"~CmdPipe child process returned error\n"); + } + } +} diff --git a/Src/CmdPipe.h b/Src/CmdPipe.h new file mode 100644 index 0000000..4bff300 --- /dev/null +++ b/Src/CmdPipe.h @@ -0,0 +1,98 @@ +/* + * CmdPipe.h + * + * Frodo (C) 1994-1997,2002-2005 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef CmdPipe_h +#define CmdPipe_h + +extern "C" { + #include + #include +} + +class Pipe { + +protected: + + int fds[2]; + +public: + + bool fail; + + Pipe(void); + Pipe(int fdin, int fdout) : fail(false) { + fds[0] = fdin; + fds[1] = fdout; + } + ~Pipe(void); + + unsigned long ewrite(const void * buf, unsigned long len); + unsigned long eread (void * buf, unsigned long len); + + int get_read_fd(void) const { + return fds[0]; + } + + int get_write_fd(void) const { + return fds[1]; + } + + int probe(void) const; +}; + +class CmdPipe { + +protected: + + Pipe tocmd; + Pipe fromcmd; + + int childpid; + +public: + + bool fail; + + CmdPipe(const char * command, const char * arg, int nicediff = 0); + ~CmdPipe(void); + + unsigned long ewrite(const void * buf, unsigned long len) { + return tocmd.ewrite(buf, len); + } + + unsigned long eread (void * buf, unsigned long len) { + return fromcmd.eread(buf, len); + } + + int get_read_fd(void) const { + return fromcmd.get_read_fd(); + } + + int get_write_fd(void) const { + return tocmd.get_write_fd(); + } + + int probe(void) const { + return fromcmd.probe(); + } + +}; + +#endif // CmdPipe_h diff --git a/Src/Display.cpp b/Src/Display.cpp new file mode 100644 index 0000000..4a192c5 --- /dev/null +++ b/Src/Display.cpp @@ -0,0 +1,109 @@ +/* + * Display.cpp - C64 graphics display, emulator window handling + * + * Frodo (C) 1994-1997,2002-2005 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "sysdeps.h" + +#include "Display.h" +#include "main.h" +#include "Prefs.h" + + +// LED states +enum { + LED_OFF, // LED off + LED_ON, // LED on (green) + LED_ERROR_ON, // LED blinking (red), currently on + LED_ERROR_OFF // LED blinking, currently off +}; + + +#define USE_PEPTO_COLORS 1 + +#ifdef USE_PEPTO_COLORS + +// C64 color palette +// Values based on measurements by Philip "Pepto" Timmermann +// (see http://www.pepto.de/projects/colorvic/) +const uint8 palette_red[16] = { + 0x00, 0xff, 0x86, 0x4c, 0x88, 0x35, 0x20, 0xcf, 0x88, 0x40, 0xcb, 0x34, 0x68, 0x8b, 0x68, 0xa1 +}; + +const uint8 palette_green[16] = { + 0x00, 0xff, 0x19, 0xc1, 0x17, 0xac, 0x07, 0xf2, 0x3e, 0x2a, 0x55, 0x34, 0x68, 0xff, 0x4a, 0xa1 +}; + +const uint8 palette_blue[16] = { + 0x00, 0xff, 0x01, 0xe3, 0xbd, 0x0a, 0xc0, 0x2d, 0x00, 0x00, 0x37, 0x34, 0x68, 0x59, 0xff, 0xa1 +}; + +#else + +// C64 color palette (traditional Frodo colors) +const uint8 palette_red[16] = { + 0x00, 0xff, 0x99, 0x00, 0xcc, 0x44, 0x11, 0xff, 0xaa, 0x66, 0xff, 0x40, 0x80, 0x66, 0x77, 0xc0 +}; + +const uint8 palette_green[16] = { + 0x00, 0xff, 0x00, 0xff, 0x00, 0xcc, 0x00, 0xdd, 0x55, 0x33, 0x66, 0x40, 0x80, 0xff, 0x77, 0xc0 +}; + +const uint8 palette_blue[16] = { + 0x00, 0xff, 0x00, 0xcc, 0xcc, 0x44, 0x99, 0x00, 0x00, 0x00, 0x66, 0x40, 0x80, 0x66, 0xff, 0xc0 +}; + +#endif + + +/* + * Update drive LED display (deferred until Update()) + */ + +void C64Display::UpdateLEDs(int l0, int l1, int l2, int l3) +{ + led_state[0] = l0; + led_state[1] = l1; + led_state[2] = l2; + led_state[3] = l3; +} + + +#if defined(__BEOS__) +#include "Display_Be.h" +#elif defined(AMIGA) +#include "Display_Amiga.h" +#elif defined(HAVE_SDL) +# if defined(QTOPIA) or defined(MAEMO) +# include "Display_EmbeddedSDL.h" +# else +# include "Display_SDL.h" +# endif +#elif defined(__unix) +# ifdef __svgalib__ +# include "Display_svga.h" +# else +# include "Display_x.h" +# endif +#elif defined(__mac__) +#include "Display_mac.h" +#elif defined(WIN32) +#include "Display_WIN32.h" +#elif defined(__riscos__) +#include "Display_Acorn.h" +#endif diff --git a/Src/Display.h b/Src/Display.h new file mode 100644 index 0000000..ba74c06 --- /dev/null +++ b/Src/Display.h @@ -0,0 +1,247 @@ +/* + * Display.h - C64 graphics display, emulator window handling + * + * Frodo (C) 1994-1997,2002-2009 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _DISPLAY_H +#define _DISPLAY_H + +#ifdef __BEOS__ +#include +#endif + +#ifdef AMIGA +#include +#endif + +#ifdef HAVE_SDL +#include +extern SDL_Surface *real_screen; +#endif + +#ifdef WIN32 +#include +#endif + +#ifdef __riscos__ +#include "ROlib.h" +#endif + + +// Display dimensions +#if defined(SMALL_DISPLAY) +const int DISPLAY_X = 0x168; +const int DISPLAY_Y = 0x110; +#else +const int DISPLAY_X = 0x180; +const int DISPLAY_Y = 0x110; +#endif + +#if defined(HAVE_SDL) +const int FULL_DISPLAY_X = 640; +const int FULL_DISPLAY_Y = 480; +#endif + +class C64Window; +class C64Screen; +class C64; +class Prefs; + +// Class for C64 graphics display +class C64Display { +public: + C64Display(C64 *the_c64); + ~C64Display(); + + void Update(void); + void UpdateLEDs(int l0, int l1, int l2, int l3); + void Speedometer(int speed); + uint8 *BitmapBase(void); + int BitmapXMod(void); +#ifdef __riscos__ + void PollKeyboard(uint8 *key_matrix, uint8 *rev_matrix, uint8 *joystick, uint8 *joystick2); +#else + void PollKeyboard(uint8 *key_matrix, uint8 *rev_matrix, uint8 *joystick); +#endif +#if defined(HAVE_SDL) + void FakeKeyPress(int kc, bool shift, uint8 *CIA_key_matrix, + uint8 *CIA_rev_matrix); + void TranslateKey(SDLKey key, bool key_up, uint8 *key_matrix, uint8 *rev_matrix, uint8 *joystick); + void UpdateKeyMatrix(int c64_key, bool key_up, uint8 *key_matrix, uint8 *rev_matrix); +#endif + bool NumLock(void); + void InitColors(uint8 *colors); + void NewPrefs(Prefs *prefs); + + C64 *TheC64; + +#ifdef __BEOS__ + void Pause(void); + void Resume(void); +#endif + +#ifdef __riscos__ + void ModeChange(void); + unsigned int *GetColourTable(void); // returns pointer to mode_cols + bool CheckForUnpause(bool CheckLastState); + + ROScreen *screen; + Joy_Keys JoystickKeys[2]; // it's easier making the joystick keys public +#endif + +#if defined(__unix) || defined(GEKKO) + bool quit_requested; +#endif + +private: + int led_state[4]; + int old_led_state[4]; + +#ifdef __BEOS__ + C64Window *the_window; // One of these is NULL + C64Screen *the_screen; + bool using_screen; // Flag: Using the_screen + key_info old_key_info; + int draw_bitmap; // Number of bitmap for the VIC to draw into +#endif + +#ifdef AMIGA + void draw_led_bar(void); // Draw LED bar at the bottom of the window + void draw_led(int num, int state); // Draw one LED + + struct Window *the_window; // Pointer to C64 display window + struct Screen *the_screen; // The window's screen + struct RastPort *the_rp; // The window's RastPort + struct VisualInfo *the_visual_info; + struct Menu *the_menus; + struct TextFont *led_font; + struct TextFont *speedo_font; + struct RastPort temp_rp; // For WritePixelArray8() + struct BitMap *temp_bm; + uint8 *chunky_buf; // Chunky buffer for drawing into + LONG pens[16]; // Pens for C64 colors + int xo, yo; // Window X/Y border size + struct FileRequester *open_req, *save_req; // File requesters for load/save snapshot +#endif + +#ifdef HAVE_SDL + char speedometer_string[16]; // Speedometer text + void draw_string(SDL_Surface *s, int x, int y, const char *str, uint8 front_color, uint8 back_color); +#endif + +#ifdef __unix + void draw_led(int num, int state); // Draw one LED + static void pulse_handler(...); // LED error blinking +#endif + +#ifdef WIN32 +public: + long ShowRequester(const char *str, const char *button1, const char *button2 = NULL); + void WaitUntilActive(); + void NewPrefs(); + void Pause(); + void Resume(); + void Quit(); + + struct DisplayMode { + int x; + int y; + int depth; + BOOL modex; + }; + int GetNumDisplayModes() const; + const DisplayMode *GetDisplayModes() const; + +private: + // Window members. + void ResetKeyboardState(); + BOOL MakeWindow(); + static LRESULT CALLBACK StaticWindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); + long WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); + static int VirtKey2C64(int virtkey, DWORD keydata); + BOOL CalcViewPort(); + BOOL SetupWindow(); + BOOL SetupWindowMode(BOOL full_screen); + BOOL RestoreWindow(); + BOOL ResizeWindow(int side, RECT *pRect); + void WindowTitle(); + void CreateObjects(); + void DeleteObjects(); + + // DirectDraw management members. + BOOL StartDirectDraw(); + BOOL ResumeDirectDraw(); + BOOL ResetDirectDraw(); + BOOL StopDirectDraw(); + static HRESULT CALLBACK EnumModesCallback(LPDDSURFACEDESC pDDSD, LPVOID lpContext); + HRESULT EnumModesCallback(LPDDSURFACEDESC pDDSD); + static int CompareModes(const void *e1, const void *e2); + BOOL Fail(const char *message); + + // DirectDraw worker members. + BOOL SetPalettes(); + BOOL BuildColorTable(); + BOOL CopySurface(RECT &rcWork); + BOOL FlipSurfaces(); + BOOL EraseSurfaces(); + BOOL RestoreSurfaces(); + + void draw_led_bar(void); // Draw LED bar on the window + void draw_leds(BOOL force = false); // Draw LEDs if force or changed + void led_rect(int n, RECT &rc, RECT &led); // Compute LED rectangle + void InsertNextDisk(); // should be a common func + BOOL FileNameDialog(char *prefs_path, BOOL save = false); + void OfferSave(); // Offer chance to save changes + + UBYTE *chunky_buf; // Chunky buffer for drawing + BOOL active; // is application active? + BOOL paused; // is application paused? + BOOL waiting; // is application waiting? + DWORD windowed_style; // style of windowed window + DWORD fullscreen_style; // style of fullscreen window + char failure_message[128]; // what when wrong + int speed_index; // look ma, no hands + BOOL show_leds; // cached prefs option + BOOL full_screen; // cached prefs option + BOOL in_constructor; // if we are being contructed + BOOL in_destructor; // if we are being destroyed + + LPDIRECTDRAW pDD; // DirectDraw object + LPDIRECTDRAWSURFACE pPrimary; // DirectDraw primary surface + LPDIRECTDRAWSURFACE pBack; // DirectDraw back surface + LPDIRECTDRAWSURFACE pWork; // DirectDraw working surface + LPDIRECTDRAWCLIPPER pClipper; // DirectDraw clipper + LPDIRECTDRAWPALETTE pPalette; // DirectDraw palette + + DWORD colors[256]; // our palette colors + int colors_depth; // depth of the colors table +#endif + +#ifdef __riscos__ + unsigned int mode_cols[256]; // Colours in the current mode corresponding to C64's + uint8 *bitmap; + uint32 lastkeys[8]; // bitfield describing keys pressed last time. +#endif +}; + + +// Exported functions +extern long ShowRequester(const char *str, const char *button1, const char *button2 = NULL); + + +#endif diff --git a/Src/Display_Acorn.h b/Src/Display_Acorn.h new file mode 100644 index 0000000..7449c3b --- /dev/null +++ b/Src/Display_Acorn.h @@ -0,0 +1,441 @@ +/* + * Display_Acorn.h - C64 graphics display, emulator window handling, + * RISC OS specific stuff + * + * Frodo (C) 1994-1997,2002-2009 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "C64.h" +#include "ROlib.h" +#include "AcornGUI.h" +#include "SAM.h" +#include "VIC.h" + + + +// (from Display_x.i) +/* + C64 keyboard matrix: + + Bit 7 6 5 4 3 2 1 0 + 0 CUD F5 F3 F1 F7 CLR RET DEL + 1 SHL E S Z 4 A W 3 + 2 X T F C 6 D R 5 + 3 V U H B 8 G Y 7 + 4 N O K M 0 J I 9 + 5 , @ : . - L P + + 6 / ^ = SHR HOM ; * £ + 7 R/S Q C= SPC 2 CTL <- 1 +*/ + + +#define IntKey_MinCode 3 // Scan from ShiftLeft (leave out Shift, Ctrl, Alt) +#define IntKey_MaxCode 124 +#define IntKey_Copy 105 + +// Maps internal keyboard numbers (Acorn) to C64 keyboard-matrix. +// Format: top nibble - row#, bottom nibble - column (bit#). +// Entry == 0xff <==> don't map +char KeysAcornToCBM[] = { + 0x17, 0x72, 0x75, 0x17, // 0 - 3: SHL, CTRL, ALT(C=), SHL + 0x72, 0x75, 0x64, 0x72, // 4 - 7: CTRL, ALT, SHR, CTRL + 0x75, 0xff, 0xff, 0xff, // 8 - 11: ALT, MouseSlct, MouseMen, MouseAdj + 0xff, 0xff, 0xff, 0xff, // 12 - 15: dummies + 0x76, 0x10, 0x13, 0x20, // 16 - 19: q, 3,4,5 + 0x03, 0x33, 0xff, 0x53, // 20 - 23: F4(F7), 8, F7, - + 0x23, 0x02, 0xff, 0xff, // 24 - 27: 6, crsrL, num6, num7 + 0xff, 0xff, 0xff, 0xff, // 28 - 31: F11, F12, F10, ScrLock + 0xff, 0x11, 0x16, 0x26, // 32 - 35: Print, w, e, t + 0x30, 0x41, 0x40, 0x43, // 36 - 39: 7, i, 9, 0 + 0x53, 0x07, 0xff, 0xff, // 40 - 43: -, crsrD, num8, num9 + 0x77, 0x71, 0x60, 0x00, // 44 - 47: break, `, £, DEL + 0x70, 0x73, 0x22, 0x21, // 48 - 51: 1, 2, d, r + 0x23, 0x36, 0x46, 0x51, // 52 - 55: 6, u, o, p + 0x56, 0x07, 0x50, 0x53, // 56 - 59: [(@), crsrU, num+(+), num-(-) + 0xff, 0x00, 0x63, 0xff, // 60 - 63: numENTER, insert, home, pgUp + 0x17, 0x12, 0x27, 0x25, // 64 - 67: capsLCK, a, x, f + 0x31, 0x42, 0x45, 0x73, // 68 - 71: y, j, k, 2 + 0x55, 0x01, 0xff, 0xff, // 72 - 75: ;(:), RET, num/, dummy + 0xff, 0xff, 0xff, 0x62, // 76 - 79: num., numLCK, pgDown, '(;) + 0xff, 0x15, 0x24, 0x32, // 80 - 83: dummy, s, c, g + 0x35, 0x47, 0x52, 0x55, // 84 - 87: h, n, l, ;(:) + 0x61, 0x00, 0xff, 0xff, // 88 - 91: ](*), Delete, num#, num* + 0xff, 0x65, 0xff, 0xff, // 92 - 95: dummy, =, dummies + 0x72, 0x14, 0x74, 0x37, // 96 - 99: TAB(CTRL), z, SPACE, v + 0x34, 0x44, 0x57, 0x54, // 100-103: b, m, ',', . + 0x67, 0xff, 0xff, 0xff, // 104-107: /, Copy, num0, num1 + 0xff, 0xff, 0xff, 0xff, // 108-111: num3, dummies + 0x77, 0x04, 0x05, 0x06, // 112-115: ESC, F1(F1), F2(F3), F3(F5) + 0xff, 0xff, 0xff, 0xff, // 116-119: F5, F6, F8, F9 + 0x66, 0x02, 0xff, 0xff, // 120-123: \(^), crsrR, num4, num5 + 0xff, 0xff, 0xff, 0xff // 124-127: num2, dummies +}; + + +// Special keycodes that have to be processed seperately: +#define IntKey_CrsrL 25 +#define IntKey_CrsrR 121 +#define IntKey_CrsrU 57 +#define IntKey_CrsrD 41 +#define IntKey_Insert 61 +#define IntKey_NumLock 77 +#define IntKey_F5 116 +#define IntKey_F6 117 +#define IntKey_F7 22 +#define IntKey_F8 118 +#define IntKey_PageUp 63 +#define IntKey_PageDown 78 +#define IntKey_NumSlash 74 +#define IntKey_NumStar 91 +#define IntKey_NumCross 90 + +#define KeyJoy1_Up 108 // num3 +#define KeyJoy1_Down 76 // num. +#define KeyJoy1_Left 107 // num1 +#define KeyJoy1_Right 124 // num2 +#define KeyJoy1_Fire 60 // numReturn +#define KeyJoy2_Up 67 // "f" +#define KeyJoy2_Down 82 // "c" +#define KeyJoy2_Left 97 // "z" +#define KeyJoy2_Right 66 // "x" +#define KeyJoy2_Fire 83 // "g" + + + + +C64Display::C64Display(C64 *the_c64) : TheC64(the_c64) +{ + int i; + + bitmap = new uint8[DISPLAY_X * DISPLAY_Y]; + screen = new ROScreen(); + ModeChange(); + for (i=0; i<8; i++) {lastkeys[i] = 0;} + // First joystick: mapped to port 2 if numLOCK is on, else port 2 + JoystickKeys[0].up = KeyJoy1_Up; JoystickKeys[0].down = KeyJoy1_Down; + JoystickKeys[0].left = KeyJoy1_Left; JoystickKeys[0].right = KeyJoy1_Right; + JoystickKeys[0].fire = KeyJoy1_Fire; + // Second joystick: only active if numLOCK is off! Mapped to port 2 then. + JoystickKeys[1].up = KeyJoy2_Up; JoystickKeys[1].down = KeyJoy2_Down; + JoystickKeys[1].left = KeyJoy2_Left; JoystickKeys[1].right = KeyJoy2_Right; + JoystickKeys[1].fire = KeyJoy2_Fire; +} + + +C64Display::~C64Display(void) +{ + delete bitmap; delete screen; +} + + +void C64Display::ModeChange(void) +{ + register int i; + + screen->ReadMode(); + // find best matching colours in current mode. + switch (screen->ldbpp) + { + case 0: + case 1: + case 2: + case 3: for (i=0; i<16; i++) // for 1,2,4 and 8bpp + { + mode_cols[i] = ModeColourNumber((palette_blue[i] << 24) + (palette_green[i] << 16) + (palette_red[i] << 8)); + } + break; + case 4: for (i=0; i<16; i++) // for 16bpp + { + int r,g,b; + + r = (palette_red[i] + 4) & 0x1f8; if (r > 0xff) {r = 0xf8;} + g = (palette_green[i] + 4) & 0x1f8; if (g > 0xff) {g = 0xf8;} + b = (palette_blue[i] + 4) & 0x1f8; if (b > 0xff) {b = 0xf8;} + mode_cols[i] = (r >> 3) | (g << 2) | (b << 7); + } + break; + case 5: for (i=0; i<16; i++) // for 32bpp + { + mode_cols[i] = palette_red[i] | (palette_green[i] << 8) | (palette_blue[i] << 16); + } + break; + } +} + + +uint8 *C64Display::BitmapBase(void) +{ + return bitmap; +} + + +void C64Display::InitColors(uint8 *colors) +{ + register int i; + + // write index mapping C64colours -> ROcolours + if (screen->ldbpp <= 3) // at most 8bpp ==> use actual colour + { + for (i=0; i<256; i++) {colors[i] = mode_cols[i&15];} + } + else // else use index (takes time but can't be changed... + { + for (i=0; i<256; i++) {colors[i] = i&15;} + } +} + + +int C64Display::BitmapXMod(void) +{ + return DISPLAY_X; +} + + +// This routine reads the raw keyboard data from the host machine. Not entirely +// conformant with Acorn's rules but the only way to detect multiple simultaneous +// keypresses. +void C64Display::PollKeyboard(uint8 *key_matrix, uint8 *rev_matrix, uint8 *joystick, uint8 *joystick2) +{ + register int scan_from=IntKey_MinCode, code, row, col; + int status; + uint8 kjoy, kjoy2; + uint32 newkeys[8]; + UBYTE kjoy, kjoy2; + + // Clear keyboard + for (code=0; code<8; code++) {key_matrix[code] = 0xff; rev_matrix[code] = 0xff; newkeys[code] = 0;} + kjoy = kjoy2 = 0xff; + status = ReadKeyboardStatus(); + if ((status & 16) == 0) {key_matrix[1] &= 0x7f; rev_matrix[7] &= 0xfd;} // Caps lock + + while (scan_from <= IntKey_MaxCode) + { + if ((code = ScanKeys(scan_from)) != 0xff) + { + newkeys[code >> 5] |= (1 << (code & 0x1f)); // update keys pressed + row = KeysAcornToCBM[code]; + if ((status & 4) != 0) // numLOCK off? ==> check for 2nd keyboard joystick too + { + if (code == JoystickKeys[1].up) {kjoy2 &= 0xfe; row = 0xff;} + else if (code == JoystickKeys[1].down) {kjoy2 &= 0xfd; row = 0xff;} + else if (code == JoystickKeys[1].left) {kjoy2 &= 0xfb; row = 0xff;} + else if (code == JoystickKeys[1].right) {kjoy2 &= 0xf7; row = 0xff;} + else if (code == JoystickKeys[1].fire) {kjoy2 &= 0xef; row = 0xff;} + } + // check 1st keyboard joystick + if (code == JoystickKeys[0].up) {kjoy &= 0xfe; row = 0xff;} + else if (code == JoystickKeys[0].down) {kjoy &= 0xfd; row = 0xff;} + else if (code == JoystickKeys[0].left) {kjoy &= 0xfb; row = 0xff;} + else if (code == JoystickKeys[0].right) {kjoy &= 0xf7; row = 0xff;} + else if (code == JoystickKeys[0].fire) {kjoy &= 0xef; row = 0xff;} + + // If key not mapped to joystick: try mapping to keyboard + if (row != 0xff) + { + col = row & 7; row >>= 4; + key_matrix[row] &= ~(1<Pause(); SAM(TheC64); TheC64->Resume(); + } + break; + case IntKey_F7: TheC64->NMI(); break; + case IntKey_F8: TheC64->Reset(); break; + default: break; + } + + // These shouldn't auto-repeat, therefore I check them seperately. + if ((lastkeys[code >> 5] & (1 << (code & 0x1f))) == 0) + { + // Icons should be updated, not force-redrawed (--> single tasking) + switch (code) + { + // decrease framerate + case IntKey_PageUp: + TheC64->TheWIMP->PrefsWindow-> + WriteIconNumberU(Icon_Prefs_SkipFText,++ThePrefs.SkipFrames); + break; + // increase framerate + case IntKey_PageDown: if (ThePrefs.SkipFrames > 0) + { + TheC64->TheWIMP->PrefsWindow-> + WriteIconNumberU(Icon_Prefs_SkipFText,--ThePrefs.SkipFrames); + } + break; + // toggle floppy emulation status + case IntKey_NumSlash: + { + register int eor, i; + Prefs *prefs = new Prefs(ThePrefs); + + // If Emulation active then ungrey icons now, else grey them + prefs->Emul1541Proc = !prefs->Emul1541Proc; + TheC64->TheWIMP->SetLEDIcons(prefs->Emul1541Proc); + TheC64->NewPrefs(prefs); + ThePrefs = *prefs; + // Show change in prefs window too + TheC64->TheWIMP->PrefsWindow-> + SetIconState(Icon_Prefs_Emul1541,(prefs->Emul1541Proc)?IFlg_Slct:0,IFlg_Slct); + delete prefs; + } + break; + // toggle speed limiter + case IntKey_NumStar: + ThePrefs.LimitSpeed = !ThePrefs.LimitSpeed; + TheC64->TheWIMP->SetSpeedLimiter(ThePrefs.LimitSpeed); + break; + // toggle sound emulation + case IntKey_F5: + { + Window *pw = TheC64->TheWIMP->PrefsWindow; + int i, j; + Prefs *prefs = new Prefs(ThePrefs); + + if (prefs->SIDType == SIDTYPE_NONE) {prefs->SIDType = SIDTYPE_DIGITAL; i = 1;} + else {prefs->SIDType = SIDTYPE_NONE; i = 0;} + for (j=0; j<3; j++) + { + pw->SetIconState(SIDtoIcon[j], (j==i) ? IFlg_Slct : 0, IFlg_Slct); + } + TheC64->TheWIMP->SoundWindow-> + SetIconState(Icon_Sound_Notes, (i==0) ? IFlg_Grey : 0, IFlg_Grey); + TheC64->NewPrefs(prefs); + ThePrefs = *prefs; + delete prefs; + } + break; + case IntKey_Copy: TheC64->Pause(); + TheC64->TheWIMP->EmuPane->WriteIconTextU(Icon_Pane_Pause,PANE_TEXT_RESUME); break; + default: break; + } + } + } + scan_from = code+1; + } + for (code=0; code<8; code++) {lastkeys[code] = newkeys[code];} + *joystick = kjoy; *joystick2 = kjoy2; +} + + +bool C64Display::NumLock(void) +{ + return(((ReadKeyboardStatus() & 4) == 0) ? true : false); +} + + +/* + * Prefs may have changed + */ + +void C64Display::NewPrefs(Prefs *prefs) +{ +} + + +void C64Display::Update(void) +{ + int i, state; + int *ic; + + // Do a redraw of the emulator window + TheC64->TheWIMP->UpdateEmuWindow(); + + // Update the LEDs if necessary + for (i=0; i<4; i++) + { + if ((state = led_state[i]) != old_led_state[i]) + { + ic = (int*)TheC64->TheWIMP->EmuPane->GetIcon(LEDtoIcon[i]); + switch(state) + { + case LED_OFF: + case LED_ERROR_OFF: + sprintf((char*)ic[5],"led_off"); break; + case LED_ON: + sprintf((char*)ic[5],"led_on"); break; + case LED_ERROR_ON: + sprintf((char*)ic[5],"led_error"); break; + } + TheC64->TheWIMP->EmuPane->UpdateIcon(LEDtoIcon[i]); // update, not force-redraw! + old_led_state[i] = state; + } + } +} + + +unsigned int *C64Display::GetColourTable(void) +{ + return (mode_cols); +} + + +// Check whether unpause-key (copy) is pressed +bool C64Display::CheckForUnpause(bool CheckLastState) +{ + int scan_from = IntKey_MinCode, code; + uint32 newkeys[8]; + uint32 lastpause; + + for (code=0; code<8; code++) {newkeys[code] = 0;} + + while (scan_from <= IntKey_MaxCode) + { + if ((code = ScanKeys(scan_from)) != 0xff) + { + newkeys[code >> 5] |= (1 << (code & 0x1f)); + } + scan_from = code+1; + } + lastpause = lastkeys[IntKey_Copy >> 5] & (1 << (IntKey_Copy & 0x1f)); + for (code=0; code<8; code++) {lastkeys[code] = newkeys[code];} + // unpause-key pressed? + if ((newkeys[IntKey_Copy >> 5] & (1 << (IntKey_Copy & 0x1f))) != 0) + { + if ((lastpause == 0) || !CheckLastState) + { + TheC64->Resume(); + TheC64->TheWIMP->EmuPane->WriteIconTextU(Icon_Pane_Pause,PANE_TEXT_PAUSE); + return(true); + } + } + return(false); +} + + +// Requester dialogue box +long ShowRequester(const char *str, const char *button1, const char *button2) +{ + _kernel_oserror myerr; + + myerr.errnum = 0x0; strcpy(myerr.errmess,str); + Wimp_ReportError(&myerr,1,TASKNAME); // always provide an OK box + return(1); +} diff --git a/Src/Display_Amiga.h b/Src/Display_Amiga.h new file mode 100644 index 0000000..daa567b --- /dev/null +++ b/Src/Display_Amiga.h @@ -0,0 +1,641 @@ +/* + * Display_Amiga.h - C64 graphics display, emulator window handling, + * Amiga specific stuff + * + * Frodo (C) 1994-1997,2002-2009 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "C64.h" +#include "SAM.h" +#include "Version.h" + + +/* + C64 keyboard matrix: + + Bit 7 6 5 4 3 2 1 0 + 0 CUD F5 F3 F1 F7 CLR RET DEL + 1 SHL E S Z 4 A W 3 + 2 X T F C 6 D R 5 + 3 V U H B 8 G Y 7 + 4 N O K M 0 J I 9 + 5 , @ : . - L P + + 6 / ^ = SHR HOM ; * £ + 7 R/S Q C= SPC 2 CTL <- 1 +*/ + + +/* + Tables for key translation + Bit 0..2: row/column in C64 keyboard matrix + Bit 3 : implicit shift + Bit 5 : joystick emulation (bit 0..4: mask) +*/ + +const int key_byte[128] = { + 7, 7, 7, 1, 1, 2, 2, 3, + 3, 4, 4, 5, 5, 6, -1,0x30, + 7, 1, 1, 2, 2, 3, 3, 4, + 4, 5, 5, 6, -1,0x26,0x22,0x2a, + 1, 1, 2, 2, 3, 3, 4, 4, + 5, 5, 6, 6, -1,0x24,0x30,0x28, + 6, 1, 2, 2, 3, 3, 4, 4, + 5, 5, 6, -1, -1,0x25,0x21,0x29, + 7, 0, -1, 0, 0, 7, 6, -1, + -1, -1, -1, -1,8+0, 0, 0, 8+0, + 0,8+0, 0,8+0, 0, 8+0, 0, 8+0, + -1, -1, 6, 6, -1, -1, -1, -1, + 1, 6, 1, 7, 7, 7, 7, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1 +}; + +const int key_bit[128] = { + 1, 0, 3, 0, 3, 0, 3, 0, + 3, 0, 3, 0, 3, 0, -1, -1, + 6, 1, 6, 1, 6, 1, 6, 1, + 6, 1, 6, 1, -1, -1, -1, -1, + 2, 5, 2, 5, 2, 5, 2, 5, + 2, 5, 2, 5, -1, -1, -1, -1, + 6, 4, 7, 4, 7, 4, 7, 4, + 7, 4, 7, -1, -1, -1, -1, -1, + 4, 0, -1, 1, 1, 7, 3, -1, + -1, -1, -1, -1, 7, 7, 2, 2, + 4, 4, 5, 5, 6, 6, 3, 3, + -1, -1, 6, 5, -1, -1, -1, -1, + 7, 4, 7, 2, 5, 5, 5, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1 +}; + + +/* + * Menu definitions + */ + +const struct NewMenu new_menus[] = { + NM_TITLE, "Frodo", NULL, 0, 0, NULL, + NM_ITEM, "About Frodo...", NULL, 0, 0, NULL, + NM_ITEM, NM_BARLABEL, NULL, 0, 0, NULL, + NM_ITEM, "Preferences...", "P", 0, 0, NULL, + NM_ITEM, NM_BARLABEL, NULL, 0, 0, NULL, + NM_ITEM, "Reset C64", NULL, 0, 0, NULL, + NM_ITEM, "Insert next disk", "D", 0, 0, NULL, + NM_ITEM, "SAM...", "M", 0, 0, NULL, + NM_ITEM, NM_BARLABEL, NULL, 0, 0, NULL, + NM_ITEM, "Load snapshot...", "O", 0, 0, NULL, + NM_ITEM, "Save snapshot...", "S", 0, 0, NULL, + NM_ITEM, NM_BARLABEL, NULL, 0, 0, NULL, + NM_ITEM, "Quit Frodo", "Q", 0, 0, NULL, + NM_END, NULL, NULL, 0, 0, NULL +}; + + +/* + * Font attributes + */ + +const struct TextAttr led_font_attr = { + "Helvetica.font", 11, FS_NORMAL, 0 +}; + +const struct TextAttr speedo_font_attr = { + "Courier.font", 11, FS_NORMAL, 0 +}; + + +/* + * Display constructor: Create window/screen + */ + +C64Display::C64Display(C64 *the_c64) : TheC64(the_c64) +{ + int i; + + // LEDs off + for (i=0; i<4; i++) + led_state[i] = old_led_state[i] = LED_OFF; + + // Allocate chunky buffer to draw into + chunky_buf = new UBYTE[DISPLAY_X * DISPLAY_Y]; + + // Open fonts + led_font = OpenDiskFont(&led_font_attr); + speedo_font = OpenDiskFont(&speedo_font_attr); + + // Open window on default pubscreen + the_window = OpenWindowTags(NULL, + WA_Left, 0, + WA_Top, 0, + WA_InnerWidth, DISPLAY_X, + WA_InnerHeight, DISPLAY_Y + 16, + WA_Title, (ULONG)"Frodo", + WA_ScreenTitle, (ULONG)"Frodo C64 Emulator", + WA_IDCMP, IDCMP_CLOSEWINDOW | IDCMP_RAWKEY | IDCMP_MENUPICK | IDCMP_REFRESHWINDOW, + WA_DragBar, TRUE, + WA_DepthGadget, TRUE, + WA_CloseGadget, TRUE, + WA_SimpleRefresh, TRUE, + WA_Activate, TRUE, + WA_NewLookMenus, TRUE, + TAG_DONE); + the_screen = the_window->WScreen; + the_rp = the_window->RPort; + xo = the_window->BorderLeft; + yo = the_window->BorderTop; + + // Create menus + the_visual_info = GetVisualInfo(the_screen, NULL); + the_menus = CreateMenus(new_menus, GTMN_FullMenu, TRUE, TAG_DONE); + LayoutMenus(the_menus, the_visual_info, GTMN_NewLookMenus, TRUE, TAG_DONE); + SetMenuStrip(the_window, the_menus); + + // Obtain 16 pens for the C64 colors + for (i=0; i<16; i++) + pens[i] = ObtainBestPen(the_screen->ViewPort.ColorMap, + palette_red[i] * 0x01010101, palette_green[i] * 0x01010101, palette_blue[i] * 0x01010101); + + // Allocate temporary RastPort for WritePixelArra8() + temp_bm = AllocBitMap(DISPLAY_X, 1, 8, 0, NULL); + InitRastPort(&temp_rp); + temp_rp.BitMap = temp_bm; + + // Draw LED bar + draw_led_bar(); + + // Allocate file requesters + open_req = (struct FileRequester *)AllocAslRequestTags(ASL_FileRequest, + ASLFR_Window, (ULONG)the_window, + ASLFR_SleepWindow, TRUE, + ASLFR_TitleText, (ULONG)"Frodo: Load snapshot...", + ASLFR_RejectIcons, TRUE, + TAG_DONE); + save_req = (struct FileRequester *)AllocAslRequestTags(ASL_FileRequest, + ASLFR_Window, (ULONG)the_window, + ASLFR_SleepWindow, TRUE, + ASLFR_TitleText, (ULONG)"Frodo: Save snapshot...", + ASLFR_DoSaveMode, TRUE, + ASLFR_RejectIcons, TRUE, + TAG_DONE); +} + + +/* + * Display destructor + */ + +C64Display::~C64Display() +{ + // Free file requesters + if (open_req != NULL) + FreeAslRequest(open_req); + if (save_req != NULL) + FreeAslRequest(save_req); + + // Free temporary RastPort + if (temp_bm != NULL) + FreeBitMap(temp_bm); + + // Free pens + for (int i=0; i<16; i++) + ReleasePen(the_screen->ViewPort.ColorMap, pens[i]); + + // Delete menus + if (the_menus != NULL) { + if (the_window != NULL) + ClearMenuStrip(the_window); + FreeMenus(the_menus); + } + + // Delete VisualInfo + if (the_visual_info != NULL) + FreeVisualInfo(the_visual_info); + + // Close window + if (the_window != NULL) + CloseWindow(the_window); + + // Close fonts + CloseFont(speedo_font); + CloseFont(led_font); + + // Free chunky buffer + delete chunky_buf; +} + + +/* + * Prefs may have changed + */ + +void C64Display::NewPrefs(Prefs *prefs) +{ +} + + +/* + * Redraw bitmap + */ + +void C64Display::Update(void) +{ + // Update C64 display + WritePixelArray8(the_rp, xo, yo, DISPLAY_X + xo - 1, DISPLAY_Y + yo - 1, + chunky_buf, &temp_rp); + + // Update drive LEDs + for (int i=0; i<4; i++) + if (led_state[i] != old_led_state[i]) { + draw_led(i, led_state[i]); + old_led_state[i] = led_state[i]; + } +} + + +/* + * Draw LED bar at the bottom of the window + */ + +void C64Display::draw_led_bar(void) +{ + int i; + char str[16]; + + SetAPen(the_rp, pens[15]); // Light gray + SetBPen(the_rp, pens[15]); // Light gray + RectFill(the_rp, xo, yo+DISPLAY_Y, xo+DISPLAY_X-1, yo+DISPLAY_Y+15); + + SetAPen(the_rp, pens[1]); // White + Move(the_rp, xo, yo+DISPLAY_Y); Draw(the_rp, xo+DISPLAY_X-1, yo+DISPLAY_Y); + for (i=0; i<5; i++) { + Move(the_rp, xo+DISPLAY_X*i/5, yo+DISPLAY_Y); Draw(the_rp, xo+DISPLAY_X*i/5, yo+DISPLAY_Y+14); + } + for (i=2; i<6; i++) { + Move(the_rp, xo+DISPLAY_X*i/5-23, yo+DISPLAY_Y+11); Draw(the_rp, xo+DISPLAY_X*i/5-9, yo+DISPLAY_Y+11); + Move(the_rp, xo+DISPLAY_X*i/5-9, yo+DISPLAY_Y+11); Draw(the_rp, xo+DISPLAY_X*i/5-9, yo+DISPLAY_Y+5); + } + + SetAPen(the_rp, pens[12]); // Medium gray + Move(the_rp, xo, yo+DISPLAY_Y+15); Draw(the_rp, xo+DISPLAY_X-1, yo+DISPLAY_Y+15); + for (i=1; i<6; i++) { + Move(the_rp, xo+DISPLAY_X*i/5-1, yo+DISPLAY_Y+1); Draw(the_rp, xo+DISPLAY_X*i/5-1, yo+DISPLAY_Y+15); + } + for (i=2; i<6; i++) { + Move(the_rp, xo+DISPLAY_X*i/5-24, yo+DISPLAY_Y+11); Draw(the_rp, xo+DISPLAY_X*i/5-24, yo+DISPLAY_Y+4); + Move(the_rp, xo+DISPLAY_X*i/5-24, yo+DISPLAY_Y+4); Draw(the_rp, xo+DISPLAY_X*i/5-9, yo+DISPLAY_Y+4); + } + + SetFont(the_rp, led_font); + for (i=0; i<4; i++) { + sprintf(str, "Drive %d", i+8); + SetAPen(the_rp, pens[0]); // Black + Move(the_rp, xo+DISPLAY_X*(i+1)/5+8, yo+DISPLAY_Y+11); + Text(the_rp, str, strlen(str)); + draw_led(i, LED_OFF); + } +} + + +/* + * Draw one LED + */ + +void C64Display::draw_led(int num, int state) +{ + switch (state) { + case LED_OFF: + case LED_ERROR_OFF: + SetAPen(the_rp, pens[0]); // Black; + break; + case LED_ON: + SetAPen(the_rp, pens[5]); // Green + break; + case LED_ERROR_ON: + SetAPen(the_rp, pens[2]); // Red + break; + } + RectFill(the_rp, xo+DISPLAY_X*(num+2)/5-23, yo+DISPLAY_Y+5, xo+DISPLAY_X*(num+2)/5-10, yo+DISPLAY_Y+10); +} + + +/* + * Update speedometer + */ + +void C64Display::Speedometer(int speed) +{ + static int delay = 0; + + if (delay >= 20) { + char str[16]; + sprintf(str, "%d%%", speed); + SetAPen(the_rp, pens[15]); // Light gray + RectFill(the_rp, xo+1, yo+DISPLAY_Y+1, xo+DISPLAY_X/5-2, yo+DISPLAY_Y+14); + SetAPen(the_rp, pens[0]); // Black + SetFont(the_rp, speedo_font); + Move(the_rp, xo+24, yo+DISPLAY_Y+10); + Text(the_rp, str, strlen(str)); + delay = 0; + } else + delay++; +} + + +/* + * Return pointer to bitmap data + */ + +UBYTE *C64Display::BitmapBase(void) +{ + return chunky_buf; +} + + +/* + * Return number of bytes per row + */ + +int C64Display::BitmapXMod(void) +{ + return DISPLAY_X; +} + + +/* + * Handle IDCMP messages + */ + +void C64Display::PollKeyboard(UBYTE *key_matrix, UBYTE *rev_matrix, UBYTE *joystick) +{ + struct IntuiMessage *msg; + + // Get and analyze all pending window messages + while ((msg = (struct IntuiMessage *)GetMsg(the_window->UserPort)) != NULL) { + + // Extract data and reply message + ULONG iclass = msg->Class; + USHORT code = msg->Code; + ReplyMsg((struct Message *)msg); + + // Action depends on message class + switch (iclass) { + + case IDCMP_CLOSEWINDOW: // Closing the window quits Frodo + TheC64->Quit(); + break; + + case IDCMP_RAWKEY: + switch (code) { + + case 0x58: // F9: NMI (Restore) + TheC64->NMI(); + break; + + case 0x59: // F10: Reset + TheC64->Reset(); + break; + + case 0x5e: // '+' on keypad: Increase SkipFrames + ThePrefs.SkipFrames++; + break; + + case 0x4a: // '-' on keypad: Decrease SkipFrames + if (ThePrefs.SkipFrames > 1) + ThePrefs.SkipFrames--; + break; + + case 0x5d: // '*' on keypad: Toggle speed limiter + ThePrefs.LimitSpeed = !ThePrefs.LimitSpeed; + break; + + case 0x5c:{ // '/' on keypad: Toggle processor-level 1541 emulation + Prefs *prefs = new Prefs(ThePrefs); + prefs->Emul1541Proc = !prefs->Emul1541Proc; + TheC64->NewPrefs(prefs); + ThePrefs = *prefs; + delete prefs; + break; + } + + default:{ + // Convert Amiga keycode to C64 row/column + int c64_byte = key_byte[code & 0x7f]; + int c64_bit = key_bit[code & 0x7f]; + + if (c64_byte != -1) { + if (!(c64_byte & 0x20)) { + + // Normal keys + bool shifted = c64_byte & 8; + c64_byte &= 7; + if (!(code & 0x80)) { + + // Key pressed + if (shifted) { + key_matrix[6] &= 0xef; + rev_matrix[4] &= 0xbf; + } + key_matrix[c64_byte] &= ~(1 << c64_bit); + rev_matrix[c64_bit] &= ~(1 << c64_byte); + } else { + + // Key released + if (shifted) { + key_matrix[6] |= 0x10; + rev_matrix[4] |= 0x40; + } + key_matrix[c64_byte] |= (1 << c64_bit); + rev_matrix[c64_bit] |= (1 << c64_byte); + } + } else { + + // Joystick emulation + c64_byte &= 0x1f; + if (code & 0x80) + *joystick |= c64_byte; + else + *joystick &= ~c64_byte; + } + } + } + } + break; + + case IDCMP_MENUPICK:{ + if (code == MENUNULL) + break; + + // Get item number + int item_number = ITEMNUM(code); + switch (item_number) { + + case 0: { // About Frodo + TheC64->Pause(); + char str[256]; + sprintf(str, "%s by Christian Bauer\n\n© Copyright 1994-1997,2002-2005", VERSION_STRING); + ShowRequester(str, "OK"); + TheC64->Resume(); + break; + } + + case 2: // Preferences + TheC64->Pause(); + be_app->RunPrefsEditor(); + TheC64->Resume(); + break; + + case 4: // Reset C64 + TheC64->Reset(); + break; + + case 5: // Insert next disk + if (strlen(ThePrefs.DrivePath[0]) > 4) { + char str[256]; + strcpy(str, ThePrefs.DrivePath[0]); + char *p = str + strlen(str) - 5; + + // If path matches "*.?64", increment character before the '.' + if (p[1] == '.' && p[3] == '6' && p[4] == '4') { + p[0]++; + + // If no such file exists, set character before the '.' to '1', 'a' or 'A' + FILE *file; + if ((file = fopen(str, "rb")) == NULL) { + if (isdigit(p[0])) + p[0] = '1'; + else if (isupper(p[0])) + p[0] = 'A'; + else + p[0] = 'a'; + } else + fclose(file); + + // Set new prefs + Prefs *prefs = new Prefs(ThePrefs); + strcpy(prefs->DrivePath[0], str); + TheC64->NewPrefs(prefs); + ThePrefs = *prefs; + delete prefs; + } + } + break; + + case 6: // SAM + TheC64->Pause(); + SAM(TheC64); + TheC64->Resume(); + break; + + case 8: // Load snapshot + if (open_req != NULL && AslRequest(open_req, NULL)) { + char path[256]; + strncpy(path, open_req->fr_Drawer, 255); + AddPart(path, open_req->fr_File, 255); + TheC64->Pause(); + TheC64->LoadSnapshot(path); + TheC64->Resume(); + } + break; + + case 9: // Save snapshot + if (save_req != NULL && AslRequest(save_req, NULL)) { + char path[256]; + strncpy(path, save_req->fr_Drawer, 255); + AddPart(path, save_req->fr_File, 255); + TheC64->Pause(); + TheC64->SaveSnapshot(path); + TheC64->Resume(); + } + break; + + case 11: // Quit Frodo + TheC64->Quit(); + break; + } + break; + } + + case IDCMP_REFRESHWINDOW: + BeginRefresh(the_window); + draw_led_bar(); + EndRefresh(the_window, TRUE); + break; + } + } +} + + +/* + * Check if NumLock is down (for switching the joystick keyboard emulation) + */ + +bool C64Display::NumLock(void) +{ + return FALSE; +} + + +/* + * Allocate C64 colors + */ + +void C64Display::InitColors(UBYTE *colors) +{ + // Spread pens into colors array + for (int i=0; i<256; i++) + colors[i] = pens[i & 0x0f]; +} + + +/* + * Show a requester + */ + +long ShowRequester(const char *str, const char *button1, const char *button2) +{ + struct EasyStruct es; + char gads[256]; + + strcpy(gads, button1); + if (button2) { + strcat(gads, "|"); + strcat(gads, button2); + } + + es.es_StructSize = sizeof(struct EasyStruct); + es.es_Flags = 0; + es.es_Title = "Frodo"; + es.es_TextFormat = str; + es.es_GadgetFormat = gads; + + return EasyRequestArgs(NULL, &es, NULL, NULL) % 1; +} diff --git a/Src/Display_Be.h b/Src/Display_Be.h new file mode 100644 index 0000000..6f3d92c --- /dev/null +++ b/Src/Display_Be.h @@ -0,0 +1,1181 @@ +/* + * Display_Be.h - C64 graphics display, emulator window handling, + * Be specific stuff + * + * Frodo (C) 1994-1997,2002-2009 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include + +#include "C64.h" +#include "main.h" + + +#ifndef BIT_BANG +#define BIT_BANG 0 +#endif +#ifndef MGA_HACK +#define MGA_HACK 0 +#endif + + +// Window thread messages +const uint32 MSG_REDRAW = 1; + + +// C64 display and window frame +const BRect DisplayFrame = BRect(0, 0, DISPLAY_X-1, DISPLAY_Y-1); +const BRect WindowFrame = BRect(0, 0, DISPLAY_X-1, DISPLAY_Y-1 + 16); + + +// Background color +const rgb_color fill_gray = {208, 208, 208, 0}; +const rgb_color shine_gray = {232, 232, 232, 0}; +const rgb_color shadow_gray = {152, 152, 152, 0}; + + +/* + C64 keyboard matrix: + + Bit 7 6 5 4 3 2 1 0 + 0 CUD F5 F3 F1 F7 CLR RET DEL + 1 SHL E S Z 4 A W 3 + 2 X T F C 6 D R 5 + 3 V U H B 8 G Y 7 + 4 N O K M 0 J I 9 + 5 , @ : . - L P + + 6 / ^ = SHR HOM ; * £ + 7 R/S Q C= SPC 2 CTL <- 1 +*/ + + +/* + Tables for key translation + Bit 0..2: row/column in C64 keyboard matrix + Bit 3 : implicit shift + Bit 5 : joystick emulation (bit 0..4: mask) +*/ + +const int key_byte[128] = { + -1, 7, 0,8+0, 0,8+0, 0, 8+0, + 0, 8+0, -1, -1, -1, -1, -1, -1, + + 7, 7, 7, 7, 1, 1, 2, 2, + 3, 3, 4, 4, 5, 5, 0, 8+0, + + 6, 6, -1, -1, -1, -1, -1, 7, + 1, 1, 2, 2, 3, 3, 4, 4, + + 5, 5, 6, 6, 0, 6, 6,0x25, + 0x21,0x29, -1, 1, 1, 1, 2, 2, + + 3, 3, 4, 4, 5, 5, 6, 0, + 0x24,0x30,0x28, 1, 1, 2, 2, 3, + + 3, 4, 4, 5, 5, 6, 6, 8+0, + 0x26,0x22,0x2a, 0, 7, -1, 7, -1, + + 0x30, 8+0, 0, 0,0x30, -1, 7, 7, + -1, -1, -1, -1, -1, -1, -1, -1, + + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1 +}; + +const int key_bit[128] = { + -1, 7, 4, 4, 5, 5, 6, 6, + 3, 3, -1, -1, -1, -1, -1, -1, + + 7, 1, 0, 3, 0, 3, 0, 3, + 0, 3, 0, 3, 0, 3, 0, 0, + + 3, 0, -1, -1, -1, -1, -1, 6, + 1, 6, 1, 6, 1, 6, 1, 6, + + 1, 6, 1, 6, 0, 0, 5, -1, + -1, -1, -1, 7, 2, 5, 2, 5, + + 2, 5, 2, 5, 2, 5, 2, 1, + -1, -1, -1, 7, 4, 7, 4, 7, + + 4, 7, 4, 7, 4, 7, 4, 7, + -1, -1, -1, 1, 2, -1, 4, -1, + + 5, 2, 7, 2, -1, -1, 5, 5, + -1, -1, -1, -1, -1, -1, -1, -1, + + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1 +}; + + +/* + * A simple view class for blitting a bitmap on the screen + */ + +class BitmapView : public BView { +public: + BitmapView(BRect frame, BBitmap *bitmap); + virtual void Draw(BRect update); + virtual void KeyDown(const char *bytes, int32 numBytes); + void ChangeBitmap(BBitmap *bitmap); + +private: + BBitmap *the_bitmap; +}; + + +/* + * Class for the main C64 display window + */ + +class SpeedoView; +class LEDView; + +class C64Window : public BDirectWindow { +public: + C64Window(); + + virtual bool QuitRequested(void); + virtual void MessageReceived(BMessage *msg); + virtual void DirectConnected(direct_buffer_info *info); + + BBitmap *TheBitmap[2]; + SpeedoView *Speedometer; + LEDView *LED[4]; + +#if BIT_BANG + uint8 *bits; + int bytes_per_row; +#endif + +private: + BitmapView *main_view; +}; + + +/* + * Class for the main C64 display using the GameKit + */ + +class C64Screen : public BWindowScreen { +public: + C64Screen(C64Display *display) : BWindowScreen("Frodo", B_8_BIT_640x480, &error), the_display(display) + { + Lock(); + TheBitmap = new BBitmap(DisplayFrame, B_COLOR_8_BIT); + main_view = new BitmapView(Bounds(), TheBitmap); + AddChild(main_view); + main_view->MakeFocus(); + Connected = false; + first_connected = true; +#if MGA_HACK + mga_ready = false; +#endif + Unlock(); + } + ~C64Screen() + { + delete TheBitmap; + } + + virtual void ScreenConnected(bool active); + virtual void DispatchMessage(BMessage *msg, BHandler *handler); + void DrawLED(int i, int state); + void DrawSpeedometer(void); + void FillRect(int x1, int y1, int x2, int y2, int color); + + bool Connected; // Flag: screen connected + int Speed; + char SpeedoStr[16]; // Speedometer value converted to a string + BBitmap *TheBitmap; + +private: + C64Display *the_display; + BitmapView *main_view; + bool first_connected; + status_t error; + +#if MGA_HACK + area_id mga_clone_area; + volatile uint8 *isa_io; + bool mga_ready; + + void CRTC_out(int reg, uint8 val) {isa_io[0x3d4] = reg; __eieio(); isa_io[0x3d5] = val; __eieio();} + uint8 CRTC_in(int reg) {isa_io[0x3d4] = reg; __eieio(); return isa_io[0x3d5];} + void SEQ_out(int reg, uint8 val) {isa_io[0x3c4] = reg; __eieio(); isa_io[0x3c5] = val; __eieio();} + uint8 SEQ_in(int reg) {isa_io[0x3c4] = reg; __eieio(); return isa_io[0x3c5];} + void GDC_out(int reg, uint8 val) {isa_io[0x3ce] = reg; __eieio(); isa_io[0x3cf] = val; __eieio();} + uint8 GDC_in(int reg) {isa_io[0x3ce] = reg; __eieio(); return isa_io[0x3cf];} + void ATC_out(int reg, uint8 val) {isa_io[0x3c0] = reg; __eieio(); isa_io[0x3c0] = val; __eieio();} +#endif +}; + + +/* + * Class for speedometer + */ + +class SpeedoView : public BView { +public: + SpeedoView(BRect frame); + virtual void Draw(BRect update); + virtual void Pulse(void); + void SetValue(int percent); + +private: + char speedostr[16]; // Speedometer value converted to a string + BRect bounds; +}; + + +/* + * Class for drive LED + */ + +class LEDView : public BView { +public: + LEDView(BRect frame, const char *label); + virtual void Draw(BRect update); + virtual void Pulse(void); + void DrawLED(void); + void SetState(int state); + +private: + int current_state; + const char *the_label; + BRect bounds; +}; + + +/* + * Display constructor: Create window/screen + */ + +C64Display::C64Display(C64 *the_c64) : TheC64(the_c64) +{ + // LEDs off + for (int i=0; i<4; i++) + led_state[i] = old_led_state[i] = LED_OFF; + + // Open window/screen + draw_bitmap = 1; + if (ThePrefs.DisplayType == DISPTYPE_SCREEN) { + using_screen = true; + the_screen = new C64Screen(this); + the_screen->Show(); + while (!the_screen->Connected) + snooze(20000); + } else { + using_screen = false; + the_window = new C64Window(); + the_window->Show(); + } + + // Prepare key_info buffer + get_key_info(&old_key_info); +} + + +/* + * Display destructor + */ + +C64Display::~C64Display() +{ + if (using_screen) { + the_screen->Lock(); + the_screen->Quit(); + } else { + the_window->Lock(); + the_window->Quit(); + } +} + + +/* + * Prefs may have changed + */ + +void C64Display::NewPrefs(Prefs *prefs) +{ + if (prefs->DisplayType == DISPTYPE_SCREEN) { + if (!using_screen) { + // Switch to full screen display + using_screen = true; + the_window->Lock(); + the_window->Quit(); + the_screen = new C64Screen(this); + the_screen->Show(); + while (!the_screen->Connected) + snooze(20000); + } + } else { + if (using_screen) { + // Switch to window display + using_screen = false; + the_screen->Lock(); + the_screen->Quit(); + the_window = new C64Window(); + the_window->Show(); + } + } +} + + +/* + * Redraw bitmap (let the window thread do it) + */ + +void C64Display::Update(void) +{ + if (using_screen) { + + // Update LEDs/speedometer + for (int i=0; i<4; i++) + the_screen->DrawLED(i, led_state[i]); + the_screen->DrawSpeedometer(); + + // Update C64 display in dobule scan mode + if (ThePrefs.DoubleScan) { + uint8 *src = (uint8 *)the_screen->TheBitmap->Bits(); + uint32 src_xmod = the_screen->TheBitmap->BytesPerRow(); + src += src_xmod * 16 + 32; + uint8 *dest = (uint8 *)the_screen->CardInfo()->frame_buffer; + uint32 dest_xmod = the_screen->CardInfo()->bytes_per_row; +#ifdef __POWERPC__ + double tmp[1]; + for (int y=0; y<240; y++) { + uint32 *p = (uint32 *)src - 1; + double *q1 = (double *)dest - 1; + double *q2 = q1 + dest_xmod / sizeof(double); + for (int x=0; x<80; x++) { + uint32 val = *(++p); + uint8 *r = (uint8 *)&tmp[1]; + *(--r) = val; + *(--r) = val; + val >>= 8; + *(--r) = val; + *(--r) = val; + val >>= 8; + *(--r) = val; + *(--r) = val; + val >>= 8; + *(--r) = val; + *(--r) = val; + double tmp2 = tmp[0]; + *(++q1) = tmp2; + *(++q2) = tmp2; + } + src += src_xmod; + dest += dest_xmod * 2; + } +#else + for (int y=0; y<240; y++) { + uint32 *p = (uint32 *)src; + uint32 *q1 = (uint32 *)dest; + uint32 *q2 = q1 + dest_xmod / sizeof(uint32); + for (int x=0; x<80; x++) { + uint32 val = *p++; + uint32 tmp = val & 0x000000ff; + tmp |= (val << 8) & 0x0000ff00; + tmp |= (val << 8) & 0x00ff0000; + tmp |= (val << 16) & 0xff000000; + *q1++ = tmp; + *q2++ = tmp; + tmp = (val >> 16) & 0x000000ff; + tmp |= (val >> 8) & 0x0000ff00; + tmp |= (val >> 8) & 0x00ff0000; + tmp |= val & 0xff000000; + *q1++ = tmp; + *q2++ = tmp; + } + src += src_xmod; + dest += dest_xmod * 2; + } +#endif + } + + } else { + +#if !BIT_BANG + // Update C64 display + BMessage msg(MSG_REDRAW); + msg.AddInt32("bitmap", draw_bitmap); + the_window->PostMessage(&msg); + draw_bitmap ^= 1; +#endif + + // Update LEDs + for (int i=0; i<4; i++) + if (led_state[i] != old_led_state[i]) { + the_window->LED[i]->SetState(led_state[i]); + old_led_state[i] = led_state[i]; + } + } +} + + +/* + * Set value displayed by the speedometer + */ + +void C64Display::Speedometer(int speed) +{ + if (using_screen) { + the_screen->Speed = speed; + sprintf(the_screen->SpeedoStr, "%3d%%", speed); + } else + the_window->Speedometer->SetValue(speed); +} + + +/* + * Return pointer to bitmap data + */ + +uint8 *C64Display::BitmapBase(void) +{ + if (using_screen) { + if (ThePrefs.DoubleScan) + return (uint8 *)the_screen->TheBitmap->Bits(); + else + return (uint8 *)the_screen->CardInfo()->frame_buffer; + } else +#if BIT_BANG + return (uint8 *)the_window->bits; +#else + return (uint8 *)the_window->TheBitmap[draw_bitmap]->Bits(); +#endif +} + + +/* + * Return number of bytes per row + */ + +int C64Display::BitmapXMod(void) +{ + if (using_screen) { + if (ThePrefs.DoubleScan) + return the_screen->TheBitmap->BytesPerRow(); + else + return the_screen->CardInfo()->bytes_per_row; + } else +#if BIT_BANG + return the_window->bytes_per_row; +#else + return the_window->TheBitmap[draw_bitmap]->BytesPerRow(); +#endif +} + + +/* + * Poll the keyboard + */ + +void C64Display::PollKeyboard(uint8 *key_matrix, uint8 *rev_matrix, uint8 *joystick) +{ + key_info the_key_info; + int be_code, be_byte, be_bit, c64_byte, c64_bit; + bool shifted; + + // Window must be active, command key must be up + if (using_screen) { + if (!the_screen->Connected) + return; + } else + if (!the_window->IsActive()) + return; + if (!(modifiers() & B_COMMAND_KEY)) { + + // Read the state of all keys + get_key_info(&the_key_info); + + // Did anything change at all? + if (!memcmp(&old_key_info, &the_key_info, sizeof(key_info))) + return; + + // Loop to convert BeOS keymap to C64 keymap + for (be_code=0; be_code<0x68; be_code++) { + be_byte = be_code >> 3; + be_bit = 1 << (~be_code & 7); + + // Key state changed? + if ((the_key_info.key_states[be_byte] & be_bit) + != (old_key_info.key_states[be_byte] & be_bit)) { + + c64_byte = key_byte[be_code]; + c64_bit = key_bit[be_code]; + if (c64_byte != -1) { + if (!(c64_byte & 0x20)) { + + // Normal keys + shifted = c64_byte & 8; + c64_byte &= 7; + if (the_key_info.key_states[be_byte] & be_bit) { + + // Key pressed + if (shifted) { + key_matrix[6] &= 0xef; + rev_matrix[4] &= 0xbf; + } + key_matrix[c64_byte] &= ~(1 << c64_bit); + rev_matrix[c64_bit] &= ~(1 << c64_byte); + } else { + + // Key released + if (shifted) { + key_matrix[6] |= 0x10; + rev_matrix[4] |= 0x40; + } + key_matrix[c64_byte] |= (1 << c64_bit); + rev_matrix[c64_bit] |= (1 << c64_byte); + } + } else { + + // Joystick emulation + c64_byte &= 0x1f; + if (the_key_info.key_states[be_byte] & be_bit) + *joystick &= ~c64_byte; + else + *joystick |= c64_byte; + } + } + } + } + + old_key_info = the_key_info; + } +} + + +/* + * Check if NumLock is down (for switching the joystick keyboard emulation) + */ + +bool C64Display::NumLock(void) +{ + return modifiers() & B_NUM_LOCK; +} + + +/* + * Allocate C64 colors + */ + +void C64Display::InitColors(uint8 *colors) +{ + BScreen scr(using_screen ? (BWindow *)the_screen : the_window); + for (int i=0; i<256; i++) + colors[i] = scr.IndexForColor(palette_red[i & 0x0f], palette_green[i & 0x0f], palette_blue[i & 0x0f]); +} + + +/* + * Pause display (GameKit only) + */ + +void C64Display::Pause(void) +{ + if (using_screen) + the_screen->Hide(); +} + + +/* + * Resume display (GameKit only) + */ + +void C64Display::Resume(void) +{ + if (using_screen) + the_screen->Show(); +} + + +/* + * Window constructor + */ + +C64Window::C64Window() : BDirectWindow(WindowFrame, "Frodo", B_TITLED_WINDOW, B_NOT_RESIZABLE | B_NOT_ZOOMABLE) +{ + // Move window to right position + Lock(); + MoveTo(80, 60); + + // Set up menus + BMenuBar *bar = new BMenuBar(Bounds(), ""); + BMenu *menu = new BMenu("Frodo"); + menu->AddItem(new BMenuItem("About Frodo" B_UTF8_ELLIPSIS, new BMessage(B_ABOUT_REQUESTED))); + menu->AddItem(new BSeparatorItem); + menu->AddItem(new BMenuItem("Preferences" B_UTF8_ELLIPSIS, new BMessage(MSG_PREFS), 'P')); + menu->AddItem(new BSeparatorItem); + menu->AddItem(new BMenuItem("Reset C64", new BMessage(MSG_RESET))); + menu->AddItem(new BMenuItem("Insert next disk", new BMessage(MSG_NEXTDISK), 'D')); + menu->AddItem(new BMenuItem("SAM" B_UTF8_ELLIPSIS, new BMessage(MSG_SAM), 'M')); + menu->AddItem(new BSeparatorItem); + menu->AddItem(new BMenuItem("Load snapshot" B_UTF8_ELLIPSIS, new BMessage(MSG_OPEN_SNAPSHOT), 'O')); + menu->AddItem(new BMenuItem("Save snapshot" B_UTF8_ELLIPSIS, new BMessage(MSG_SAVE_SNAPSHOT), 'S')); + menu->AddItem(new BSeparatorItem); + menu->AddItem(new BMenuItem("Quit Frodo", new BMessage(B_QUIT_REQUESTED), 'Q')); + menu->SetTargetForItems(be_app); + bar->AddItem(menu); + AddChild(bar); + SetKeyMenuBar(bar); + int mbar_height = int(bar->Frame().bottom) + 1; + + // Resize window to fit menu bar + ResizeBy(0, mbar_height); + + // Allocate bitmaps + TheBitmap[0] = new BBitmap(DisplayFrame, B_COLOR_8_BIT); + TheBitmap[1] = new BBitmap(DisplayFrame, B_COLOR_8_BIT); + + // Create top view + BRect b = Bounds(); + BView *top = new BView(BRect(0, mbar_height, b.right, b.bottom), "top", B_FOLLOW_NONE, 0); + AddChild(top); + + // Create bitmap view + main_view = new BitmapView(DisplayFrame, TheBitmap[0]); + top->AddChild(main_view); + main_view->MakeFocus(); + + // Create speedometer + Speedometer = new SpeedoView(BRect(0, DISPLAY_Y, DISPLAY_X/5-1, DISPLAY_Y+15)); + top->AddChild(Speedometer); + + // Create drive LEDs + LED[0] = new LEDView(BRect(DISPLAY_X/5, DISPLAY_Y, DISPLAY_X*2/5-1, DISPLAY_Y+15), "Drive 8"); + top->AddChild(LED[0]); + LED[1] = new LEDView(BRect(DISPLAY_X*2/5, DISPLAY_Y, DISPLAY_X*3/5-1, DISPLAY_Y+15), "Drive 9"); + top->AddChild(LED[1]); + LED[2] = new LEDView(BRect(DISPLAY_X*3/5, DISPLAY_Y, DISPLAY_X*4/5-1, DISPLAY_Y+15), "Drive 10"); + top->AddChild(LED[2]); + LED[3] = new LEDView(BRect(DISPLAY_X*4/5, DISPLAY_Y, DISPLAY_X-1, DISPLAY_Y+15), "Drive 11"); + top->AddChild(LED[3]); + + // Set pulse rate to 0.4 seconds for blinking drive LEDs + SetPulseRate(400000); + Unlock(); +} + + +/* + * Closing the window quits Frodo + */ + +bool C64Window::QuitRequested(void) +{ + be_app->PostMessage(B_QUIT_REQUESTED); + return false; +} + + +/* + * Handles redraw messages + */ + +void C64Window::MessageReceived(BMessage *msg) +{ + BMessage *msg2; + + switch (msg->what) { + case MSG_REDRAW: // Redraw bitmap + MessageQueue()->Lock(); + while ((msg2 = MessageQueue()->FindMessage(MSG_REDRAW, 0)) != NULL) { + MessageQueue()->RemoveMessage(msg2); + delete msg2; + } + MessageQueue()->Unlock(); + main_view->ChangeBitmap(TheBitmap[msg->FindInt32("bitmap")]); + Lock(); + main_view->Draw(DisplayFrame); + Unlock(); + break; + + default: + BWindow::MessageReceived(msg); + } +} + + +/* + * Window connected/disconnected + */ + +void C64Window::DirectConnected(direct_buffer_info *info) +{ +#if BIT_BANG + switch (info->buffer_state & B_DIRECT_MODE_MASK) { + case B_DIRECT_STOP: +// acquire_sem(drawing_sem); + break; + case B_DIRECT_MODIFY: +// acquire_sem(drawing_sem); + case B_DIRECT_START: + bits = ((uint8 *)info->bits + info->window_bounds.top * info->bytes_per_row + info->window_bounds.left * info->bits_per_pixel / 8); + bytes_per_row = info->bytes_per_row; +// release_sem(drawing_sem); + break; + } +#endif +} + + +/* + * Workspace activated/deactivated + */ + +void C64Screen::ScreenConnected(bool active) +{ + if (active) { + if (first_connected) { + first_connected = false; + +#if MGA_HACK + mga_clone_area = -1; + + // Construct register area name + char mga_area_name[64]; + int bus = 0, device = 13, function = 0; + sprintf(mga_area_name, "102B_0519_%02X%02X%02X regs", bus, device, function); + + // Find MGA register area + area_id mga_area = find_area(mga_area_name); + if (mga_area > 0) { + + // Clone area, remove write protection + volatile uint8 *mga_io; + mga_clone_area = clone_area("mga registers", (void **)&mga_io, B_ANY_ADDRESS, B_READ_AREA | B_WRITE_AREA, mga_area); + if (mga_clone_area > 0) { + isa_io = mga_io + 0x1c00; + mga_ready = true; + } + } +#endif + } + +#if MGA_HACK + if (mga_ready) { + CRTC_out(0x09, 1); // Enable double scan + int a = 4 * 640; + CRTC_out(0x0c, a >> 8); // Center screen vertically + CRTC_out(0x0d, a); + // defaults: + // total 0x67 + // display end 0x4f + // blank start 0x4f + // blank end 2b + // sync start 0x53 + // sync end 1f + CRTC_out(0x00, 0x3f); // Horizontal timing + CRTC_out(0x01, 0x2f); + CRTC_out(0x02, 0x2f); + CRTC_out(0x03, 0x83); + CRTC_out(0x04, 0x32); + CRTC_out(0x05, 0x1a); + } +#endif + + FillRect(0, 0, 639, 479, 0); // Clear screen + the_display->TheC64->Resume(); + Connected = true; + } else { + the_display->TheC64->Pause(); + Connected = false; + } + BWindowScreen::ScreenConnected(active); +} + + +/* + * Simulate menu commands + */ + +void C64Screen::DispatchMessage(BMessage *msg, BHandler *handler) +{ + switch (msg->what) { + case B_KEY_DOWN: { + uint32 mods = msg->FindInt32("modifiers"); + if (mods & B_COMMAND_KEY) { + uint32 key = msg->FindInt32("raw_char"); + switch (key) { + case 'p': + be_app->PostMessage(MSG_PREFS); + break; + case 'd': + be_app->PostMessage(MSG_NEXTDISK); + break; + case 'm': + be_app->PostMessage(MSG_SAM); + break; + } + } + BWindowScreen::DispatchMessage(msg, handler); + break; + } + + default: + BWindowScreen::DispatchMessage(msg, handler); + } +} + + +/* + * Draw drive LEDs + */ + +void C64Screen::DrawLED(int i, int state) +{ + int maxy; + if (ThePrefs.DoubleScan) + maxy = 480; + else + maxy = DISPLAY_Y; + + switch (state) { + case LED_ON: + FillRect(10+i*20, maxy-20, 20+i*20, maxy-12, 54); + break; + case LED_ERROR_ON: + FillRect(10+i*20, maxy-20, 20+i*20, maxy-12, 44); + break; + } +} + + +/* + * Draw speedometer + */ + +static const int8 Digits[11][8] = { // Digit images + {0x3c, 0x66, 0x6e, 0x76, 0x66, 0x66, 0x3c, 0x00}, + {0x18, 0x18, 0x38, 0x18, 0x18, 0x18, 0x7e, 0x00}, + {0x3c, 0x66, 0x06, 0x0c, 0x30, 0x60, 0x7e, 0x00}, + {0x3c, 0x66, 0x06, 0x1c, 0x06, 0x66, 0x3c, 0x00}, + {0x06, 0x0e, 0x1e, 0x66, 0x7f, 0x06, 0x06, 0x00}, + {0x7e, 0x60, 0x7c, 0x06, 0x06, 0x66, 0x3c, 0x00}, + {0x3c, 0x66, 0x60, 0x7c, 0x66, 0x66, 0x3c, 0x00}, + {0x7e, 0x66, 0x0c, 0x18, 0x18, 0x18, 0x18, 0x00}, + {0x3c, 0x66, 0x66, 0x3c, 0x66, 0x66, 0x3c, 0x00}, + {0x3c, 0x66, 0x66, 0x3e, 0x06, 0x66, 0x3c, 0x00}, + {0x62, 0x66, 0x0c, 0x18, 0x30, 0x66, 0x46, 0x00}, +}; + +void C64Screen::DrawSpeedometer() +{ + // Don't display speedometer if we're running at about 100% + if (Speed >= 50 && Speed <= 101) + return; + + int maxx, maxy; + if (ThePrefs.DoubleScan) { + maxx = 640; + maxy = 480; + } else { + maxx = DISPLAY_X; + maxy = DISPLAY_Y; + } + + char *s = SpeedoStr; + char c; + long xmod = CardInfo()->bytes_per_row; + uint8 *p = (uint8 *)CardInfo()->frame_buffer + maxx - 8*8 + (maxy-20) * xmod; + while ((c = *s++) != 0) { + if (c == ' ') + continue; + if (c == '%') + c = 10; + else + c -= '0'; + uint8 *q = p; + for (int y=0; y<8; y++) { + uint8 data = Digits[c][y]; + for (int x=0; x<8; x++) { + if (data & (1 << (7-x))) + q[x] = 255; + else + q[x] = 0; + } + q += xmod; + } + p += 8; + } +} + + +/* + * Fill rectangle + */ + +void C64Screen::FillRect(int x1, int y1, int x2, int y2, int color) +{ + long xmod = CardInfo()->bytes_per_row; + uint8 *p = (uint8 *)CardInfo()->frame_buffer + y1 * xmod + x1; + int n = x2 - x1 + 1; + for(int y=y1; y<=y2; y++) { +#ifdef __POWERPC__ + memset_nc(p, color, n); +#else + memset(p, color, n); +#endif + p += xmod; + } +} + + +/* + * Bitmap view constructor + */ + +BitmapView::BitmapView(BRect frame, BBitmap *bitmap) : BView(frame, "", B_FOLLOW_NONE, B_WILL_DRAW) +{ + ChangeBitmap(bitmap); +} + + +/* + * Blit the bitmap + */ + +void BitmapView::Draw(BRect update) +{ + if (the_bitmap != NULL) + DrawBitmapAsync(the_bitmap, update, update); +} + + +/* + * Receive special key-down events (main C64 keyboard handling is done in PollKeyboard) + */ + +void BitmapView::KeyDown(const char *bytes, int32 numBytes) +{ + if (bytes[0] == B_FUNCTION_KEY || bytes[0] == '+' || bytes[0] == '-' || bytes[0] == '*' || bytes[0] == '/') { + BMessage *msg = Window()->CurrentMessage(); + long key; + if (msg->FindInt32("key", &key) == B_NO_ERROR) { + switch (key) { + + case B_F11_KEY: // F11: NMI (Restore) + be_app->PostMessage(MSG_NMI); + break; + + case B_F12_KEY: // F12: Reset + be_app->PostMessage(MSG_RESET); + break; + + case 0x3a: // '+' on keypad: Increase SkipFrames + ThePrefs.SkipFrames++; + break; + + case 0x25: // '-' on keypad: Decrease SkipFrames + if (ThePrefs.SkipFrames > 1) + ThePrefs.SkipFrames--; + break; + + case 0x24: // '*' on keypad: Toggle speed limiter + ThePrefs.LimitSpeed = !ThePrefs.LimitSpeed; + break; + + case 0x23: // '/' on keypad: Toggle processor-level 1541 emulation + be_app->PostMessage(MSG_TOGGLE_1541); + break; + } + } + } +} + + +/* + * Change view bitmap + */ + +void BitmapView::ChangeBitmap(BBitmap *bitmap) +{ + the_bitmap = bitmap; +} + + +/* + * Speedometer constructor + */ + +SpeedoView::SpeedoView(BRect frame) : BView(frame, "", B_FOLLOW_NONE, B_WILL_DRAW | B_PULSE_NEEDED) +{ + speedostr[0] = 0; + bounds = Bounds(); + SetViewColor(fill_gray); + SetLowColor(fill_gray); + SetFont(be_plain_font); +} + + +/* + * Draw speedometer + */ + +void SpeedoView::Draw(BRect update) +{ + // Draw bevelled border + SetHighColor(shine_gray); + StrokeLine(BPoint(0, bounds.bottom), BPoint(0, 0)); + StrokeLine(BPoint(bounds.right, 0)); + SetHighColor(shadow_gray); + StrokeLine(BPoint(bounds.right, bounds.bottom), BPoint(bounds.right, 1)); + + // Draw text + SetHighColor(0, 0, 0); + DrawString(speedostr, BPoint(24, 12)); +} + + +/* + * Update speedometer at regular intervals + */ + +void SpeedoView::Pulse(void) +{ + Invalidate(BRect(1, 1, bounds.right-1, 15)); +} + + +/* + * Set new speedometer value + */ + +void SpeedoView::SetValue(int speed) +{ + sprintf(speedostr, "%d%%", speed); +} + + +/* + * LED view constructor + */ + +LEDView::LEDView(BRect frame, const char *label) : BView(frame, "", B_FOLLOW_NONE, B_WILL_DRAW | B_PULSE_NEEDED) +{ + current_state = 0; + the_label = label; + bounds = Bounds(); + SetViewColor(fill_gray); + SetFont(be_plain_font); +} + + +/* + * Draw drive LED + */ + +void LEDView::Draw(BRect update) +{ + // Draw bevelled border + SetHighColor(shine_gray); + StrokeLine(BPoint(0, bounds.bottom), BPoint(0, 0)); + StrokeLine(BPoint(bounds.right, 0)); + SetHighColor(shadow_gray); + StrokeLine(BPoint(bounds.right, bounds.bottom), BPoint(bounds.right, 1)); + + // Draw label + SetHighColor(0, 0, 0); + SetLowColor(fill_gray); + DrawString(the_label, BPoint(8, 12)); + + // Draw LED + SetHighColor(shadow_gray); + StrokeLine(BPoint(bounds.right-24, 12), BPoint(bounds.right-24, 4)); + StrokeLine(BPoint(bounds.right-8, 4)); + SetHighColor(shine_gray); + StrokeLine(BPoint(bounds.right-23, 12), BPoint(bounds.right-8, 12)); + StrokeLine(BPoint(bounds.right-8, 5)); + DrawLED(); +} + + +/* + * Redraw just the LED + */ + +void LEDView::DrawLED(void) +{ + Window()->Lock(); + switch (current_state) { + case LED_OFF: + case LED_ERROR_OFF: + SetHighColor(32, 32, 32); + break; + case LED_ON: + SetHighColor(0, 240, 0); + break; + case LED_ERROR_ON: + SetHighColor(240, 0, 0); + break; + } + FillRect(BRect(bounds.right-23, 5, bounds.right-9, 11)); + Window()->Unlock(); +} + + +/* + * Set LED state + */ + +void LEDView::SetState(int state) +{ + if (state != current_state) { + current_state = state; + DrawLED(); + } +} + + +/* + * Toggle red error LED + */ + +void LEDView::Pulse(void) +{ + switch (current_state) { + case LED_ERROR_ON: + current_state = LED_ERROR_OFF; + DrawLED(); + break; + case LED_ERROR_OFF: + current_state = LED_ERROR_ON; + DrawLED(); + break; + } +} + + +/* + * Show a requester + */ + +long ShowRequester(const char *str, const char *button1, const char *button2) +{ + BAlert *the_alert; + + the_alert = new BAlert("", str, button1, button2, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT); + return the_alert->Go(); +} diff --git a/Src/Display_EmbeddedSDL.h b/Src/Display_EmbeddedSDL.h new file mode 100644 index 0000000..2d43ded --- /dev/null +++ b/Src/Display_EmbeddedSDL.h @@ -0,0 +1,961 @@ +/* + * DisplayEmbeddedSDL.h - C64 graphics display, emulator window handling + * for embedded Linux devices (Qtopia, Maemo) + * + * Frodo (C) 1994-1997,2002-2009 Christian Bauer + * Changes for embedded Linux devices (Qtopia, Maemo) (C) 2006 Bernd Lachner + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "C64.h" +#include "SAM.h" +#include "main.h" +#include "Version.h" + +#include "sdlgui.h" +#include "dlgMain.h" + +#define QWS +#include +#include + +// Display surface +static SDL_Surface *screen = 0; +static SDL_Surface *backgroundSurf = 0; +static SDL_Surface *surf = 0; + +static SDL_mutex *screenLock = 0; + +// Mode of Joystick emulation. 0 = none, 1 = Joyport 1, 2 = Joyport 2 +static short joy_emu = 0; + +// Keyboard +static bool tab_pressed = false; + +// For LED error blinking +static C64Display *c64_disp; +static struct sigaction pulse_sa; +static itimerval pulse_tv; + +// SDL joysticks +static SDL_Joystick *joy[2] = {NULL, NULL}; + +// Colors for speedometer/drive LEDs +enum +{ + black = 0, + white = 1, + fill_gray = 16, + shine_gray = 17, + shadow_gray = 18, + red = 19, + green = 20, + PALETTE_SIZE = 21 +}; + +/* + C64 keyboard matrix: + + Bit 7 6 5 4 3 2 1 0 + 0 CUD F5 F3 F1 F7 CLR RET DEL + 1 SHL E S Z 4 A W 3 + 2 X T F C 6 D R 5 + 3 V U H B 8 G Y 7 + 4 N O K M 0 J I 9 + 5 , @ : . - L P + + 6 / ^ = SHR HOM ; * + 7 R/S Q C= SPC 2 CTL <- 1 +*/ + +#define MATRIX(a,b) (((a) << 3) | (b)) + + +/* + * Open window + */ + +char *buffer; + +int width = 320; +int height = 240; + +bool isGuiAvailable = true; // TODO from main.cpp +bool GUIOpened = false; + +static SDL_Thread *GUIthread = NULL; +static const int GUI_RETURN_INFO = (SDL_USEREVENT+1); +static bool doUpdate = true; + +void update( int32 x, int32 y, int32 w, int32 h, bool forced ) +{ + if ( !forced && !doUpdate ) // the HW surface is available + return; + + // SDL_UpdateRect(SDL_GetVideoSurface(), 0, 0, width, height); + // SDL_UpdateRect(surf, x, y, w, h); + SDL_UpdateRect(screen, x, y, w, h); +} + +void update( bool forced ) +{ + update( 0, 0, width, height, forced ); +} + +void update() +{ + update( 0, 0, width, height, false ); +} + +void restoreBackground() +{ + if (backgroundSurf != NULL) { + SDL_BlitSurface(backgroundSurf, NULL, screen, NULL); + update(true); + fprintf(stderr, "video surface restored\n"); + } +} + +void allocateBackgroundSurf() +{ + // allocate new background video surface + backgroundSurf = SDL_ConvertSurface(screen, screen->format, screen->flags); + fprintf(stderr, "Allocating background video surface\n"); +} + +void freeBackgroundSurf() +{ + // free background video surface + if (backgroundSurf != NULL) + { + fprintf(stderr, "Freeing background video surface\n"); + SDL_FreeSurface(backgroundSurf); + backgroundSurf = NULL; + } +} + +void openGUI() +{ + fprintf(stderr, "open GUI\n"); + if (GUIOpened) + { + fprintf(stderr, "GUI is already open!\n"); + return; + } + allocateBackgroundSurf(); + surf = backgroundSurf; + GUIOpened = true; +} + +void closeGUI() +{ + fprintf(stderr, "close GUI\n"); + // update the main surface and then redirect VDI to it + restoreBackground(); + surf = screen; // redirect VDI to main surface + fprintf(stderr, "VDI redirected back to main video surface\n"); + freeBackgroundSurf(); + GUIOpened = false; +} + +void saveBackground() +{ + if (backgroundSurf != NULL) { + SDL_BlitSurface(screen, NULL, backgroundSurf, NULL); + surf = backgroundSurf; // redirect VDI to background surface + fprintf(stderr, "video surface saved to background, VDI redirected\n"); + } +} + +void blendBackgrounds() +{ + if (backgroundSurf != NULL) + { + SDL_Rect *Rect; + Rect = SDLGui_GetFirstBackgroundRect(); + while (Rect != NULL) + { + SDL_BlitSurface(backgroundSurf, Rect, screen, Rect); + Rect = SDLGui_GetNextBackgroundRect(); + } + update(true); + } +} + +static Prefs DialogPrefs; + +// running in a different thread +static int open_gui(void * /*ptr*/) +{ + openGUI(); + DialogPrefs = ThePrefs; + // Show main dialog + int status = Dialog_Main(DialogPrefs); + // The status is sent to event checking thread by the USEREVENT+1 message + SDL_Event ev; + ev.type = GUI_RETURN_INFO; + ev.user.code = status; // STATUS_SHUTDOWN or STATUS_REBOOT + ev.user.data1 = NULL; + SDL_PeepEvents(&ev, 1, SDL_ADDEVENT, SDL_EVENTMASK(GUI_RETURN_INFO)); + closeGUI(); + return 0; +} + +bool start_GUI_thread() +{ + if (isGuiAvailable) // TODO && !hostScreen.isGUIopen()) + { + GUIthread = SDL_CreateThread(open_gui, NULL); + } + return (GUIthread != NULL); +} + +void kill_GUI_thread() +{ + if (GUIthread != NULL) { + SDL_KillThread(GUIthread); + GUIthread = NULL; + } +} + +void screenlock() +{ + while (SDL_mutexP(screenLock)==-1) + { + SDL_Delay(20); + fprintf(stderr, "Couldn't lock mutex\n"); + } +} + +void screenunlock() +{ + while (SDL_mutexV(screenLock)==-1) + { + SDL_Delay(20); + fprintf(stderr, "Couldn't unlock mutex\n"); + } +} + +int init_graphics(void) +{ + // Init SDL + if (SDL_Init(SDL_INIT_VIDEO) < 0) { + fprintf(stderr, "Couldn't initialize SDL (%s)\n", SDL_GetError()); + return 0; + } + + screenLock = SDL_CreateMutex(); + + buffer = new char[DISPLAY_X*DISPLAY_Y]; + // Open window + SDL_WM_SetCaption(VERSION_STRING, "Frodo"); + screen = SDL_SetVideoMode(320, 240, 8, SDL_DOUBLEBUF); + surf = screen; + if (screen == NULL) + { + fprintf(stderr, "SDL Couldn't set video mode to %d x %d\n", DISPLAY_X, DISPLAY_Y+17); + } + else + { + fprintf(stderr, "SDL Set video mode to %d x %d\n", DISPLAY_X, DISPLAY_Y+17); + SDLGui_Init(screen); + start_GUI_thread(); + } + return 1; +} + + + +/* + * Display constructor + */ + +C64Display::C64Display(C64 *the_c64) : TheC64(the_c64) +{ + quit_requested = false; + speedometer_string[0] = 0; + + // LEDs off + for (int i=0; i<4; i++) + led_state[i] = old_led_state[i] = LED_OFF; + + // Start timer for LED error blinking + c64_disp = this; + pulse_sa.sa_handler = (void (*)(int))pulse_handler; + pulse_sa.sa_flags = SA_RESTART; + sigemptyset(&pulse_sa.sa_mask); + sigaction(SIGALRM, &pulse_sa, NULL); + pulse_tv.it_interval.tv_sec = 0; + pulse_tv.it_interval.tv_usec = 400000; + pulse_tv.it_value.tv_sec = 0; + pulse_tv.it_value.tv_usec = 400000; + setitimer(ITIMER_REAL, &pulse_tv, NULL); +} + + +/* + * Display destructor + */ + +C64Display::~C64Display() +{ + SDL_Quit(); + delete[] buffer; +} + + +/* + * Prefs may have changed + */ + +void C64Display::NewPrefs(Prefs *prefs) +{ +} + + +/* + * Redraw bitmap + */ + +void C64Display::Update(void) +{ + screenlock(); + if (surf == NULL) + return; + int iOffsetX = (DISPLAY_X - surf->w) / 2; + int iOffsetY = (DISPLAY_Y - surf->h) / 2; + for (int j=0; j < surf->h - 17; j++) + { + memcpy(static_cast(surf->pixels)+surf->w*j, buffer+iOffsetX+DISPLAY_X*(j+iOffsetY), surf->w); + } + // Draw speedometer/LEDs + SDL_Rect r = {0, (surf->h - 17), DISPLAY_X, 15}; + SDL_FillRect(surf, &r, fill_gray); + r.w = DISPLAY_X; r.h = 1; + SDL_FillRect(surf, &r, shine_gray); + r.y = (surf->h - 17) + 14; + SDL_FillRect(surf, &r, shadow_gray); + r.w = 16; + for (int i=2; i<5; i++) + { + r.x = DISPLAY_X * i/5 - 24; r.y = (surf->h - 17) + 4; + SDL_FillRect(surf, &r, shadow_gray); + r.y = (surf->h - 17) + 10; + SDL_FillRect(surf, &r, shine_gray); + } + r.y = (surf->h - 17); r.w = 1; r.h = 15; + for (int i=0; i<4; i++) + { + r.x = DISPLAY_X * i / 5; + SDL_FillRect(surf, &r, shine_gray); + r.x = DISPLAY_X * (i+1) / 5 - 1; + SDL_FillRect(surf, &r, shadow_gray); + } + r.y = (surf->h - 17) + 4; r.h = 7; + for (int i=2; i<5; i++) + { + r.x = DISPLAY_X * i/5 - 24; + SDL_FillRect(surf, &r, shadow_gray); + r.x = DISPLAY_X * i/5 - 9; + SDL_FillRect(surf, &r, shine_gray); + } + r.y = (surf->h - 17) + 5; r.w = 14; r.h = 5; + for (int i=0; i<3; i++) + { + r.x = DISPLAY_X * (i+2) / 5 - 23; + int c; + switch (led_state[i]) + { + case LED_ON: + c = green; + break; + case LED_ERROR_ON: + c = red; + break; + default: + c = black; + break; + } + SDL_FillRect(surf, &r, c); + } + + draw_string(surf, DISPLAY_X * 1/5 + 8, (surf->h - 17) + 4, "D\x12 8", black, fill_gray); + draw_string(surf, DISPLAY_X * 2/5 + 8, (surf->h - 17) + 4, "D\x12 9", black, fill_gray); + draw_string(surf, DISPLAY_X * 3/5 + 8, (surf->h - 17) + 4, "D\x12 10", black, fill_gray); + if (joy_emu == 1) + draw_string(surf, DISPLAY_X * 4/5 + 2, (surf->h - 17) + 4, "1", black, fill_gray); + else if (joy_emu == 2) + draw_string(surf, DISPLAY_X * 4/5 + 2, (surf->h - 17) + 4, "2", black, fill_gray); + draw_string(surf, 24, (surf->h - 17) + 4, speedometer_string, black, fill_gray); + + // Update display + SDL_Flip(surf); + + blendBackgrounds(); + screenunlock(); +} + + +/* + * Draw string into surface using the C64 ROM font + */ + +void C64Display::draw_string(SDL_Surface *s, int x, int y, const char *str, uint8 front_color, uint8 back_color) +{ + uint8 *pb = (uint8 *)s->pixels + s->pitch*y + x; + char c; + while ((c = *str++) != 0) + { + uint8 *q = TheC64->Char + c*8 + 0x800; + uint8 *p = pb; + for (int y=0; y<8; y++) + { + uint8 v = *q++; + p[0] = (v & 0x80) ? front_color : back_color; + p[1] = (v & 0x40) ? front_color : back_color; + p[2] = (v & 0x20) ? front_color : back_color; + p[3] = (v & 0x10) ? front_color : back_color; + p[4] = (v & 0x08) ? front_color : back_color; + p[5] = (v & 0x04) ? front_color : back_color; + p[6] = (v & 0x02) ? front_color : back_color; + p[7] = (v & 0x01) ? front_color : back_color; + p += s->pitch; + } + pb += 8; + } +} + + +/* + * LED error blink + */ + +void C64Display::pulse_handler(...) +{ + for (int i=0; i<4; i++) + switch (c64_disp->led_state[i]) + { + case LED_ERROR_ON: + c64_disp->led_state[i] = LED_ERROR_OFF; + break; + case LED_ERROR_OFF: + c64_disp->led_state[i] = LED_ERROR_ON; + break; + } +} + + +/* + * Draw speedometer + */ + +void C64Display::Speedometer(int speed) +{ + static int delay = 0; + + if (delay >= 20) + { + delay = 0; + sprintf(speedometer_string, "%d%%", speed); + } + else + delay++; +} + + +/* + * Return pointer to bitmap data + */ + +uint8 *C64Display::BitmapBase(void) +{ + //return (uint8 *)screen->pixels; + return (uint8 *)buffer; +} + + +/* + * Return number of bytes per row + */ + +int C64Display::BitmapXMod(void) +{ + //return screen->pitch; + return DISPLAY_X; +} + + + +/* + * Poll the keyboard + */ + +static void translate_key(SDLKey key, bool key_up, uint8 *key_matrix, uint8 *rev_matrix, uint8 *joystick) +{ + int c64_key = -1; + if (tab_pressed) + { + // Function and run/stop key emulation on Zaurus keyboard + switch (key) + { + case SDLK_q: c64_key = MATRIX(0,4); break; + case SDLK_w: c64_key = MATRIX(0,4) | 0x80; break; + case SDLK_e: c64_key = MATRIX(0,5); break; + case SDLK_r: c64_key = MATRIX(0,5) | 0x80; break; + case SDLK_t: c64_key = MATRIX(0,6); break; + case SDLK_z: c64_key = MATRIX(0,6) | 0x80; break; + case SDLK_u: c64_key = MATRIX(0,3); break; + case SDLK_i: c64_key = MATRIX(0,3) | 0x80; break; + case SDLK_o: c64_key = MATRIX(7,7); break; + } + } + else + { + switch (key) + { + case SDLK_a: c64_key = MATRIX(1,2); break; + case SDLK_b: c64_key = MATRIX(3,4); break; + case SDLK_c: c64_key = MATRIX(2,4); break; + case SDLK_d: c64_key = MATRIX(2,2); break; + case SDLK_e: c64_key = MATRIX(1,6); break; + case SDLK_f: c64_key = MATRIX(2,5); break; + case SDLK_g: c64_key = MATRIX(3,2); break; + case SDLK_h: c64_key = MATRIX(3,5); break; + case SDLK_i: c64_key = MATRIX(4,1); break; + case SDLK_j: c64_key = MATRIX(4,2); break; + case SDLK_k: c64_key = MATRIX(4,5); break; + case SDLK_l: c64_key = MATRIX(5,2); break; + case SDLK_m: c64_key = MATRIX(4,4); break; + case SDLK_n: c64_key = MATRIX(4,7); break; + case SDLK_o: c64_key = MATRIX(4,6); break; + case SDLK_p: c64_key = MATRIX(5,1); break; + case SDLK_q: c64_key = MATRIX(7,6); break; + case SDLK_r: c64_key = MATRIX(2,1); break; + case SDLK_s: c64_key = MATRIX(1,5); break; + case SDLK_t: c64_key = MATRIX(2,6); break; + case SDLK_u: c64_key = MATRIX(3,6); break; + case SDLK_v: c64_key = MATRIX(3,7); break; + case SDLK_w: c64_key = MATRIX(1,1); break; + case SDLK_x: c64_key = MATRIX(2,7); break; + case SDLK_y: c64_key = MATRIX(3,1); break; + case SDLK_z: c64_key = MATRIX(1,4); break; + + case SDLK_0: c64_key = MATRIX(4,3); break; + case SDLK_1: c64_key = MATRIX(7,0); break; + case SDLK_2: c64_key = MATRIX(7,3); break; + case SDLK_3: c64_key = MATRIX(1,0); break; + case SDLK_4: c64_key = MATRIX(1,3); break; + case SDLK_5: c64_key = MATRIX(2,0); break; + case SDLK_6: c64_key = MATRIX(2,3); break; + case SDLK_7: c64_key = MATRIX(3,0); break; + case SDLK_8: c64_key = MATRIX(3,3); break; + case SDLK_9: c64_key = MATRIX(4,0); break; + + case SDLK_SPACE: c64_key = MATRIX(7,4); break; + case SDLK_BACKQUOTE: c64_key = MATRIX(7,1); break; + case SDLK_BACKSLASH: c64_key = MATRIX(6,6); break; + case SDLK_COMMA: c64_key = MATRIX(5,7); break; + case SDLK_PERIOD: c64_key = MATRIX(5,4); break; + case SDLK_MINUS: c64_key = MATRIX(5,0); break; + case SDLK_EQUALS: c64_key = MATRIX(5,3); break; + case SDLK_LEFTBRACKET: c64_key = MATRIX(5,6); break; + case SDLK_RIGHTBRACKET: c64_key = MATRIX(6,1); break; + case SDLK_SEMICOLON: c64_key = MATRIX(5,5); break; + case SDLK_QUOTE: c64_key = MATRIX(6,2); break; + case SDLK_SLASH: c64_key = MATRIX(6,7); break; + + case SDLK_ESCAPE: c64_key = MATRIX(7,7); break; + case SDLK_RETURN: c64_key = MATRIX(0,1); break; + case SDLK_BACKSPACE: case SDLK_DELETE: c64_key = MATRIX(0,0); break; + case SDLK_INSERT: c64_key = MATRIX(6,3); break; + case SDLK_HOME: c64_key = MATRIX(6,3); break; + case SDLK_END: c64_key = MATRIX(6,0); break; + case SDLK_PAGEUP: c64_key = MATRIX(6,0); break; + case SDLK_PAGEDOWN: c64_key = MATRIX(6,5); break; + + case SDLK_LCTRL: c64_key = MATRIX(7,2); break; + case SDLK_RCTRL: c64_key = MATRIX(7,5); break; + case SDLK_LSHIFT: c64_key = MATRIX(1,7); break; + case SDLK_RSHIFT: c64_key = MATRIX(6,4); break; + case SDLK_LALT: case SDLK_LMETA: c64_key = MATRIX(7,5); break; + case SDLK_RALT: case SDLK_RMETA: c64_key = MATRIX(7,5); break; + + case SDLK_UP: c64_key = MATRIX(0,7)| 0x80; break; + case SDLK_DOWN: c64_key = MATRIX(0,7); break; + case SDLK_LEFT: c64_key = MATRIX(0,2) | 0x80; break; + case SDLK_RIGHT: c64_key = MATRIX(0,2); break; + + case SDLK_F1: c64_key = MATRIX(0,4); break; + case SDLK_F2: c64_key = MATRIX(0,4) | 0x80; break; + case SDLK_F3: c64_key = MATRIX(0,5); break; + case SDLK_F4: c64_key = MATRIX(0,5) | 0x80; break; + case SDLK_F5: c64_key = MATRIX(0,6); break; + case SDLK_F6: c64_key = MATRIX(0,6) | 0x80; break; + case SDLK_F7: c64_key = MATRIX(0,3); break; + case SDLK_F8: c64_key = MATRIX(0,3) | 0x80; break; + + case SDLK_KP0: case SDLK_KP5: c64_key = 0x10 | 0x40; break; + case SDLK_KP1: c64_key = 0x06 | 0x40; break; + case SDLK_KP2: c64_key = 0x02 | 0x40; break; + case SDLK_KP3: c64_key = 0x0a | 0x40; break; + case SDLK_KP4: c64_key = 0x04 | 0x40; break; + case SDLK_KP6: c64_key = 0x08 | 0x40; break; + case SDLK_KP7: c64_key = 0x05 | 0x40; break; + case SDLK_KP8: c64_key = 0x01 | 0x40; break; + case SDLK_KP9: c64_key = 0x09 | 0x40; break; + + case SDLK_KP_DIVIDE: c64_key = MATRIX(6,7); break; + case SDLK_KP_ENTER: c64_key = MATRIX(0,1); break; + + // Support for Zaurus/Qtopia + case SDLK_QUOTEDBL: c64_key = MATRIX(7,3) | 0x80; break; + case SDLK_ASTERISK: c64_key = MATRIX(6,1); break; + case SDLK_DOLLAR: c64_key = MATRIX(1,3) | 0x80; break; + case SDLK_COLON: c64_key = MATRIX(5,5); break; + case SDLK_AT: c64_key = MATRIX(5,6); break; + } + } + if (c64_key < 0) + return; + + // Zaurus/Qtopia joystick emulation + if (joy_emu != 0) + { + switch (key) + { + case SDLK_SPACE: c64_key = 0x10 | 0x40; break; + case SDLK_UP: c64_key = 0x01 | 0x40; break; + case SDLK_DOWN: c64_key = 0x02 | 0x40; break; + case SDLK_LEFT: c64_key = 0x04 | 0x40; break; + case SDLK_RIGHT: c64_key = 0x08 | 0x40; break; + } + } + + // Handle joystick emulation + if (c64_key & 0x40) + { + c64_key &= 0x1f; + if (key_up) + *joystick |= c64_key; + else + *joystick &= ~c64_key; + return; + } + + // Handle other keys + bool shifted = c64_key & 0x80; + int c64_byte = (c64_key >> 3) & 7; + int c64_bit = c64_key & 7; + if (key_up) + { + if (shifted) + { + key_matrix[6] |= 0x10; + rev_matrix[4] |= 0x40; + } + key_matrix[c64_byte] |= (1 << c64_bit); + rev_matrix[c64_bit] |= (1 << c64_byte); + } + else + { + if (shifted) + { + key_matrix[6] &= 0xef; + rev_matrix[4] &= 0xbf; + } + key_matrix[c64_byte] &= ~(1 << c64_bit); + rev_matrix[c64_bit] &= ~(1 << c64_byte); + } +} + +void C64Display::PollKeyboard(uint8 *key_matrix, uint8 *rev_matrix, uint8 *joystick) +{ + SDL_Event event; + int eventmask = SDL_EVENTMASK(SDL_KEYDOWN) + | SDL_EVENTMASK(SDL_KEYUP) + | SDL_EVENTMASK(SDL_MOUSEBUTTONDOWN) + | SDL_EVENTMASK(SDL_MOUSEBUTTONUP) + | SDL_EVENTMASK(SDL_MOUSEMOTION) + | SDL_EVENTMASK(SDL_JOYAXISMOTION) + | SDL_EVENTMASK(SDL_JOYHATMOTION) + | SDL_EVENTMASK(SDL_JOYBUTTONDOWN) + | SDL_EVENTMASK(SDL_JOYBUTTONUP) + | SDL_EVENTMASK(SDL_ACTIVEEVENT) + | SDL_EVENTMASK(GUI_RETURN_INFO) + | SDL_EVENTMASK(SDL_QUIT); + + SDL_PumpEvents(); + while (SDL_PeepEvents(&event, 1, SDL_GETEVENT, eventmask)) + { + if (GUIOpened) + { + if (event.type == SDL_MOUSEBUTTONDOWN || event.type == SDL_MOUSEBUTTONUP) + { + if (event.type == SDL_MOUSEBUTTONDOWN) + fprintf(stderr, "Mouse down\n"); + if (event.type == SDL_MOUSEBUTTONUP) + fprintf(stderr, "Mouse up\n"); + SDL_Event ev; + ev.type = SDL_USEREVENT; // map button down/up to user event + ev.user.code = event.type; + ev.user.data1 = (void *)(int)event.button.x; + ev.user.data2 = (void *)(int)event.button.y; + SDL_PeepEvents(&ev, 1, SDL_ADDEVENT, SDL_EVENTMASK(SDL_USEREVENT)); + } + else if (event.type == SDL_KEYDOWN || event.type == SDL_KEYUP) + { + if (event.type == SDL_KEYDOWN) + fprintf(stderr, "Key down\n"); + if (event.type == SDL_KEYUP) + fprintf(stderr, "Key up\n"); + SDLKey sym = event.key.keysym.sym; + int state = SDL_GetModState(); // keysym.mod does not deliver single mod key presses for + SDL_Event ev; + ev.type = SDL_USEREVENT; // map key down/up event to user event + ev.user.code = event.type; + ev.user.data1 = (void *)(int)sym; + ev.user.data2 = (void *)(int)state; + SDL_PeepEvents(&ev, 1, SDL_ADDEVENT, SDL_EVENTMASK(SDL_USEREVENT)); + } + } + else + { + switch (event.type) + { + case GUI_RETURN_INFO: + { + fprintf(stderr, "Return code from gui: %d\n", event.user.code); + switch (event.user.code) + { + case DO_RESET: + TheC64->Reset(); + break; + case DO_QUIT: + quit_requested = true; + break; + case DO_USEPREFS: + TheC64->NewPrefs(&DialogPrefs); + ThePrefs = DialogPrefs; + break; + case DO_SAVEPREFS: + TheC64->NewPrefs(&DialogPrefs); + ThePrefs = DialogPrefs; + ThePrefs.Save(Frodo::get_prefs_path()); + break; + } + break; + } + case SDL_MOUSEBUTTONDOWN: + case SDL_MOUSEBUTTONUP: + start_GUI_thread(); + break; + // Key pressed + case SDL_KEYDOWN: +// fprintf(stderr, "SDL-Key: %d\n", event.key.keysym.sym); + if (tab_pressed && event.key.keysym.sym == SDLK_j) + { + if (joy_emu < 2) + joy_emu++; + else + joy_emu = 0; + } + if (tab_pressed && event.key.keysym.sym == SDLK_p) + { + // NMI (Restore) + TheC64->NMI(); + } + else + { + switch (event.key.keysym.sym) + { + case SDLK_TAB: + tab_pressed = true; + break; + + case SDLK_F9: // F9: Invoke SAM + SAM(TheC64); + break; + + case SDLK_F11: // F10: Quit + // Iconify not implemented in Qtopia SDL yet. Quit instead show gui. + //SDL_WM_IconifyWindow(); + quit_requested = true; + break; + + case SDLK_F12: // F12: Reset + TheC64->Reset(); + break; + + case SDLK_KP_PLUS: // '+' on keypad: Increase SkipFrames + ThePrefs.SkipFrames++; + break; + + case SDLK_KP_MINUS: // '-' on keypad: Decrease SkipFrames + if (ThePrefs.SkipFrames > 1) + ThePrefs.SkipFrames--; + break; + + case SDLK_KP_MULTIPLY: // '*' on keypad: Toggle speed limiter + ThePrefs.LimitSpeed = !ThePrefs.LimitSpeed; + break; + + default: + translate_key(event.key.keysym.sym, false, key_matrix, rev_matrix, joystick); + break; + } + } + break; + + // Key released + case SDL_KEYUP: + if (event.key.keysym.sym == SDLK_TAB) + tab_pressed = false; + else +{ + translate_key(event.key.keysym.sym, true, key_matrix, rev_matrix, joystick); +} + break; + + // Quit Frodo + case SDL_QUIT: + quit_requested = true; + break; + } + } + } +} + + +/* + * Check if NumLock is down (for switching the joystick keyboard emulation) + */ + +bool C64Display::NumLock(void) +{ + if (joy_emu == 2) + return true; + return false; +} + + +/* + * Open/close joystick drivers given old and new state of + * joystick preferences + */ + +void C64::open_close_joystick(int port, int oldjoy, int newjoy) +{ + if (oldjoy != newjoy) { + joy_minx[port] = joy_miny[port] = 32767; // Reset calibration + joy_maxx[port] = joy_maxy[port] = -32768; + if (newjoy) { + joy[port] = SDL_JoystickOpen(newjoy - 1); + if (joy[port] == NULL) + fprintf(stderr, "Couldn't open joystick %d\n", port + 1); + } else { + if (joy[port]) { + SDL_JoystickClose(joy[port]); + joy[port] = NULL; + } + } + } +} + +void C64::open_close_joysticks(int oldjoy1, int oldjoy2, int newjoy1, int newjoy2) +{ + open_close_joystick(0, oldjoy1, newjoy1); + open_close_joystick(1, oldjoy2, newjoy2); +} + + +/* + * Poll joystick port, return CIA mask + */ + +uint8 C64::poll_joystick(int port) +{ + uint8 j = 0xff; + + if (port == 0 && (joy[0] || joy[1])) + SDL_JoystickUpdate(); + + if (joy[port]) { + int x = SDL_JoystickGetAxis(joy[port], 0), y = SDL_JoystickGetAxis(joy[port], 1); + + if (x > joy_maxx[port]) + joy_maxx[port] = x; + if (x < joy_minx[port]) + joy_minx[port] = x; + if (y > joy_maxy[port]) + joy_maxy[port] = y; + if (y < joy_miny[port]) + joy_miny[port] = y; + + if (joy_maxx[port] - joy_minx[port] < 100 || joy_maxy[port] - joy_miny[port] < 100) + return 0xff; + + if (x < (joy_minx[port] + (joy_maxx[port]-joy_minx[port])/3)) + j &= 0xfb; // Left + else if (x > (joy_minx[port] + 2*(joy_maxx[port]-joy_minx[port])/3)) + j &= 0xf7; // Right + + if (y < (joy_miny[port] + (joy_maxy[port]-joy_miny[port])/3)) + j &= 0xfe; // Up + else if (y > (joy_miny[port] + 2*(joy_maxy[port]-joy_miny[port])/3)) + j &= 0xfd; // Down + + if (SDL_JoystickGetButton(joy[port], 0)) + j &= 0xef; // Button + } + + return j; +} + +/* + * Allocate C64 colors + */ + +void C64Display::InitColors(uint8 *colors) +{ + SDL_Color palette[PALETTE_SIZE]; + for (int i=0; i<16; i++) + { + palette[i].r = palette_red[i]; + palette[i].g = palette_green[i]; + palette[i].b = palette_blue[i]; + } + palette[fill_gray].r = palette[fill_gray].g = palette[fill_gray].b = 0xd0; + palette[shine_gray].r = palette[shine_gray].g = palette[shine_gray].b = 0xf0; + palette[shadow_gray].r = palette[shadow_gray].g = palette[shadow_gray].b = 0x80; + palette[red].r = 0xf0; + palette[red].g = palette[red].b = 0; + palette[green].g = 0xf0; + palette[green].r = palette[green].b = 0; + SDL_SetColors(screen, palette, 0, PALETTE_SIZE); + + for (int i=0; i<256; i++) + colors[i] = i & 0x0f; +} + + +/* + * Show a requester (error message) + */ + +long int ShowRequester(const char *a, const char *b, const char *) +{ + printf("%s: %s\n", a, b); + return 1; +} diff --git a/Src/Display_SDL.h b/Src/Display_SDL.h new file mode 100644 index 0000000..5a35172 --- /dev/null +++ b/Src/Display_SDL.h @@ -0,0 +1,736 @@ +/* + * Display_SDL.h - C64 graphics display, emulator window handling, + * SDL specific stuff + * + * Frodo (C) 1994-1997,2002-2009 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "C64.h" +#include "SAM.h" +#include "Version.h" + +#include "CIA.h" + +#include +#if defined(GEKKO) +#include +#endif + +// Display surface +SDL_Surface *screen = NULL; +SDL_Surface *real_screen = NULL; + +// Keyboard +static bool num_locked = false; + +#if defined(DO_ERROR_BLINK) +// For LED error blinking +static C64Display *c64_disp; +static struct sigaction pulse_sa; +static itimerval pulse_tv; +#endif +// SDL joysticks +static SDL_Joystick *joy[2] = {NULL, NULL}; + +// Colors for speedometer/drive LEDs +enum { + black = 0, + white = 1, + fill_gray = 16, + shine_gray = 17, + shadow_gray = 18, + red = 19, + green = 20, + PALETTE_SIZE = 21 +}; + +/* + C64 keyboard matrix: + + Bit 7 6 5 4 3 2 1 0 + 0 CUD F5 F3 F1 F7 CLR RET DEL + 1 SHL E S Z 4 A W 3 + 2 X T F C 6 D R 5 + 3 V U H B 8 G Y 7 + 4 N O K M 0 J I 9 + 5 , @ : . - L P + + 6 / ^ = SHR HOM ; * � + 7 R/S Q C= SPC 2 CTL <- 1 +*/ + +#define MATRIX(a,b) (((a) << 3) | (b)) + + +/* + * Open window + */ + +int init_graphics(void) +{ + Uint32 rmask, gmask, bmask, amask; + + /* SDL interprets each pixel as a 32-bit number, so our masks must depend + on the endianness (byte order) of the machine */ +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + rmask = 0xff000000; + gmask = 0x00ff0000; + bmask = 0x0000ff00; + amask = 0x000000ff; +#else + rmask = 0x000000ff; + gmask = 0x0000ff00; + bmask = 0x00ff0000; + amask = 0xff000000; +#endif + + // Open window + SDL_ShowCursor(SDL_DISABLE); + + screen = SDL_CreateRGBSurface(SDL_SWSURFACE, DISPLAY_X, DISPLAY_Y + 17, 8, + rmask, gmask, bmask, amask); + if (!screen) + { + fprintf(stderr, "Cannot allocate surface to draw on: %s\n", + SDL_GetError()); + exit(1); + } + real_screen = SDL_SetVideoMode(FULL_DISPLAY_X, FULL_DISPLAY_Y, 8, + SDL_DOUBLEBUF); + if (!real_screen) + { + fprintf(stderr, "\n\nCannot initialize video: %s\n", SDL_GetError()); + exit(1); + } + + return 1; +} + + +/* + * Display constructor + */ + +C64Display::C64Display(C64 *the_c64) : TheC64(the_c64) +{ + quit_requested = false; + speedometer_string[0] = 0; + + printf("ssof2 %d:%d\n", sizeof(C64Display), sizeof(C64)); + // Open window + SDL_WM_SetCaption(VERSION_STRING, "Frodo"); + // LEDs off + for (int i=0; i<4; i++) + led_state[i] = old_led_state[i] = LED_OFF; + +#if defined(DO_ERROR_BLINK) + // Start timer for LED error blinking + c64_disp = this; + pulse_sa.sa_handler = (void (*)(int))pulse_handler; + pulse_sa.sa_flags = SA_RESTART; + sigemptyset(&pulse_sa.sa_mask); + sigaction(SIGALRM, &pulse_sa, NULL); + pulse_tv.it_interval.tv_sec = 0; + pulse_tv.it_interval.tv_usec = 400000; + pulse_tv.it_value.tv_sec = 0; + pulse_tv.it_value.tv_usec = 400000; + setitimer(ITIMER_REAL, &pulse_tv, NULL); +#endif +} + + +/* + * Display destructor + */ + +C64Display::~C64Display() +{ + SDL_Quit(); +} + + +/* + * Prefs may have changed + */ + +void C64Display::NewPrefs(Prefs *prefs) +{ +} + + +/* + * Redraw bitmap + */ + +void C64Display::Update(void) +{ + if (ThePrefs.DisplayOption == 0) { + const int x_border = (DISPLAY_X - FULL_DISPLAY_X / 2) / 2; + const int y_border = (DISPLAY_Y - FULL_DISPLAY_Y / 2) / 2; + Uint8 *src_pixels = (Uint8*)screen->pixels; + Uint8 *dst_pixels = (Uint8*)real_screen->pixels; + const Uint16 src_pitch = screen->pitch; + const Uint16 dst_pitch = real_screen->pitch; + + /* Center, double size */ + for (int y = y_border; y < (FULL_DISPLAY_Y/2) + y_border; y++) + { + for (int x = x_border; x < (FULL_DISPLAY_X / 2 + x_border); x++) + { + int src_off = y * src_pitch + x; + int dst_off = (y * 2 - y_border * 2) * dst_pitch + (x * 2 - x_border * 2); + Uint8 v = src_pixels[src_off]; + + dst_pixels[ dst_off ] = v; + dst_pixels[ dst_off + 1 ] = v; + dst_pixels[ dst_off + dst_pitch ] = v; + dst_pixels[ dst_off + dst_pitch + 1] = v; + } + } + } + else { + SDL_Rect srcrect = {0, 0, DISPLAY_X, DISPLAY_Y}; + SDL_Rect dstrect = {0, 0, FULL_DISPLAY_X, FULL_DISPLAY_Y}; + + /* Stretch */ + SDL_SoftStretch(screen, &srcrect, real_screen, &dstrect); + } + SDL_Flip(real_screen); +} + + +/* + * Draw string into surface using the C64 ROM font + */ + +void C64Display::draw_string(SDL_Surface *s, int x, int y, const char *str, uint8 front_color, uint8 back_color) +{ + uint8 *pb = (uint8 *)s->pixels + s->pitch*y + x; + char c; + while ((c = *str++) != 0) { + uint8 *q = TheC64->Char + c*8 + 0x800; + uint8 *p = pb; + for (int y=0; y<8; y++) { + uint8 v = *q++; + p[0] = (v & 0x80) ? front_color : back_color; + p[1] = (v & 0x40) ? front_color : back_color; + p[2] = (v & 0x20) ? front_color : back_color; + p[3] = (v & 0x10) ? front_color : back_color; + p[4] = (v & 0x08) ? front_color : back_color; + p[5] = (v & 0x04) ? front_color : back_color; + p[6] = (v & 0x02) ? front_color : back_color; + p[7] = (v & 0x01) ? front_color : back_color; + p += s->pitch; + } + pb += 8; + } +} + + +/* + * LED error blink + */ + +#if defined(DO_ERROR_BLINK) +void C64Display::pulse_handler(...) +{ + for (int i=0; i<4; i++) + switch (c64_disp->led_state[i]) { + case LED_ERROR_ON: + c64_disp->led_state[i] = LED_ERROR_OFF; + break; + case LED_ERROR_OFF: + c64_disp->led_state[i] = LED_ERROR_ON; + break; + } +} +#endif + + +/* + * Draw speedometer + */ + +void C64Display::Speedometer(int speed) +{ + static int delay = 0; + + if (delay >= 20) { + delay = 0; + sprintf(speedometer_string, "%d%%", speed); + } else + delay++; +} + + +/* + * Return pointer to bitmap data + */ + +uint8 *C64Display::BitmapBase(void) +{ + return (uint8 *)screen->pixels; +} + + +/* + * Return number of bytes per row + */ + +int C64Display::BitmapXMod(void) +{ + return screen->pitch; +} + +void C64Display::FakeKeyPress(int kc, bool shift, uint8 *CIA_key_matrix, + uint8 *CIA_rev_matrix) +{ + // Clear matrices + for (int i = 0; i < 8; i ++) + { + CIA_key_matrix[i] = 0xFF; + CIA_rev_matrix[i] = 0xFF; + } + + if (shift) + { + CIA_key_matrix[6] &= 0xef; + CIA_rev_matrix[4] &= 0xbf; + } + + if (kc != -1) + { + int c64_byte, c64_bit, shifted; + c64_byte = kc >> 3; + c64_bit = kc & 7; + shifted = kc & 128; + c64_byte &= 7; + if (shifted) + { + CIA_key_matrix[6] &= 0xef; + CIA_rev_matrix[4] &= 0xbf; + } + CIA_key_matrix[c64_byte] &= ~(1 << c64_bit); + CIA_rev_matrix[c64_bit] &= ~(1 << c64_byte); + } +} + +void C64Display::UpdateKeyMatrix(int c64_key, bool key_up, uint8 *key_matrix, uint8 *rev_matrix) +{ + bool shifted = c64_key & 0x80; + int c64_byte = (c64_key >> 3) & 7; + int c64_bit = c64_key & 7; + + if (key_up) { + if (shifted) { + key_matrix[6] |= 0x10; + rev_matrix[4] |= 0x40; + } + key_matrix[c64_byte] |= (1 << c64_bit); + rev_matrix[c64_bit] |= (1 << c64_byte); + } else { + if (shifted) { + key_matrix[6] &= 0xef; + rev_matrix[4] &= 0xbf; + } + key_matrix[c64_byte] &= ~(1 << c64_bit); + rev_matrix[c64_bit] &= ~(1 << c64_byte); + } +} + +/* + * Poll the keyboard + */ + +void C64Display::TranslateKey(SDLKey key, bool key_up, uint8 *key_matrix, + uint8 *rev_matrix, uint8 *joystick) +{ + int c64_key = -1; + switch (key) { + case SDLK_a: c64_key = MATRIX(1,2); break; + case SDLK_b: c64_key = MATRIX(3,4); break; + case SDLK_c: c64_key = MATRIX(2,4); break; + case SDLK_d: c64_key = MATRIX(2,2); break; + case SDLK_e: c64_key = MATRIX(1,6); break; + case SDLK_f: c64_key = MATRIX(2,5); break; + case SDLK_g: c64_key = MATRIX(3,2); break; + case SDLK_h: c64_key = MATRIX(3,5); break; + case SDLK_i: c64_key = MATRIX(4,1); break; + case SDLK_j: c64_key = MATRIX(4,2); break; + case SDLK_k: c64_key = MATRIX(4,5); break; + case SDLK_l: c64_key = MATRIX(5,2); break; + case SDLK_m: c64_key = MATRIX(4,4); break; + case SDLK_n: c64_key = MATRIX(4,7); break; + case SDLK_o: c64_key = MATRIX(4,6); break; + case SDLK_p: c64_key = MATRIX(5,1); break; + case SDLK_q: c64_key = MATRIX(7,6); break; + case SDLK_r: c64_key = MATRIX(2,1); break; + case SDLK_s: c64_key = MATRIX(1,5); break; + case SDLK_t: c64_key = MATRIX(2,6); break; + case SDLK_u: c64_key = MATRIX(3,6); break; + case SDLK_v: c64_key = MATRIX(3,7); break; + case SDLK_w: c64_key = MATRIX(1,1); break; + case SDLK_x: c64_key = MATRIX(2,7); break; + case SDLK_y: c64_key = MATRIX(3,1); break; + case SDLK_z: c64_key = MATRIX(1,4); break; + + case SDLK_0: c64_key = MATRIX(4,3); break; + case SDLK_1: c64_key = MATRIX(7,0); break; + case SDLK_2: c64_key = MATRIX(7,3); break; + case SDLK_3: c64_key = MATRIX(1,0); break; + case SDLK_4: c64_key = MATRIX(1,3); break; + case SDLK_5: c64_key = MATRIX(2,0); break; + case SDLK_6: c64_key = MATRIX(2,3); break; + case SDLK_7: c64_key = MATRIX(3,0); break; + case SDLK_8: c64_key = MATRIX(3,3); break; + case SDLK_9: c64_key = MATRIX(4,0); break; + + case SDLK_SPACE: c64_key = MATRIX(7,4); break; + case SDLK_BACKQUOTE: c64_key = MATRIX(7,1); break; + case SDLK_BACKSLASH: c64_key = MATRIX(6,6); break; + case SDLK_COMMA: c64_key = MATRIX(5,7); break; + case SDLK_PERIOD: c64_key = MATRIX(5,4); break; + case SDLK_MINUS: c64_key = MATRIX(5,0); break; + case SDLK_EQUALS: c64_key = MATRIX(5,3); break; + case SDLK_LEFTBRACKET: c64_key = MATRIX(5,6); break; + case SDLK_RIGHTBRACKET: c64_key = MATRIX(6,1); break; + case SDLK_SEMICOLON: c64_key = MATRIX(5,5); break; + case SDLK_QUOTE: c64_key = MATRIX(6,2); break; + case SDLK_SLASH: c64_key = MATRIX(6,7); break; + + case SDLK_ESCAPE: c64_key = MATRIX(7,7); break; + case SDLK_RETURN: c64_key = MATRIX(0,1); break; + case SDLK_BACKSPACE: case SDLK_DELETE: c64_key = MATRIX(0,0); break; + case SDLK_INSERT: c64_key = MATRIX(6,3); break; + case SDLK_HOME: c64_key = MATRIX(6,3); break; + case SDLK_END: c64_key = MATRIX(6,0); break; + case SDLK_PAGEUP: c64_key = MATRIX(6,0); break; + case SDLK_PAGEDOWN: c64_key = MATRIX(6,5); break; + + case SDLK_LCTRL: c64_key = 0x10 | 0x40; break; + case SDLK_TAB: c64_key = MATRIX(7,2); break; + case SDLK_RCTRL: c64_key = MATRIX(7,5); break; + case SDLK_LSHIFT: c64_key = MATRIX(1,7); break; + case SDLK_RSHIFT: c64_key = MATRIX(6,4); break; + case SDLK_LALT: case SDLK_LMETA: c64_key = MATRIX(7,5); break; + case SDLK_RALT: case SDLK_RMETA: c64_key = MATRIX(7,5); break; + + case SDLK_UP: c64_key = 0x01 | 0x40; break; + case SDLK_DOWN: c64_key = 0x02 | 0x40; break; + case SDLK_LEFT: c64_key = 0x04 | 0x40; break; + case SDLK_RIGHT: c64_key = 0x08 | 0x40; break; + + case SDLK_F1: c64_key = MATRIX(0,4); break; + case SDLK_F2: c64_key = MATRIX(0,4) | 0x80; break; + case SDLK_F3: c64_key = MATRIX(0,5); break; + case SDLK_F4: c64_key = MATRIX(0,5) | 0x80; break; + case SDLK_F5: c64_key = MATRIX(0,6); break; + case SDLK_F6: c64_key = MATRIX(0,6) | 0x80; break; + case SDLK_F7: c64_key = MATRIX(0,3); break; + case SDLK_F8: c64_key = MATRIX(0,3) | 0x80; break; + + case SDLK_KP0: case SDLK_KP5: c64_key = 0x10 | 0x40; break; + case SDLK_KP1: c64_key = 0x06 | 0x40; break; + case SDLK_KP2: c64_key = 0x02 | 0x40; break; + case SDLK_KP3: c64_key = 0x0a | 0x40; break; + case SDLK_KP4: c64_key = 0x04 | 0x40; break; + case SDLK_KP6: c64_key = 0x08 | 0x40; break; + case SDLK_KP7: c64_key = 0x05 | 0x40; break; + case SDLK_KP8: c64_key = 0x01 | 0x40; break; + case SDLK_KP9: c64_key = 0x09 | 0x40; break; + + case SDLK_KP_DIVIDE: c64_key = MATRIX(6,7); break; + case SDLK_KP_ENTER: c64_key = MATRIX(0,1); break; + default: break; + } + + if (c64_key < 0) + return; + + // Handle joystick emulation + if (c64_key & 0x40) { + c64_key &= 0x1f; + if (key_up) + *joystick |= c64_key; + else + *joystick &= ~c64_key; + return; + } + + this->UpdateKeyMatrix(c64_key, key_up, key_matrix, rev_matrix); +} + +void C64Display::PollKeyboard(uint8 *key_matrix, uint8 *rev_matrix, uint8 *joystick) +{ + SDL_Event event; + while (SDL_PollEvent(&event)) { + switch (event.type) { + + // Key pressed + case SDL_KEYDOWN: + switch (event.key.keysym.sym) { + + case SDLK_F9: // F9: Invoke SAM + SAM(TheC64); + break; + + case SDLK_F10: // F10: Quit + quit_requested = true; + break; + + case SDLK_F11: // F11: NMI (Restore) + TheC64->NMI(); + break; + + case SDLK_F12: // F12: Reset + TheC64->Reset(); + break; + + case SDLK_HOME: // Home: Pause and enter menu + TheC64->enter_menu(); + break; + + case SDLK_SCROLLOCK: + num_locked = true; + break; + + case SDLK_KP_PLUS: // '+' on keypad: Increase SkipFrames + ThePrefs.SkipFrames++; + break; + + case SDLK_KP_MINUS: // '-' on keypad: Decrease SkipFrames + if (ThePrefs.SkipFrames > 1) + ThePrefs.SkipFrames--; + break; + + case SDLK_KP_MULTIPLY: // '*' on keypad: Toggle speed limiter + ThePrefs.LimitSpeed = !ThePrefs.LimitSpeed; + break; + + default: + TranslateKey(event.key.keysym.sym, false, key_matrix, rev_matrix, joystick); + break; + } + break; + + // Key released + case SDL_KEYUP: + if (event.key.keysym.sym == SDLK_SCROLLOCK) + num_locked = false; + else + TranslateKey(event.key.keysym.sym, true, key_matrix, rev_matrix, joystick); + break; + + // Quit Frodo + case SDL_QUIT: + quit_requested = true; + break; + } + } +} + + +/* + * Check if NumLock is down (for switching the joystick keyboard emulation) + */ + +bool C64Display::NumLock(void) +{ + return num_locked; +} + + +/* + * Open/close joystick drivers given old and new state of + * joystick preferences + */ +void C64::open_close_joystick(int port, int oldjoy, int newjoy) +{ +#if !defined(GEKKO) + if (oldjoy != newjoy) { + joy_minx[port] = joy_miny[port] = 32767; // Reset calibration + joy_maxx[port] = joy_maxy[port] = -32768; + if (newjoy) { + joy[port] = SDL_JoystickOpen(newjoy - 1); + if (joy[port] == NULL) + fprintf(stderr, "Couldn't open joystick %d\n", port + 1); + } else { + if (joy[port]) { + SDL_JoystickClose(joy[port]); + joy[port] = NULL; + } + } + } +#endif +} + +void C64::open_close_joysticks(int oldjoy1, int oldjoy2, int newjoy1, int newjoy2) +{ + open_close_joystick(0, oldjoy1, newjoy1); + open_close_joystick(1, oldjoy2, newjoy2); +} + +/* + * Poll joystick port, return CIA mask + */ + +uint8 C64::poll_joystick(int port) +{ + uint8 j = 0xff; + +#ifdef GEKKO + int extra_keys[N_WIIMOTE_BINDINGS]; + int controller = port; + WPADData *wpad, *wpad_other; + Uint32 held, held_other, held_classic, held_classic_other; + + if (ThePrefs.JoystickSwap) + controller = !port; + held_classic = held_classic_other = 0; + + wpad = WPAD_Data(controller); + wpad_other = WPAD_Data(!controller); + held = wpad->btns_h; + held_other = wpad_other->btns_h; + + // Check classic controller as well + if (wpad->exp.type == WPAD_EXP_CLASSIC) + held_classic = wpad->exp.classic.btns_held; + if (wpad_other->exp.type == WPAD_EXP_CLASSIC) + held_classic_other = wpad_other->exp.classic.btns_held; + + if ( (held & WPAD_BUTTON_UP) || (held_classic & CLASSIC_CTRL_BUTTON_LEFT) ) + j &= 0xfb; // Left + if ( (held & WPAD_BUTTON_DOWN) || (held_classic & CLASSIC_CTRL_BUTTON_RIGHT) ) + j &= 0xf7; // Right + if ( (held & WPAD_BUTTON_RIGHT) || (held_classic & CLASSIC_CTRL_BUTTON_UP) ) + j &= 0xfe; // Up + if ( (held & WPAD_BUTTON_LEFT) || (held_classic & CLASSIC_CTRL_BUTTON_DOWN) ) + j &= 0xfd; // Down + if ( (held & WPAD_BUTTON_2) || (held_classic & CLASSIC_CTRL_BUTTON_A) ) + j &= 0xef; // Button + if ( (held & WPAD_BUTTON_HOME) || (held_classic & CLASSIC_CTRL_BUTTON_HOME) ) + TheC64->enter_menu(); + + extra_keys[WIIMOTE_A] = (held | held_other) & WPAD_BUTTON_A; + extra_keys[WIIMOTE_B] = (held | held_other) & WPAD_BUTTON_B; + extra_keys[WIIMOTE_PLUS] = (held | held_other) & WPAD_BUTTON_PLUS; + extra_keys[WIIMOTE_MINUS] = (held | held_other) & WPAD_BUTTON_MINUS; + extra_keys[WIIMOTE_1] = (held | held_other) & WPAD_BUTTON_1; + + /* Classic buttons (might not be connected) */ + extra_keys[CLASSIC_X] = (held_classic | held_classic_other) & CLASSIC_CTRL_BUTTON_X; + extra_keys[CLASSIC_Y] = (held_classic | held_classic_other) & CLASSIC_CTRL_BUTTON_Y; + extra_keys[CLASSIC_B] = (held_classic | held_classic_other) & CLASSIC_CTRL_BUTTON_B; + extra_keys[CLASSIC_L] = (held_classic | held_classic_other) & CLASSIC_CTRL_BUTTON_FULL_L; + extra_keys[CLASSIC_R] = (held_classic | held_classic_other) & CLASSIC_CTRL_BUTTON_FULL_R; + extra_keys[CLASSIC_ZL] = (held_classic | held_classic_other) & CLASSIC_CTRL_BUTTON_ZL; + extra_keys[CLASSIC_ZR] = (held_classic | held_classic_other) & CLASSIC_CTRL_BUTTON_ZR; + + extra_keys[WIIMOTE_PLUS] = (held_classic | held_classic_other) & CLASSIC_CTRL_BUTTON_MINUS; + extra_keys[WIIMOTE_MINUS] = (held_classic | held_classic_other) & CLASSIC_CTRL_BUTTON_PLUS; + + for (int i = 0; i < N_WIIMOTE_BINDINGS; i++) + { + int kc = ThePrefs.JoystickKeyBinding[i]; + + if ( kc < 0 ) + continue; + + if (extra_keys[i]) + TheDisplay->UpdateKeyMatrix(kc, false, + TheCIA1->KeyMatrix, TheCIA1->RevMatrix); + else + TheDisplay->UpdateKeyMatrix(kc, true, + TheCIA1->KeyMatrix, TheCIA1->RevMatrix); + } + + return j; +#else + if (port == 0 && (joy[0] || joy[1])) + SDL_JoystickUpdate(); + + if (joy[port]) { + int x = SDL_JoystickGetAxis(joy[port], 0), y = SDL_JoystickGetAxis(joy[port], 1); + + if (x > joy_maxx[port]) + joy_maxx[port] = x; + if (x < joy_minx[port]) + joy_minx[port] = x; + if (y > joy_maxy[port]) + joy_maxy[port] = y; + if (y < joy_miny[port]) + joy_miny[port] = y; + + if (joy_maxx[port] - joy_minx[port] < 100 || joy_maxy[port] - joy_miny[port] < 100) + return 0xff; + + if (x < (joy_minx[port] + (joy_maxx[port]-joy_minx[port])/3)) + j &= 0xfb; // Left + else if (x > (joy_minx[port] + 2*(joy_maxx[port]-joy_minx[port])/3)) + j &= 0xf7; // Right + + if (y < (joy_miny[port] + (joy_maxy[port]-joy_miny[port])/3)) + j &= 0xfe; // Up + else if (y > (joy_miny[port] + 2*(joy_maxy[port]-joy_miny[port])/3)) + j &= 0xfd; // Down + + if (SDL_JoystickGetButton(joy[port], 0)) + j &= 0xef; // Button + } + + return j; +#endif +} + + +/* + * Allocate C64 colors + */ + +void C64Display::InitColors(uint8 *colors) +{ + SDL_Color palette[PALETTE_SIZE]; + for (int i=0; i<16; i++) { + palette[i].r = palette_red[i]; + palette[i].g = palette_green[i]; + palette[i].b = palette_blue[i]; + } + palette[fill_gray].r = palette[fill_gray].g = palette[fill_gray].b = 0xd0; + palette[shine_gray].r = palette[shine_gray].g = palette[shine_gray].b = 0xf0; + palette[shadow_gray].r = palette[shadow_gray].g = palette[shadow_gray].b = 0x80; + palette[red].r = 0xf0; + palette[red].g = palette[red].b = 0; + palette[green].g = 0xf0; + palette[green].r = palette[green].b = 0; + SDL_SetColors(screen, palette, 0, PALETTE_SIZE); + SDL_SetColors(real_screen, palette, 0, PALETTE_SIZE); + + + for (int i=0; i<256; i++) + colors[i] = i & 0x0f; +} + + +/* + * Show a requester (error message) + */ + +long int ShowRequester(const char *a, const char *b, const char *) +{ + printf("%s: %s\n", a, b); + return 1; +} diff --git a/Src/Display_WIN32.h b/Src/Display_WIN32.h new file mode 100644 index 0000000..e740fea --- /dev/null +++ b/Src/Display_WIN32.h @@ -0,0 +1,2380 @@ +/* + * Display_WIN32.h - C64 graphics display, emulator window handling, + * WIN32 specific stuff + * + * Frodo (C) 1994-1997,2002-2009 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include + +#include "C64.h" +#include "SAM.h" +#include "Version.h" +#include "VIC.h" +#include "resource.h" + +#define NAME "Frodo" +#define TITLE (IsFrodoSC ? "FrodoSC" : "Frodo") + +#ifdef DEBUG + +class TimeScope +{ + +public: + TimeScope(const char *s) + { + tag = s; + QueryPerformanceCounter(&liStart); + } + ~TimeScope() + { + QueryPerformanceCounter(&liFinish); + OutputTime(); + } + +private: + void + OutputTime() + { + LARGE_INTEGER liFreq; + + QueryPerformanceFrequency(&liFreq); + Debug("%s: %.0f usec\n", tag, + double(liFinish.LowPart - liStart.LowPart) + /liFreq.LowPart*1000000); + } + const char *tag; + LARGE_INTEGER liStart, liFinish; +}; + +#define TIMESCOPE(var, tag) TimeScope var(tag) + +#else + +#define TIMESCOPE(var, tag) + +#endif + +/* + C64 keyboard matrix: + + Bit 7 6 5 4 3 2 1 0 + 0 CUD F5 F3 F1 F7 CLR RET DEL + 1 SHL E S Z 4 A W 3 + 2 X T F C 6 D R 5 + 3 V U H B 8 G Y 7 + 4 N O K M 0 J I 9 + 5 , @ : . - L P + + 6 / ^ = SHR HOM ; * £ + 7 R/S Q C= SPC 2 CTL <- 1 +*/ + +#define MATRIX(a,b) (((a) << 3) | (b)) + +#define KEY_F9 256 +#define KEY_F10 257 +#define KEY_F11 258 +#define KEY_F12 259 + +#define KEY_FIRE 260 +#define KEY_JUP 261 +#define KEY_JDN 262 +#define KEY_JLF 263 +#define KEY_JRT 264 +#define KEY_JUPLF 265 +#define KEY_JUPRT 266 +#define KEY_JDNLF 267 +#define KEY_JDNRT 268 +#define KEY_CENTER 269 + +#define KEY_NUMLOCK 270 + +#define KEY_KPPLUS 271 +#define KEY_KPMINUS 272 +#define KEY_KPMULT 273 +#define KEY_KPDIV 274 +#define KEY_KPENTER 275 +#define KEY_KPPERIOD 276 + +#define KEY_PAUSE 277 +#define KEY_ALTENTER 278 +#define KEY_CTRLENTER 279 + +#define VK_bracketleft 0xdb +#define VK_bracketright 0xdd +#define VK_comma 0xbc +#define VK_period 0xbe +#define VK_slash 0xbf +#define VK_semicolon 0xba +#define VK_grave 0xc0 +#define VK_minus 0xbd +#define VK_equal 0xbb +#define VK_quote 0xde +#define VK_backslash 0xdc + +static C64Display *TheDisplay; +static int keystate[256]; +static UBYTE rev_matrix[8], key_matrix[8]; +static int quit = 0; +static int numlock = 0; +static int joystate = 0xff; + +static RECT rcScreen; +static RECT rcLast; +static RECT rcWindow; +static RECT rcWork; +static BOOL need_new_color_table = FALSE; +static int view_x, view_y; + +static int led_rows = 16; + +static HCURSOR invisible_cursor; +static HCURSOR arrow_cursor; + +static HFONT led_font; + +static HPEN led_highlight; +static HPEN led_shadow; + +static HBRUSH led_brush; +static HBRUSH off_brush; +static HBRUSH error_off_brush; +static HBRUSH on_brush; +static HBRUSH error_on_brush; + +// Not fully working yet. +#ifdef WORKBUFFER_BITMAP +static BOOL workbuffer_bitmap = FALSE; +static BOOL workbuffer_locked = FALSE; +static DDSURFACEDESC bitmap_ddsd; +#endif + +C64Display::DisplayMode default_modes[] = { + { 320, 200, 8 }, + { 320, 240, 8 }, + { 512, 384, 8 }, + { 640, 400, 8 }, + { 640, 480, 8 }, + { 320, 200, 16 }, + { 320, 240, 16 }, + { 512, 384, 16 }, + { 640, 400, 16 }, + { 640, 480, 16 }, +}; +static int num_default_modes = + sizeof(default_modes)/sizeof(C64Display::DisplayMode); + +static C64Display::DisplayMode *display_modes = NULL; +static int num_display_modes = 0; +static int max_display_modes = 16; + +int C64Display::GetNumDisplayModes() const +{ + if (num_display_modes == 0) + return num_default_modes; + return num_display_modes; +} + +const C64Display::DisplayMode *C64Display::GetDisplayModes() const +{ + if (num_display_modes == 0) + return default_modes; + return display_modes; +} + +long ShowRequester(const char *str, const char *button1, const char *button2) +{ + if (!TheDisplay) { + MessageBox(hwnd, str, "Frodo", MB_OK | MB_ICONSTOP); + return FALSE; + } + return TheDisplay->ShowRequester(str, button1, button2); +} + +/* + * Display constructor: Create window/screen + */ + +C64Display::C64Display(C64 *the_c64) : TheC64(the_c64) +{ + in_constructor = TRUE; + in_destructor = FALSE; + + TheDisplay = this; + speed_index = 0; + + pDD = NULL; + pPrimary = NULL; + pBack = NULL; + pWork = NULL; + pClipper = NULL; + pPalette = NULL; + active = FALSE; + paused = FALSE; + waiting = FALSE; + show_leds = ThePrefs.ShowLEDs; + full_screen = ThePrefs.DisplayType == DISPTYPE_SCREEN; + + // Turn LEDs off. + for (int i = 0; i < 4; i++) + led_state[i] = old_led_state[i] = LED_OFF; + + // Allocate chunky buffer to draw into. + chunky_buf = new UBYTE[DISPLAY_X * DISPLAY_Y]; + + CalcViewPort(); + + ResetKeyboardState(); + + if (!MakeWindow()) { + ShowRequester("Failed to create window.", "Quit"); + Quit(); + } + else { + WindowTitle(); + + if (!StartDirectDraw()) { + ShowRequester(failure_message, "Quit"); + Quit(); + } + else + draw_led_bar(); + } + + in_constructor = FALSE; +} + +/* + * Display destructor + */ + +C64Display::~C64Display() +{ + in_destructor = TRUE; + + Debug("~C64Display\n"); + + StopDirectDraw(); + + // Offer to save now that we are not in full screen mode. + OfferSave(); + + // Free the display modes table. + delete[] display_modes; + + // Free chunky buffer + delete chunky_buf; + + // Destroy the main window. + DestroyWindow(hwnd); + + // Drain the window message queue. + for (;;) + { + MSG msg; + if (!GetMessage(&msg, NULL, 0, 0)) + break; + if (ThePrefs.SystemKeys) + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + DeleteObjects(); + + in_destructor = FALSE; +} + +/* + * Prefs may have changed + */ + +void C64Display::NewPrefs(Prefs *prefs) +{ +} + +void C64Display::DeleteObjects() +{ + // Delete objects we created. + DeleteObject(led_highlight); + DeleteObject(led_shadow); + DeleteObject(led_font); + DeleteObject(led_brush); + DeleteObject(off_brush); + DeleteObject(error_off_brush); + DeleteObject(on_brush); + DeleteObject(error_on_brush); +} + +BOOL C64Display::CalcViewPort() +{ + int old_view_x = view_x, old_view_y = view_y; + const char *view_port = ThePrefs.ViewPort; + if (view_port[0] == '\0' || + stricmp(view_port, "Default") == 0) + view_port = NULL; + if (!view_port || sscanf(view_port, "%dx%d", &view_x, &view_y) != 2) { + view_x = DISPLAY_X; + view_y = DISPLAY_Y; + } + SetRect(&rcWork, 0, 0, view_x, view_y); + if (view_x != old_view_x || view_y != old_view_y) + return TRUE; + return FALSE; +} + + +BOOL C64Display::ResizeWindow(int side, RECT *pRect) +{ + // Compute size of non-client borders. + DWORD style = GetWindowLong(hwnd, GWL_STYLE); + RECT rc; + SetRect(&rc, 0, 0, view_x, view_y); + BOOL has_menu = GetMenu(hwnd) != NULL; + AdjustWindowRect(&rc, style, has_menu); + if (ThePrefs.ShowLEDs) + rc.bottom += led_rows; + int nc_x = rc.right - rc.left - view_x; + int nc_y = rc.bottom - rc.top - view_y; + + // Compute client area corresponding to resizing. + int old_x = pRect->right - pRect->left - nc_x; + int old_y = pRect->bottom - pRect->top - nc_y; + + // Compute nearest integral scaling numerators. + int d = ThePrefs.ScalingDenominator; + int x = (old_x + view_x/d/2)/(view_x/d); + if (x == 0) + x = 1; + int y = (old_y + view_y/4)/(view_y/d); + if (y == 0) + y = 1; + + // When resizing corners make the scale factors agree. + switch (side) { + case WMSZ_BOTTOMRIGHT: + case WMSZ_BOTTOMLEFT: + case WMSZ_TOPRIGHT: + case WMSZ_TOPLEFT: + if (x < y) + y = x; + else + x = y; + } + + // Compute the quantized size of the window area. + int new_x = x*(view_x/d) + nc_x; + int new_y = y*(view_y/d) + nc_y; + + // Adjust the resizing rectangle. + switch (side) { + + case WMSZ_BOTTOMRIGHT: + case WMSZ_BOTTOMLEFT: + case WMSZ_BOTTOM: + pRect->bottom = pRect->top + new_y; + break; + + case WMSZ_TOPRIGHT: + case WMSZ_TOPLEFT: + case WMSZ_TOP: + pRect->top = pRect->bottom - new_y; + break; + } + switch (side) { + + case WMSZ_TOPRIGHT: + case WMSZ_BOTTOMRIGHT: + case WMSZ_RIGHT: + pRect->right = pRect->left + new_x; + break; + + case WMSZ_TOPLEFT: + case WMSZ_BOTTOMLEFT: + case WMSZ_LEFT: + pRect->left = pRect->right - new_x; + break; + } + + return TRUE; +} + +/* + * Update speedometer + */ + +void C64Display::Speedometer(int speed) +{ + Debug("speed = %d %%\n", speed); + speed_index = speed; + + if (full_screen) + return; + + if (!ThePrefs.ShowLEDs) { + WindowTitle(); + return; + } + + if (speed_index == 0) + return; + + HDC hdc = GetDC(hwnd); + RECT rc; + GetClientRect(hwnd, &rc); + rc.top = rc.bottom - led_rows; + rc.right = rc.left + (rc.right - rc.left)/5; + FillRect(hdc, &rc, led_brush); + SelectObject(hdc, led_font); + SetTextAlign(hdc, TA_TOP | TA_LEFT); + SetBkMode(hdc, TRANSPARENT); + SetTextColor(hdc, (COLORREF) GetSysColor(COLOR_MENUTEXT)); + char str[128]; + if (IsFrodoSC) + sprintf(str, "%d%%", speed_index); + else + sprintf(str, "%d%%", speed_index); + int x = rc.left + 4; + int y = rc.top + 2; + TextOut(hdc, x, y, str, strlen(str)); + ReleaseDC(hwnd, hdc); +} + + +/* + * Return pointer to bitmap data + */ + +UBYTE *C64Display::BitmapBase() +{ +#ifdef WORKBUFFER_BITMAP + if (colors_depth == 8 && pWork) { + if (workbuffer_locked) { + pWork->Unlock(NULL); + workbuffer_locked = FALSE; + } + HRESULT ddrval; + for (;;) { + bitmap_ddsd.dwSize = sizeof(bitmap_ddsd); + ddrval = pWork->Lock(NULL, &bitmap_ddsd, 0, NULL); + if (ddrval != DDERR_WASSTILLDRAWING) + break; + } + if (ddrval == DD_OK) { + workbuffer_locked = TRUE; + workbuffer_bitmap = TRUE; + return (UBYTE *) bitmap_ddsd.lpSurface; + } + } + workbuffer_bitmap = FALSE; +#endif + return chunky_buf; +} + + +/* + * Return number of bytes per row + */ + +int C64Display::BitmapXMod() +{ +#ifdef WORKBUFFER_BITMAP + if (workbuffer_locked) + return bitmap_ddsd.lPitch; +#endif + return DISPLAY_X; +} + + +/* + * Freshen keyboard state + */ + +void C64Display::PollKeyboard(UBYTE *CIA_key_matrix, UBYTE *CIA_rev_matrix, UBYTE *joystick) +{ + //Debug("Display::PollKeyboard\n"); + +#ifdef WORKBUFFER_BITMAP + if (workbuffer_locked) { + pWork->Unlock(NULL); + workbuffer_locked = FALSE; + } +#endif + + for (;;) + { + MSG msg; + if (!PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) + break; + if (ThePrefs.SystemKeys) + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + *joystick = joystate; + memcpy(CIA_key_matrix, key_matrix, sizeof(key_matrix)); + memcpy(CIA_rev_matrix, rev_matrix, sizeof(rev_matrix)); +} + +/* + * Check if NumLock is down (for switching the joystick keyboard emulation) + */ + +bool C64Display::NumLock() +{ + return numlock; +} + + +/* + * Allocate C64 colors + */ + +void C64Display::InitColors(UBYTE *array) +{ + if (colors_depth == 8) { + for (int i = 0; i < 256; i++) + array[i] = colors[i & 0x0f]; + } + else { + for (int i = 0; i < 256; i++) + array[i] = i & 0x0f; + } +} + + +long C64Display::ShowRequester(const char *str, const char *button1, const char *button2) +{ + // This could be a lot nicer but quick and dirty is fine with me. + char message[1024]; + strcpy(message, str); + strcat(message, "\nPress OK to "); + strcat(message, button1); + if (button2) { + strcat(message, ", Cancel to "); + strcat(message, button2); + } + strcat(message, "."); + UINT type; + if (button2) + type = MB_OKCANCEL | MB_ICONQUESTION; + else + type = MB_OK | MB_ICONSTOP; + Pause(); + if (full_screen) + StopDirectDraw(); + int result = MessageBox(hwnd, message, NAME " Error", type); + if (full_screen) + StartDirectDraw(); + Resume(); + if (result == IDCANCEL) + return TRUE; + return FALSE; +} + +void C64Display::WaitUntilActive() +{ + Debug("waiting until not paused...\n"); + waiting = TRUE; + WindowTitle(); + for (;;) { + + // Check for termination condition. + if (!paused || quit) + break; + + // Process message queue. + MSG msg; + if (GetMessage(&msg, NULL, 0, 0) != TRUE) + break; + + // Always translate system keys while paused. + TranslateMessage(&msg); + DispatchMessage(&msg); + } + waiting = FALSE; + Debug("...done waiting\n"); + WindowTitle(); + ResetKeyboardState(); +} + +void C64Display::ResetKeyboardState() +{ + memset(keystate, 0, sizeof(keystate)); + memset(key_matrix, 0xff, sizeof(key_matrix)); + memset(rev_matrix, 0xff, sizeof(rev_matrix)); + joystate = 0xff; +} + +BOOL C64Display::MakeWindow() +{ + // Set up and register window class. + WNDCLASS wc; + wc.style = CS_HREDRAW | CS_VREDRAW; + wc.lpfnWndProc = StaticWindowProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = hInstance; + wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(FRODO_ICON)); + wc.hCursor = NULL; + wc.hbrBackground = NULL; + wc.lpszMenuName = MAKEINTRESOURCE(IDR_MAIN_MENU); + wc.lpszClassName = NAME; + RegisterClass(&wc); + + // Set up our preferred styles for our window depending on the mode. + windowed_style = WS_VISIBLE | WS_SYSMENU | WS_OVERLAPPED | WS_CAPTION | WS_MINIMIZEBOX | WS_THICKFRAME; + fullscreen_style = WS_POPUP | WS_VISIBLE; + + // Compute the initial window size. + DWORD style = windowed_style; + RECT rc; + int n = ThePrefs.ScalingNumerator; + int d = ThePrefs.ScalingDenominator; + SetRect(&rc, 0, 0, n*view_x/d, n*view_y/d); + BOOL has_menu = wc.lpszMenuName != NULL; + AdjustWindowRect(&rc, style, has_menu); + if (ThePrefs.ShowLEDs) + rc.bottom += led_rows; + int x_size = rc.right - rc.left; + int y_size = rc.bottom - rc.top; + + // Create the window and save the initial position. + hwnd = CreateWindowEx(0, NAME, TITLE, style, CW_USEDEFAULT, 0, x_size, y_size, NULL, NULL, hInstance, NULL); + GetWindowRect(hwnd, &rcLast); + SetRect(&rcScreen, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN)); + + // Load cursors. + invisible_cursor = LoadCursor(hInstance, MAKEINTRESOURCE(IDC_INVISIBLE)); + arrow_cursor = LoadCursor(0, MAKEINTRESOURCE(IDC_ARROW)); + + // Create fonts, pens, brushes, etc. + CreateObjects(); + + if (!hwnd) + return FALSE; + + ShowWindow(hwnd, nCmdShow); + UpdateWindow(hwnd); + + return TRUE; +} + +void C64Display::CreateObjects() +{ + // Create fonts. + led_font = CreateFont(14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + VARIABLE_PITCH | FF_SWISS, ""); + + // Create pens. + led_highlight = CreatePen(PS_SOLID, 0, GetSysColor(COLOR_3DHIGHLIGHT)); + led_shadow = CreatePen(PS_SOLID, 0, GetSysColor(COLOR_3DSHADOW)); + + // Create brushes. + LOGBRUSH logbrush; + logbrush.lbStyle = BS_SOLID; + logbrush.lbHatch = 0; + logbrush.lbColor = GetSysColor(COLOR_MENU); + led_brush = CreateBrushIndirect(&logbrush); + logbrush.lbColor = RGB(0x00, 0x00, 0x00); // black + off_brush = CreateBrushIndirect(&logbrush); + logbrush.lbColor = RGB(0x00, 0x00, 0x00); // black + error_off_brush = CreateBrushIndirect(&logbrush); + logbrush.lbColor = RGB(0x00, 0xff, 0x00); // green + on_brush = CreateBrushIndirect(&logbrush); + logbrush.lbColor = RGB(0xff, 0x00, 0x00); // red + error_on_brush = CreateBrushIndirect(&logbrush); +} + +HRESULT CALLBACK C64Display::StaticWindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + return TheDisplay->WindowProc(hWnd, message, wParam, lParam); +} + +long C64Display::WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + Debug("window message: 0x%x\n", message); + + switch (message) { + + case WM_SYSCOLORCHANGE: + DeleteObjects(); + CreateObjects(); + InvalidateRect(hwnd, NULL, FALSE); + break; + + case WM_MOUSEMOVE: + SetCursor(ThePrefs.HideCursor ? invisible_cursor : arrow_cursor); + break; + + case WM_MENUCHAR: + // Eat Alt-foo characters so that it doesn't beep. + if (HIWORD(wParam) == 0) + return MAKELONG(0, 1); + break; + + case WM_ENTERSIZEMOVE: + Pause(); + break; + + case WM_EXITSIZEMOVE: + Resume(); + break; + + case WM_SIZING: + ResizeWindow(wParam, (RECT *) lParam); + return TRUE; + + case WM_SIZE: + case WM_MOVE: + if (full_screen) + SetRect(&rcWindow, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN)); + else { + GetClientRect(hWnd, &rcWindow); + if (ThePrefs.ShowLEDs) + rcWindow.bottom -= led_rows; + ClientToScreen(hWnd, (LPPOINT) &rcWindow); + ClientToScreen(hWnd, (LPPOINT) &rcWindow + 1); + + // Align the client rect to a four-byte + // boundary because this can triple the + // speed of a memcpy to the display. + int align_to = 4; + int misalignment = rcWindow.left % align_to; + if (misalignment == 0) + Update(); + else { + if (misalignment > align_to/2) + misalignment -= align_to; + RECT rc; + GetWindowRect(hwnd, &rc); + MoveWindow(hwnd, rc.left - misalignment, + rc.top, rc.right - rc.left, + rc.bottom - rc.top, TRUE); + } + } + break; + + case WM_DISPLAYCHANGE: + if (!full_screen) + ResumeDirectDraw(); + SetRect(&rcScreen, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN)); + break; + + case WM_ACTIVATE: + Debug("WM_ACTIVATE\n"); + { + int old_active = active; + active = LOWORD(wParam) != WA_INACTIVE; + if (ThePrefs.AutoPause && active != old_active) { + if (!active) + Pause(); + else + Resume(); + } + } + if (active) { + ResumeDirectDraw(); + ResetKeyboardState(); + } + + // Kick the message loop since this was sent to us, not posted. + PostMessage(hWnd, WM_USER, 0, 0); + break; + + case WM_COMMAND: + { + int id = LOWORD(wParam); + switch (id) { + + case ID_FILE_NEW: + { + OfferSave(); + Prefs *prefs = new Prefs; + TheC64->NewPrefs(prefs); + ThePrefs = *prefs; + ThePrefsOnDisk = ThePrefs; + delete prefs; + strcpy(TheApp->prefs_path, "Untitled.fpr"); + NewPrefs(); + } + break; + + case ID_FILE_OPEN: + Pause(); + OfferSave(); + if (FileNameDialog(TheApp->prefs_path)) { + Prefs *prefs = new Prefs; + prefs->Load(TheApp->prefs_path); + TheC64->NewPrefs(prefs); + ThePrefs = *prefs; + delete prefs; + NewPrefs(); + } + Resume(); + break; + + case ID_FILE_SAVE: + ThePrefs.Save(TheApp->prefs_path); + break; + + case ID_FILE_SAVEAS: + Pause(); + if (FileNameDialog(TheApp->prefs_path, TRUE)) { + ThePrefs.Save(TheApp->prefs_path); + WindowTitle(); + } + Resume(); + break; + + case ID_FILE_EX: + PostMessage(hWnd, WM_CLOSE, 0, 0); + break; + + case ID_TOOLS_PREFERENCES: + Pause(); + TheApp->RunPrefsEditor(); + NewPrefs(); + Resume(); + break; + + case ID_TOOLS_FULLSCREEN: + Pause(); + StopDirectDraw(); + full_screen = !full_screen; + if (!StartDirectDraw()) { + StopDirectDraw(); + full_screen = !full_screen; + StartDirectDraw(); + if (!full_screen) + ShowRequester(failure_message, "Continue"); + } + Resume(); + CheckMenuItem(GetMenu(hWnd), ID_TOOLS_FULLSCREEN, full_screen ? MF_CHECKED : MF_UNCHECKED); + if (paused) + Update(); + break; + + case ID_TOOLS_RESETDIRECTDRAW: + ResetDirectDraw(); + break; + + case ID_TOOLS_PAUSE: + if (!paused) + Pause(); + else { + // XXX: Shouldn't happen but be safe. + while (paused) + Resume(); + ResetKeyboardState(); + } + CheckMenuItem(GetMenu(hWnd), ID_TOOLS_PAUSE, paused ? MF_CHECKED : MF_UNCHECKED); + break; + + case ID_TOOLS_RESETC64: + TheC64->Reset(); + break; + + case ID_TOOLS_INSERTNEXTDISK: + InsertNextDisk(); + break; + + case ID_TOOLS_SAM: + Pause(); + MessageBox(hWnd, "SAM not yet implemented.", NAME, MB_OK); + Resume(); + break; + + case ID_HELP_CONTENTS: + case ID_HELP_KEYBOARD: + case ID_HELP_SETTINGS: + { + const char *html; + switch (id) { + case ID_HELP_CONTENTS: html = "Main"; break; + case ID_HELP_KEYBOARD: html = "keyboard"; break; + case ID_HELP_SETTINGS: html = "settings"; break; + } + char helpfile[256]; + sprintf(helpfile, "%s\\Docs\\%s.html", AppDirPath, html); + ShellExecute(0, 0, helpfile, 0, 0, SW_NORMAL); + } + break; + + case ID_HELP_ABOUT: + { + Pause(); + char message[256]; + sprintf(message, "%s by %s\n%s by %s", + VERSION_STRING, + "Christian Bauer", + "WIN32 port", + "J. Richard Sladkey"); + MessageBox(hWnd, message, NAME, MB_OK); + Resume(); + } + break; + } + ResetKeyboardState(); + } + break; + + case WM_CLOSE: + Quit(); + return 0; + + case WM_DESTROY: + PostQuitMessage(0); + break; + + case WM_QUERYNEWPALETTE: + if (!full_screen && pPalette && pPrimary) { + SetPalettes(); + BuildColorTable(); + if (!active) + Update(); + } + break; + + case WM_PALETTECHANGED: + if (!full_screen) { + if ((HWND) wParam != hWnd) { + need_new_color_table = TRUE; + InvalidateRect(hwnd, NULL, FALSE); + } + } + break; + + case WM_PAINT: + if (!full_screen) + { + PAINTSTRUCT ps; + HDC hdc = BeginPaint(hWnd, &ps); + EndPaint(hWnd, &ps); + if (need_new_color_table) { + BuildColorTable(); + need_new_color_table = FALSE; + } + if (paused) + Update(); + draw_led_bar(); + Speedometer(speed_index); + return 0; + } + break; + + case WM_ENTERMENULOOP: + Pause(); + break; + + case WM_EXITMENULOOP: + Resume(); + ResetKeyboardState(); + break; + + case WM_SYSKEYDOWN: + case WM_KEYDOWN: + Debug("Display::WindowProc: KEYDOWN: 0x%x\n", wParam); + { + int kc = VirtKey2C64(wParam, lParam); + switch (kc) { + + case KEY_PAUSE: + PostMessage(hWnd, WM_COMMAND, ID_TOOLS_PAUSE, 0); + break; + + case KEY_KPPLUS: + if (ThePrefs.SkipFrames < 10) + ThePrefs.SkipFrames++; + break; + + case KEY_KPMINUS: + if (ThePrefs.SkipFrames > 1) + ThePrefs.SkipFrames--; + break; + + case KEY_KPMULT: + ThePrefs.LimitSpeed = !ThePrefs.LimitSpeed; + break; + + case KEY_KPDIV: + { + Prefs *prefs = new Prefs(ThePrefs); + prefs->Emul1541Proc = !prefs->Emul1541Proc; + TheC64->NewPrefs(prefs); + ThePrefs = *prefs; + delete prefs; + } + break; + + case KEY_KPPERIOD: + ThePrefs.JoystickSwap = !ThePrefs.JoystickSwap; + break; + + case KEY_F9: + PostMessage(hWnd, WM_COMMAND, ID_TOOLS_INSERTNEXTDISK, 0); + break; + + case KEY_ALTENTER: + PostMessage(hWnd, WM_COMMAND, ID_TOOLS_FULLSCREEN, 0); + break; + + case KEY_CTRLENTER: + PostMessage(hWnd, WM_COMMAND, ID_TOOLS_RESETDIRECTDRAW, 0); + break; + + case KEY_F10: + PostMessage(hWnd, WM_CLOSE, 0, 0); + break; + + case KEY_F11: + if (!paused) + TheC64->NMI(); + break; + + case KEY_F12: + if (!paused) + TheC64->Reset(); + break; + + case KEY_FIRE: + joystate &= ~0x10; + break; + + case KEY_JUP: + joystate |= 0x02; + joystate &= ~0x01; + break; + + case KEY_JDN: + joystate |= 0x01; + joystate &= ~0x02; + break; + + case KEY_JLF: + joystate |= 0x08; + joystate &= ~0x04; + break; + + case KEY_JRT: + joystate |= 0x04; + joystate &= ~0x08; + break; + + case KEY_JUPLF: + joystate |= 0x0a; + joystate &= ~0x05; + break; + + case KEY_JUPRT: + joystate |= 0x06; + joystate &= ~0x09; + break; + + case KEY_JDNLF: + joystate |= 0x09; + joystate &= ~0x06; + break; + + case KEY_JDNRT: + joystate |= 0x05; + joystate &= ~0x0a; + break; + + case KEY_CENTER: + joystate |= 0x0f; + break; + + default: + if (kc < 0 || kc >= 256) + break; + if (keystate[kc]) + break; + keystate[kc] = 1; + int c64_byte = kc >> 3; + int c64_bit = kc & 7; + int shifted = kc & 128; + c64_byte &= 7; + if (shifted) { + key_matrix[6] &= 0xef; + rev_matrix[4] &= 0xbf; + } + key_matrix[c64_byte] &= ~(1 << c64_bit); + rev_matrix[c64_bit] &= ~(1 << c64_byte); + break; + } + return 0; + } + break; + + case WM_SYSKEYUP: + case WM_KEYUP: + Debug("Display::WindowProc: KEYUP: 0x%x\n", wParam); + { + int kc = VirtKey2C64(wParam, lParam); + switch (kc) { + + case KEY_FIRE: + joystate |= 0x10; + break; + + case KEY_JUP: + joystate |= 0x01; + break; + + case KEY_JDN: + joystate |= 0x02; + break; + + case KEY_JLF: + joystate |= 0x04; + break; + + case KEY_JRT: + joystate |= 0x08; + break; + + case KEY_JUPLF: + joystate |= 0x05; + break; + + case KEY_JUPRT: + joystate |= 0x09; + break; + + case KEY_JDNLF: + joystate |= 0x06; + break; + + case KEY_JDNRT: + joystate |= 0x0a; + break; + + default: + if (kc < 0 || kc >= 256) + break; + if (!keystate[kc]) + break; + keystate[kc] = 0; + int c64_byte = kc >> 3; + int c64_bit = kc & 7; + int shifted = kc & 128; + c64_byte &= 7; + if (shifted) { + key_matrix[6] |= 0x10; + rev_matrix[4] |= 0x40; + } + key_matrix[c64_byte] |= (1 << c64_bit); + rev_matrix[c64_bit] |= (1 << c64_byte); + break; + } + return 0; + } + break; + } + + return DefWindowProc(hWnd, message, wParam, lParam); +} + +int C64Display::VirtKey2C64(int virtkey, DWORD keydata) +{ + int ext = keydata & 0x01000000; + int sc = (keydata & 0x00ff0000) >> 16; + int result = -1; + + switch (virtkey) { + + case VK_NUMPAD0: numlock = 1; return KEY_FIRE; + case VK_NUMPAD1: numlock = 1; return KEY_JDNLF; + case VK_NUMPAD2: numlock = 1; return KEY_JDN; + case VK_NUMPAD3: numlock = 1; return KEY_JDNRT; + case VK_NUMPAD4: numlock = 1; return KEY_JLF; + case VK_NUMPAD5: numlock = 1; return KEY_CENTER; + case VK_NUMPAD6: numlock = 1; return KEY_JRT; + case VK_NUMPAD7: numlock = 1; return KEY_JUPLF; + case VK_NUMPAD8: numlock = 1; return KEY_JUP; + case VK_NUMPAD9: numlock = 1; return KEY_JUPRT; + + case VK_NUMLOCK: return KEY_NUMLOCK; + case VK_MULTIPLY: return KEY_KPMULT; + case VK_DIVIDE: return KEY_KPDIV; + case VK_SUBTRACT: return KEY_KPMINUS; + case VK_ADD: return KEY_KPPLUS; + case VK_DECIMAL: return KEY_KPPERIOD; + + case VK_F9: return KEY_F9; + case VK_F10: return KEY_F10; + case VK_F11: return KEY_F11; + case VK_F12: return KEY_F12; + case VK_PAUSE: return KEY_PAUSE; + + case VK_BACK: return MATRIX(0,0); + case VK_DELETE: return ext ? MATRIX(0,0) : /*KP*/ KEY_KPPERIOD; + case VK_TAB: return -1; + case VK_RETURN: + if ((GetKeyState(VK_MENU) & 0x8000)) + return KEY_ALTENTER; + if ((GetKeyState(VK_CONTROL) & 0x8000)) + return KEY_CTRLENTER; + return ext ? /*KP*/ MATRIX(0,1) : MATRIX(0,1); + case VK_SPACE: return MATRIX(7,4); + case VK_ESCAPE: return MATRIX(7,7); + case VK_INSERT: if (!ext) numlock = 0; return ext ? MATRIX(0,0) | 0x80 : /*KP*/ KEY_FIRE; + case VK_HOME: if (!ext) numlock = 0; return ext ? MATRIX(6,3) : /*KP*/ KEY_JUPLF; + case VK_END: if (!ext) numlock = 0; return ext ? MATRIX(6,0) : /*KP*/ KEY_JDNLF; + case VK_PRIOR: if (!ext) numlock = 0; return ext ? MATRIX(6,6) : /*KP*/ KEY_JUPRT; + case VK_NEXT: if (!ext) numlock = 0; return ext ? MATRIX(6,5) : /*KP*/ KEY_JDNRT; + case VK_CLEAR: return KEY_CENTER; + + case VK_SHIFT: return sc == 0x36 ? /*R*/ MATRIX(6,4) : MATRIX(1,7); + case VK_CONTROL: return ext ? /*R*/ MATRIX(7,5) : MATRIX(7,2); + case VK_MENU: return ext ? /*R*/ MATRIX(7,5) : MATRIX(7,5); + + case VK_UP: if (!ext) numlock = 0; return ext ? MATRIX(0,7) | 0x80 : /*KP*/ KEY_JUP; + case VK_DOWN: if (!ext) numlock = 0; return ext ? MATRIX(0,7) : /*KP*/ KEY_JDN; + case VK_LEFT: if (!ext) numlock = 0; return ext ? MATRIX(0,2) | 0x80 : /*KP*/ KEY_JLF; + case VK_RIGHT: if (!ext) numlock = 0; return ext ? MATRIX(0,2) : /*KP*/ KEY_JRT; + + case VK_F1: return MATRIX(0,4); + case VK_F2: return MATRIX(0,4) | 0x80; + case VK_F3: return MATRIX(0,5); + case VK_F4: return MATRIX(0,5) | 0x80; + case VK_F5: return MATRIX(0,6); + case VK_F6: return MATRIX(0,6) | 0x80; + case VK_F7: return MATRIX(0,3); + case VK_F8: return MATRIX(0,3) | 0x80; + + case '0': return MATRIX(4,3); + case '1': return MATRIX(7,0); + case '2': return MATRIX(7,3); + case '3': return MATRIX(1,0); + case '4': return MATRIX(1,3); + case '5': return MATRIX(2,0); + case '6': return MATRIX(2,3); + case '7': return MATRIX(3,0); + case '8': return MATRIX(3,3); + case '9': return MATRIX(4,0); + + case VK_bracketleft: return MATRIX(5,6); + case VK_bracketright: return MATRIX(6,1); + case VK_slash: return MATRIX(6,7); + case VK_semicolon: return MATRIX(5,5); + case VK_grave: return MATRIX(7,1); + case VK_minus: return MATRIX(5,0); + case VK_equal: return MATRIX(5,3); + case VK_comma: return MATRIX(5,7); + case VK_period: return MATRIX(5,4); + case VK_quote: return MATRIX(6,2); + case VK_backslash: return MATRIX(6,6); + + case 'A': result = MATRIX(1,2); break; + case 'B': result = MATRIX(3,4); break; + case 'C': result = MATRIX(2,4); break; + case 'D': result = MATRIX(2,2); break; + case 'E': result = MATRIX(1,6); break; + case 'F': result = MATRIX(2,5); break; + case 'G': result = MATRIX(3,2); break; + case 'H': result = MATRIX(3,5); break; + case 'I': result = MATRIX(4,1); break; + case 'J': result = MATRIX(4,2); break; + case 'K': result = MATRIX(4,5); break; + case 'L': result = MATRIX(5,2); break; + case 'M': result = MATRIX(4,4); break; + case 'N': result = MATRIX(4,7); break; + case 'O': result = MATRIX(4,6); break; + case 'P': result = MATRIX(5,1); break; + case 'Q': result = MATRIX(7,6); break; + case 'R': result = MATRIX(2,1); break; + case 'S': result = MATRIX(1,5); break; + case 'T': result = MATRIX(2,6); break; + case 'U': result = MATRIX(3,6); break; + case 'V': result = MATRIX(3,7); break; + case 'W': result = MATRIX(1,1); break; + case 'X': result = MATRIX(2,7); break; + case 'Y': result = MATRIX(3,1); break; + case 'Z': result = MATRIX(1,4); break; + + } + + if (result != -1 && GetKeyState(VK_CAPITAL)) + result |= 0x80; + + return result; +} + +BOOL C64Display::SetupWindow() +{ + // Setup the window. + SetupWindowMode(full_screen); + + UpdateWindow(hwnd); + + if (full_screen) + ShowCursor(FALSE); + + return TRUE; +} + +BOOL C64Display::SetupWindowMode(BOOL full_screen_mode) +{ + DWORD style; + int x0, y0, x, y; + if (full_screen_mode) { + style = fullscreen_style; + x0 = 0; + y0 = 0; + x = GetSystemMetrics(SM_CXSCREEN); + y = GetSystemMetrics(SM_CYSCREEN); + } + else { + style = windowed_style; + x0 = rcLast.left; + y0 = rcLast.top; + x = rcLast.right - rcLast.left; + y = rcLast.bottom - rcLast.top; + } + SetWindowLong(hwnd, GWL_STYLE, style); + SetWindowPos(hwnd, NULL, x0, y0, x, y, SWP_NOZORDER | SWP_NOACTIVATE); + SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE); + GetClientRect(hwnd, &rcWindow); + if (!full_screen_mode && ThePrefs.ShowLEDs) + rcWindow.bottom -= led_rows; + ClientToScreen(hwnd, (LPPOINT) &rcWindow); + ClientToScreen(hwnd, (LPPOINT) &rcWindow + 1); + + // Windowed mode has a menu, full screen mode doesn't. + HMENU old_menu = GetMenu(hwnd); + if (old_menu) { + SetMenu(hwnd, NULL); + DestroyMenu(old_menu); + } + if (!full_screen_mode) { + HMENU new_menu = LoadMenu(hInstance, MAKEINTRESOURCE(IDR_MAIN_MENU)); + SetMenu(hwnd, new_menu); + } + + return TRUE; +} + +BOOL C64Display::RestoreWindow() +{ + if (full_screen) + ShowCursor(TRUE); + + if (!full_screen) + GetWindowRect(hwnd, &rcLast); + + SetupWindowMode(FALSE); + + return TRUE; +} + +HRESULT CALLBACK C64Display::EnumModesCallback(LPDDSURFACEDESC pDDSD, LPVOID lpContext) +{ + C64Display *pDisplay = (C64Display *) lpContext; + return pDisplay->EnumModesCallback(pDDSD); +} + +HRESULT C64Display::EnumModesCallback(LPDDSURFACEDESC pDDSD) +{ + DisplayMode mode; + mode.x = pDDSD->dwWidth; + mode.y = pDDSD->dwHeight; + mode.depth = pDDSD->ddpfPixelFormat.dwRGBBitCount; + mode.modex = (pDDSD->ddsCaps.dwCaps & DDSCAPS_MODEX) != 0; + Debug("EnumModesCallback: %dx%dx%d (modex: %d)\n", + mode.x, mode.y, mode.depth, mode.modex); + if (display_modes == NULL) + display_modes = new DisplayMode[max_display_modes]; + if (num_display_modes == max_display_modes) { + int old_max = max_display_modes; + max_display_modes *= 2; + DisplayMode *new_modes = new DisplayMode[max_display_modes]; + memcpy(new_modes, display_modes, sizeof(DisplayMode)*old_max); + delete[] display_modes; + display_modes = new_modes; + } + display_modes[num_display_modes++] = mode; + return DDENUMRET_OK; +} + +int C64Display::CompareModes(const void *e1, const void *e2) +{ + DisplayMode *m1 = (DisplayMode *) e1; + DisplayMode *m2 = (DisplayMode *) e2; + if (m1->depth != m2->depth) + return m1->depth - m2->depth; + if (m1->x != m2->x) + return m1->x - m2->x; + if (m1->y != m2->y) + return m1->y - m2->y; + if (m1->modex != m2->modex) + return int(m1->modex) - int(m2->modex); + return 0; +} + +BOOL C64Display::StartDirectDraw() +{ + // Setup our window size, position, style, etc. + SetupWindow(); + + // Create the main DirectDraw object. + HRESULT ddrval = DirectDrawCreate(NULL, &pDD, NULL); + if (ddrval != DD_OK) { + DebugResult("DirectDrawCreate failed", ddrval); + return Fail("Failed to initialize direct draw."); + } + + if (full_screen) { + + // Set exclusive mode. + ddrval = pDD->SetCooperativeLevel(hwnd, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN | DDSCL_ALLOWMODEX); + if (ddrval != DD_OK) { + DebugResult("SetCooperativeLevel failed", ddrval); + return Fail("Failed to set exclusive cooperative level."); + } + + if (!display_modes) { + + // Get all available video modes and sort them. + num_display_modes = 0; + pDD->EnumDisplayModes(0, NULL, this, EnumModesCallback); + qsort(display_modes, num_display_modes, sizeof(DisplayMode), CompareModes); + } + + // Set the video mode. + const char *display_mode = ThePrefs.DisplayMode; + if (display_mode[0] == '\0' || + stricmp(display_mode, "Default") == 0) + display_mode = NULL; + if (display_mode) { + int x, y, depth = 8; + if (sscanf(display_mode, "%dx%dx%d", &x, &y, &depth) < 2) + return Fail("Invalid command line mode format."); + ddrval = pDD->SetDisplayMode(x, y, depth); + if (ddrval != DD_OK) { + DebugResult("SetDisplayMode failed", ddrval); + return Fail("Failed to set the video mode."); + } + } + else { + for (int i = 0; i < num_display_modes; i++) { + DisplayMode *mode = &display_modes[i]; + if (mode->x < view_x || mode->y < view_y) + continue; + ddrval = pDD->SetDisplayMode(mode->x, mode->y, mode->depth); + if (ddrval == DD_OK) + break; + } + if (i == num_display_modes) + return Fail("Failed to find a suitable video mode."); + } + } + else { + + // Set normal mode. + ddrval = pDD->SetCooperativeLevel(hwnd, DDSCL_NORMAL); + if (ddrval != DD_OK) + return Fail("Failed to set normal cooperative level."); + } + + // Create the primary surface with one back buffer. + DDSURFACEDESC ddsd; + memset(&ddsd, 0, sizeof(ddsd)); + ddsd.dwSize = sizeof(ddsd); + ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT; + ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_FLIP | DDSCAPS_COMPLEX; + ddsd.dwBackBufferCount = 1; + ddrval = pDD->CreateSurface(&ddsd, &pPrimary, NULL); + if (ddrval != DD_OK) { + memset(&ddsd, 0, sizeof(ddsd)); + ddsd.dwSize = sizeof(ddsd); + ddsd.dwFlags = DDSD_CAPS; + ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; + ddrval = pDD->CreateSurface(&ddsd, &pPrimary, NULL); + if (ddrval != DD_OK) + return Fail("Failed to create primary surface."); + } + + if (ddsd.dwBackBufferCount == 1) { + DDSCAPS ddscaps; + ddscaps.dwCaps = DDSCAPS_BACKBUFFER; + ddrval = pPrimary->GetAttachedSurface(&ddscaps, &pBack); + if (ddrval != DD_OK) + return Fail("Failed to get attached surface."); + } + + // Create work surface. It displays correctly without + // this but doesn't handle clipping. We would have to + // do that ourselves. + memset(&ddsd, 0, sizeof(ddsd)); + ddsd.dwSize = sizeof(ddsd); + ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH; + ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN; + if (ThePrefs.SystemMemory) + ddsd.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY; + ddsd.dwHeight = DISPLAY_Y; + ddsd.dwWidth = DISPLAY_X; + ddrval = pDD->CreateSurface(&ddsd, &pWork, NULL); + if (ddrval != DD_OK) { + //return Fail("Failed to create work surface."); + Debug("cannot create work surface: %d\n", ddrval); + } + if (pWork) { + pWork->GetCaps(&ddsd.ddsCaps); + if (ddsd.ddsCaps.dwCaps & DDSCAPS_VIDEOMEMORY) + Debug("Work surface is in video memory.\n"); + else if (ddsd.ddsCaps.dwCaps & DDSCAPS_SYSTEMMEMORY) + Debug("Work surface is in system memory.\n"); + else + Debug("Work surface is in unknown memory.\n"); + } + + if (!full_screen) { + + // Create clipper object. + ddrval = pDD->CreateClipper(0, &pClipper, NULL); + if (ddrval != DD_OK) + return Fail("Failed to create direct draw clipper."); + ddrval = pClipper->SetHWnd(0, hwnd); + if (ddrval != DD_OK) + return Fail("Failed setting clipper window handle."); + ddrval = pPrimary->SetClipper(pClipper); + if (ddrval != DD_OK) + return Fail("Failed setting primary surface clipper."); + } + + // We need to use a 256 color palette otherwise we get an + // invalid pixel format error when trying to set the palette + // on a windowed surface. + PALETTEENTRY ape[256]; + HDC hdc = GetDC(NULL); + int entries = GetSystemPaletteEntries(hdc, 0, 256, ape); + ReleaseDC(NULL, hdc); + if (entries != 256) { + Debug("failed to get 256 system palette entries: %d (%d)\n", + entries, GetLastError()); + + // Build a 332 palette as the default. This makes it easy for + // other apps to find colors when they aren't the foreground. + for (int i = 0; i < 256; i++) { + ape[i].peRed = (BYTE)(((i >> 5) & 0x07) * 255 / 7); + ape[i].peGreen = (BYTE)(((i >> 2) & 0x07) * 255 / 7); + ape[i].peBlue = (BYTE)(((i >> 0) & 0x03) * 255 / 3); + ape[i].peFlags = 0; + } + } + + // Now override the first 16 entries with the C64 colors. + // If we were really obsessive we could try to find the + // nearest matches and replace them instead. + for (int i = 0; i < 16; i++) { + ape[i].peRed = palette_red[i]; + ape[i].peGreen = palette_green[i]; + ape[i].peBlue = palette_blue[i]; + ape[i].peFlags = 0; + } + + // Create the palette and set it on all surfaces. + ddrval = pDD->CreatePalette(DDPCAPS_8BIT, ape, &pPalette, NULL); + if (ddrval != DD_OK) + return Fail("Failed to create palette."); + if (!SetPalettes()) + return Fail("Failed to set palettes."); + if (!BuildColorTable()) + return Fail("Failed to build color table."); + + // Start with a clean slate. + if (!EraseSurfaces()) { + // Some display drivers have bugs, I guess. + // What's a little problem erasing gonna hurt. +#if 0 + return Fail("Failed to erase surfaces."); +#endif + } + + + return TRUE; +} + +BOOL C64Display::ResumeDirectDraw() +{ + if (!RestoreSurfaces()) + ResetDirectDraw(); + + return TRUE; +} + +BOOL C64Display::ResetDirectDraw() +{ + Pause(); + StopDirectDraw(); + StartDirectDraw(); + Resume(); + if (paused) + Update(); + + return TRUE; +} + +BOOL C64Display::StopDirectDraw() +{ + if (pDD != NULL) { + if (pClipper != NULL) { + pClipper->Release(); + pClipper = NULL; + } + if (pWork != NULL) { + pWork->Release(); + pWork = NULL; + } + if (pBack != NULL) { + pBack->Release(); + pBack = NULL; + } + if (pPrimary != NULL) { + pPrimary->Release(); + pPrimary = NULL; + } + if (pPalette != NULL) { + pPalette->Release(); + pPalette = NULL; + } + pDD->RestoreDisplayMode(); + pDD->Release(); + pDD = NULL; + } + + // Restore windowing state, window position, etc. + RestoreWindow(); + + return TRUE; +} + + +/* + * This function is called if the initialization function fails + */ +BOOL C64Display::Fail(const char *error) +{ + Debug(error); + Debug("\n"); + strcpy(failure_message, error); + return FALSE; +} + + +BOOL C64Display::SetPalettes() +{ + // Only try to set palettes when in 256 color mode. + HDC hdc = GetDC(NULL); + int depth = GetDeviceCaps(hdc, PLANES) * GetDeviceCaps(hdc, BITSPIXEL); + ReleaseDC(NULL, hdc); + if (depth != 8) + return TRUE; + + // Set palette on primary surface. + HRESULT ddrval = pPrimary->SetPalette(pPalette); + if (ddrval == DDERR_SURFACELOST) { + pPrimary->Restore(); + ddrval = pPrimary->SetPalette(pPalette); + } + if (ddrval == DDERR_NOT8BITCOLOR) + return TRUE; + if (ddrval != DD_OK) { + DebugResult("failed to set palette on primary", ddrval); + return FALSE; + } + + // Set palette on back surface. + if (pBack) { + FlipSurfaces(); + pPrimary->SetPalette(pPalette); + if (ddrval == DDERR_SURFACELOST) { + pPrimary->Restore(); + ddrval = pPrimary->SetPalette(pPalette); + } + if (ddrval != DD_OK) { + DebugResult("failed to set palette on back", ddrval); + return FALSE; + } + } + + // Set palette on work surface. + if (pWork) { + ddrval = pWork->SetPalette(pPalette); + if (ddrval == DDERR_SURFACELOST) { + pWork->Restore(); + ddrval = pWork->SetPalette(pPalette); + } + if (ddrval != DD_OK) { + DebugResult("failed to set palette on work", ddrval); + return FALSE; + } + } + + return TRUE; +} + +BOOL C64Display::BuildColorTable() +{ + if (!pPrimary) + return FALSE; + + // Determine the physical colors corresponding to the 16 C64 colors. + for (int j = 0; j < 16; j++) { + + // Compute the true color in RGB format. + int red = palette_red[j]; + int green = palette_green[j]; + int blue = palette_blue[j]; + COLORREF rgb = RGB(red, green, blue); + + // Set pixel(0, 0) to that value. + LPDIRECTDRAWSURFACE pSurface = pBack ? pBack : pPrimary; + HDC hdc; + if (pSurface->GetDC(&hdc) != DD_OK) + return Fail("Failed getting direct draw device context."); + COLORREF new_rgb = SetPixel(hdc, 0, 0, PALETTERGB(red, green, blue)); + Debug("new: %.8x, old %.8x\n", new_rgb, rgb); + pSurface->ReleaseDC(hdc); + + // Read the physical color from linear memory. + DDSURFACEDESC ddsd; + ddsd.dwSize = sizeof(ddsd); + HRESULT ddrval; + for (;;) { + ddrval = pSurface->Lock(NULL, &ddsd, 0, NULL); + if (ddrval != DDERR_WASSTILLDRAWING) + break; + } + if (ddrval != DD_OK) + return Fail("Failed to lock surface."); + colors_depth = ddsd.ddpfPixelFormat.dwRGBBitCount; + DWORD dw = *(DWORD *) ddsd.lpSurface; + Debug("DWORD = %.8x, depth = %d\n", dw, colors_depth); + if (colors_depth != 32) + dw &= (1 << colors_depth) - 1; + pSurface->Unlock(NULL); + + // Store the physical color in the colors array. + colors[j] = dw; + Debug("colors[%d] = %d\n", j, dw); + } + + // Replicate the physical colors into the rest of the color array. + for (int k = 16; k < 256; k++) + colors[k] = colors[k & 0x0f]; + + // Tell the VIC all about it; + if (!in_constructor) + TheC64->TheVIC->ReInitColors(); + + return TRUE; +} + +/* + * Redraw bitmap using double buffering when possible. + */ + +void C64Display::Update() +{ + TIMESCOPE(ts0, "Update"); + + //Debug("Display::Update\n"); + + if (full_screen && !active) + return; + + if (!pPrimary) + return; + +#ifdef WORKBUFFER_BITMAP + // Special case for using the workbuffer as a bitmap. + if (workbuffer_bitmap) { + if (workbuffer_locked) { + pWork->Unlock(NULL); + workbuffer_locked = FALSE; + } + RECT rc; + rc.left = (DISPLAY_X - view_x)/2; + rc.top = (DISPLAY_Y - view_y)/2 - 1; + if (rc.top < 0) + rc.top = 0; + rc.right = rc.left + view_x; + rc.bottom = rc.top + view_y; + CopySurface(rc); + draw_leds(); + return; + + } +#endif + + // Work on the backing surface unless there isn't one. + // We'll flip to it when we're done. + LPDIRECTDRAWSURFACE pSurface = pBack ? pBack : pPrimary; + + // Use a work surface when we have to: + // * when always copy is on + // * when possibly clipped + // * when streching + // * when partially offscreen + + if (!full_screen && pWork) { + if (ThePrefs.AlwaysCopy || !active || paused || +#if 0 + GetForegroundWindow() != hwnd || +#endif + rcWindow.right - rcWindow.left != view_x || + rcWindow.bottom - rcWindow.top != view_y || + rcWindow.left < rcScreen.left || + rcWindow.top < rcScreen.top || + rcWindow.right > rcScreen.right || + rcWindow.bottom > rcScreen.bottom) { + pSurface = pWork; + //Debug("using work surface\n"); + } + } + + // Lock the surface. + DDSURFACEDESC ddsd; + ddsd.dwSize = sizeof(ddsd); + + for (;;) { + HRESULT ddrval = pSurface->Lock(NULL, &ddsd, 0, NULL); + if (ddrval == DD_OK) + break; + if (ddrval == DDERR_SURFACELOST) { + Debug("surface lost\n"); + if (pSurface == pWork) + ddrval = pWork->Restore(); + else + ddrval = pPrimary->Restore(); + if (ddrval != DD_OK) { + DebugResult("surface Restore failed", ddrval); + return; + } + EraseSurfaces(); + BuildColorTable(); + } + else if (ddrval != DDERR_WASSTILLDRAWING) { + if (pWork && pSurface != pWork) + pSurface = pWork; + else { + DebugResult("surface Lock failed", ddrval); + return; + } + } + Debug("was still drawing\n"); + } + + // Compute the optimal placement of our window depending on + // the screen dimensions. + int x_off, y_off; + int x_beg, y_beg; + int x_siz, y_siz; + + // XXX: Do these calculations only when the parameters change. + if (full_screen) { + if (rcWindow.right >= view_x) { + x_off = (rcWindow.right - view_x)/2; + x_beg = (DISPLAY_X - view_x)/2; + x_siz = view_x; + } + else { + x_off = 0; + x_beg = (DISPLAY_X - rcWindow.right)/2; + x_siz = rcWindow.right; + } + if (rcWindow.bottom >= view_y) { + y_off = (rcWindow.bottom - view_y)/2; + y_beg = (DISPLAY_Y - view_y)/2 - 1; + y_siz = view_y; + } + else { + y_off = 0; + y_beg = (DISPLAY_Y - rcWindow.bottom)/2 - 1; + y_siz = rcWindow.bottom; + } + } + else { + if (pSurface == pWork) { + x_off = 0; + y_off = 0; + } + else { + x_off = rcWindow.left; + y_off = rcWindow.top; + } + x_beg = (DISPLAY_X - view_x)/2; + y_beg = (DISPLAY_Y - view_y)/2 - 1; + x_siz = view_x; + y_siz = view_y; + } + if (y_beg < 0) + y_beg = 0; + + // Translate chunky colors into the surface's linear memory. + int pitch = ddsd.lPitch; + int depth = ddsd.ddpfPixelFormat.dwRGBBitCount; + BYTE *surface = (BYTE *) ddsd.lpSurface + pitch*y_off + x_off*(depth/8); + BYTE *chunky = chunky_buf + DISPLAY_X*y_beg + x_beg; + + // These tight loops are where the display speed action is at. + // Note that MSVC optimizes out the mulitiplications and + // reverses the direction of the loop counters automatically. + if (depth == 8) { + + // Since the VIC is using our palette entries we just copy. + //TIMESCOPE(ts1, "hand blt 8"); + BYTE *scanline = surface; + BYTE *scanbuf = chunky; + //Debug("scanline = %8p, scanbuf = %8p\n", scanline, scanbuf); + for (int j = 0; j < y_siz; j++) { + memcpy(scanline, scanbuf, x_siz); + scanline += pitch; + scanbuf += DISPLAY_X; + } + } + else if (depth == 16) { + //TIMESCOPE(ts1, "hand blt 16"); + for (int j = 0; j < y_siz; j++) { + WORD *scanline = (WORD *) (surface + pitch*j); + BYTE *scanbuf = chunky + +DISPLAY_X*j; + for (int i = 0; i < x_siz; i++) + *scanline++ = (WORD) colors[*scanbuf++]; + } + } + else if (depth == 24) { + + // XXX: Works for little-endian only. + //TIMESCOPE(ts1, "hand blt 24"); + for (int j = 0; j < y_siz; j++) { + BYTE *scanline = surface + pitch*j; + BYTE *scanbuf = chunky + +DISPLAY_X*j; + for (int i = 0; i < x_siz; i++) { + *((DWORD *) scanline) = colors[*scanbuf++]; + scanline += 3; + } + } + } + else if (depth == 32) { + //TIMESCOPE(ts1, "hand blt 32"); + for (int j = 0; j < y_siz; j++) { + DWORD *scanline = (DWORD *) (surface + pitch*j); + BYTE *scanbuf = chunky + +DISPLAY_X*j; + for (int i = 0; i < x_siz; i++) + *scanline++ = colors[*scanbuf++]; + } + } + else + Debug("PixelCount not 8, 16, 24, or 32\n"); + + // Unlock the surface. + HRESULT ddrval = pSurface->Unlock(NULL); + if (ddrval != DD_OK) + Debug("DirectDrawSurface::Unlock failed\n"); + + // Now flip from the primary surface to the backing surface. + if (pSurface == pWork) + CopySurface(rcWork); + else if (full_screen && pBack) + FlipSurfaces(); + + // Update drive LEDs + draw_leds(); +} + + +BOOL C64Display::CopySurface(RECT &rcWork) +{ + // Copy work surface to primary. + for (;;) { + HRESULT ddrval = pPrimary->Blt(&rcWindow, pWork, &rcWork, DDBLT_WAIT, NULL); + if (ddrval == DD_OK) + break; + if (ddrval == DDERR_SURFACELOST) { + ddrval = pPrimary->Restore(); + if (ddrval != DD_OK) { + DebugResult("CopySurface Restore failed", ddrval); + return FALSE; + } + } + else if (ddrval != DDERR_WASSTILLDRAWING) { + DebugResult("CopySurface Blt failed", ddrval); + return FALSE; + } + } + return TRUE; +} + +BOOL C64Display::FlipSurfaces() +{ + // Flip buffers. + for (;;) { + HRESULT ddrval = pPrimary->Flip(NULL, 0); + if (ddrval == DD_OK) + break; + if (ddrval == DDERR_SURFACELOST) { + ddrval = pPrimary->Restore(); + if (ddrval != DD_OK) { + Debug("Restore failed\n"); + return FALSE; + } + } + else if (ddrval != DDERR_WASSTILLDRAWING) + return FALSE; + } + return TRUE; +} + +BOOL C64Display::EraseSurfaces() +{ + DDBLTFX ddbltfx; + ddbltfx.dwSize = sizeof(ddbltfx); + ddbltfx.dwFillColor = 0; + + // Erase the backing surface. + for (;;) { + if (!pBack) + break; + HRESULT ddrval = pBack->Blt(&rcWindow, NULL, NULL, DDBLT_COLORFILL, &ddbltfx); + + if (ddrval == DD_OK) + break; + + if (ddrval == DDERR_SURFACELOST) { + ddrval = pPrimary->Restore(); + if (ddrval != DD_OK) { + DebugResult("Restore primary failed", ddrval); + return FALSE; + } + } + else if (ddrval != DDERR_WASSTILLDRAWING) { + DebugResult("Blt erase back failed", ddrval); + return FALSE; + } + } + + // Erase the primary surface. + for (;;) { + HRESULT ddrval = pPrimary->Blt(&rcWindow, NULL, NULL, DDBLT_COLORFILL, &ddbltfx); + + if (ddrval == DD_OK) + break; + + if (ddrval == DDERR_SURFACELOST) { + ddrval = pPrimary->Restore(); + if (ddrval != DD_OK) { + DebugResult("Restore primary failed", ddrval); + return FALSE; + } + } + else if (ddrval != DDERR_WASSTILLDRAWING) { + DebugResult("Blt erase primary failed", ddrval); + return FALSE; + } + } + + return TRUE; +} + +BOOL C64Display::RestoreSurfaces() +{ + if (pPrimary) { + HRESULT ddrval = pPrimary->Restore(); + if (ddrval != DD_OK) + return FALSE; + } + + if (pWork) { + HRESULT ddrval = pWork->Restore(); + if (ddrval != DD_OK) + return FALSE; + } + + return TRUE; +} + +/* + * Draw LED bar at the bottom of the window + */ + +void C64Display::draw_led_bar() +{ + if (full_screen || !ThePrefs.ShowLEDs) + return; + + HDC hdc = GetDC(hwnd); + RECT rc; + GetClientRect(hwnd, &rc); + rc.top = rc.bottom - led_rows; + FillRect(hdc, &rc, led_brush); + if (rc.right - rc.left > view_x) + rc.left = rc.right - view_x; + SelectObject(hdc, led_font); + SetTextAlign(hdc, TA_TOP | TA_RIGHT); + SetBkMode(hdc, TRANSPARENT); + SetTextColor(hdc, (COLORREF) GetSysColor(COLOR_MENUTEXT)); + for (int i = 0; i < 4; i++) { + char str[128]; + if (rc.right - rc.left < view_x) + sprintf(str, "%d", i + 8); + else + sprintf(str, "Drive %d", i + 8); + RECT led; + led_rect(i, rc, led); + SelectObject(hdc, led_shadow); + MoveToEx(hdc, led.left - 1, led.bottom - 1, NULL); + LineTo(hdc, led.left - 1, led.top - 1); + LineTo(hdc, led.right, led.top - 1); + SelectObject(hdc, led_highlight); + LineTo(hdc, led.right, led.bottom); + LineTo(hdc, led.left - 2, led.bottom); + TextOut(hdc, led.left - 4, rc.top + 2, str, strlen(str)); + } + ReleaseDC(hwnd, hdc); + draw_leds(TRUE); +} + +/* + * Draw one LED + */ + +void C64Display::draw_leds(BOOL force) +{ + if (full_screen || !ThePrefs.ShowLEDs) + return; + + if (!force) { + int i; + for (i = 0; i < 4; i++) { + if (led_state[i] != old_led_state[i]) + break; + } + if (i == 4) + return; + } + + HDC hdc = GetDC(hwnd); + RECT rc; + GetClientRect(hwnd, &rc); + rc.top = rc.bottom - led_rows; + if (rc.right - rc.left > view_x) + rc.left = rc.right - view_x; + for (int i = 0; i < 4; i++) { + old_led_state[i] = led_state[i]; + HBRUSH brush; + switch (led_state[i]) { + case LED_OFF: brush = off_brush; break; + case LED_ERROR_OFF: brush = error_off_brush; break; + case LED_ON: brush = on_brush; break; + case LED_ERROR_ON: brush = error_on_brush; break; + } + RECT led; + led_rect(i, rc, led); + FillRect(hdc, &led, brush); + } + ReleaseDC(hwnd, hdc); +} + +void C64Display::led_rect(int n, RECT &rc, RECT &led) +{ + int x = rc.left + (rc.right - rc.left)*(n + 2)/5 - 20; + int y = rc.top + 2 + led_rows/3; + SetRect(&led, x, y, x + 13, y + led_rows/3); +} + +void C64Display::InsertNextDisk() +{ + if (strlen(ThePrefs.DrivePath[0]) > 4) { + char str[256]; + strcpy(str, ThePrefs.DrivePath[0]); + char *p = str + strlen(str) - 5; + + // If path matches "*.?64", increment character before the '.' + if (p[1] == '.' && p[3] == '6' && p[4] == '4') { + p[0]++; + + // If no such file exists, set character before the '.' to '1', 'a' or 'A' + FILE *file; + if ((file = fopen(str, "rb")) == NULL) { + if (isdigit(p[0])) + p[0] = '1'; + else if (isupper(p[0])) + p[0] = 'A'; + else + p[0] = 'a'; + } else + fclose(file); + + // Set new prefs + Pause(); + Prefs *prefs = new Prefs(ThePrefs); + strcpy(prefs->DrivePath[0], str); + TheC64->NewPrefs(prefs); + ThePrefs = *prefs; + delete prefs; + Resume(); + } + } +} + +BOOL C64Display::FileNameDialog(char *prefs_path, BOOL save) +{ + char filename[256]; + strcpy(filename, prefs_path); + OPENFILENAME ofn; + memset(&ofn, 0, sizeof(ofn)); + ofn.lStructSize = sizeof(ofn); + ofn.hwndOwner = hwnd; + ofn.hInstance = hInstance; + ofn.lpstrFilter = + "Preferences Files (*.fpr)\0*.fpr\0" + "All Files (*.*)\0*.*\0" + ; + ofn.lpstrCustomFilter = NULL; + ofn.nMaxCustFilter = 0; + ofn.nFilterIndex = 1; + ofn.lpstrFile = filename; + ofn.nMaxFile = sizeof(filename); + ofn.lpstrFileTitle = NULL; + ofn.nMaxFileTitle = 0; + ofn.lpstrInitialDir = NULL; + ofn.lpstrTitle = NULL; + ofn.Flags = OFN_EXPLORER | OFN_HIDEREADONLY | OFN_NOTESTFILECREATE | + OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR | OFN_SHAREAWARE; + ofn.nFileOffset = 0; + ofn.nFileExtension = 0; + ofn.lpstrDefExt = "fpr"; + ofn.lpfnHook = NULL; + ofn.lpTemplateName = NULL; + BOOL result = save ? GetSaveFileName(&ofn) : GetOpenFileName(&ofn); + if (result) { + char cwd[256]; + GetCurrentDirectory(sizeof(cwd), cwd); + int cwd_len = strlen(cwd); + if (cwd_len > 0 && cwd[cwd_len - 1] != '\\') { + strcat(cwd, "\\"); + cwd_len++; + } + if (strnicmp(filename, cwd, cwd_len) == 0) + strcpy(prefs_path, filename + cwd_len); + else + strcpy(prefs_path, filename); + } + return result; +} + +void C64Display::WindowTitle() +{ + // Show the program name, the current preferences file, + // and the paused state or the speedometer. + const char *prefs_path = TheApp->prefs_path; + int prefs_path_length = strlen(prefs_path); + if (prefs_path_length > 4 && + stricmp(prefs_path + prefs_path_length - 4, ".fpr") == 0) + prefs_path_length -= 4; + const char *info = NULL; + char tmp[128]; + if (waiting) + info = "PAUSED"; + else if (!ThePrefs.ShowLEDs && speed_index != 0) { + if (IsFrodoSC) + sprintf(tmp, "%.1f%%", speed_index); + else + sprintf(tmp, "%.0f%%", speed_index); + info = tmp; + } + const char *sep1 = info ? " (" : ""; + const char *sep2 = info ? ")" : ""; + char title[256]; + sprintf(title, "%s - %.*s%s%s%s", TITLE, + prefs_path_length, prefs_path, sep1, info ? info : "", sep2); + SetWindowText(hwnd, title); +} + +void C64Display::NewPrefs() +{ + // Resize the window to the new viewport while preserving + // as closely as possible the previous scaling factors. + RECT rc; + GetWindowRect(hwnd, &rc); + int x_nc = rc.right - rc.left - (rcWindow.right - rcWindow.left); + int y_nc = rc.bottom - rc.top - (rcWindow.bottom - rcWindow.top); + if (show_leds) + y_nc -= led_rows; + double x_scale = double(rcWindow.right - rcWindow.left)/view_x; + double y_scale = double(rcWindow.bottom - rcWindow.top)/view_y; + if (CalcViewPort() || show_leds != ThePrefs.ShowLEDs) { + show_leds = ThePrefs.ShowLEDs; + rc.right = int(rc.left + x_scale*view_x + x_nc); + rc.bottom = int(rc.top + y_scale*view_y + y_nc); + if (show_leds) + rc.bottom += led_rows; + ResizeWindow(WMSZ_BOTTOMRIGHT, &rc); + MoveWindow(hwnd, rc.left, rc.top, + rc.right - rc.left, + rc.bottom - rc.top, TRUE); + } + + // The prefs filename might have changed. + WindowTitle(); +} + +void C64Display::OfferSave() +{ + if (ThePrefs == ThePrefsOnDisk) + return; + const char *str = "Preferences have changed.\nSave preferences now?"; + int result = MessageBox(hwnd, str, "Frodo", MB_YESNO | MB_ICONQUESTION); + if (result == IDYES) + ThePrefs.Save(TheApp->prefs_path); +} + +void C64Display::Pause() +{ + // It's not safe to call this from the contructor or destructor. + if (in_constructor || in_destructor) + return; + + if (paused == 0) + TheC64->Pause(); + paused++; +} + +void C64Display::Resume() +{ + // It's not safe to call this from the contructor or destructor. + if (in_constructor || in_destructor) + return; + + if (paused > 0) { + paused--; + if (!paused) + TheC64->Resume(); + } + else + _ASSERTE(paused > 0); +} + +void C64Display::Quit() +{ + quit = 1; + TheC64->Quit(); +} diff --git a/Src/Display_svga.h b/Src/Display_svga.h new file mode 100644 index 0000000..156ddbd --- /dev/null +++ b/Src/Display_svga.h @@ -0,0 +1,569 @@ +/* + * Display_svga.h - C64 graphics display, emulator window handling, + * SVGAlib specific stuff + * + * Frodo (C) 1994-1997,2002-2009 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include + +#include "C64.h" + + +#define SCODE_CURSORBLOCKUP 103 /* Cursor key block. */ +#define SCODE_CURSORBLOCKLEFT 105 +#define SCODE_CURSORBLOCKRIGHT 106 +#define SCODE_CURSORBLOCKDOWN 108 + +#define SCODE_INSERT 110 +#define SCODE_HOME 102 +#define SCODE_PGUP 104 +#define SCODE_DELETE 111 +#define SCODE_END 107 +#define SCODE_PGDN 109 + +#define SCODE_NUMLOCK 69 + +#define SCODE_KEYPAD0 82 +#define SCODE_KEYPAD1 79 +#define SCODE_KEYPAD2 80 +#define SCODE_KEYPAD3 81 +#define SCODE_KEYPAD4 75 +#define SCODE_KEYPAD5 76 +#define SCODE_KEYPAD6 77 +#define SCODE_KEYPAD7 71 +#define SCODE_KEYPAD8 72 +#define SCODE_KEYPAD9 73 +#define SCODE_KEYPADENTER 96 +#define SCODE_KEYPADPLUS 78 +#define SCODE_KEYPADMINUS 74 +#define SCODE_KEYPADMULTIPLY 55 +#define SCODE_KEYPADDIVIDE 98 + +#define SCODE_Q 16 +#define SCODE_W 17 +#define SCODE_E 18 +#define SCODE_R 19 +#define SCODE_T 20 +#define SCODE_Y 21 +#define SCODE_U 22 +#define SCODE_I 23 +#define SCODE_O 24 +#define SCODE_P 25 + +#define SCODE_A 30 +#define SCODE_S 31 +#define SCODE_D 32 +#define SCODE_F 33 +#define SCODE_G 34 +#define SCODE_H 35 +#define SCODE_J 36 +#define SCODE_K 37 +#define SCODE_L 38 + +#define SCODE_Z 44 +#define SCODE_X 45 +#define SCODE_C 46 +#define SCODE_V 47 +#define SCODE_B 48 +#define SCODE_N 49 +#define SCODE_M 50 + +#define SCODE_ESCAPE 1 +#define SCODE_ENTER 28 +#define SCODE_RIGHTCONTROL 97 +#define SCODE_CONTROL 97 +#define SCODE_RIGHTALT 100 +#define SCODE_LEFTCONTROL 29 +#define SCODE_LEFTALT 56 +#define SCODE_SPACE 57 + +#define SCODE_F1 59 +#define SCODE_F2 60 +#define SCODE_F3 61 +#define SCODE_F4 62 +#define SCODE_F5 63 +#define SCODE_F6 64 +#define SCODE_F7 65 +#define SCODE_F8 66 +#define SCODE_F9 67 +#define SCODE_F10 68 + +#define SCODE_0 11 +#define SCODE_1 2 +#define SCODE_2 3 +#define SCODE_3 4 +#define SCODE_4 5 +#define SCODE_5 6 +#define SCODE_6 7 +#define SCODE_7 8 +#define SCODE_8 9 +#define SCODE_9 10 + +#define SCODE_LEFTSHIFT 42 +#define SCODE_RIGHTSHIFT 54 +#define SCODE_TAB 15 + +#define SCODE_F11 87 +#define SCODE_F12 88 +#define SCODE_NEXT 81 +#define SCODE_PRIOR 73 +#define SCODE_BS 14 + +#define SCODE_asciicircum 41 +#define SCODE_bracketleft 26 +#define SCODE_bracketright 27 +#define SCODE_comma 51 +#define SCODE_period 52 +#define SCODE_slash 53 +#define SCODE_semicolon 39 +#define SCODE_grave 40 +#define SCODE_minus 12 +#define SCODE_equal 13 +#define SCODE_numbersign 43 +#define SCODE_ltgt 86 +#define SCODE_scrolllock 70 + +static int bitdepth; +static char *bufmem; +static int hsize; +static vga_modeinfo modeinfo; +static char *linear_mem; + +static int keystate[256]; +static int f11pressed = 0, f12pressed = 0, quit = 0; +static int joystate = 0xFF; +static int numlock = 0; +static uint8 rev_matrix[8], key_matrix[8]; + +/* + C64 keyboard matrix: + + Bit 7 6 5 4 3 2 1 0 + 0 CUD F5 F3 F1 F7 CLR RET DEL + 1 SHL E S Z 4 A W 3 + 2 X T F C 6 D R 5 + 3 V U H B 8 G Y 7 + 4 N O K M 0 J I 9 + 5 , @ : . - L P + + 6 / ^ = SHR HOM ; * £ + 7 R/S Q C= SPC 2 CTL <- 1 +*/ +#define MATRIX(a,b) (((a) << 3) | (b)) +#define KEY_F10 512 +#define KEY_F11 513 +#define KEY_F12 514 + +#define KEY_FIRE 515 +#define KEY_JUP 516 +#define KEY_JDN 517 +#define KEY_JLF 518 +#define KEY_JRT 519 + +#define KEY_NUMLOCK 520 + +#define KEY_KPPLUS 521 +#define KEY_KPMINUS 522 +#define KEY_KPMULT 523 +#define KEY_KPDIV 524 + +static int scode2c64(int scancode) +{ + switch (scancode) { + case SCODE_asciicircum: return MATRIX(7,1); + case SCODE_KEYPAD0: return KEY_FIRE; + case SCODE_KEYPAD1: return -1; + case SCODE_KEYPAD2: return KEY_JDN; + case SCODE_KEYPAD3: return -1; + case SCODE_KEYPAD4: return KEY_JLF; + case SCODE_KEYPAD5: return -1; + case SCODE_KEYPAD6: return KEY_JRT; + case SCODE_KEYPAD7: return -1; + case SCODE_KEYPAD8: return KEY_JUP; + case SCODE_KEYPAD9: return -1; + + case SCODE_NUMLOCK: return KEY_NUMLOCK; + case SCODE_KEYPADMULTIPLY: return KEY_KPMULT; + case SCODE_KEYPADDIVIDE: return KEY_KPDIV; + case SCODE_KEYPADMINUS: return KEY_KPMINUS; + case SCODE_KEYPADPLUS: return KEY_KPPLUS; + case SCODE_KEYPADENTER: return MATRIX(0,1); + + case SCODE_F10: return KEY_F10; + case SCODE_F11: return KEY_F11; + case SCODE_F12: return KEY_F12; + + case SCODE_comma: return MATRIX(5,7); + case SCODE_period: return MATRIX(5,4); + + case SCODE_A: return MATRIX(1,2); + case SCODE_B: return MATRIX(3,4); + case SCODE_C: return MATRIX(2,4); + case SCODE_D: return MATRIX(2,2); + case SCODE_E: return MATRIX(1,6); + case SCODE_F: return MATRIX(2,5); + case SCODE_G: return MATRIX(3,2); + case SCODE_H: return MATRIX(3,5); + case SCODE_I: return MATRIX(4,1); + case SCODE_J: return MATRIX(4,2); + case SCODE_K: return MATRIX(4,5); + case SCODE_L: return MATRIX(5,2); + case SCODE_M: return MATRIX(4,4); + case SCODE_N: return MATRIX(4,7); + case SCODE_O: return MATRIX(4,6); + case SCODE_P: return MATRIX(5,1); + case SCODE_Q: return MATRIX(7,6); + case SCODE_R: return MATRIX(2,1); + case SCODE_S: return MATRIX(1,5); + case SCODE_T: return MATRIX(2,6); + case SCODE_U: return MATRIX(3,6); + case SCODE_V: return MATRIX(3,7); + case SCODE_W: return MATRIX(1,1); + case SCODE_X: return MATRIX(2,7); + case SCODE_Y: return MATRIX(3,1); + case SCODE_Z: return MATRIX(1,4); + + case SCODE_BS: return MATRIX(0,0); + case SCODE_DELETE: return MATRIX(0,0); + case SCODE_LEFTCONTROL: return MATRIX(7,2); + case SCODE_TAB: return MATRIX(7,1); + case SCODE_ENTER: return MATRIX(0,1); + case SCODE_SPACE: return MATRIX(7,4); + case SCODE_LEFTSHIFT: return MATRIX(1,7); + case SCODE_RIGHTSHIFT: return MATRIX(6,4); + case SCODE_ESCAPE: return MATRIX(7,7); + case SCODE_RIGHTCONTROL: + case SCODE_LEFTALT: + case SCODE_RIGHTALT: return MATRIX(7,5); + + case SCODE_INSERT: return MATRIX(0,0) | 0x80; + case SCODE_HOME: return MATRIX(6,3); + case SCODE_END: return MATRIX(6,0); + case SCODE_PGUP: return MATRIX(6,6); + case SCODE_PGDN: return MATRIX(6,5); + + case SCODE_CURSORBLOCKUP: return MATRIX(0,7)| 0x80; + case SCODE_CURSORBLOCKDOWN: return MATRIX(0,7); + case SCODE_CURSORBLOCKLEFT: return MATRIX(0,2) | 0x80; + case SCODE_CURSORBLOCKRIGHT: return MATRIX(0,2); + + case SCODE_F1: return MATRIX(0,4); + case SCODE_F2: return MATRIX(0,4) | 0x80; + case SCODE_F3: return MATRIX(0,5); + case SCODE_F4: return MATRIX(0,5) | 0x80; + case SCODE_F5: return MATRIX(0,6); + case SCODE_F6: return MATRIX(0,6) | 0x80; + case SCODE_F7: return MATRIX(0,3); + case SCODE_F8: return MATRIX(0,3) | 0x80; + + case SCODE_0: return MATRIX(4,3); + case SCODE_1: return MATRIX(7,0); + case SCODE_2: return MATRIX(7,3); + case SCODE_3: return MATRIX(1,0); + case SCODE_4: return MATRIX(1,3); + case SCODE_5: return MATRIX(2,0); + case SCODE_6: return MATRIX(2,3); + case SCODE_7: return MATRIX(3,0); + case SCODE_8: return MATRIX(3,3); + case SCODE_9: return MATRIX(4,0); + + case SCODE_bracketleft: return MATRIX(5,6); + case SCODE_bracketright: return MATRIX(6,1); + case SCODE_slash: return MATRIX(6,7); + case SCODE_semicolon: return MATRIX(5,5); + case SCODE_grave: return MATRIX(6,2); + case SCODE_numbersign: return MATRIX(6,5); + case SCODE_ltgt: return MATRIX(6,6); + case SCODE_minus: return MATRIX(5,0); + case SCODE_equal: return MATRIX(5,3); + } +} + +static void my_kbd_handler(int scancode, int newstate) +{ + int kc = scode2c64(scancode); +#if 0 + if (kc == -1) { + printf("%d\n",kc); + return; + } +#endif + if (newstate == KEY_EVENTPRESS) { + switch (kc) { + case KEY_KPPLUS: + if (ThePrefs.SkipFrames < 10) + ThePrefs.SkipFrames++; + break; + + case KEY_KPMINUS: + if (ThePrefs.SkipFrames > 1) + ThePrefs.SkipFrames--; + break; + + case KEY_KPMULT: + ThePrefs.LimitSpeed = !ThePrefs.LimitSpeed; + break; + + case KEY_KPDIV: + ThePrefs.JoystickSwap = !ThePrefs.JoystickSwap; + break; + + case KEY_NUMLOCK: + numlock = !numlock; + break; + + case KEY_F10: + quit = 1; + break; + + case KEY_F11: + f11pressed = 1; + break; + + case KEY_F12: + f12pressed = 1; + break; + + case KEY_FIRE: + joystate &= ~0x10; + break; + + case KEY_JDN: + joystate &= ~0x2; + break; + + case KEY_JUP: + joystate &= ~0x1; + break; + + case KEY_JLF: + joystate &= ~0x4; + break; + + case KEY_JRT: + joystate &= ~0x8; + break; + + default: + if (keystate[kc]) + break; + keystate[kc] = 1; + int c64_byte, c64_bit, shifted; + c64_byte = kc >> 3; + c64_bit = kc & 7; + shifted = kc & 128; + c64_byte &= 7; + if (shifted) { + key_matrix[6] &= 0xef; + rev_matrix[4] &= 0xbf; + } + key_matrix[c64_byte] &= ~(1 << c64_bit); + rev_matrix[c64_bit] &= ~(1 << c64_byte); + break; + } + } else { + switch (kc) { + case KEY_FIRE: + joystate |= 0x10; + break; + + case KEY_JDN: + joystate |= 0x2; + break; + + case KEY_JUP: + joystate |= 0x1; + break; + + case KEY_JLF: + joystate |= 0x4; + break; + + case KEY_JRT: + joystate |= 0x8; + break; + + default: + if (!keystate[kc]) + break; + keystate[kc] = 0; + int c64_byte, c64_bit, shifted; + c64_byte = kc >> 3; + c64_bit = kc & 7; + shifted = kc & 128; + c64_byte &= 7; + if (shifted) { + key_matrix[6] |= 0x10; + rev_matrix[4] |= 0x40; + } + key_matrix[c64_byte] |= (1 << c64_bit); + rev_matrix[c64_bit] |= (1 << c64_byte); + break; + } + + } +} + + +C64Display::C64Display(C64 *the_c64) : TheC64(the_c64) +{ + quit_requested = false; +} + + +C64Display::~C64Display() +{ + sleep(1); + vga_setmode(TEXT); +} + + +/* + * Prefs may have changed + */ + +void C64Display::NewPrefs(Prefs *prefs) +{ +} + + +void C64Display::Speedometer(int speed) +{ +} + + +int init_graphics(void) +{ + int vgamode = G640x480x256; + modeinfo = *vga_getmodeinfo (vgamode); + + if (vga_setmode(vgamode) < 0) { + sleep(1); + vga_setmode(TEXT); + fprintf(stderr, "SVGAlib doesn't like my video mode. Giving up.\n"); + return 0; + } + + hsize = modeinfo.linewidth; + if (hsize < DISPLAY_X) + hsize = DISPLAY_X; + + bufmem = NULL; + if ((modeinfo.flags & CAPABLE_LINEAR) && modeinfo.linewidth >= DISPLAY_X) { + if (vga_setlinearaddressing() != -1) { + linear_mem = (char *)vga_getgraphmem(); + printf("Using linear addressing: %p.\n", linear_mem); + bufmem = linear_mem; + } + } + if (bufmem == NULL) + bufmem = (char *)malloc(hsize * DISPLAY_Y); + + if (keyboard_init() != 0) + abort(); + keyboard_seteventhandler(my_kbd_handler); + /* keyboard_translatekeys(DONT_CATCH_CTRLC);*/ + + memset(keystate, 0, sizeof(keystate)); + memset(key_matrix, 0xFF, 8); + memset(rev_matrix, 0xFF, 8); + return 1; +} + + +void C64Display::Update(void) +{ + int y; + + if (linear_mem) + return; + + for (y = 0; y < DISPLAY_Y; y++) { + vga_drawscanline(y, (uint8 *)bufmem + hsize * y); + } +} + + +uint8 *C64Display::BitmapBase(void) +{ + return (uint8 *)bufmem; +} + + +int C64Display::BitmapXMod(void) +{ + return hsize; +} + + +void C64Display::PollKeyboard(uint8 *CIA_key_matrix, uint8 *CIA_rev_matrix, uint8 *joystick) +{ + keyboard_update(); + *joystick = joystate; + memcpy(CIA_key_matrix, key_matrix, 8); + memcpy(CIA_rev_matrix, rev_matrix, 8); + if (f11pressed) + TheC64->NMI(); + if (f12pressed) + TheC64->Reset(); + if (quit) + quit_requested = true; + f11pressed = f12pressed = 0; +} + + +/* + * Check if NumLock is down (for switching the joystick keyboard emulation) + */ + +bool C64Display::NumLock(void) +{ + return numlock; +} + + +/* + * Allocate C64 colors + */ + +static int colorval(int v) +{ + return ((v & 255)*0x01010101) >> 26; +} + +void C64Display::InitColors(uint8 *colors) +{ + int i; + + for (i=0; i< 256; i++) { + vga_setpalette(i, colorval(palette_red[i & 0x0f]), colorval(palette_green[i & 0x0f]), colorval(palette_blue[i & 0x0f])); + colors[i] = i; + } +} + + +/* + * Show a requester (error message) + */ + +long int ShowRequester(const char *a, const char *b, const char *) +{ + printf("%s: %s\n", a, b); + return 1; +} diff --git a/Src/Display_x.h b/Src/Display_x.h new file mode 100644 index 0000000..c3045e3 --- /dev/null +++ b/Src/Display_x.h @@ -0,0 +1,826 @@ +/* + * Display_x.h - C64 graphics display, emulator window handling, + * X specific stuff + * + * Frodo (C) 1994-1997,2002-2009 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "SAM.h" +#include "C64.h" + +#include +#include +#include +#include + +#if defined(X_USE_SHM) +#include +#include +#include +static XShmSegmentInfo shminfo; +#endif + +static Display *display; +static int screen; +static Window rootwin, mywin; + +static GC black_gc, led_gc; +static XColor black, fill_gray, shine_gray, shadow_gray, red, green; +static Colormap cmap; +static Font led_font; + +static XImage *img; +static Visual *vis; +static XVisualInfo visualInfo; +static int bitdepth; +static char *bufmem; +static int hsize; + +// For LED error blinking +static C64Display *c64_disp; +static struct sigaction pulse_sa; +static itimerval pulse_tv; + +// Keyboard and joystick +static int keystate[256]; +static int joystate = 0xFF; +static bool num_locked = false; + +static const long int eventmask = (KeyPressMask|KeyReleaseMask|FocusChangeMask|ExposureMask); + + +/* + C64 keyboard matrix: + + Bit 7 6 5 4 3 2 1 0 + 0 CUD F5 F3 F1 F7 CLR RET DEL + 1 SHL E S Z 4 A W 3 + 2 X T F C 6 D R 5 + 3 V U H B 8 G Y 7 + 4 N O K M 0 J I 9 + 5 , @ : . - L P + + 6 / ^ = SHR HOM ; * £ + 7 R/S Q C= SPC 2 CTL <- 1 +*/ + +#define MATRIX(a,b) (((a) << 3) | (b)) + +#define KEY_F9 512 +#define KEY_F10 513 +#define KEY_F11 514 +#define KEY_F12 515 + +#ifdef SUN +#define KEY_FIRE 58 +#define KEY_JU 135 +#define KEY_JD 7 +#define KEY_JL 130 +#define KEY_JR 2 +#else +#define KEY_FIRE 516 +#define KEY_JU 517 +#define KEY_JD 518 +#define KEY_JL 519 +#define KEY_JR 520 +#endif + +#define KEY_JUL 521 +#define KEY_JUR 522 +#define KEY_JDL 523 +#define KEY_JDR 524 + +#define KEY_KP_PLUS 525 +#define KEY_KP_MINUS 526 +#define KEY_KP_MULT 527 +#define KEY_NUM_LOCK 528 + + +/* + * Decode KeySyms. This function knows about all keys that + * are common between different keyboard languages. + */ + +static int kc_decode(KeySym ks) +{ + switch (ks) { + case XK_A: case XK_a: return MATRIX(1,2); + case XK_B: case XK_b: return MATRIX(3,4); + case XK_C: case XK_c: return MATRIX(2,4); + case XK_D: case XK_d: return MATRIX(2,2); + case XK_E: case XK_e: return MATRIX(1,6); + case XK_F: case XK_f: return MATRIX(2,5); + case XK_G: case XK_g: return MATRIX(3,2); + case XK_H: case XK_h: return MATRIX(3,5); + case XK_I: case XK_i: return MATRIX(4,1); + case XK_J: case XK_j: return MATRIX(4,2); + case XK_K: case XK_k: return MATRIX(4,5); + case XK_L: case XK_l: return MATRIX(5,2); + case XK_M: case XK_m: return MATRIX(4,4); + case XK_N: case XK_n: return MATRIX(4,7); + case XK_O: case XK_o: return MATRIX(4,6); + case XK_P: case XK_p: return MATRIX(5,1); + case XK_Q: case XK_q: return MATRIX(7,6); + case XK_R: case XK_r: return MATRIX(2,1); + case XK_S: case XK_s: return MATRIX(1,5); + case XK_T: case XK_t: return MATRIX(2,6); + case XK_U: case XK_u: return MATRIX(3,6); + case XK_V: case XK_v: return MATRIX(3,7); + case XK_W: case XK_w: return MATRIX(1,1); + case XK_X: case XK_x: return MATRIX(2,7); + case XK_Y: case XK_y: return MATRIX(3,1); + case XK_Z: case XK_z: return MATRIX(1,4); + + case XK_0: return MATRIX(4,3); + case XK_1: return MATRIX(7,0); + case XK_2: return MATRIX(7,3); + case XK_3: return MATRIX(1,0); + case XK_4: return MATRIX(1,3); + case XK_5: return MATRIX(2,0); + case XK_6: return MATRIX(2,3); + case XK_7: return MATRIX(3,0); + case XK_8: return MATRIX(3,3); + case XK_9: return MATRIX(4,0); + + case XK_space: return MATRIX(7,4); + case XK_grave: return MATRIX(7,1); + case XK_backslash: return MATRIX(6,6); + case XK_comma: return MATRIX(5,7); + case XK_period: return MATRIX(5,4); + + case XK_Escape: return MATRIX(7,7); + case XK_Return: return MATRIX(0,1); + case XK_BackSpace: case XK_Delete: return MATRIX(0,0); + case XK_Insert: return MATRIX(6,3); + case XK_Home: case XK_Help: return MATRIX(6,3); + case XK_End: return MATRIX(6,0); +#ifdef __hpux + case XK_Prior: return MATRIX(6,0); + case XK_Next: return MATRIX(6,5); +#else + case XK_Page_Up: return MATRIX(6,0); + case XK_Page_Down: return MATRIX(6,5); +#endif + case XK_Control_L: return MATRIX(7,2); + case XK_Control_R: return MATRIX(7,5); + case XK_Shift_L: return MATRIX(1,7); + case XK_Shift_R: return MATRIX(6,4); + case XK_Alt_L: return MATRIX(7,5); + case XK_Alt_R: return MATRIX(7,5); + + case XK_Up: return MATRIX(0,7)| 0x80; + case XK_Down: return MATRIX(0,7); + case XK_Left: return MATRIX(0,2) | 0x80; + case XK_Right: return MATRIX(0,2); + + case XK_F1: return MATRIX(0,4); + case XK_F2: return MATRIX(0,4) | 0x80; + case XK_F3: return MATRIX(0,5); + case XK_F4: return MATRIX(0,5) | 0x80; + case XK_F5: return MATRIX(0,6); + case XK_F6: return MATRIX(0,6) | 0x80; + case XK_F7: return MATRIX(0,3); + case XK_F8: return MATRIX(0,3) | 0x80; + + case XK_F9: return KEY_F9; + case XK_F10: return KEY_F10; + case XK_F11: return KEY_F11; + case XK_F12: return KEY_F12; + + /* You never know which Keysyms might be missing on some workstation + * This #ifdef should be enough. */ +#if defined(XK_KP_Prior) && defined(XK_KP_Left) && defined(XK_KP_Insert) && defined (XK_KP_End) + case XK_KP_0: case XK_KP_Insert: return KEY_FIRE; + case XK_KP_1: case XK_KP_End: return KEY_JDL; + case XK_KP_2: case XK_KP_Down: return KEY_JD; + case XK_KP_3: case XK_KP_Next: return KEY_JDR; + case XK_KP_4: case XK_KP_Left: return KEY_JL; + case XK_KP_5: case XK_KP_Begin: return KEY_FIRE; + case XK_KP_6: case XK_KP_Right: return KEY_JR; + case XK_KP_7: case XK_KP_Home: return KEY_JUL; + case XK_KP_8: case XK_KP_Up: return KEY_JU; + case XK_KP_9: case XK_KP_Prior: return KEY_JUR; +#else + case XK_KP_0: return KEY_FIRE; + case XK_KP_1: return KEY_JDL; + case XK_KP_2: return KEY_JD; + case XK_KP_3: return KEY_JDR; + case XK_KP_4: return KEY_JL; + case XK_KP_5: return KEY_FIRE; + case XK_KP_6: return KEY_JR; + case XK_KP_7: return KEY_JUL; + case XK_KP_8: return KEY_JU; + case XK_KP_9: return KEY_JUR; +#endif + + case XK_KP_Add: return KEY_KP_PLUS; + case XK_KP_Subtract: return KEY_KP_MINUS; + case XK_KP_Multiply: return KEY_KP_MULT; + case XK_KP_Divide: return MATRIX(6,7); + case XK_KP_Enter: return MATRIX(0,1); + +#ifdef SUN + case XK_Num_Lock: return KEY_NUM_LOCK; +#endif + } + return -1; +} + +static int decode_us(KeySym ks) +{ + switch(ks) { /* US specific */ + case XK_minus: return MATRIX(5,0); + case XK_equal: return MATRIX(5,3); + case XK_bracketleft: return MATRIX(5,6); + case XK_bracketright: return MATRIX(6,1); + case XK_semicolon: return MATRIX(5,5); + case XK_apostrophe: return MATRIX(6,2); + case XK_slash: return MATRIX(6,7); + } + + return -1; +} + +static int decode_de(KeySym ks) +{ + switch(ks) { /* DE specific */ + case XK_ssharp: return MATRIX(5,0); + case XK_apostrophe: return MATRIX(5,3); + case XK_Udiaeresis: case XK_udiaeresis: return MATRIX(5,6); + case XK_plus: return MATRIX(6,1); + case XK_Odiaeresis: case XK_odiaeresis: return MATRIX(5,5); + case XK_Adiaeresis: case XK_adiaeresis: return MATRIX(6,2); + case XK_numbersign: return MATRIX(6,5); + case XK_less: case XK_greater: return MATRIX(6,0); + case XK_minus: return MATRIX(6,7); + } + + return -1; +} + +static int keycode2c64(XKeyEvent *event) +{ + KeySym ks; + int as; + int index = 0; + + do { + ks = XLookupKeysym(event, index); + as = kc_decode(ks); + + if (as == -1) + as = KBD_LANG == 0 ? decode_us(ks) : decode_de(ks); + if (as != -1) + return as; + index++; + } while (ks != NoSymbol); + + return -1; +} + + +/* + * Display constructor: Draw Speedometer/LEDs in window + */ + +C64Display::C64Display(C64 *the_c64) : TheC64(the_c64) +{ + int i; + char str[16]; + + quit_requested = false; + + // LEDs off + for (i=0; i<4; i++) + led_state[i] = old_led_state[i] = LED_OFF; + + // Draw speedometer/LEDs + led_gc = XCreateGC(display, mywin, 0, 0); + XSetFont(display, led_gc, led_font); + + XSetForeground(display, led_gc, fill_gray.pixel); + XFillRectangle(display, mywin, led_gc, 0, DISPLAY_Y, DISPLAY_X-1, 16); + + XSetForeground(display, led_gc, shine_gray.pixel); + XDrawLine(display, mywin, led_gc, 0, DISPLAY_Y, DISPLAY_X-1, DISPLAY_Y); + for (i=0; i<5; i++) + XDrawLine(display, mywin, led_gc, DISPLAY_X*i/5, DISPLAY_Y, DISPLAY_X*i/5, DISPLAY_Y+14); + for (i=2; i<6; i++) { + XDrawLine(display, mywin, led_gc, DISPLAY_X*i/5-23, DISPLAY_Y+11, DISPLAY_X*i/5-9, DISPLAY_Y+11); + XDrawLine(display, mywin, led_gc, DISPLAY_X*i/5-9, DISPLAY_Y+11, DISPLAY_X*i/5-9, DISPLAY_Y+5); + } + + XSetForeground(display, led_gc, shadow_gray.pixel); + XDrawLine(display, mywin, led_gc, 0, DISPLAY_Y+15, DISPLAY_X-1, DISPLAY_Y+15); + for (i=1; i<6; i++) + XDrawLine(display, mywin, led_gc, DISPLAY_X*i/5-1, DISPLAY_Y+1, DISPLAY_X*i/5-1, DISPLAY_Y+15); + for (i=2; i<6; i++) { + XDrawLine(display, mywin, led_gc, DISPLAY_X*i/5-24, DISPLAY_Y+11, DISPLAY_X*i/5-24, DISPLAY_Y+4); + XDrawLine(display, mywin, led_gc, DISPLAY_X*i/5-24, DISPLAY_Y+4, DISPLAY_X*i/5-9, DISPLAY_Y+4); + } + + for (i=0; i<4; i++) { + sprintf(str, "Drive %d", i+8); + XSetForeground(display, led_gc, black.pixel); + XDrawString(display, mywin, led_gc, DISPLAY_X*(i+1)/5+8, DISPLAY_Y+12, str, strlen(str)); + draw_led(i, LED_OFF); + } + + // Start timer for LED error blinking + c64_disp = this; + pulse_sa.sa_handler = (void (*)(int))pulse_handler; + pulse_sa.sa_flags = SA_RESTART; + sigemptyset(&pulse_sa.sa_mask); + sigaction(SIGALRM, &pulse_sa, NULL); + pulse_tv.it_interval.tv_sec = 0; + pulse_tv.it_interval.tv_usec = 400000; + pulse_tv.it_value.tv_sec = 0; + pulse_tv.it_value.tv_usec = 400000; + setitimer(ITIMER_REAL, &pulse_tv, NULL); +} + + +/* + * Display destructor + */ + +C64Display::~C64Display() +{ + XAutoRepeatOn(display); + XSync(display, 0); +} + + +/* + * Prefs may have changed + */ + +void C64Display::NewPrefs(Prefs *prefs) +{ +} + + +/* + * Connect to X server and open window + */ + +int init_graphics(void) +{ + int i; + char *display_name = 0; + XSetWindowAttributes wattr; + XSizeHints *hints; + XColor exact_color; + int pixbytes; + + display = XOpenDisplay(display_name); + if (display == 0) { + fprintf(stderr, "Can't connect to X server %s\n", XDisplayName(display_name)); + return 0; + } + + screen = XDefaultScreen(display); + rootwin = XRootWindow(display, screen); + if (XMatchVisualInfo(display, screen, 8, PseudoColor, &visualInfo)) { + /* for our HP boxes */ + } else if (XMatchVisualInfo(display, screen, 8, GrayScale, &visualInfo)) { + } else { + fprintf(stderr, "Can't obtain appropriate X visual\n"); + return 0; + } + + vis = visualInfo.visual; + bitdepth = visualInfo.depth; + pixbytes = (bitdepth == 24 || bitdepth == 32 ? 4 : bitdepth == 12 || bitdepth == 16 ? 2 : 1); + fprintf(stderr, "Using %d bit visual\n", bitdepth); + + hsize = (DISPLAY_X + 3) & ~3; + +#if defined(X_USE_SHM) + img = XShmCreateImage(display, vis, bitdepth, ZPixmap, 0, &shminfo, + hsize, DISPLAY_Y); + + shminfo.shmid = shmget(IPC_PRIVATE, DISPLAY_Y * img->bytes_per_line, + IPC_CREAT | 0777); + shminfo.shmaddr = img->data = bufmem = (char *)shmat(shminfo.shmid, 0, 0); + shminfo.readOnly = False; + XShmAttach(display, &shminfo); + XSync(display,0); + /* now deleting means making it temporary */ + shmctl(shminfo.shmid, IPC_RMID, 0); +#else + bufmem = (char *)malloc(pixbytes * hsize * DISPLAY_Y); + img = XCreateImage(display, vis, bitdepth, ZPixmap, 0, bufmem, hsize, DISPLAY_Y, 32, 0); +#endif + + cmap = XCreateColormap(display, rootwin, vis, AllocNone); + + XParseColor(display, cmap, "#000000", &black); + if (!XAllocColor(display, cmap, &black)) + fprintf(stderr, "Whoops??\n"); + + wattr.event_mask = eventmask; + wattr.background_pixel = black.pixel; + wattr.backing_store = Always; + wattr.backing_planes = bitdepth; + wattr.border_pixmap = None; + wattr.border_pixel = black.pixel; + wattr.colormap = cmap; + + mywin = XCreateWindow(display, rootwin, 0, 0, DISPLAY_X, DISPLAY_Y + 16, 0, + bitdepth, InputOutput, vis, + CWEventMask|CWBackPixel|CWBorderPixel|CWBackingStore + |CWBackingPlanes|CWColormap, + &wattr); + XMapWindow(display, mywin); + XStoreName(display, mywin, "Frodo"); + + if ((hints = XAllocSizeHints()) != NULL) { + hints->min_width = DISPLAY_X; + hints->max_width = DISPLAY_X; + hints->min_height = DISPLAY_Y + 16; + hints->max_height = DISPLAY_Y + 16; + hints->flags = PMinSize | PMaxSize; + XSetWMNormalHints(display, mywin, hints); + XFree((char *)hints); + } + + black_gc = XCreateGC(display,mywin, 0, 0); + XSetForeground(display, black_gc, black.pixel); + + // Allocate colors for speedometer/LEDs + if (!XAllocNamedColor(display, cmap, "rgb:d0/d0/d0", &fill_gray, &exact_color)) + return 0; + if (!XAllocNamedColor(display, cmap, "rgb:e8/e8/e8", &shine_gray, &exact_color)) + return 0; + if (!XAllocNamedColor(display, cmap, "rgb:98/98/98", &shadow_gray, &exact_color)) + return 0; + if (!XAllocNamedColor(display, cmap, "rgb:f0/00/00", &red, &exact_color)) + return 0; + if (!XAllocNamedColor(display, cmap, "rgb:00/f0/00", &green, &exact_color)) + return 0; + + // Load font for speedometer/LED labels + led_font = XLoadFont(display, "-*-helvetica-medium-r-*-*-10-*"); + + for(i=0; i<256; i++) + keystate[i] = 0; + + return 1; +} + + +/* + * Redraw bitmap + */ + +void C64Display::Update(void) +{ + // Update C64 display + XSync(display, 0); +#if defined(X_USE_SHM) + XShmPutImage(display, mywin, black_gc, img, 0, 0, 0, 0, DISPLAY_X, DISPLAY_Y, 0); +#else + XPutImage(display, mywin, black_gc, img, 0, 0, 0, 0, DISPLAY_X, DISPLAY_Y); +#endif + + // Update drive LEDs + for (int i=0; i<4; i++) + if (led_state[i] != old_led_state[i]) { + draw_led(i, led_state[i]); + old_led_state[i] = led_state[i]; + } +} + + +/* + * Draw one drive LED + */ + +void C64Display::draw_led(int num, int state) +{ + switch (state) { + case LED_OFF: + case LED_ERROR_OFF: + XSetForeground(display, led_gc, black.pixel); + break; + case LED_ON: + XSetForeground(display, led_gc, green.pixel); + break; + case LED_ERROR_ON: + XSetForeground(display, led_gc, red.pixel); + break; + } + XFillRectangle(display, mywin, led_gc, DISPLAY_X*(num+2)/5-23, DISPLAY_Y+5, 14, 6); +} + + +/* + * LED error blink + */ + +void C64Display::pulse_handler(...) +{ + for (int i=0; i<4; i++) + switch (c64_disp->led_state[i]) { + case LED_ERROR_ON: + c64_disp->led_state[i] = LED_ERROR_OFF; + break; + case LED_ERROR_OFF: + c64_disp->led_state[i] = LED_ERROR_ON; + break; + } +} + + +/* + * Draw speedometer + */ + +void C64Display::Speedometer(int speed) +{ + static int delay = 0; + + if (delay >= 20) { + char str[16]; + sprintf(str, "%d%%", speed); + XSetForeground(display, led_gc, fill_gray.pixel); + XFillRectangle(display,mywin, led_gc, 1, DISPLAY_Y+1, DISPLAY_X/5-2, 14); + XSetForeground(display, led_gc, black.pixel); + XDrawString(display, mywin, led_gc, 24, DISPLAY_Y+12, str, strlen(str)); + delay = 0; + } else + delay++; +} + + +/* + * Return pointer to bitmap data + */ + +uint8 *C64Display::BitmapBase(void) +{ + return (uint8 *)bufmem; +} + + +/* + * Return number of bytes per row + */ + +int C64Display::BitmapXMod(void) +{ + return hsize; +} + + +/* + * Poll the keyboard + */ + +void C64Display::PollKeyboard(uint8 *key_matrix, uint8 *rev_matrix, uint8 *joystick) +{ + static bool auto_rep = true; + for(;;) { + XEvent event; + if (!XCheckMaskEvent(display, eventmask, &event)) + break; + + switch(event.type) { + + case KeyPress: { + int kc = keycode2c64((XKeyEvent *)&event); + if (kc == -1) + break; + switch (kc) { + + case KEY_F9: // F9: Invoke SAM + SAM(TheC64); + break; + + case KEY_F10: // F10: Quit + quit_requested = true; + break; + + case KEY_F11: // F11: NMI (Restore) + TheC64->NMI(); + break; + + case KEY_F12: // F12: Reset + TheC64->Reset(); + break; + + case KEY_NUM_LOCK: // NumLock: Toggle joyport + num_locked = true; + break; + + case KEY_FIRE: + joystate &= ~0x10; + break; + case KEY_JD: + joystate &= ~0x02; + break; + case KEY_JU: + joystate &= ~0x01; + break; + case KEY_JL: + joystate &= ~0x04; + break; + case KEY_JR: + joystate &= ~0x08; + break; + case KEY_JUL: + joystate &= ~0x05; + break; + case KEY_JUR: + joystate &= ~0x09; + break; + case KEY_JDL: + joystate &= ~0x06; + break; + case KEY_JDR: + joystate &= ~0x0a; + break; + + case KEY_KP_PLUS: // '+' on keypad: Increase SkipFrames + ThePrefs.SkipFrames++; + break; + + case KEY_KP_MINUS: // '-' on keypad: Decrease SkipFrames + if (ThePrefs.SkipFrames > 1) + ThePrefs.SkipFrames--; + break; + + case KEY_KP_MULT: // '*' on keypad: Toggle speed limiter + ThePrefs.LimitSpeed = !ThePrefs.LimitSpeed; + break; + + default: + if (keystate[kc]) + break; + keystate[kc] = 1; + int c64_byte, c64_bit, shifted; + c64_byte = kc >> 3; + c64_bit = kc & 7; + shifted = kc & 128; + c64_byte &= 7; + if (shifted) { + key_matrix[6] &= 0xef; + rev_matrix[4] &= 0xbf; + } + key_matrix[c64_byte] &= ~(1 << c64_bit); + rev_matrix[c64_bit] &= ~(1 << c64_byte); + break; + } + break; + } + + case KeyRelease: { + int kc = keycode2c64((XKeyEvent *)&event); + if (kc == -1) + break; + switch (kc) { + + case KEY_NUM_LOCK: + num_locked = false; + break; + + case KEY_FIRE: + joystate |= 0x10; + break; + case KEY_JD: + joystate |= 0x02; + break; + case KEY_JU: + joystate |= 0x01; + break; + case KEY_JL: + joystate |= 0x04; + break; + case KEY_JR: + joystate |= 0x08; + break; + case KEY_JUL: + joystate |= 0x05; + break; + case KEY_JUR: + joystate |= 0x09; + break; + case KEY_JDL: + joystate |= 0x06; + break; + case KEY_JDR: + joystate |= 0x0a; + break; + + default: + if (!keystate[kc]) + break; + keystate[kc] = 0; + int c64_byte, c64_bit, shifted; + c64_byte = kc >> 3; + c64_bit = kc & 7; + shifted = kc & 128; + c64_byte &= 7; + if (shifted) { + key_matrix[6] |= 0x10; + rev_matrix[4] |= 0x40; + } + key_matrix[c64_byte] |= (1 << c64_bit); + rev_matrix[c64_bit] |= (1 << c64_byte); + break; + } + } + + case FocusIn: + if (auto_rep) { + XAutoRepeatOff(display); + auto_rep = false; + } + break; + + case FocusOut: + if (!auto_rep) { + XAutoRepeatOn(display); + auto_rep = true; + } + break; + } + } + *joystick = joystate; +} + + +/* + * Check if NumLock is down (for switching the joystick keyboard emulation) + */ + +bool C64Display::NumLock(void) +{ + return num_locked; +} + + +/* + * Open/close joystick drivers given old and new state of + * joystick preferences + */ + +void C64::open_close_joysticks(int oldjoy1, int oldjoy2, int newjoy1, int newjoy2) +{ +} + + +/* + * Poll joystick port, return CIA mask + */ + +uint8 C64::poll_joystick(int port) +{ + return 0xff; +} + + +/* + * Allocate C64 colors + */ + +void C64Display::InitColors(uint8 *colors) +{ + int i; + XColor col; + char str[20]; + + for (i=0; i< 256; i++) { + sprintf(str, "rgb:%x/%x/%x", palette_red[i & 0x0f], palette_green[i & 0x0f], palette_blue[i & 0x0f]); + XParseColor(display, cmap, str, &col); + if (XAllocColor(display, cmap, &col)) + colors[i] = col.pixel; + else + fprintf(stderr, "Couldn't get all colors\n"); + } +} + + +/* + * Show a requester (error message) + */ + +long int ShowRequester(const char *a, const char *b, const char *) +{ + printf("%s: %s\n", a, b); + return 1; +} diff --git a/Src/FixPoint.h b/Src/FixPoint.h new file mode 100644 index 0000000..1a46f6e --- /dev/null +++ b/Src/FixPoint.h @@ -0,0 +1,433 @@ +/* + * FixPoint.h - Provides fixed point arithmetic (for use in SID.cpp) + * + * (C) 1997 Andreas Dehmel + * + * Frodo (C) 1994-1997,2002-2005 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * You need to define FIXPOINT_PREC (number of fractional bits) and + * ldSINTAB (ld of the size of the sinus table) as well M_PI + * _before_ including this file. + * Requires at least 32bit ints! + */ + + +#define FIXPOINT_BITS 32 +// Sign-bit +#define FIXPOINT_SIGN (1<<(FIXPOINT_BITS-1)) + + +/* + * Elementary functions for the FixPoint class + */ + +// Multiplies two fixpoint numbers, result is a fixpoint number. +static inline int fixmult(int x, int y) +{ + register unsigned int a,b; + register bool sign; + + sign = (x ^ y) < 0; + if (x < 0) {x = -x;} + if (y < 0) {y = -y;} + // a, b : integer part; x, y : fractional part. All unsigned now (for shift right)!!! + a = (((unsigned int)x) >> FIXPOINT_PREC); x &= ~(a << FIXPOINT_PREC); + b = (((unsigned int)y) >> FIXPOINT_PREC); y &= ~(b << FIXPOINT_PREC); + x = ((a*b) << FIXPOINT_PREC) + (a*y + b*x) + + ((unsigned int)((x*y) + (1 << (FIXPOINT_PREC-1))) >> FIXPOINT_PREC); +#ifdef FIXPOINT_SIGN + if (x < 0) {x ^= FIXPOINT_SIGN;} +#endif + if (sign) {x = -x;} + return(x); +} + + +// Multiplies a fixpoint number with an integer, result is a 32 bit (!) integer in +// contrast to using the standard member-functions which can provide only (32-FIXPOINT_PREC) +// valid bits. +static inline int intmult(int x, int y) // x is fixpoint, y integer +{ + register unsigned int i,j; + register bool sign; + + sign = (x ^ y) < 0; + if (x < 0) {x = -x;} + if (y < 0) {y = -y;} + i = (((unsigned int)x) >> 16); x &= ~(i << 16); // split both into 16.16 parts + j = (((unsigned int)y) >> 16); y &= ~(j << 16); +#if FIXPOINT_PREC <= 16 + // This '32' is independent of the number of bits used, it's due to the 16 bit shift + i = ((i*j) << (32 - FIXPOINT_PREC)) + ((i*y + j*x) << (16 - FIXPOINT_PREC)) + + ((unsigned int)(x*y + (1 << (FIXPOINT_PREC - 1))) >> FIXPOINT_PREC); +#else + { + register unsigned int h; + + h = (i*y + j*x); + i = ((i*j) << (32 - FIXPOINT_PREC)) + (h >> (FIXPOINT_PREC - 16)); + h &= ((1 << (FIXPOINT_PREC - 16)) - 1); x *= y; + i += (x >> FIXPOINT_PREC); x &= ((1 << FIXPOINT_PREC) - 1); + i += (((h + (x >> 16)) + (1 << (FIXPOINT_PREC - 17))) >> (FIXPOINT_PREC - 16)); + } +#endif +#ifdef FIXPOINT_SIGN + if (i < 0) {i ^= FIXPOINT_SIGN;} +#endif + if (sign) {i = -i;} + return(i); +} + + +// Computes the product of a fixpoint number with itself. +static inline int fixsquare(int x) +{ + register unsigned int a; + + if (x < 0) {x = -x;} + a = (((unsigned int)x) >> FIXPOINT_PREC); x &= ~(a << FIXPOINT_PREC); + x = ((a*a) << FIXPOINT_PREC) + ((a*x) << 1) + + ((unsigned int)((x*x) + (1 << (FIXPOINT_PREC-1))) >> FIXPOINT_PREC); +#ifdef FIXPOINT_SIGN + if (x < 0) {x ^= FIXPOINT_SIGN;} +#endif + return(x); +} + + +// Computes the square root of a fixpoint number. +static inline int fixsqrt(int x) +{ + register int test, step; + + if (x < 0) return(-1); if (x == 0) return(0); + step = (x <= (1<>1)); + test = 0; + while (step != 0) + { + register int h; + + h = fixsquare(test + step); + if (h <= x) {test += step;} + if (h == x) break; + step >>= 1; + } + return(test); +} + + +// Divides a fixpoint number by another fixpoint number, yielding a fixpoint result. +static inline int fixdiv(int x, int y) +{ + register int res, mask; + register bool sign; + + sign = (x ^ y) < 0; + if (x < 0) {x = -x;} + if (y < 0) {y = -y;} + mask = (1< y) {y <<= 1; mask <<= 1;} + while (mask != 0) + { + if (x >= y) {res |= mask; x -= y;} + mask >>= 1; y >>= 1; + } +#ifdef FIXPOINT_SIGN + if (res < 0) {res ^= FIXPOINT_SIGN;} +#endif + if (sign) {res = -res;} + return(res); +} + + + + + +/* + * The C++ Fixpoint class. By no means exhaustive... + * Since it contains only one int data, variables of type FixPoint can be + * passed directly rather than as a reference. + */ + +class FixPoint +{ +private: + int x; + +public: + FixPoint(void); + FixPoint(int y); + ~FixPoint(void); + + // conversions + int Value(void); + int round(void); + operator int(void); + + // unary operators + FixPoint sqrt(void); + FixPoint sqr(void); + FixPoint abs(void); + FixPoint operator+(void); + FixPoint operator-(void); + FixPoint operator++(void); + FixPoint operator--(void); + + // binary operators + int imul(int y); + FixPoint operator=(FixPoint y); + FixPoint operator=(int y); + FixPoint operator+(FixPoint y); + FixPoint operator+(int y); + FixPoint operator-(FixPoint y); + FixPoint operator-(int y); + FixPoint operator/(FixPoint y); + FixPoint operator/(int y); + FixPoint operator*(FixPoint y); + FixPoint operator*(int y); + FixPoint operator+=(FixPoint y); + FixPoint operator+=(int y); + FixPoint operator-=(FixPoint y); + FixPoint operator-=(int y); + FixPoint operator*=(FixPoint y); + FixPoint operator*=(int y); + FixPoint operator/=(FixPoint y); + FixPoint operator/=(int y); + FixPoint operator<<(int y); + FixPoint operator>>(int y); + FixPoint operator<<=(int y); + FixPoint operator>>=(int y); + + // conditional operators + bool operator<(FixPoint y); + bool operator<(int y); + bool operator<=(FixPoint y); + bool operator<=(int y); + bool operator>(FixPoint y); + bool operator>(int y); + bool operator>=(FixPoint y); + bool operator>=(int y); + bool operator==(FixPoint y); + bool operator==(int y); + bool operator!=(FixPoint y); + bool operator!=(int y); +}; + + +/* + * int gets treated differently according to the case: + * + * a) Equations (=) or condition checks (==, <, <= ...): raw int (i.e. no conversion) + * b) As an argument for an arithmetic operation: conversion to fixpoint by shifting + * + * Otherwise loading meaningful values into FixPoint variables would be very awkward. + */ + +FixPoint::FixPoint(void) {x = 0;} + +FixPoint::FixPoint(int y) {x = y;} + +FixPoint::~FixPoint(void) {;} + +inline int FixPoint::Value(void) {return(x);} + +inline int FixPoint::round(void) {return((x + (1 << (FIXPOINT_PREC-1))) >> FIXPOINT_PREC);} + +inline FixPoint::operator int(void) {return(x);} + + +// unary operators +inline FixPoint FixPoint::sqrt(void) {return(fixsqrt(x));} + +inline FixPoint FixPoint::sqr(void) {return(fixsquare(x));} + +inline FixPoint FixPoint::abs(void) {return((x < 0) ? -x : x);} + +inline FixPoint FixPoint::operator+(void) {return(x);} + +inline FixPoint FixPoint::operator-(void) {return(-x);} + +inline FixPoint FixPoint::operator++(void) {x += (1 << FIXPOINT_PREC); return x;} + +inline FixPoint FixPoint::operator--(void) {x -= (1 << FIXPOINT_PREC); return x;} + + +// binary operators +inline int FixPoint::imul(int y) {return(intmult(x,y));} + +inline FixPoint FixPoint::operator=(FixPoint y) {x = y.Value(); return x;} + +inline FixPoint FixPoint::operator=(int y) {x = y; return x;} + +inline FixPoint FixPoint::operator+(FixPoint y) {return(x + y.Value());} + +inline FixPoint FixPoint::operator+(int y) {return(x + (y << FIXPOINT_PREC));} + +inline FixPoint FixPoint::operator-(FixPoint y) {return(x - y.Value());} + +inline FixPoint FixPoint::operator-(int y) {return(x - (y << FIXPOINT_PREC));} + +inline FixPoint FixPoint::operator/(FixPoint y) {return(fixdiv(x,y.Value()));} + +inline FixPoint FixPoint::operator/(int y) {return(x/y);} + +inline FixPoint FixPoint::operator*(FixPoint y) {return(fixmult(x,y.Value()));} + +inline FixPoint FixPoint::operator*(int y) {return(x*y);} + +inline FixPoint FixPoint::operator+=(FixPoint y) {x += y.Value(); return x;} + +inline FixPoint FixPoint::operator+=(int y) {x += (y << FIXPOINT_PREC); return x;} + +inline FixPoint FixPoint::operator-=(FixPoint y) {x -= y.Value(); return x;} + +inline FixPoint FixPoint::operator-=(int y) {x -= (y << FIXPOINT_PREC); return x;} + +inline FixPoint FixPoint::operator*=(FixPoint y) {x = fixmult(x,y.Value()); return x;} + +inline FixPoint FixPoint::operator*=(int y) {x *= y; return x;} + +inline FixPoint FixPoint::operator/=(FixPoint y) {x = fixdiv(x,y.Value()); return x;} + +inline FixPoint FixPoint::operator/=(int y) {x /= y; return x;} + +inline FixPoint FixPoint::operator<<(int y) {return(x << y);} + +inline FixPoint FixPoint::operator>>(int y) {return(x >> y);} + +inline FixPoint FixPoint::operator<<=(int y) {x <<= y; return x;} + +inline FixPoint FixPoint::operator>>=(int y) {x >>= y; return x;} + + +// conditional operators +inline bool FixPoint::operator<(FixPoint y) {return(x < y.Value());} + +inline bool FixPoint::operator<(int y) {return(x < y);} + +inline bool FixPoint::operator<=(FixPoint y) {return(x <= y.Value());} + +inline bool FixPoint::operator<=(int y) {return(x <= y);} + +inline bool FixPoint::operator>(FixPoint y) {return(x > y.Value());} + +inline bool FixPoint::operator>(int y) {return(x > y);} + +inline bool FixPoint::operator>=(FixPoint y) {return(x >= y.Value());} + +inline bool FixPoint::operator>=(int y) {return(x >= y);} + +inline bool FixPoint::operator==(FixPoint y) {return(x == y.Value());} + +inline bool FixPoint::operator==(int y) {return(x == y);} + +inline bool FixPoint::operator!=(FixPoint y) {return(x != y.Value());} + +inline bool FixPoint::operator!=(int y) {return(x != y);} + + + +/* + * In case the first argument is an int (i.e. member-operators not applicable): + * Not supported: things like int/FixPoint. The same difference in conversions + * applies as mentioned above. + */ + + +// binary operators +inline FixPoint operator+(int x, FixPoint y) {return((x << FIXPOINT_PREC) + y.Value());} + +inline FixPoint operator-(int x, FixPoint y) {return((x << FIXPOINT_PREC) - y.Value());} + +inline FixPoint operator*(int x, FixPoint y) {return(x*y.Value());} + + +// conditional operators +inline bool operator==(int x, FixPoint y) {return(x == y.Value());} + +inline bool operator!=(int x, FixPoint y) {return(x != y.Value());} + +inline bool operator<(int x, FixPoint y) {return(x < y.Value());} + +inline bool operator<=(int x, FixPoint y) {return(x <= y.Value());} + +inline bool operator>(int x, FixPoint y) {return(x > y.Value());} + +inline bool operator>=(int x, FixPoint y) {return(x >= y.Value());} + + + +/* + * For more convenient creation of constant fixpoint numbers from constant floats. + */ + +#define FixNo(n) (FixPoint)((int)(n*(1<= 3*(1<= 2*(1<= (1<> (FIXPOINT_PREC - ldSINTAB - 1)) & ((1<<(ldSINTAB+2))-1); + FIXPOINT_SIN_COS_GENERIC +} + + +static inline FixPoint fixcos(FixPoint x) +{ + int angle = x; + + // cos(x) = sin(x+PI/2) + angle = (angle + (1<<(FIXPOINT_PREC-1)) >> (FIXPOINT_PREC - ldSINTAB - 1)) & ((1<<(ldSINTAB+2))-1); + FIXPOINT_SIN_COS_GENERIC +} + + + +static inline void InitFixSinTab(void) +{ + int i; + float step; + + for (i=0, step=0; i<(1<l5<-ed{(!4c{sR8&4pF2eEq`N)_CCTTWl9Rf@n(&Q6NMrYn~c3P zJKmFgd(upEb=_Ky_qlY>zFrr;nMt<`X@k~*M=;YBt2E#}9|J`y!%#L>QZOi`L}7}* zrpX!LXUWTd#5j(r=>u(r zJO1M6V_P>*#b5O>w0aou*h`=MK5;#L3?~<()A~yCo_+uhFBmi``lQBEC3U-$m_1A^N#z6Xko8 Z_Pvxr0&h>!_E~z_7C5)&nmzW`bKks8HVFU# literal 0 HcmV?d00001 diff --git a/Src/Frodo.rc b/Src/Frodo.rc new file mode 100644 index 0000000..3e0232f --- /dev/null +++ b/Src/Frodo.rc @@ -0,0 +1,291 @@ +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include +#include + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +FRODO_ICON ICON DISCARDABLE "Frodo.ico" + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDR_MAIN_MENU MENU DISCARDABLE +BEGIN + POPUP "&File" + BEGIN + MENUITEM "&New", ID_FILE_NEW + MENUITEM "&Open...", ID_FILE_OPEN + MENUITEM SEPARATOR + MENUITEM "&Save", ID_FILE_SAVE + MENUITEM "Save &As...", ID_FILE_SAVEAS + MENUITEM SEPARATOR + MENUITEM "E&xit\tF10", ID_FILE_EX + END + POPUP "&Tools" + BEGIN + MENUITEM "&Preferences...", ID_TOOLS_PREFERENCES + MENUITEM SEPARATOR + MENUITEM "P&ause\tPause", ID_TOOLS_PAUSE + MENUITEM "&Fullscreen\tAlt-Enter", ID_TOOLS_FULLSCREEN + MENUITEM "Reset &DirectDraw\tCtrl-Enter", ID_TOOLS_RESETDIRECTDRAW + MENUITEM SEPARATOR + MENUITEM "Reset &C64\tF12", ID_TOOLS_RESETC64 + MENUITEM "&Insert Next Disk", ID_TOOLS_INSERTNEXTDISK + MENUITEM SEPARATOR + MENUITEM "&SAM...", ID_TOOLS_SAM, GRAYED + END + POPUP "&Help" + BEGIN + MENUITEM "&Contents...", ID_HELP_CONTENTS + MENUITEM "&Keyboard...", ID_HELP_KEYBOARD + MENUITEM "&Settings...", ID_HELP_SETTINGS + MENUITEM SEPARATOR + MENUITEM "&About...", ID_HELP_ABOUT + END +END + + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include \r\n" + "#include \r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_PREFERENCES_STANDARD DIALOG DISCARDABLE 0, 0, 272, 221 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Standard" +FONT 8, "MS Sans Serif" +BEGIN + GROUPBOX "&Devices",IDC_STATIC,5,0,260,90 + RTEXT "8",IDC_STATIC,10,10,15,10 + EDITTEXT IDC_DEVICE8,35,10,160,14,ES_AUTOHSCROLL + PUSHBUTTON "Browse...",IDC_BROWSE8,205,10,50,14 + RTEXT "9",IDC_STATIC,10,30,15,10 + EDITTEXT IDC_DEVICE9,35,30,160,14,ES_AUTOHSCROLL + PUSHBUTTON "Browse...",IDC_BROWSE9,205,30,50,14 + RTEXT "10",IDC_STATIC,10,50,15,10 + EDITTEXT IDC_DEVICE10,35,50,160,14,ES_AUTOHSCROLL + PUSHBUTTON "Browse...",IDC_BROWSE10,205,50,50,14 + RTEXT "11",IDC_STATIC,10,70,15,10 + EDITTEXT IDC_DEVICE11,35,70,160,14,ES_AUTOHSCROLL + PUSHBUTTON "Browse...",IDC_BROWSE11,205,70,50,14 + GROUPBOX "&Parameters",IDC_STATIC,5,95,260,45 + LTEXT "Normal",IDC_STATIC,15,105,40,8 + EDITTEXT IDC_NORMAL,15,120,35,14,ES_AUTOHSCROLL + CONTROL "Spin1",IDC_NORMAL_SPIN,"msctls_updown32", + UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | + UDS_ARROWKEYS,40,120,11,14 + LTEXT "Bad Lines",IDC_STATIC,55,105,40,8 + EDITTEXT IDC_BADLINES,55,120,35,14,ES_AUTOHSCROLL + CONTROL "Spin1",IDC_BADLINES_SPIN,"msctls_updown32", + UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | + UDS_ARROWKEYS,80,120,11,14 + LTEXT "CIA",IDC_STATIC,95,105,35,8 + EDITTEXT IDC_CIA,95,120,35,14,ES_AUTOHSCROLL + CONTROL "Spin1",IDC_CIA_SPIN,"msctls_updown32",UDS_SETBUDDYINT | + UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,120,120, + 11,14 + LTEXT "Floppy",IDC_STATIC,135,105,40,8 + EDITTEXT IDC_FLOPPY,135,120,35,14,ES_AUTOHSCROLL + CONTROL "Spin1",IDC_FLOPPY_SPIN,"msctls_updown32", + UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | + UDS_ARROWKEYS,161,120,11,14 + LTEXT "Draw Every",IDC_STATIC,175,105,45,8 + EDITTEXT IDC_DRAWEVERY,175,120,35,14,ES_AUTOHSCROLL + CONTROL "Spin1",IDC_DRAWEVERY_SPIN,"msctls_updown32", + UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | + UDS_ARROWKEYS,201,120,11,14 + LTEXT "REU Size",IDC_STATIC,220,105,40,8 + COMBOBOX IDC_REUSIZE,220,120,35,95,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + GROUPBOX "&Options",IDC_STATIC,5,145,260,70 + CONTROL "Limit Speed",IDC_LIMITSPEED,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,15,155,80,10 + CONTROL "Sprites",IDC_SPRITES,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,101,155,80,10 + CONTROL "Sprite Collisions",IDC_SPRITECOLLISIONS,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,185,155,75,10 + CONTROL "Joystick 1",IDC_JOYSTICK1,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,15,170,80,10 + CONTROL "Joystick 2",IDC_JOYSTICK2,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,101,170,80,10 + CONTROL "Swap Joysticks",IDC_SWAPJOYSTICKS,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,185,170,75,10 + CONTROL "Fast Reset",IDC_FASTRESET,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,15,185,80,10 + CONTROL "CIA IRQ Hack",IDC_CIAIRQHACK,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,101,185,80,10 + CONTROL "Map /",IDC_MAPSLASH,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,185,185,75,10 + CONTROL "SID Emulation",IDC_SIDEMULATION,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,15,200,80,10 + CONTROL "SID Filters",IDC_SIDFILTERS,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,100,200,80,10 + CONTROL "1541 Emulation",IDC_1541EMULATION,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,185,200,75,10 +END + +IDD_PREFERENCES_WIN32 DIALOG DISCARDABLE 0, 0, 272, 221 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "WIN32" +FONT 8, "MS Sans Serif" +BEGIN + GROUPBOX "&Video",IDC_STATIC,5,0,260,60 + CONTROL "Fullscreen at startup",IDC_FULLSCREEN,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,15,15,85,10 + CONTROL "System Memory",IDC_SYSTEMMEMORY,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,15,30,85,10 + CONTROL "Always Copy",IDC_ALWAYSCOPY,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,15,45,85,10 + LTEXT "View&port",IDC_STATIC,110,20,70,8 + COMBOBOX IDC_VIEWPORT,110,31,70,184,CBS_DROPDOWN | + CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP + LTEXT "Display &Mode",IDC_STATIC,185,20,70,8 + COMBOBOX IDC_DISPLAYMODE,185,31,70,184,CBS_DROPDOWN | + CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP + GROUPBOX "&Window",IDC_STATIC,5,65,260,50 + CONTROL "Hide Cursor",IDC_HIDECURSOR,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,15,80,85,10 + CONTROL "System Keys",IDC_SYSTEMKEYS,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,15,95,85,10 + RTEXT "Scaling &Numerator",IDC_STATIC,110,75,95,10 + EDITTEXT IDC_SCALINGNUMERATOR,215,75,40,14,ES_AUTOHSCROLL + CONTROL "Spin1",IDC_SCALINGNUMERATOR_SPIN,"msctls_updown32", + UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | + UDS_ARROWKEYS,245,75,11,14 + RTEXT "Scaling &Denominator",IDC_STATIC,110,95,95,10 + EDITTEXT IDC_SCALINGDENOMINATOR,215,95,40,14,ES_AUTOHSCROLL + CONTROL "Spin1",IDC_SCALINGDENOMINATOR_SPIN,"msctls_updown32", + UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | + UDS_ARROWKEYS,245,95,11,14 + GROUPBOX "&Sound",IDC_STATIC,5,120,260,45 + CONTROL "DirectSound",IDC_DIRECTSOUND,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,15,135,85,10 + CONTROL "Exclusive",IDC_EXCLUSIVESOUND,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,15,150,85,10 + LTEXT "Latency Min",IDC_STATIC,105,130,50,8 + EDITTEXT IDC_LATENCYMIN,105,140,40,14,ES_AUTOHSCROLL + CONTROL "Spin1",IDC_LATENCYMIN_SPIN,"msctls_updown32", + UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | + UDS_ARROWKEYS,135,140,11,14 + LTEXT "Latency Max",IDC_STATIC,160,130,50,8 + EDITTEXT IDC_LATENCYMAX,160,140,40,14,ES_AUTOHSCROLL + CONTROL "Spin1",IDC_LATENCYMAX_SPIN,"msctls_updown32", + UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | + UDS_ARROWKEYS,190,140,11,14 + LTEXT "Latency Avg",IDC_STATIC,215,130,45,8 + EDITTEXT IDC_LATENCYAVG,215,140,40,14,ES_AUTOHSCROLL + CONTROL "Spin1",IDC_LATENCYAVG_SPIN,"msctls_updown32", + UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | + UDS_ARROWKEYS,245,140,11,14 + GROUPBOX "&General",IDC_STATIC,5,170,260,45 + CONTROL "Auto Pause",IDC_AUTOPAUSE,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,15,185,85,10 + CONTROL "Show LEDs",IDC_SHOWLEDS,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,105,185,85,10 + CONTROL "Preferences at startup",IDC_PREFSATSTARTUP,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,15,200,85,10 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + IDD_PREFERENCES_STANDARD, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 265 + TOPMARGIN, 7 + BOTTOMMARGIN, 214 + END + + IDD_PREFERENCES_WIN32, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 265 + TOPMARGIN, 7 + BOTTOMMARGIN, 214 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Cursor +// + +IDC_INVISIBLE CURSOR DISCARDABLE "Invisible.cur" +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/Src/Frodo.rsrc b/Src/Frodo.rsrc new file mode 100644 index 0000000000000000000000000000000000000000..873bb388b00b5aca0920a96d1c5c65f9e530b797 GIT binary patch literal 4506 zcmeHKyKWOf6rJ7JF$4#1f{`c?5k*Ah01^dC19{lSU}KAoBB=86k_4jI3IRFLtPr38 z5=}b92c)1tRFo7HDUe9viU#OFnj#-y3C^9JS+BEx6|n@uBkj&T_uSXaosB0~3md1` ztb4aB#={&pf=gT*FLOiRN0 zx$iM!0ZAQ&tVuZnSq)e*6k-ilczYvKO--295g64~+d*!N?2#P6Q^H3ROB1KR;x ze}S7*sYo{{9=fw4@hoAI6_4B1O&%(`*CkuuVzxF^R)r`G-DNzVZGG44|IUejH(fC{ zE+XGYf%~`VM%D-XeP6!4dYJbQpQPItVu-XO-#!fyrhL%tI5`)oE|DL)52V9U*C}HGu z)2ddg2oifbt~#YGBv#Nf!&l3sGA7M<4zd36c-9;p%jIUwRMJhEF_TGYqBBF7BvR@4 z%+#%1+;pwrH7%_UvjI2Ibo_>y%3gQPQWF4-S$KjnSbEf)nw$}YDlU_nP*%OtD&x08 z%7jeW$0BaZM0zY)t*iFx@?bSTvS7_Gmtu*vLcRcRrnMEOy^7jfU7K6g-oCfdz*iS` H#Y+DGX5k1{ literal 0 HcmV?d00001 diff --git a/Src/Frodo_GUI.tcl b/Src/Frodo_GUI.tcl new file mode 100755 index 0000000..4d76c0b --- /dev/null +++ b/Src/Frodo_GUI.tcl @@ -0,0 +1,1031 @@ +#!/usr/bin/wish + +# Frodo Tk GUI by Lutz Vieweg +# requires Tk >= 4.1 +# +# Frodo (C) 1994-1997,2002-2005 Christian Bauer +# +# 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 2 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +#package require Tk 4.1 + +set prefname "$env(HOME)/.frodorc" + +proc defaults {} { + global pref + set pref(NormalCycles) "63" + set pref(BadLineCycles) "23" + set pref(CIACycles) "63" + set pref(FloppyCycles) "64" + set pref(SkipFrames) "4" + set pref(DriveType8) "DIR" + set pref(DrivePath8) "./64prgs" + set pref(DriveType9) "D64" + set pref(DrivePath9) "./disk1.d64" + set pref(DriveType10) "DIR" + set pref(DrivePath10) "" + set pref(DriveType11) "DIR" + set pref(DrivePath11) "" + set pref(SIDType) "NONE" + set pref(SpritesOn) "TRUE" + set pref(SpriteCollisions) "TRUE" + set pref(Joystick1On) "FALSE" + set pref(Joystick2On) "TRUE" + set pref(JoystickSwap) "FALSE" + set pref(LimitSpeed) "FALSE" + set pref(FastReset) "FALSE" + set pref(CIAIRQHack) "FALSE" + set pref(MapSlash) "TRUE" + set pref(Emul1541Proc) "FALSE" + set pref(ShowOptions) "FALSE" + set pref(SIDFilters) "TRUE" +} + +proc s2bool { s } { + if {$s == "TRUE"} {return 1} + return 0 +} + +defaults + +if {![catch { set in [open $prefname] }]} { + while {![eof $in]} { + set line [gets $in] + if [regexp {^([^ ]*)[ ]*=[ ]*([^ ]*)$} $line range name val] { + switch -exact $name { + "NormalCycles" { + set pref(NormalCycles) $val + } + "BadLineCycles" { + set pref(BadLineCycles) $val + } + "CIACycles" { + set pref(CIACycles) $val + } + "FloppyCycles" { + set pref(FloppyCycles) $val + } + "SkipFrames" { + set pref(SkipFrames) $val + } + "DriveType8" { + set pref(DriveType8) $val + } + "DrivePath8" { + set pref(DrivePath8) $val + } + "DriveType9" { + set pref(DriveType9) $val + } + "DrivePath9" { + set pref(DrivePath9) $val + } + "DriveType10" { + set pref(DriveType10) $val + } + "DrivePath10" { + set pref(DrivePath10) $val + } + "DriveType11" { + set pref(DriveType11) $val + } + "DrivePath11" { + set pref(DrivePath11) $val + } + "SIDType" { + set pref(SIDType) $val + } + "SpritesOn" { + set pref(SpritesOn) [s2bool $val] + } + "SpriteCollisions" { + set pref(SpriteCollisions) [s2bool $val] + } + "Joystick1On" { + set pref(Joystick1On) [s2bool $val] + } + "Joystick2On" { + set pref(Joystick2On) [s2bool $val] + } + "JoystickSwap" { + set pref(JoystickSwap) [s2bool $val] + } + "LimitSpeed" { + set pref(LimitSpeed) [s2bool $val] + } + "FastReset" { + set pref(FastReset) [s2bool $val] + } + "CIAIRQHack" { + set pref(CIAIRQHack) [s2bool $val] + } + "MapSlash" { + set pref(MapSlash) [s2bool $val] + } + "Emul1541Proc" { + set pref(Emul1541Proc) [s2bool $val] + } + "ShowOptions" { + set pref(ShowOptions) [s2bool $val] + } + "SIDFilters" { + set pref(SIDFilters) [s2bool $val] + } + } + } + } +} + +proc bool2s { b } { + if {$b} { return "TRUE" } + return "FALSE" +} + +proc WritePrefs { } { + global pref prefname + + if [catch { set out [open "$prefname" "w"] }] { + puts stderr "unable to write preferences file '$prefname'" + } else { + puts $out "NormalCycles = $pref(NormalCycles)" + puts $out "BadLineCycles = $pref(BadLineCycles)" + puts $out "CIACycles = $pref(CIACycles)" + puts $out "FloppyCycles = $pref(FloppyCycles)" + puts $out "SkipFrames = $pref(SkipFrames)" + puts $out "DriveType8 = $pref(DriveType8)" + puts $out "DrivePath8 = $pref(DrivePath8)" + puts $out "DriveType9 = $pref(DriveType9)" + puts $out "DrivePath9 = $pref(DrivePath9)" + puts $out "DriveType10 = $pref(DriveType10)" + puts $out "DrivePath10 = $pref(DrivePath10)" + puts $out "DriveType11 = $pref(DriveType11)" + puts $out "DrivePath11 = $pref(DrivePath11)" + puts $out "SIDType = $pref(SIDType)" + puts $out "SpritesOn = [bool2s $pref(SpritesOn)]" + puts $out "SpriteCollisions = [bool2s $pref(SpriteCollisions)]" + puts $out "Joystick1On = [bool2s $pref(Joystick1On)]" + puts $out "Joystick2On = [bool2s $pref(Joystick2On)]" + puts $out "JoystickSwap = [bool2s $pref(JoystickSwap)]" + puts $out "LimitSpeed = [bool2s $pref(LimitSpeed)]" + puts $out "FastReset = [bool2s $pref(FastReset)]" + puts $out "CIAIRQHack = [bool2s $pref(CIAIRQHack)]" + puts $out "MapSlash = [bool2s $pref(MapSlash)]" + puts $out "Emul1541Proc = [bool2s $pref(Emul1541Proc)]" + puts $out "ShowOptions = [bool2s $pref(ShowOptions)]" + puts $out "SIDFilters = [bool2s $pref(SIDFilters)]" + + close $out + + puts -nonewline "p" + flush stdout + } +} + +proc Quit {} { + puts -nonewline "q" + flush stdout + exit 0 +} + +# ============================================================= + +frame .cmds +pack .cmds -expand false -fill both + +button .cmds.quit -text "Quit" -command "Quit" +pack .cmds.quit -side left -expand true -fill both + +button .cmds.reset -text "Reset" -command {puts -nonewline "r" ; flush stdout} +pack .cmds.reset -side left -expand true -fill both + +# ============================================================= + +proc Change { {dummy1 ""} {dummy2 ""}} { + WritePrefs +} + +#====================== begin of fs-box ======================== + +proc check_file_type {filename var} { + global pref + switch [file extension $filename] { + .d64 - .x64 { set $var D64; Change } + .lnx - .t64 { set $var T64; Change } + } +} + +proc fs_FileSelect {w {title {FileSelector}} {filter {*}} {name {}} typevar} { + global fs_priv + + if {[info commands tk_getOpenFile] != ""} { + switch $filter { + "*.{t64,lnx}" { + set types { + {{t64/lnx archive files} {.t64 .lnx}} + {{disk files} {.d64 .x64}} + {{all files} *} + } + } + "*.{d64,x64}" { + set types { + {{disk files} {.d64 .x64}} + {{t64/lnx archive files} {.t64 .lnx}} + {{all files} *} + } + } + default { + set types { + {{all files} *} + {{disk files} {.d64 .x64}} + {{t64/lnx archive files} {.t64 .lnx}} + } + } + } + if {[file isdir $name]} { + set name $name + } else { + set name "[file dirname $name]" + } + set fs_priv(result) [tk_getOpenFile -filetypes $types -initialdir $name] + check_file_type $fs_priv(result) $typevar + return $fs_priv(result) + } + + # remainder of the code is for pre-tk8.0 + + if {$name != ""} { + if {[file isdir $name]} { + set filter "$name/$filter" + } else { + set filter "[file dirname $name]/$filter" + } + } + + set fs_priv(window) $w + set fs_priv(filter) $filter + set fs_priv(name) "" + set fs_priv(result) "" + + # if this window already exists, destroy it + catch {destroy $w} + + # create new toplevel + toplevel $w + wm title $w $title + + # create frames + + # create filter-entry + frame $w.filter + pack $w.filter -side top -fill x + label $w.filter.lbl -text "Filter" + pack $w.filter.lbl -side top -anchor w + entry $w.filter.et -textvar fs_priv(filter) + pack $w.filter.et -side top -fill x -expand true + bind $w.filter.et { fs_newpath } + button $w.filter.up -text "Up" -command { + set f [file dirname $fs_priv(filter)] + set t [file tail $fs_priv(filter)] + if {$f == "."} {set f [pwd]} + set f [file dirname $f] + if {$f == "/"} {set f ""} + set fs_priv(filter) "$f/$t" + fs_newpath + } + pack $w.filter.up -side top -anchor w + + #create list-frames + frame $w.l + pack $w.l -side top -fill both -expand true + frame $w.l.d + pack $w.l.d -side left -fill both -expand true + frame $w.l.f + pack $w.l.f -side left -fill both -expand true + + fs_slist $w.l.d Directories single + + fs_slist $w.l.f Files single + bind $w.l.f.top.lst { + focus %W + global fs_priv + set sel [%W curselection] + if {$sel != ""} { + set fs_priv(name) [%W get [%W curselection]] + } + } + + bind $w.l.f.top.lst "$w.bts.ok flash; $w.bts.ok invoke" + bind $w.l.d.top.lst { + global fs_priv + set f [file dirname $fs_priv(filter)] + set t [file tail $fs_priv(filter)] + set d [%W get active] + switch $d { + "." { } + ".." { + if {$f == "."} {set f [pwd]} + set fs_priv(filter) "[file dirname $f]/$t" + } + default { + if {$f == "/"} {set f ""} + set fs_priv(filter) "$f/$d/$t" + } + } + fs_newpath + } + + #create name-entry + + frame $w.name + pack $w.name -side top -fill x + label $w.name.lbl -text "Filename" + pack $w.name.lbl -side top -anchor w + entry $w.name.et -textvar fs_priv(name) + pack $w.name.et -side top -fill x + bind $w.name.et { + global fs_priv + set fs_priv(filter) \ + "[file dirname $fs_priv(name)]/*[file extension $fs_priv(filter)]" + fs_newpath + } + bind $w.name.et { + global fs_priv + set n $fs_priv(name) + + if {[string index $n 0] != "/" && [string index $n 0] != "~"} { + set n "[file dirname $fs_priv(filter)]/$n" + } + + set n "[file dirname $n]/[file tail $n]" + + set fs_priv(result) $n + } + + # create buttons + frame $w.bts + pack $w.bts -side top -fill x + button $w.bts.ok -text "OK" -command { + global fs_priv + set w $fs_priv(window) + set sel [$w.l.f.top.lst curselection] + if {$sel != ""} { + set val [$w.l.f.top.lst get $sel] + set fs_priv(result) "[file dirname $fs_priv(filter)]/$val" + } + } + pack $w.bts.ok -side left -expand true + + button $w.bts.cancel -text "Cancel" -command { + global fs_priv + set fs_priv(result) "" + } + pack $w.bts.cancel -side left -expand true + + fs_newpath + + set oldfocus [focus] + grab $w + focus $w + + tkwait variable fs_priv(result) + + if { "$oldfocus" != "" } { focus $oldfocus } + + destroy $w + + check_file_type $fs_priv(result) $typevar + + return $fs_priv(result) +} + +proc fs_DirSelect {w {title {FileSelector}} {filter {*}} {name {}} } { + global fs_priv + + if {$name != ""} { + if {[file isdir $name]} { + set filter $name + } else { + set filter [file dirname $name] + } + } + + if {[info commands tk_chooseDirectory] != ""} { + return [tk_chooseDirectory -initialdir $filter] + } + + # remainder of the code is for pre-tk8.3 + + set fs_priv(window) $w + set fs_priv(filter) $filter + set fs_priv(name) $name + set fs_priv(result) "" + + # if this window already exists, destroy it + catch {destroy $w} + + # create new toplevel + toplevel $w + wm title $w $title + + # create frames + + # create filter-entry + frame $w.filter + pack $w.filter -side top -fill x + label $w.filter.lbl -text "Directory" + pack $w.filter.lbl -side top -anchor w + entry $w.filter.et -textvar fs_priv(filter) + pack $w.filter.et -side top -fill x -expand true + bind $w.filter.et { fs_dir_newpath } + button $w.filter.up -text "Up" -command { + set f $fs_priv(filter) + if {$f == "."} {set f [pwd]} + set fs_priv(filter) [file dirname $f] + fs_dir_newpath + } + pack $w.filter.up -side top -anchor w + + #create list-frames + frame $w.l + pack $w.l -side top -fill both -expand true + frame $w.l.d + pack $w.l.d -side left -fill both -expand true + + fs_slist $w.l.d "Sub Directories" single + + bind $w.l.d.top.lst { + global fs_priv + set f [string trimright $fs_priv(filter) /] + set d [%W get active] + switch $d { + "." { } + ".." { + if {$f == "."} {set f [pwd]} + set fs_priv(filter) [file dirname $f] + } + default { + if {$f == "/"} {set f ""} + set fs_priv(filter) "$f/$d" + } + } + fs_dir_newpath + } + + # create buttons + frame $w.bts + pack $w.bts -side top -fill x + button $w.bts.ok -text "OK" -command { + global fs_priv + set w $fs_priv(window) + set sel [$w.l.d.top.lst curselection] + if {$sel != ""} { + set val [$w.l.d.top.lst get $sel] + set fs_priv(result) "$fs_priv(filter)/$val" + } else { + set fs_priv(result) $fs_priv(filter) + } + } + pack $w.bts.ok -side left -expand true + + button $w.bts.cancel -text "Cancel" -command { + global fs_priv + set fs_priv(result) "" + } + pack $w.bts.cancel -side left -expand true + + fs_dir_newpath + + set oldfocus [focus] + grab $w + focus $w + + tkwait variable fs_priv(result) + + if { "$oldfocus" != "" } { focus $oldfocus } + + destroy $w + + return $fs_priv(result) +} + +proc fs_slist {w title mode} { + + if {$title != ""} { + label $w.lbl -text $title + pack $w.lbl -side top -anchor w + } + + frame $w.top + pack $w.top -side top -fill both -expand true + frame $w.bot + pack $w.bot -side top -fill x + + listbox $w.top.lst -relief sunken -bd 2 -yscrollcommand "$w.top.vs set" \ + -xscrollcommand "$w.bot.hs set" -selectmode $mode \ + -font -*-courier-medium-r-normal--14-*-*-*-*-*-* + pack $w.top.lst -side left -fill both -expand true + + scrollbar $w.top.vs -relief sunken -bd 2 -command "$w.top.lst yview" \ + -orient vertical + pack $w.top.vs -side left -fill y + + scrollbar $w.bot.hs -relief sunken -bd 2 -command "$w.top.lst xview" \ + -orient horizontal + pack $w.bot.hs -side left -fill x -expand true + + frame $w.bot.pad -width [expr [lindex [$w.top.vs config -width] 4] + \ + [lindex [$w.top.vs config -bd] 4] *2] + + pack $w.bot.pad -side left + +} + +proc fs_newpath {} { + + global fs_priv + + if {$fs_priv(filter) == ""} { + set fs_priv(filter) "./*" + } + + if {[file isdir $fs_priv(filter)]} { + set fs_priv(filter) "$fs_priv(filter)/*" + } + + set w $fs_priv(window) + set filter $fs_priv(filter) + + $w.l.d.top.lst delete 0 end + + $w.l.f.top.lst delete 0 end + + # update dirs + set dwidth 5 + set files [lsort "[glob -nocomplain "[file dirname $filter]/*" ] \ + [glob -nocomplain "[file dirname $filter]/.*"]" ] + foreach j $files { + if [file isdir $j] { + set name [file tail $j] + $w.l.d.top.lst insert end $name + if {[string length $name] > $dwidth} { set dwidth [string length $name] } + } + } + + #update files + set pos 0 + set fwidth 5 + set files [lsort [glob -nocomplain "$filter"]] + foreach j $files { + if [file isfile $j] { + set name [file tail $j] + $w.l.f.top.lst insert end $name + if {[string length $name] > $fwidth} { + set pos [string length [file dirname $j]] + set fwidth [string length $name] + } + } + } + + if {$fwidth < 20} { set fwidth 20 } + $w.l.f.top.lst configure -width [expr $fwidth+1] + + if {$dwidth < 20} { set dwidth 20 } + $w.l.d.top.lst configure -width [expr $dwidth+1] + + + if {$pos == 1} { set pos 0 } + + update idletasks + + $w.l.f.top.lst xview $pos + +} + +proc fs_dir_newpath {} { + + global fs_priv + + if {$fs_priv(filter) == ""} { + set fs_priv(filter) "." + } + + set w $fs_priv(window) + set filter $fs_priv(filter) + + $w.l.d.top.lst delete 0 end + + # update dirs + set dwidth 5 + set files [lsort "[glob -nocomplain "$filter/*" ] \ + [glob -nocomplain "$filter/.*"]" ] + foreach j $files { + if [file isdir $j] { + set name [file tail $j] + $w.l.d.top.lst insert end $name + if {[string length $name] > $dwidth} { set dwidth [string length $name] } + } + } + + + if {$dwidth < 20} { set dwidth 20 } + $w.l.d.top.lst configure -width [expr $dwidth+1] + + update idletasks + +} + +#====================== end of fs-box ======================== + +set num(1) "ABCDEFGHIJKLMNOPQRSTUVWXYZA" +set num(2) "abcdefghijklmnopqrstuvwxyza" +set num(3) "12345678901" + +proc NDname { name } { + global num + if [string match *.?64 $name] { + set len [string length $name] + set z [string index $name [expr $len-5]] + + foreach i "1 2 3" { + set c [string first $z $num($i)] + if {$c >= 0} { break } + } + incr c + set nname "[string range $name 0 [expr $len-6]][string index $num($i) $c][string range $name [expr $len-4] end]" + if [file exists $nname] { return $nname } + set nname "[string range $name 0 [expr $len-6]][string index $num($i) 0][string range $name [expr $len-4] end]" + if [file exists $nname] { return $nname } + } + return $name +} + +# =========================================================== + +frame .drives -borderwidth 0 +pack .drives -side top -expand false -fill x + +label .drives.l -text "Disk Drive Controls" -height 2 +pack .drives.l -side top -expand true -fill both + +checkbutton .drives.ef -text "Emulate 1541 CPU (Drive 8 only)" -variable pref(Emul1541Proc) -command "Change" -bg "dark grey" -anchor w +pack .drives.ef -side top -expand true -fill both + +frame .drives.d8 -borderwidth 0 +pack .drives.d8 -side top -expand true -fill x + +label .drives.d8.l -text "8" -width 2 +pack .drives.d8.l -side left -expand false +radiobutton .drives.d8.d64 -text "D64" -variable pref(DriveType8) -value "D64" \ + -bg "dark grey" -command { + set erg [fs_FileSelect .fs "Choose D64 image file" "*.{d64,x64}" $pref(DrivePath8) pref(DriveType8)] + if {$erg != ""} { set pref(DrivePath8) $erg ; Change } + } +pack .drives.d8.d64 -side left -expand false -fill y + +radiobutton .drives.d8.dir -text "DIR" -variable pref(DriveType8) -value "DIR" \ + -command { + set erg [fs_DirSelect .fs "Choose directory" "*" $pref(DrivePath8)] + if {$erg != ""} { set pref(DrivePath8) $erg ; Change } + } +pack .drives.d8.dir -side left -expand false + +radiobutton .drives.d8.t64 -text "T64" -variable pref(DriveType8) -value "T64" \ + -command { + set erg [fs_FileSelect .fs "Choose T64/LYNX archive file" "*.{t64,lnx}" $pref(DrivePath8) pref(DriveType8)] + if {$erg != ""} { set pref(DrivePath8) $erg ; Change } + } +pack .drives.d8.t64 -side left -expand false + +entry .drives.d8.name -textvar pref(DrivePath8) +bind .drives.d8.name "Change" +bind .drives.d8.name { + set erg [fs_FileSelect .fs "Choose A File" "*" $pref(DrivePath8) pref(DriveType8)] + if {$erg != ""} { set pref(DrivePath8) $erg ; Change } +} +pack .drives.d8.name -side left -expand true -fill x + +button .drives.d8.n -text "N" -command { set pref(DrivePath8) [NDname $pref(DrivePath8)]; Change } +pack .drives.d8.n -side left -expand false + +frame .drives.d9 +pack .drives.d9 -side top -expand true -fill x + +label .drives.d9.l -text "9" -width 2 +pack .drives.d9.l -side left -expand false +radiobutton .drives.d9.d64 -text "D64" -variable pref(DriveType9) -value "D64" \ + -command { + set erg [fs_FileSelect .fs "Choose D64 image file" "*.{d64,x64}" $pref(DrivePath9) pref(DriveType9)] + if {$erg != ""} { set pref(DrivePath9) $erg ; Change } + } +pack .drives.d9.d64 -side left -expand false + +radiobutton .drives.d9.dir -text "DIR" -variable pref(DriveType9) -value "DIR" \ + -command { + set erg [fs_DirSelect .fs "Choose directory" "*" $pref(DrivePath9)] + if {$erg != ""} { set pref(DrivePath9) $erg ; Change } + } +pack .drives.d9.dir -side left -expand false + +radiobutton .drives.d9.t64 -text "T64" -variable pref(DriveType9) -value "T64" \ + -command { + set erg [fs_FileSelect .fs "Choose T64/LYNX archive file" "*.{t64,lnx}" $pref(DrivePath9) pref(DriveType9)] + if {$erg != ""} { set pref(DrivePath9) $erg ; Change } + } +pack .drives.d9.t64 -side left -expand false + +entry .drives.d9.name -textvar pref(DrivePath9) +bind .drives.d9.name "Change" +bind .drives.d9.name { + set erg [fs_FileSelect .fs "Choose A File" "*" $pref(DrivePath9) pref(DriveType9)] + if {$erg != ""} { set pref(DrivePath9) $erg ; Change } +} +pack .drives.d9.name -side left -expand true -fill x + +button .drives.d9.n -text "N" -command { set pref(DrivePath9) [NDname $pref(DrivePath9)]; Change } +pack .drives.d9.n -side left -expand false + + +frame .drives.d10 +pack .drives.d10 -side top -expand true -fill x + +label .drives.d10.l -text "10" -width 2 +pack .drives.d10.l -side left -expand false +radiobutton .drives.d10.d64 -text "D64" -variable pref(DriveType10) -value "D64" \ + -command { + set erg [fs_FileSelect .fs "Choose D64 image file" "*.{d64,x64}" $pref(DrivePath10) pref(DriveType10)] + if {$erg != ""} { set pref(DrivePath10) $erg ; Change } + } +pack .drives.d10.d64 -side left -expand false + +radiobutton .drives.d10.dir -text "DIR" -variable pref(DriveType10) -value "DIR" \ + -command { + set erg [fs_DirSelect .fs "Choose directory" "*" $pref(DrivePath10)] + if {$erg != ""} { set pref(DrivePath10) $erg ; Change } + } +pack .drives.d10.dir -side left -expand false + +radiobutton .drives.d10.t64 -text "T64" -variable pref(DriveType10) -value "T64" \ + -command { + set erg [fs_FileSelect .fs "Choose T64/LYNX archive file" "*.{t64,lnx}" $pref(DrivePath10) pref(DriveType10)] + if {$erg != ""} { set pref(DrivePath10) $erg ; Change } + } +pack .drives.d10.t64 -side left -expand false + +entry .drives.d10.name -textvar pref(DrivePath10) +bind .drives.d10.name "Change" +bind .drives.d10.name { + set erg [fs_FileSelect .fs "Choose A File" "*" $pref(DrivePath10) pref(DriveType10)] + if {$erg != ""} { set pref(DrivePath10) $erg ; Change } +} +pack .drives.d10.name -side left -expand true -fill x + +button .drives.d10.n -text "N" -command { set pref(DrivePath10) [NDname $pref(DrivePath10)]; Change } +pack .drives.d10.n -side left -expand false + + +frame .drives.d11 +pack .drives.d11 -side top -expand true -fill x + +label .drives.d11.l -text "11" -width 2 +pack .drives.d11.l -side left -expand false +radiobutton .drives.d11.d64 -text "D64" -variable pref(DriveType11) -value "D64" \ + -command { + set erg [fs_FileSelect .fs "Choose D64 image file" "*.{d64,x64}" $pref(DrivePath11) pref(DriveType11)] + if {$erg != ""} { set pref(DrivePath11) $erg ; Change } + } +pack .drives.d11.d64 -side left -expand false + +radiobutton .drives.d11.dir -text "DIR" -variable pref(DriveType11) -value "DIR" \ + -command { + set erg [fs_DirSelect .fs "Choose directory" "*" $pref(DrivePath11)] + if {$erg != ""} { set pref(DrivePath11) $erg ; Change } + } +pack .drives.d11.dir -side left -expand false + +radiobutton .drives.d11.t64 -text "T64" -variable pref(DriveType11) -value "T64" \ + -command { + set erg [fs_FileSelect .fs "Choose T64/LYNX archive file" "*.{t64,lnx}" $pref(DrivePath11) pref(DriveType11)] + if {$erg != ""} { set pref(DrivePath11) $erg ; Change } + } +pack .drives.d11.t64 -side left -expand false + +entry .drives.d11.name -textvar pref(DrivePath11) +bind .drives.d11.name "Change" +bind .drives.d11.name { + set erg [fs_FileSelect .fs "Choose A File" "*" $pref(DrivePath11) pref(DriveType11)] + if {$erg != ""} { set pref(DrivePath11) $erg ; Change } +} +pack .drives.d11.name -side left -expand true -fill x + +button .drives.d11.n -text "N" -command { set pref(DrivePath11) [NDname $pref(DrivePath11)]; Change } +pack .drives.d11.n -side left -expand false + + +# ============================================================= + +global show_options_string + +checkbutton .more_options -borderwidth 3 -relief raised -textvariable show_options_string -variable pref(ShowOptions) -command "Change" +pack .more_options -side top -expand false -fill x + +frame .nums -borderwidth 3 -relief raised + +scale .nums.nc -from 1 -to 200 -orient horizontal -variable pref(NormalCycles) \ + -label "Normal Cycles" +pack .nums.nc -side top -expand false -fill x + +scale .nums.bc -from 1 -to 200 -orient horizontal -variable pref(BadLineCycles) \ + -label "Bad Line Cycles" +pack .nums.bc -side top -expand false -fill x + +scale .nums.cc -from 1 -to 200 -orient horizontal -variable pref(CIACycles) \ + -label "CIA Cycles" +pack .nums.cc -side top -expand false -fill x + +scale .nums.fc -from 1 -to 200 -orient horizontal -variable pref(FloppyCycles) \ + -label "Floppy Cycles" +pack .nums.fc -side top -expand false -fill x + +scale .nums.sf -from 1 -to 10 -orient horizontal -variable pref(SkipFrames) \ + -label "Skip Frames" +pack .nums.sf -side top -expand false -fill x + +# ============================================================= + +frame .bools1 -borderwidth 3 -relief raised + +frame .bools1.sprites +pack .bools1.sprites -side left -expand true -fill both + +checkbutton .bools1.sprites.o -text "Sprites" -variable pref(SpritesOn) -command "Change" +pack .bools1.sprites.o -anchor nw -expand false -fill y + +checkbutton .bools1.sprites.c -text "Sprite Collisions" \ + -variable pref(SpriteCollisions) -command "Change" +pack .bools1.sprites.c -anchor nw -expand false -fill y + + +frame .bools1.joy +pack .bools1.joy -side left -expand true -fill both + +checkbutton .bools1.joy.j1 -text "Joy 1" -variable pref(Joystick1On) -command "Change" +pack .bools1.joy.j1 -anchor nw -expand false -fill y + +checkbutton .bools1.joy.j2 -text "Joy 2" -variable pref(Joystick2On) -command "Change" +pack .bools1.joy.j2 -anchor nw -expand false -fill y + +checkbutton .bools1.joy.swap -text "Swap 1<->2" -variable pref(JoystickSwap) -command "Change" +pack .bools1.joy.swap -anchor nw -expand false -fill y + + +frame .bools2 -borderwidth 3 -relief raised + +frame .bools2.m1 +pack .bools2.m1 -side left -expand true -fill both + +checkbutton .bools2.m1.ls -text "Limit Speed" -variable pref(LimitSpeed) -command "Change" +pack .bools2.m1.ls -anchor nw -expand false -fill y + +checkbutton .bools2.m1.fr -text "Fast Reset" -variable pref(FastReset) -command "Change" +pack .bools2.m1.fr -anchor nw -expand false -fill y + + +frame .bools2.m2 +pack .bools2.m2 -side left -expand true -fill both + +checkbutton .bools2.m2.ch -text "CIA IRQ Hack" -variable pref(CIAIRQHack) -command "Change" +pack .bools2.m2.ch -anchor nw -expand false -fill y + +checkbutton .bools2.m2.ms -text "Map '/'" -variable pref(MapSlash) -command "Change" +pack .bools2.m2.ms -anchor nw -expand false -fill y + + +frame .bools4 -relief raised -borderwidth 3 + +frame .bools4.st +pack .bools4.st -side left -expand true -fill both + +label .bools4.st.l -text "SID Emulation" +pack .bools4.st.l -anchor nw +radiobutton .bools4.st.none -text "None" -variable pref(SIDType) -value "NONE" \ + -command {Change} +pack .bools4.st.none -anchor nw + +radiobutton .bools4.st.digi -text "Digital" -variable pref(SIDType) -value "DIGITAL" \ + -command {Change} +pack .bools4.st.digi -anchor nw + +frame .bools4.sf +pack .bools4.sf -side left -expand true -fill both + +checkbutton .bools4.sf.sf -text "SID Filters" -variable pref(SIDFilters) -command "Change" +pack .bools4.sf.sf -side top -expand false -fill y + + +# ============================================================= + +frame .pcmd +pack .pcmd -side top -expand false -fill both + +button .pcmd.apply -text "Apply" -command "Change" +pack .pcmd.apply -side left -expand true -fill both + +button .pcmd.default -text "Defaults" -command "defaults ; Change" +pack .pcmd.default -side left -expand false -fill both + +# ============================================================= + +set ledcolors(0) "#d9d9d9" +set ledcolors(1) "red" +set ledcolors(2) "brown" + +proc ListenToFrodo {} { + set line [gets stdin] + set cmd [lindex $line 0] + switch -exact $cmd { + "speed" { + .speed.v configure -text "[lindex $line 1]%" + } + "ping" { + puts -nonewline "o" + flush stdout + } + "quit" { + exit 0 + } + "leds" { + global ledcolors + .drives.d8.l configure -background $ledcolors([lindex $line 1]) + .drives.d9.l configure -background $ledcolors([lindex $line 2]) + .drives.d10.l configure -background $ledcolors([lindex $line 3]) + .drives.d11.l configure -background $ledcolors([lindex $line 4]) + } + default { + puts stderr "line = $line" + } + } +} + + +proc set_Emul1541Proc args { + global pref + + if {$pref(Emul1541Proc)} { + set state disabled + set pref(DriveType8) "D64" + } else { + set state normal + } + .drives.d8.dir configure -state $state + .drives.d8.t64 configure -state $state + foreach i {9 10 11} { + .drives.d${i}.d64 configure -state $state + .drives.d${i}.dir configure -state $state + .drives.d${i}.t64 configure -state $state + .drives.d${i}.name configure -state $state + .drives.d${i}.n configure -state $state + } +} + +proc set_ShowOptions args { + global pref show_options_string + + if {$pref(ShowOptions)} { + pack .nums -side top -expand false -fill x -after .more_options + pack .bools1 -side top -expand true -fill both -after .nums + pack .bools2 -side top -expand true -fill both -after .bools1 + pack .bools4 -side top -expand true -fill both -after .bools2 + set show_options_string "Hide Advanced Options" + } else { + pack forget .nums .bools1 .bools2 .bools4 + set show_options_string "Show Advanced Options" + } +} + +fileevent stdin readable { ListenToFrodo } + +# ============================================================= + +wm title . "Frodo Preferences Menu" + +# set trace and trigger it now +trace variable pref(Emul1541Proc) w set_Emul1541Proc +set pref(Emul1541Proc) $pref(Emul1541Proc) + +# set trace and trigger it now +trace variable pref(ShowOptions) w set_ShowOptions +set pref(ShowOptions) $pref(ShowOptions) + +tkwait window . diff --git a/Src/IEC.cpp b/Src/IEC.cpp new file mode 100644 index 0000000..733a23f --- /dev/null +++ b/Src/IEC.cpp @@ -0,0 +1,953 @@ +/* + * IEC.cpp - IEC bus routines, 1541 emulation (DOS level) + * + * Frodo (C) 1994-1997,2002-2005 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * Notes: + * ------ + * + * - There are three kinds of devices on the IEC bus: controllers, + * listeners and talkers. We are always the controller and we + * can additionally be either listener or talker. There can be + * only one listener and one talker active at the same time (the + * real IEC bus allows multiple listeners, but we don't). + * - There is one Drive object for every emulated drive (8..11). + * A pointer to one of them is stored in "listener"/"talker" + * when talk()/listen() is called and is used by the functions + * called afterwards. + * - The Drive objects have four virtual functions so that the + * interface to them is independent of their implementation: + * Open() opens a channel + * Close() closes a channel + * Read() reads from a channel + * Write() writes to a channel + * - The EOI/EOF signal is special on the IEC bus in that it is + * Sent before the last byte, not after it. + */ + +#include "sysdeps.h" + +#include "IEC.h" +#include "1541fs.h" +#include "1541d64.h" +#include "1541t64.h" +#include "Prefs.h" +#include "Display.h" +#include "main.h" + + +// IEC command codes +enum { + CMD_DATA = 0x60, // Data transfer + CMD_CLOSE = 0xe0, // Close channel + CMD_OPEN = 0xf0 // Open channel +}; + +// IEC ATN codes +enum { + ATN_LISTEN = 0x20, + ATN_UNLISTEN = 0x30, + ATN_TALK = 0x40, + ATN_UNTALK = 0x50 +}; + + +/* + * Constructor: Initialize variables + */ + +Drive *IEC::create_drive(const char *path) +{ + if (IsDirectory(path)) { + // Mount host directory + return new FSDrive(this, path); + } else { + // Not a directory, check for mountable file type + int type; + if (IsMountableFile(path, type)) { + if (type == FILE_IMAGE) { + // Mount disk image + return new ImageDrive(this, path); + } else { + // Mount archive type file + return new ArchDrive(this, path); + } + } else { + // Unknown file type + // print error? + } + } +} + +IEC::IEC(C64Display *display) : the_display(display) +{ + int i; + + // Create drives 8..11 + for (i=0; i<4; i++) + drive[i] = NULL; // Important because UpdateLEDs is called from the drive constructors (via set_error) + + if (!ThePrefs.Emul1541Proc) { + for (i=0; i<4; i++) + drive[i] = create_drive(ThePrefs.DrivePath[i]); + } + + listener_active = talker_active = false; + listening = false; +} + + +/* + * Destructor: Delete drives + */ + +IEC::~IEC() +{ + for (int i=0; i<4; i++) + delete drive[i]; +} + + +/* + * Reset all drives + */ + +void IEC::Reset(void) +{ + for (int i=0; i<4; i++) + if (drive[i] != NULL && drive[i]->Ready) + drive[i]->Reset(); + + UpdateLEDs(); +} + + +/* + * Preferences have changed, prefs points to new preferences, + * ThePrefs still holds the previous ones. Check if drive settings + * have changed. + */ + +void IEC::NewPrefs(Prefs *prefs) +{ + // Delete and recreate all changed drives + for (int i=0; i<4; i++) { + if (strcmp(ThePrefs.DrivePath[i], prefs->DrivePath[i]) || ThePrefs.Emul1541Proc != prefs->Emul1541Proc) { + delete drive[i]; + drive[i] = NULL; // Important because UpdateLEDs is called from drive constructors (via set_error()) + if (!prefs->Emul1541Proc) + drive[i] = create_drive(prefs->DrivePath[i]); + } + } + + UpdateLEDs(); +} + + +/* + * Update drive LED display + */ + +void IEC::UpdateLEDs(void) +{ + if (drive[0] != NULL && drive[1] != NULL && drive[2] != NULL && drive[3] != NULL) + the_display->UpdateLEDs(drive[0]->LED, drive[1]->LED, drive[2]->LED, drive[3]->LED); +} + + +/* + * Output one byte + */ + +uint8 IEC::Out(uint8 byte, bool eoi) +{ + if (listener_active) { + if (received_cmd == CMD_OPEN) + return open_out(byte, eoi); + if (received_cmd == CMD_DATA) + return data_out(byte, eoi); + return ST_TIMEOUT; + } else + return ST_TIMEOUT; +} + + +/* + * Output one byte with ATN (Talk/Listen/Untalk/Unlisten) + */ + +uint8 IEC::OutATN(uint8 byte) +{ + received_cmd = sec_addr = 0; // Command is sent with secondary address + switch (byte & 0xf0) { + case ATN_LISTEN: + listening = true; + return listen(byte & 0x0f); + case ATN_UNLISTEN: + listening = false; + return unlisten(); + case ATN_TALK: + listening = false; + return talk(byte & 0x0f); + case ATN_UNTALK: + listening = false; + return untalk(); + } + return ST_TIMEOUT; +} + + +/* + * Output secondary address + */ + +uint8 IEC::OutSec(uint8 byte) +{ + if (listening) { + if (listener_active) { + sec_addr = byte & 0x0f; + received_cmd = byte & 0xf0; + return sec_listen(); + } + } else { + if (talker_active) { + sec_addr = byte & 0x0f; + received_cmd = byte & 0xf0; + return sec_talk(); + } + } + return ST_TIMEOUT; +} + + +/* + * Read one byte + */ + +uint8 IEC::In(uint8 &byte) +{ + if (talker_active && (received_cmd == CMD_DATA)) + return data_in(byte); + + byte = 0; + return ST_TIMEOUT; +} + + +/* + * Assert ATN (for Untalk) + */ + +void IEC::SetATN(void) +{ + // Only needed for real IEC +} + + +/* + * Release ATN + */ + +void IEC::RelATN(void) +{ + // Only needed for real IEC +} + + +/* + * Talk-attention turn-around + */ + +void IEC::Turnaround(void) +{ + // Only needed for real IEC +} + + +/* + * System line release + */ + +void IEC::Release(void) +{ + // Only needed for real IEC +} + + +/* + * Listen + */ + +uint8 IEC::listen(int device) +{ + if ((device >= 8) && (device <= 11)) { + if ((listener = drive[device-8]) != NULL && listener->Ready) { + listener_active = true; + return ST_OK; + } + } + + listener_active = false; + return ST_NOTPRESENT; +} + + +/* + * Talk + */ + +uint8 IEC::talk(int device) +{ + if ((device >= 8) && (device <= 11)) { + if ((talker = drive[device-8]) != NULL && talker->Ready) { + talker_active = true; + return ST_OK; + } + } + + talker_active = false; + return ST_NOTPRESENT; +} + + +/* + * Unlisten + */ + +uint8 IEC::unlisten(void) +{ + listener_active = false; + return ST_OK; +} + + +/* + * Untalk + */ + +uint8 IEC::untalk(void) +{ + talker_active = false; + return ST_OK; +} + + +/* + * Secondary address after Listen + */ + +uint8 IEC::sec_listen(void) +{ + switch (received_cmd) { + + case CMD_OPEN: // Prepare for receiving the file name + name_ptr = name_buf; + name_len = 0; + return ST_OK; + + case CMD_CLOSE: // Close channel + if (listener->LED != DRVLED_ERROR) { + listener->LED = DRVLED_OFF; // Turn off drive LED + UpdateLEDs(); + } + return listener->Close(sec_addr); + } + return ST_OK; +} + + +/* + * Secondary address after Talk + */ + +uint8 IEC::sec_talk(void) +{ + return ST_OK; +} + + +/* + * Byte after Open command: Store character in file name, open file on EOI + */ + +uint8 IEC::open_out(uint8 byte, bool eoi) +{ + if (name_len < NAMEBUF_LENGTH) { + *name_ptr++ = byte; + name_len++; + } + + if (eoi) { + *name_ptr = 0; // End string + listener->LED = DRVLED_ON; // Turn on drive LED + UpdateLEDs(); + return listener->Open(sec_addr, name_buf, name_len); + } + + return ST_OK; +} + + +/* + * Write byte to channel + */ + +uint8 IEC::data_out(uint8 byte, bool eoi) +{ + return listener->Write(sec_addr, byte, eoi); +} + + +/* + * Read byte from channel + */ + +uint8 IEC::data_in(uint8 &byte) +{ + return talker->Read(sec_addr, byte); +} + + +/* + * Drive constructor + */ + +Drive::Drive(IEC *iec) +{ + the_iec = iec; + LED = DRVLED_OFF; + Ready = false; + set_error(ERR_STARTUP); +} + + +/* + * Set error message on drive + */ + +// 1541 error messages +static const char *Errors_1541[] = { + "00, OK,%02d,%02d\x0d", + "01, FILES SCRATCHED,%02d,%02d\x0d", + "03, UNIMPLEMENTED,%02d,%02d\x0d", + "20, READ ERROR,%02d,%02d\x0d", + "21, READ ERROR,%02d,%02d\x0d", + "22, READ ERROR,%02d,%02d\x0d", + "23, READ ERROR,%02d,%02d\x0d", + "24, READ ERROR,%02d,%02d\x0d", + "25, WRITE ERROR,%02d,%02d\x0d", + "26, WRITE PROTECT ON,%02d,%02d\x0d", + "27, READ ERROR,%02d,%02d\x0d", + "28, WRITE ERROR,%02d,%02d\x0d", + "29, DISK ID MISMATCH,%02d,%02d\x0d", + "30, SYNTAX ERROR,%02d,%02d\x0d", + "31, SYNTAX ERROR,%02d,%02d\x0d", + "32, SYNTAX ERROR,%02d,%02d\x0d", + "33, SYNTAX ERROR,%02d,%02d\x0d", + "34, SYNTAX ERROR,%02d,%02d\x0d", + "60, WRITE FILE OPEN,%02d,%02d\x0d", + "61, FILE NOT OPEN,%02d,%02d\x0d", + "62, FILE NOT FOUND,%02d,%02d\x0d", + "63, FILE EXISTS,%02d,%02d\x0d", + "64, FILE TYPE MISMATCH,%02d,%02d\x0d", + "65, NO BLOCK,%02d,%02d\x0d", + "66, ILLEGAL TRACK OR SECTOR,%02d,%02d\x0d", + "70, NO CHANNEL,%02d,%02d\x0d", + "71, DIR ERROR,%02d,%02d\x0d", + "72, DISK FULL,%02d,%02d\x0d", + "73, CBM DOS V2.6 1541,%02d,%02d\x0d", + "74, DRIVE NOT READY,%02d,%02d\x0d" +}; + +void Drive::set_error(int error, int track, int sector) +{ + // Write error message to buffer + sprintf(error_buf, Errors_1541[error], track, sector); + error_ptr = error_buf; + error_len = strlen(error_buf); + current_error = error; + + // Set drive condition + if (error != ERR_OK && error != ERR_SCRATCHED) + if (error == ERR_STARTUP) + LED = DRVLED_OFF; + else + LED = DRVLED_ERROR; + else if (LED == DRVLED_ERROR) + LED = DRVLED_OFF; + the_iec->UpdateLEDs(); +} + + +/* + * Parse file name, determine access mode and file type + */ + +void Drive::parse_file_name(const uint8 *src, int src_len, uint8 *dest, int &dest_len, int &mode, int &type, int &rec_len, bool convert_charset) +{ + // If the string contains a ':', the file name starts after that + const uint8 *p = (const uint8 *)memchr(src, ':', src_len); + if (p) { + p++; + src_len -= p - src; + } else + p = src; + + // Transfer file name upto ',' + dest_len = 0; + uint8 *q = dest; + while (*p != ',' && src_len-- > 0) { + if (convert_charset) + *q++ = petscii2ascii(*p++); + else + *q++ = *p++; + dest_len++; + } + *q++ = 0; + + // Strip trailing CRs + while (dest_len > 0 && dest[dest_len - 1] == 0x0d) + dest[--dest_len] = 0; + + // Look for mode and type parameters separated by ',' + p++; src_len--; + while (src_len > 0) { + switch (*p) { + case 'D': + type = FTYPE_DEL; + break; + case 'S': + type = FTYPE_SEQ; + break; + case 'P': + type = FTYPE_PRG; + break; + case 'U': + type = FTYPE_USR; + break; + case 'L': + type = FTYPE_REL; + while (*p != ',' && src_len-- > 0) p++; + p++; src_len--; + rec_len = *p++; src_len--; + if (src_len < 0) + rec_len = 0; + break; + case 'R': + mode = FMODE_READ; + break; + case 'W': + mode = FMODE_WRITE; + break; + case 'A': + mode = FMODE_APPEND; + break; + case 'M': + mode = FMODE_M; + break; + } + + // Skip to ',' + while (*p != ',' && src_len-- > 0) p++; + p++; src_len--; + } +} + + +/* + * Execute DOS command (parse command and call appropriate routine) + */ + +static void parse_block_cmd_args(const uint8 *p, int &arg1, int &arg2, int &arg3, int &arg4) +{ + arg1 = arg2 = arg3 = arg4 = 0; + + while (*p == ' ' || *p == 0x1d || *p == ',') p++; + while (*p >= '0' && *p < '@') + arg1 = arg1 * 10 + (*p++ & 0x0f); + + while (*p == ' ' || *p == 0x1d || *p == ',') p++; + while (*p >= '0' && *p < '@') + arg2 = arg2 * 10 + (*p++ & 0x0f); + + while (*p == ' ' || *p == 0x1d || *p == ',') p++; + while (*p >= '0' && *p < '@') + arg3 = arg3 * 10 + (*p++ & 0x0f); + + while (*p == ' ' || *p == 0x1d || *p == ',') p++; + while (*p >= '0' && *p < '@') + arg4 = arg4 * 10 + (*p++ & 0x0f); +} + +void Drive::execute_cmd(const uint8 *cmd, int cmd_len) +{ + // Strip trailing CRs + while (cmd_len > 0 && cmd[cmd_len - 1] == 0x0d) + cmd_len--; + + // Find token delimiters + const uint8 *colon = (const uint8 *)memchr(cmd, ':', cmd_len); + const uint8 *equal = colon ? (const uint8 *)memchr(colon, '=', cmd_len - (colon - cmd)) : NULL; + const uint8 *comma = (const uint8 *)memchr(cmd, ',', cmd_len); + const uint8 *minus = (const uint8 *)memchr(cmd, '-', cmd_len); + + // Parse command name + set_error(ERR_OK); + switch (cmd[0]) { + case 'B': // Block/buffer + if (!minus) + set_error(ERR_SYNTAX31); + else { + // Parse arguments (up to 4 decimal numbers separated by + // space, cursor right or comma) + const uint8 *p = colon ? colon + 1 : cmd + 3; + int arg1, arg2, arg3, arg4; + parse_block_cmd_args(p, arg1, arg2, arg3, arg4); + + // Switch on command + switch (minus[1]) { + case 'R': + block_read_cmd(arg1, arg3, arg4); + break; + case 'W': + block_write_cmd(arg1, arg3, arg4); + break; + case 'E': + block_execute_cmd(arg1, arg3, arg4); + break; + case 'A': + block_allocate_cmd(arg2, arg3); + break; + case 'F': + block_free_cmd(arg2, arg3); + break; + case 'P': + buffer_pointer_cmd(arg1, arg2); + break; + default: + set_error(ERR_SYNTAX31); + break; + } + } + break; + + case 'M': // Memory + if (cmd[1] != '-') + set_error(ERR_SYNTAX31); + else { + // Read parameters + uint16 adr = uint8(cmd[3]) | (uint8(cmd[4]) << 8); + uint8 len = uint8(cmd[5]); + + // Switch on command + switch (cmd[2]) { + case 'R': + mem_read_cmd(adr, (cmd_len < 6) ? 1 : len); + break; + case 'W': + mem_write_cmd(adr, len, (uint8 *)cmd + 6); + break; + case 'E': + mem_execute_cmd(adr); + break; + default: + set_error(ERR_SYNTAX31); + break; + } + } + break; + + case 'C': // Copy + if (!colon) + set_error(ERR_SYNTAX31); + else if (!equal || memchr(cmd, '*', cmd_len) || memchr(cmd, '?', cmd_len) || (comma && comma < equal)) + set_error(ERR_SYNTAX30); + else + copy_cmd(colon + 1, equal - colon - 1, equal + 1, cmd_len - (equal + 1 - cmd)); + break; + + case 'R': // Rename + if (!colon) + set_error(ERR_SYNTAX34); + else if (!equal || comma || memchr(cmd, '*', cmd_len) || memchr(cmd, '?', cmd_len)) + set_error(ERR_SYNTAX30); + else + rename_cmd(colon + 1, equal - colon - 1, equal + 1, cmd_len - (equal + 1 - cmd)); + break; + + case 'S': // Scratch + if (!colon) + set_error(ERR_SYNTAX34); + else + scratch_cmd(colon + 1, cmd_len - (colon + 1 - cmd)); + break; + + case 'P': // Position + position_cmd(cmd + 1, cmd_len - 1); + break; + + case 'I': // Initialize + initialize_cmd(); + break; + + case 'N': // New (format) + if (!colon) + set_error(ERR_SYNTAX34); + else + new_cmd(colon + 1, comma ? (comma - colon - 1) : cmd_len - (colon + 1 - cmd), comma); + break; + + case 'V': // Validate + validate_cmd(); + break; + + case 'U': // User + if (cmd[1] == '0') + break; + switch (cmd[1] & 0x0f) { + case 1: { // U1/UA: Read block + const uint8 *p = colon ? colon + 1 : cmd + 2; + int arg1, arg2, arg3, arg4; + parse_block_cmd_args(p, arg1, arg2, arg3, arg4); + block_read_cmd(arg1, arg3, arg4, true); + break; + } + case 2: { // U2/UB: Write block + const uint8 *p = colon ? colon + 1 : cmd + 2; + int arg1, arg2, arg3, arg4; + parse_block_cmd_args(p, arg1, arg2, arg3, arg4); + block_write_cmd(arg1, arg3, arg4, true); + break; + } + case 9: // U9/UI: C64/VC20 mode switch + if (cmd[2] != '+' && cmd[2] != '-') + Reset(); + break; + case 10: // U:/UJ: Reset + Reset(); + break; + default: + set_error(ERR_UNIMPLEMENTED); + break; + } + break; + + default: + set_error(ERR_SYNTAX31); + break; + } +} + +// BLOCK-READ:channel,0,track,sector +void Drive::block_read_cmd(int channel, int track, int sector, bool user_cmd) +{ + set_error(ERR_UNIMPLEMENTED); +} + +// BLOCK-WRITE:channel,0,track,sector +void Drive::block_write_cmd(int channel, int track, int sector, bool user_cmd) +{ + set_error(ERR_UNIMPLEMENTED); +} + +// BLOCK-EXECUTE:channel,0,track,sector +void Drive::block_execute_cmd(int channel, int track, int sector) +{ + set_error(ERR_UNIMPLEMENTED); +} + +// BLOCK-ALLOCATE:0,track,sector +void Drive::block_allocate_cmd(int track, int sector) +{ + set_error(ERR_UNIMPLEMENTED); +} + +// BLOCK-FREE:0,track,sector +void Drive::block_free_cmd(int track, int sector) +{ + set_error(ERR_UNIMPLEMENTED); +} + +// BUFFER-POINTER:channel,pos +void Drive::buffer_pointer_cmd(int channel, int pos) +{ + set_error(ERR_UNIMPLEMENTED); +} + +// M-R[] +void Drive::mem_read_cmd(uint16 adr, uint8 len) +{ + unsupp_cmd(); + error_ptr = error_buf; + error_buf[0] = 0; + error_len = 0; + set_error(ERR_OK); +} + +// M-W +void Drive::mem_write_cmd(uint16 adr, uint8 len, uint8 *p) +{ + set_error(ERR_UNIMPLEMENTED); +} + +// M-E +void Drive::mem_execute_cmd(uint16 adr) +{ + set_error(ERR_UNIMPLEMENTED); +} + +// COPY:new=file1,file2,... +// ^ ^ +// new_file old_files +void Drive::copy_cmd(const uint8 *new_file, int new_file_len, const uint8 *old_files, int old_files_len) +{ + set_error(ERR_UNIMPLEMENTED); +} + +// RENAME:new=old +// ^ ^ +// new_file old_file +void Drive::rename_cmd(const uint8 *new_file, int new_file_len, const uint8 *old_file, int old_file_len) +{ + set_error(ERR_UNIMPLEMENTED); +} + +// SCRATCH:file1,file2,... +// ^ +// files +void Drive::scratch_cmd(const uint8 *files, int files_len) +{ + set_error(ERR_UNIMPLEMENTED); +} + +// P +// ^ +// cmd +void Drive::position_cmd(const uint8 *cmd, int cmd_len) +{ + set_error(ERR_UNIMPLEMENTED); +} + +// INITIALIZE +void Drive::initialize_cmd(void) +{ + set_error(ERR_UNIMPLEMENTED); +} + +// NEW:name,id +// ^ ^ +// name comma (or NULL) +void Drive::new_cmd(const uint8 *name, int name_len, const uint8 *comma) +{ + set_error(ERR_UNIMPLEMENTED); +} + +// VALIDATE +void Drive::validate_cmd(void) +{ + set_error(ERR_UNIMPLEMENTED); +} + + +/* + * Notice user of unsupported drive command + */ + +void Drive::unsupp_cmd(void) +{ +} + + +/* + * Convert PETSCII<->ASCII + */ + +uint8 ascii2petscii(char c) +{ + if ((c >= 'A') && (c <= 'Z') || (c >= 'a') && (c <= 'z')) + return c ^ 0x20; + return c; +} + +void ascii2petscii(uint8 *dest, const char *src, int n) +{ + while (n-- && (*dest++ = ascii2petscii(*src++))) ; +} + +char petscii2ascii(uint8 c) +{ + if ((c >= 'A') && (c <= 'Z') || (c >= 'a') && (c <= 'z')) + return c ^ 0x20; + if ((c >= 0xc1) && (c <= 0xda)) + return c ^ 0x80; + return c; +} + +void petscii2ascii(char *dest, const uint8 *src, int n) +{ + while (n-- && (*dest++ = petscii2ascii(*src++))) ; +} + + +/* + * Check whether file is a mountable disk image or archive file, return type + */ + +bool IsMountableFile(const char *path, int &type) +{ + // Read header and determine file size + uint8 header[64]; + memset(header, 0, sizeof(header)); + FILE *f = fopen(path, "rb"); + if (f == NULL) + return false; + fseek(f, 0, SEEK_END); + long size = ftell(f); + fseek(f, 0, SEEK_SET); + fread(header, 1, sizeof(header), f); + fclose(f); + + if (IsImageFile(path, header, size)) { + type = FILE_IMAGE; + return true; + } else if (IsArchFile(path, header, size)) { + type = FILE_ARCH; + return true; + } else + return false; +} + + +/* + * Read directory of mountable disk image or archive file into c64_dir_entry vector, + * returns false on error + */ + +bool ReadDirectory(const char *path, int type, vector &vec) +{ + vec.clear(); + switch (type) { + case FILE_IMAGE: + return ReadImageDirectory(path, vec); + case FILE_ARCH: + return ReadArchDirectory(path, vec); + default: + return false; + } +} diff --git a/Src/IEC.h b/Src/IEC.h new file mode 100644 index 0000000..1b40f73 --- /dev/null +++ b/Src/IEC.h @@ -0,0 +1,258 @@ +/* + * IEC.h - IEC bus routines, 1541 emulation (DOS level) + * + * Frodo (C) 1994-1997,2002-2005 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _IEC_H +#define _IEC_H + + +/* + * Definitions + */ + +// Maximum length of file names +const int NAMEBUF_LENGTH = 256; + +// C64 status codes +enum { + ST_OK = 0, // No error + ST_READ_TIMEOUT = 0x02, // Timeout on reading + ST_TIMEOUT = 0x03, // Timeout + ST_EOF = 0x40, // End of file + ST_NOTPRESENT = 0x80 // Device not present +}; + +// 1541 error codes +enum { + ERR_OK, // 00 OK + ERR_SCRATCHED, // 01 FILES SCRATCHED + ERR_UNIMPLEMENTED, // 03 UNIMPLEMENTED + ERR_READ20, // 20 READ ERROR (block header not found) + ERR_READ21, // 21 READ ERROR (no sync character) + ERR_READ22, // 22 READ ERROR (data block not present) + ERR_READ23, // 23 READ ERROR (checksum error in data block) + ERR_READ24, // 24 READ ERROR (byte decoding error) + ERR_WRITE25, // 25 WRITE ERROR (write-verify error) + ERR_WRITEPROTECT, // 26 WRITE PROTECT ON + ERR_READ27, // 27 READ ERROR (checksum error in header) + ERR_WRITE28, // 28 WRITE ERROR (long data block) + ERR_DISKID, // 29 DISK ID MISMATCH + ERR_SYNTAX30, // 30 SYNTAX ERROR (general syntax) + ERR_SYNTAX31, // 31 SYNTAX ERROR (invalid command) + ERR_SYNTAX32, // 32 SYNTAX ERROR (command too long) + ERR_SYNTAX33, // 33 SYNTAX ERROR (wildcards on writing) + ERR_SYNTAX34, // 34 SYNTAX ERROR (missing file name) + ERR_WRITEFILEOPEN, // 60 WRITE FILE OPEN + ERR_FILENOTOPEN, // 61 FILE NOT OPEN + ERR_FILENOTFOUND, // 62 FILE NOT FOUND + ERR_FILEEXISTS, // 63 FILE EXISTS + ERR_FILETYPE, // 64 FILE TYPE MISMATCH + ERR_NOBLOCK, // 65 NO BLOCK + ERR_ILLEGALTS, // 66 ILLEGAL TRACK OR SECTOR + ERR_NOCHANNEL, // 70 NO CHANNEL + ERR_DIRERROR, // 71 DIR ERROR + ERR_DISKFULL, // 72 DISK FULL + ERR_STARTUP, // 73 Power-up message + ERR_NOTREADY // 74 DRIVE NOT READY +}; + +// Mountable file types +enum { + FILE_IMAGE, // Disk image, handled by ImageDrive + FILE_ARCH // Archive file, handled by ArchDrive +}; + +// 1541 file types +enum { + FTYPE_DEL, // Deleted + FTYPE_SEQ, // Sequential + FTYPE_PRG, // Program + FTYPE_USR, // User + FTYPE_REL, // Relative + FTYPE_UNKNOWN +}; + +static const char ftype_char[9] = "DSPUL "; + +// 1541 file access modes +enum { + FMODE_READ, // Read + FMODE_WRITE, // Write + FMODE_APPEND, // Append + FMODE_M // Read open file +}; + +// Drive LED states +enum { + DRVLED_OFF, // Inactive, LED off + DRVLED_ON, // Active, LED on + DRVLED_ERROR // Error, blink LED +}; + +// Information about file in disk image/archive file +struct c64_dir_entry { + c64_dir_entry(const uint8 *n, int t, bool o, bool p, size_t s, off_t ofs = 0, uint8 sal = 0, uint8 sah = 0) + : type(t), is_open(o), is_protected(p), size(s), offset(ofs), sa_lo(sal), sa_hi(sah) + { + strncpy((char *)name, (const char *)n, 17); + name[16] = 0; + } + + // Basic information + uint8 name[17]; // File name (C64 charset, null-terminated) + int type; // File type (see defines above) + bool is_open; // Flag: file open + bool is_protected; // Flag: file protected + size_t size; // File size (may be approximated) + + // Special information + off_t offset; // Offset of file in archive file + uint8 sa_lo, sa_hi; // C64 start address +}; + +class Drive; +class C64Display; +class Prefs; + +// Class for complete IEC bus system with drives 8..11 +class IEC { +public: + IEC(C64Display *display); + ~IEC(); + + void Reset(void); + void NewPrefs(Prefs *prefs); + void UpdateLEDs(void); + + uint8 Out(uint8 byte, bool eoi); + uint8 OutATN(uint8 byte); + uint8 OutSec(uint8 byte); + uint8 In(uint8 &byte); + void SetATN(void); + void RelATN(void); + void Turnaround(void); + void Release(void); + +private: + Drive *create_drive(const char *path); + + uint8 listen(int device); + uint8 talk(int device); + uint8 unlisten(void); + uint8 untalk(void); + uint8 sec_listen(void); + uint8 sec_talk(void); + uint8 open_out(uint8 byte, bool eoi); + uint8 data_out(uint8 byte, bool eoi); + uint8 data_in(uint8 &byte); + + C64Display *the_display; // Pointer to display object (for drive LEDs) + + uint8 name_buf[NAMEBUF_LENGTH]; // Buffer for file names and command strings + uint8 *name_ptr; // Pointer for reception of file name + int name_len; // Received length of file name + + Drive *drive[4]; // 4 drives (8..11) + + Drive *listener; // Pointer to active listener + Drive *talker; // Pointer to active talker + + bool listener_active; // Listener selected, listener_data is valid + bool talker_active; // Talker selected, talker_data is valid + bool listening; // Last ATN was listen (to decide between sec_listen/sec_talk) + + uint8 received_cmd; // Received command code ($x0) + uint8 sec_addr; // Received secondary address ($0x) +}; + +// Abstract superclass for individual drives +class Drive { +public: + Drive(IEC *iec); + virtual ~Drive() {} + + virtual uint8 Open(int channel, const uint8 *name, int name_len)=0; + virtual uint8 Close(int channel)=0; + virtual uint8 Read(int channel, uint8 &byte)=0; + virtual uint8 Write(int channel, uint8 byte, bool eoi)=0; + virtual void Reset(void)=0; + + int LED; // Drive LED state + bool Ready; // Drive is ready for operation + +protected: + void set_error(int error, int track = 0, int sector = 0); + + void parse_file_name(const uint8 *src, int src_len, uint8 *dest, int &dest_len, int &mode, int &type, int &rec_len, bool convert_charset = false); + + void execute_cmd(const uint8 *cmd, int cmd_len); + virtual void block_read_cmd(int channel, int track, int sector, bool user_cmd = false); + virtual void block_write_cmd(int channel, int track, int sector, bool user_cmd = false); + virtual void block_execute_cmd(int channel, int track, int sector); + virtual void block_allocate_cmd(int track, int sector); + virtual void block_free_cmd(int track, int sector); + virtual void buffer_pointer_cmd(int channel, int pos); + virtual void mem_read_cmd(uint16 adr, uint8 len); + virtual void mem_write_cmd(uint16 adr, uint8 len, uint8 *p); + virtual void mem_execute_cmd(uint16 adr); + virtual void copy_cmd(const uint8 *new_file, int new_file_len, const uint8 *old_files, int old_files_len); + virtual void rename_cmd(const uint8 *new_file, int new_file_len, const uint8 *old_file, int old_file_len); + virtual void scratch_cmd(const uint8 *files, int files_len); + virtual void position_cmd(const uint8 *cmd, int cmd_len); + virtual void initialize_cmd(void); + virtual void new_cmd(const uint8 *name, int name_len, const uint8 *comma); + virtual void validate_cmd(void); + void unsupp_cmd(void); + + char error_buf[256]; // Buffer with current error message + char *error_ptr; // Pointer within error message + int error_len; // Remaining length of error message + int current_error; // Number of current error + + uint8 cmd_buf[64]; // Buffer for incoming command strings + int cmd_len; // Length of received command + +private: + IEC *the_iec; // Pointer to IEC object +}; + + +/* + * Functions + */ + +// Convert ASCII character to PETSCII character +extern uint8 ascii2petscii(char c); + +// Convert ASCII string to PETSCII string +extern void ascii2petscii(uint8 *dest, const char *src, int max); + +// Convert PETSCII character to ASCII character +extern char petscii2ascii(uint8 c); + +// Convert PETSCII string to ASCII string +extern void petscii2ascii(char *dest, const uint8 *src, int max); + +// Check whether file is a mountable disk image or archive file, return type +extern bool IsMountableFile(const char *path, int &type); + +// Read directory of mountable disk image or archive file into c64_dir_entry vector +extern bool ReadDirectory(const char *path, int type, vector &vec); + +#endif diff --git a/Src/Invisible.cur b/Src/Invisible.cur new file mode 100644 index 0000000000000000000000000000000000000000..6bb01a5c9bb4172cb56a952e087e6691b2c83a0e GIT binary patch literal 326 zcmZQzU}9ioP*7lC;0HnjMg|5k1_lNVAO;FCH~=vt5P|`ef^ZPz|Ns9HoB?71-7ug4 E0Gd66u>b%7 literal 0 HcmV?d00001 diff --git a/Src/Kernal_ROM.h b/Src/Kernal_ROM.h new file mode 100644 index 0000000..cb6795a --- /dev/null +++ b/Src/Kernal_ROM.h @@ -0,0 +1,1033 @@ +/* + * Kernal_ROM.h - C64 Kernal ROM + * + * Frodo (C) 1994-1997,2002-2005 Christian Bauer + * C64/1541 ROMs (C) Commodore Business Machines + */ + +static const uint8 builtin_kernal_rom[KERNAL_ROM_SIZE] = { + 0x85, 0x56, 0x20, 0x0f, 0xbc, 0xa5, 0x61, 0xc9, + 0x88, 0x90, 0x03, 0x20, 0xd4, 0xba, 0x20, 0xcc, + 0xbc, 0xa5, 0x07, 0x18, 0x69, 0x81, 0xf0, 0xf3, + 0x38, 0xe9, 0x01, 0x48, 0xa2, 0x05, 0xb5, 0x69, + 0xb4, 0x61, 0x95, 0x61, 0x94, 0x69, 0xca, 0x10, + 0xf5, 0xa5, 0x56, 0x85, 0x70, 0x20, 0x53, 0xb8, + 0x20, 0xb4, 0xbf, 0xa9, 0xc4, 0xa0, 0xbf, 0x20, + 0x59, 0xe0, 0xa9, 0x00, 0x85, 0x6f, 0x68, 0x20, + 0xb9, 0xba, 0x60, 0x85, 0x71, 0x84, 0x72, 0x20, + 0xca, 0xbb, 0xa9, 0x57, 0x20, 0x28, 0xba, 0x20, + 0x5d, 0xe0, 0xa9, 0x57, 0xa0, 0x00, 0x4c, 0x28, + 0xba, 0x85, 0x71, 0x84, 0x72, 0x20, 0xc7, 0xbb, + 0xb1, 0x71, 0x85, 0x67, 0xa4, 0x71, 0xc8, 0x98, + 0xd0, 0x02, 0xe6, 0x72, 0x85, 0x71, 0xa4, 0x72, + 0x20, 0x28, 0xba, 0xa5, 0x71, 0xa4, 0x72, 0x18, + 0x69, 0x05, 0x90, 0x01, 0xc8, 0x85, 0x71, 0x84, + 0x72, 0x20, 0x67, 0xb8, 0xa9, 0x5c, 0xa0, 0x00, + 0xc6, 0x67, 0xd0, 0xe4, 0x60, 0x98, 0x35, 0x44, + 0x7a, 0x00, 0x68, 0x28, 0xb1, 0x46, 0x00, 0x20, + 0x2b, 0xbc, 0x30, 0x37, 0xd0, 0x20, 0x20, 0xf3, + 0xff, 0x86, 0x22, 0x84, 0x23, 0xa0, 0x04, 0xb1, + 0x22, 0x85, 0x62, 0xc8, 0xb1, 0x22, 0x85, 0x64, + 0xa0, 0x08, 0xb1, 0x22, 0x85, 0x63, 0xc8, 0xb1, + 0x22, 0x85, 0x65, 0x4c, 0xe3, 0xe0, 0xa9, 0x8b, + 0xa0, 0x00, 0x20, 0xa2, 0xbb, 0xa9, 0x8d, 0xa0, + 0xe0, 0x20, 0x28, 0xba, 0xa9, 0x92, 0xa0, 0xe0, + 0x20, 0x67, 0xb8, 0xa6, 0x65, 0xa5, 0x62, 0x85, + 0x65, 0x86, 0x62, 0xa6, 0x63, 0xa5, 0x64, 0x85, + 0x63, 0x86, 0x64, 0xa9, 0x00, 0x85, 0x66, 0xa5, + 0x61, 0x85, 0x70, 0xa9, 0x80, 0x85, 0x61, 0x20, + 0xd7, 0xb8, 0xa2, 0x8b, 0xa0, 0x00, 0x4c, 0xd4, + 0xbb, 0xc9, 0xf0, 0xd0, 0x07, 0x84, 0x38, 0x86, + 0x37, 0x4c, 0x63, 0xa6, 0xaa, 0xd0, 0x02, 0xa2, + 0x1e, 0x4c, 0x37, 0xa4, 0x20, 0xd2, 0xff, 0xb0, + 0xe8, 0x60, 0x20, 0xcf, 0xff, 0xb0, 0xe2, 0x60, + 0x20, 0xad, 0xe4, 0xb0, 0xdc, 0x60, 0x20, 0xc6, + 0xff, 0xb0, 0xd6, 0x60, 0x20, 0xe4, 0xff, 0xb0, + 0xd0, 0x60, 0x20, 0x8a, 0xad, 0x20, 0xf7, 0xb7, + 0xa9, 0xe1, 0x48, 0xa9, 0x46, 0x48, 0xad, 0x0f, + 0x03, 0x48, 0xad, 0x0c, 0x03, 0xae, 0x0d, 0x03, + 0xac, 0x0e, 0x03, 0x28, 0x6c, 0x14, 0x00, 0x08, + 0x8d, 0x0c, 0x03, 0x8e, 0x0d, 0x03, 0x8c, 0x0e, + 0x03, 0x68, 0x8d, 0x0f, 0x03, 0x60, 0x20, 0xd4, + 0xe1, 0xa6, 0x2d, 0xa4, 0x2e, 0xa9, 0x2b, 0x20, + 0xd8, 0xff, 0xb0, 0x95, 0x60, 0xa9, 0x01, 0x2c, + 0xa9, 0x00, 0x85, 0x0a, 0x20, 0xd4, 0xe1, 0xa5, + 0x0a, 0xa6, 0x2b, 0xa4, 0x2c, 0x20, 0xd5, 0xff, + 0xb0, 0x57, 0xa5, 0x0a, 0xf0, 0x17, 0xa2, 0x1c, + 0x20, 0xb7, 0xff, 0x29, 0x10, 0xd0, 0x17, 0xa5, + 0x7a, 0xc9, 0x02, 0xf0, 0x07, 0xa9, 0x64, 0xa0, + 0xa3, 0x4c, 0x1e, 0xab, 0x60, 0x20, 0xb7, 0xff, + 0x29, 0xbf, 0xf0, 0x05, 0xa2, 0x1d, 0x4c, 0x37, + 0xa4, 0xa5, 0x7b, 0xc9, 0x02, 0xd0, 0x0e, 0x86, + 0x2d, 0x84, 0x2e, 0xa9, 0x76, 0xa0, 0xa3, 0x20, + 0x1e, 0xab, 0x4c, 0x2a, 0xa5, 0x20, 0x8e, 0xa6, + 0x20, 0x33, 0xa5, 0x4c, 0x77, 0xa6, 0x20, 0x19, + 0xe2, 0x20, 0xc0, 0xff, 0xb0, 0x0b, 0x60, 0x20, + 0x19, 0xe2, 0xa5, 0x49, 0x20, 0xc3, 0xff, 0x90, + 0xc3, 0x4c, 0xf9, 0xe0, 0xa9, 0x00, 0x20, 0xbd, + 0xff, 0xa2, 0x01, 0xa0, 0x00, 0x20, 0xba, 0xff, + 0x20, 0x06, 0xe2, 0x20, 0x57, 0xe2, 0x20, 0x06, + 0xe2, 0x20, 0x00, 0xe2, 0xa0, 0x00, 0x86, 0x49, + 0x20, 0xba, 0xff, 0x20, 0x06, 0xe2, 0x20, 0x00, + 0xe2, 0x8a, 0xa8, 0xa6, 0x49, 0x4c, 0xba, 0xff, + 0x20, 0x0e, 0xe2, 0x4c, 0x9e, 0xb7, 0x20, 0x79, + 0x00, 0xd0, 0x02, 0x68, 0x68, 0x60, 0x20, 0xfd, + 0xae, 0x20, 0x79, 0x00, 0xd0, 0xf7, 0x4c, 0x08, + 0xaf, 0xa9, 0x00, 0x20, 0xbd, 0xff, 0x20, 0x11, + 0xe2, 0x20, 0x9e, 0xb7, 0x86, 0x49, 0x8a, 0xa2, + 0x01, 0xa0, 0x00, 0x20, 0xba, 0xff, 0x20, 0x06, + 0xe2, 0x20, 0x00, 0xe2, 0x86, 0x4a, 0xa0, 0x00, + 0xa5, 0x49, 0xe0, 0x03, 0x90, 0x01, 0x88, 0x20, + 0xba, 0xff, 0x20, 0x06, 0xe2, 0x20, 0x00, 0xe2, + 0x8a, 0xa8, 0xa6, 0x4a, 0xa5, 0x49, 0x20, 0xba, + 0xff, 0x20, 0x06, 0xe2, 0x20, 0x0e, 0xe2, 0x20, + 0x9e, 0xad, 0x20, 0xa3, 0xb6, 0xa6, 0x22, 0xa4, + 0x23, 0x4c, 0xbd, 0xff, 0xa9, 0xe0, 0xa0, 0xe2, + 0x20, 0x67, 0xb8, 0x20, 0x0c, 0xbc, 0xa9, 0xe5, + 0xa0, 0xe2, 0xa6, 0x6e, 0x20, 0x07, 0xbb, 0x20, + 0x0c, 0xbc, 0x20, 0xcc, 0xbc, 0xa9, 0x00, 0x85, + 0x6f, 0x20, 0x53, 0xb8, 0xa9, 0xea, 0xa0, 0xe2, + 0x20, 0x50, 0xb8, 0xa5, 0x66, 0x48, 0x10, 0x0d, + 0x20, 0x49, 0xb8, 0xa5, 0x66, 0x30, 0x09, 0xa5, + 0x12, 0x49, 0xff, 0x85, 0x12, 0x20, 0xb4, 0xbf, + 0xa9, 0xea, 0xa0, 0xe2, 0x20, 0x67, 0xb8, 0x68, + 0x10, 0x03, 0x20, 0xb4, 0xbf, 0xa9, 0xef, 0xa0, + 0xe2, 0x4c, 0x43, 0xe0, 0x20, 0xca, 0xbb, 0xa9, + 0x00, 0x85, 0x12, 0x20, 0x6b, 0xe2, 0xa2, 0x4e, + 0xa0, 0x00, 0x20, 0xf6, 0xe0, 0xa9, 0x57, 0xa0, + 0x00, 0x20, 0xa2, 0xbb, 0xa9, 0x00, 0x85, 0x66, + 0xa5, 0x12, 0x20, 0xdc, 0xe2, 0xa9, 0x4e, 0xa0, + 0x00, 0x4c, 0x0f, 0xbb, 0x48, 0x4c, 0x9d, 0xe2, + 0x81, 0x49, 0x0f, 0xda, 0xa2, 0x83, 0x49, 0x0f, + 0xda, 0xa2, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x84, 0xe6, 0x1a, 0x2d, 0x1b, 0x86, 0x28, 0x07, + 0xfb, 0xf8, 0x87, 0x99, 0x68, 0x89, 0x01, 0x87, + 0x23, 0x35, 0xdf, 0xe1, 0x86, 0xa5, 0x5d, 0xe7, + 0x28, 0x83, 0x49, 0x0f, 0xda, 0xa2, 0xa5, 0x66, + 0x48, 0x10, 0x03, 0x20, 0xb4, 0xbf, 0xa5, 0x61, + 0x48, 0xc9, 0x81, 0x90, 0x07, 0xa9, 0xbc, 0xa0, + 0xb9, 0x20, 0x0f, 0xbb, 0xa9, 0x3e, 0xa0, 0xe3, + 0x20, 0x43, 0xe0, 0x68, 0xc9, 0x81, 0x90, 0x07, + 0xa9, 0xe0, 0xa0, 0xe2, 0x20, 0x50, 0xb8, 0x68, + 0x10, 0x03, 0x4c, 0xb4, 0xbf, 0x60, 0x0b, 0x76, + 0xb3, 0x83, 0xbd, 0xd3, 0x79, 0x1e, 0xf4, 0xa6, + 0xf5, 0x7b, 0x83, 0xfc, 0xb0, 0x10, 0x7c, 0x0c, + 0x1f, 0x67, 0xca, 0x7c, 0xde, 0x53, 0xcb, 0xc1, + 0x7d, 0x14, 0x64, 0x70, 0x4c, 0x7d, 0xb7, 0xea, + 0x51, 0x7a, 0x7d, 0x63, 0x30, 0x88, 0x7e, 0x7e, + 0x92, 0x44, 0x99, 0x3a, 0x7e, 0x4c, 0xcc, 0x91, + 0xc7, 0x7f, 0xaa, 0xaa, 0xaa, 0x13, 0x81, 0x00, + 0x00, 0x00, 0x00, 0x20, 0xcc, 0xff, 0xa9, 0x00, + 0x85, 0x13, 0x20, 0x7a, 0xa6, 0x58, 0xa2, 0x80, + 0x6c, 0x00, 0x03, 0x8a, 0x30, 0x03, 0x4c, 0x3a, + 0xa4, 0x4c, 0x74, 0xa4, 0x20, 0x53, 0xe4, 0x20, + 0xbf, 0xe3, 0x20, 0x22, 0xe4, 0xa2, 0xfb, 0x9a, + 0xd0, 0xe4, 0xe6, 0x7a, 0xd0, 0x02, 0xe6, 0x7b, + 0xad, 0x60, 0xea, 0xc9, 0x3a, 0xb0, 0x0a, 0xc9, + 0x20, 0xf0, 0xef, 0x38, 0xe9, 0x30, 0x38, 0xe9, + 0xd0, 0x60, 0x80, 0x4f, 0xc7, 0x52, 0x58, 0xa9, + 0x4c, 0x85, 0x54, 0x8d, 0x10, 0x03, 0xa9, 0x48, + 0xa0, 0xb2, 0x8d, 0x11, 0x03, 0x8c, 0x12, 0x03, + 0xa9, 0x91, 0xa0, 0xb3, 0x85, 0x05, 0x84, 0x06, + 0xa9, 0xaa, 0xa0, 0xb1, 0x85, 0x03, 0x84, 0x04, + 0xa2, 0x1c, 0xbd, 0xa2, 0xe3, 0x95, 0x73, 0xca, + 0x10, 0xf8, 0xa9, 0x03, 0x85, 0x53, 0xa9, 0x00, + 0x85, 0x68, 0x85, 0x13, 0x85, 0x18, 0xa2, 0x01, + 0x8e, 0xfd, 0x01, 0x8e, 0xfc, 0x01, 0xa2, 0x19, + 0x86, 0x16, 0x38, 0x20, 0x9c, 0xff, 0x86, 0x2b, + 0x84, 0x2c, 0x38, 0x20, 0x99, 0xff, 0x86, 0x37, + 0x84, 0x38, 0x86, 0x33, 0x84, 0x34, 0xa0, 0x00, + 0x98, 0x91, 0x2b, 0xe6, 0x2b, 0xd0, 0x02, 0xe6, + 0x2c, 0x60, 0xa5, 0x2b, 0xa4, 0x2c, 0x20, 0x08, + 0xa4, 0xa9, 0x73, 0xa0, 0xe4, 0x20, 0x1e, 0xab, + 0xa5, 0x37, 0x38, 0xe5, 0x2b, 0xaa, 0xa5, 0x38, + 0xe5, 0x2c, 0x20, 0xcd, 0xbd, 0xa9, 0x60, 0xa0, + 0xe4, 0x20, 0x1e, 0xab, 0x4c, 0x44, 0xa6, 0x8b, + 0xe3, 0x83, 0xa4, 0x7c, 0xa5, 0x1a, 0xa7, 0xe4, + 0xa7, 0x86, 0xae, 0xa2, 0x0b, 0xbd, 0x47, 0xe4, + 0x9d, 0x00, 0x03, 0xca, 0x10, 0xf7, 0x60, 0x00, + 0x20, 0x42, 0x41, 0x53, 0x49, 0x43, 0x20, 0x42, + 0x59, 0x54, 0x45, 0x53, 0x20, 0x46, 0x52, 0x45, + 0x45, 0x0d, 0x00, 0x93, 0x0d, 0x20, 0x20, 0x20, + 0x20, 0x2a, 0x2a, 0x2a, 0x2a, 0x20, 0x43, 0x4f, + 0x4d, 0x4d, 0x4f, 0x44, 0x4f, 0x52, 0x45, 0x20, + 0x36, 0x34, 0x20, 0x42, 0x41, 0x53, 0x49, 0x43, + 0x20, 0x56, 0x32, 0x20, 0x2a, 0x2a, 0x2a, 0x2a, + 0x0d, 0x0d, 0x20, 0x36, 0x34, 0x4b, 0x20, 0x52, + 0x41, 0x4d, 0x20, 0x53, 0x59, 0x53, 0x54, 0x45, + 0x4d, 0x20, 0x20, 0x00, 0x81, 0x48, 0x20, 0xc9, + 0xff, 0xaa, 0x68, 0x90, 0x01, 0x8a, 0x60, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0x85, 0xa9, 0xa9, 0x01, 0x85, + 0xab, 0x60, 0xad, 0x86, 0x02, 0x91, 0xf3, 0x60, + 0x69, 0x02, 0xa4, 0x91, 0xc8, 0xd0, 0x04, 0xc5, + 0xa1, 0xd0, 0xf7, 0x60, 0x19, 0x26, 0x44, 0x19, + 0x1a, 0x11, 0xe8, 0x0d, 0x70, 0x0c, 0x06, 0x06, + 0xd1, 0x02, 0x37, 0x01, 0xae, 0x00, 0x69, 0x00, + 0xa2, 0x00, 0xa0, 0xdc, 0x60, 0xa2, 0x28, 0xa0, + 0x19, 0x60, 0xb0, 0x07, 0x86, 0xd6, 0x84, 0xd3, + 0x20, 0x6c, 0xe5, 0xa6, 0xd6, 0xa4, 0xd3, 0x60, + 0x20, 0xa0, 0xe5, 0xa9, 0x00, 0x8d, 0x91, 0x02, + 0x85, 0xcf, 0xa9, 0x48, 0x8d, 0x8f, 0x02, 0xa9, + 0xeb, 0x8d, 0x90, 0x02, 0xa9, 0x0a, 0x8d, 0x89, + 0x02, 0x8d, 0x8c, 0x02, 0xa9, 0x0e, 0x8d, 0x86, + 0x02, 0xa9, 0x04, 0x8d, 0x8b, 0x02, 0xa9, 0x0c, + 0x85, 0xcd, 0x85, 0xcc, 0xad, 0x88, 0x02, 0x09, + 0x80, 0xa8, 0xa9, 0x00, 0xaa, 0x94, 0xd9, 0x18, + 0x69, 0x28, 0x90, 0x01, 0xc8, 0xe8, 0xe0, 0x1a, + 0xd0, 0xf3, 0xa9, 0xff, 0x95, 0xd9, 0xa2, 0x18, + 0x20, 0xff, 0xe9, 0xca, 0x10, 0xfa, 0xa0, 0x00, + 0x84, 0xd3, 0x84, 0xd6, 0xa6, 0xd6, 0xa5, 0xd3, + 0xb4, 0xd9, 0x30, 0x08, 0x18, 0x69, 0x28, 0x85, + 0xd3, 0xca, 0x10, 0xf4, 0x20, 0xf0, 0xe9, 0xa9, + 0x27, 0xe8, 0xb4, 0xd9, 0x30, 0x06, 0x18, 0x69, + 0x28, 0xe8, 0x10, 0xf6, 0x85, 0xd5, 0x4c, 0x24, + 0xea, 0xe4, 0xc9, 0xf0, 0x03, 0x4c, 0xed, 0xe6, + 0x60, 0xea, 0x20, 0xa0, 0xe5, 0x4c, 0x66, 0xe5, + 0xa9, 0x03, 0x85, 0x9a, 0xa9, 0x00, 0x85, 0x99, + 0xa2, 0x2f, 0xbd, 0xb8, 0xec, 0x9d, 0xff, 0xcf, + 0xca, 0xd0, 0xf7, 0x60, 0xac, 0x77, 0x02, 0xa2, + 0x00, 0xbd, 0x78, 0x02, 0x9d, 0x77, 0x02, 0xe8, + 0xe4, 0xc6, 0xd0, 0xf5, 0xc6, 0xc6, 0x98, 0x58, + 0x18, 0x60, 0x20, 0x16, 0xe7, 0xa5, 0xc6, 0x85, + 0xcc, 0x8d, 0x92, 0x02, 0xf0, 0xf7, 0x78, 0xa5, + 0xcf, 0xf0, 0x0c, 0xa5, 0xce, 0xae, 0x87, 0x02, + 0xa0, 0x00, 0x84, 0xcf, 0x20, 0x13, 0xea, 0x20, + 0xb4, 0xe5, 0xc9, 0x83, 0xd0, 0x10, 0xa2, 0x09, + 0x78, 0x86, 0xc6, 0xbd, 0xe6, 0xec, 0x9d, 0x76, + 0x02, 0xca, 0xd0, 0xf7, 0xf0, 0xcf, 0xc9, 0x0d, + 0xd0, 0xc8, 0xa4, 0xd5, 0x84, 0xd0, 0xb1, 0xd1, + 0xc9, 0x20, 0xd0, 0x03, 0x88, 0xd0, 0xf7, 0xc8, + 0x84, 0xc8, 0xa0, 0x00, 0x8c, 0x92, 0x02, 0x84, + 0xd3, 0x84, 0xd4, 0xa5, 0xc9, 0x30, 0x1b, 0xa6, + 0xd6, 0x20, 0x91, 0xe5, 0xe4, 0xc9, 0xd0, 0x12, + 0xa5, 0xca, 0x85, 0xd3, 0xc5, 0xc8, 0x90, 0x0a, + 0xb0, 0x2b, 0x98, 0x48, 0x8a, 0x48, 0xa5, 0xd0, + 0xf0, 0x93, 0xa4, 0xd3, 0xb1, 0xd1, 0x85, 0xd7, + 0x29, 0x3f, 0x06, 0xd7, 0x24, 0xd7, 0x10, 0x02, + 0x09, 0x80, 0x90, 0x04, 0xa6, 0xd4, 0xd0, 0x04, + 0x70, 0x02, 0x09, 0x40, 0xe6, 0xd3, 0x20, 0x84, + 0xe6, 0xc4, 0xc8, 0xd0, 0x17, 0xa9, 0x00, 0x85, + 0xd0, 0xa9, 0x0d, 0xa6, 0x99, 0xe0, 0x03, 0xf0, + 0x06, 0xa6, 0x9a, 0xe0, 0x03, 0xf0, 0x03, 0x20, + 0x16, 0xe7, 0xa9, 0x0d, 0x85, 0xd7, 0x68, 0xaa, + 0x68, 0xa8, 0xa5, 0xd7, 0xc9, 0xde, 0xd0, 0x02, + 0xa9, 0xff, 0x18, 0x60, 0xc9, 0x22, 0xd0, 0x08, + 0xa5, 0xd4, 0x49, 0x01, 0x85, 0xd4, 0xa9, 0x22, + 0x60, 0x09, 0x40, 0xa6, 0xc7, 0xf0, 0x02, 0x09, + 0x80, 0xa6, 0xd8, 0xf0, 0x02, 0xc6, 0xd8, 0xae, + 0x86, 0x02, 0x20, 0x13, 0xea, 0x20, 0xb6, 0xe6, + 0x68, 0xa8, 0xa5, 0xd8, 0xf0, 0x02, 0x46, 0xd4, + 0x68, 0xaa, 0x68, 0x18, 0x58, 0x60, 0x20, 0xb3, + 0xe8, 0xe6, 0xd3, 0xa5, 0xd5, 0xc5, 0xd3, 0xb0, + 0x3f, 0xc9, 0x4f, 0xf0, 0x32, 0xad, 0x92, 0x02, + 0xf0, 0x03, 0x4c, 0x67, 0xe9, 0xa6, 0xd6, 0xe0, + 0x19, 0x90, 0x07, 0x20, 0xea, 0xe8, 0xc6, 0xd6, + 0xa6, 0xd6, 0x16, 0xd9, 0x56, 0xd9, 0xe8, 0xb5, + 0xd9, 0x09, 0x80, 0x95, 0xd9, 0xca, 0xa5, 0xd5, + 0x18, 0x69, 0x28, 0x85, 0xd5, 0xb5, 0xd9, 0x30, + 0x03, 0xca, 0xd0, 0xf9, 0x4c, 0xf0, 0xe9, 0xc6, + 0xd6, 0x20, 0x7c, 0xe8, 0xa9, 0x00, 0x85, 0xd3, + 0x60, 0xa6, 0xd6, 0xd0, 0x06, 0x86, 0xd3, 0x68, + 0x68, 0xd0, 0x9d, 0xca, 0x86, 0xd6, 0x20, 0x6c, + 0xe5, 0xa4, 0xd5, 0x84, 0xd3, 0x60, 0x48, 0x85, + 0xd7, 0x8a, 0x48, 0x98, 0x48, 0xa9, 0x00, 0x85, + 0xd0, 0xa4, 0xd3, 0xa5, 0xd7, 0x10, 0x03, 0x4c, + 0xd4, 0xe7, 0xc9, 0x0d, 0xd0, 0x03, 0x4c, 0x91, + 0xe8, 0xc9, 0x20, 0x90, 0x10, 0xc9, 0x60, 0x90, + 0x04, 0x29, 0xdf, 0xd0, 0x02, 0x29, 0x3f, 0x20, + 0x84, 0xe6, 0x4c, 0x93, 0xe6, 0xa6, 0xd8, 0xf0, + 0x03, 0x4c, 0x97, 0xe6, 0xc9, 0x14, 0xd0, 0x2e, + 0x98, 0xd0, 0x06, 0x20, 0x01, 0xe7, 0x4c, 0x73, + 0xe7, 0x20, 0xa1, 0xe8, 0x88, 0x84, 0xd3, 0x20, + 0x24, 0xea, 0xc8, 0xb1, 0xd1, 0x88, 0x91, 0xd1, + 0xc8, 0xb1, 0xf3, 0x88, 0x91, 0xf3, 0xc8, 0xc4, + 0xd5, 0xd0, 0xef, 0xa9, 0x20, 0x91, 0xd1, 0xad, + 0x86, 0x02, 0x91, 0xf3, 0x10, 0x4d, 0xa6, 0xd4, + 0xf0, 0x03, 0x4c, 0x97, 0xe6, 0xc9, 0x12, 0xd0, + 0x02, 0x85, 0xc7, 0xc9, 0x13, 0xd0, 0x03, 0x20, + 0x66, 0xe5, 0xc9, 0x1d, 0xd0, 0x17, 0xc8, 0x20, + 0xb3, 0xe8, 0x84, 0xd3, 0x88, 0xc4, 0xd5, 0x90, + 0x09, 0xc6, 0xd6, 0x20, 0x7c, 0xe8, 0xa0, 0x00, + 0x84, 0xd3, 0x4c, 0xa8, 0xe6, 0xc9, 0x11, 0xd0, + 0x1d, 0x18, 0x98, 0x69, 0x28, 0xa8, 0xe6, 0xd6, + 0xc5, 0xd5, 0x90, 0xec, 0xf0, 0xea, 0xc6, 0xd6, + 0xe9, 0x28, 0x90, 0x04, 0x85, 0xd3, 0xd0, 0xf8, + 0x20, 0x7c, 0xe8, 0x4c, 0xa8, 0xe6, 0x20, 0xcb, + 0xe8, 0x4c, 0x44, 0xec, 0x29, 0x7f, 0xc9, 0x7f, + 0xd0, 0x02, 0xa9, 0x5e, 0xc9, 0x20, 0x90, 0x03, + 0x4c, 0x91, 0xe6, 0xc9, 0x0d, 0xd0, 0x03, 0x4c, + 0x91, 0xe8, 0xa6, 0xd4, 0xd0, 0x3f, 0xc9, 0x14, + 0xd0, 0x37, 0xa4, 0xd5, 0xb1, 0xd1, 0xc9, 0x20, + 0xd0, 0x04, 0xc4, 0xd3, 0xd0, 0x07, 0xc0, 0x4f, + 0xf0, 0x24, 0x20, 0x65, 0xe9, 0xa4, 0xd5, 0x20, + 0x24, 0xea, 0x88, 0xb1, 0xd1, 0xc8, 0x91, 0xd1, + 0x88, 0xb1, 0xf3, 0xc8, 0x91, 0xf3, 0x88, 0xc4, + 0xd3, 0xd0, 0xef, 0xa9, 0x20, 0x91, 0xd1, 0xad, + 0x86, 0x02, 0x91, 0xf3, 0xe6, 0xd8, 0x4c, 0xa8, + 0xe6, 0xa6, 0xd8, 0xf0, 0x05, 0x09, 0x40, 0x4c, + 0x97, 0xe6, 0xc9, 0x11, 0xd0, 0x16, 0xa6, 0xd6, + 0xf0, 0x37, 0xc6, 0xd6, 0xa5, 0xd3, 0x38, 0xe9, + 0x28, 0x90, 0x04, 0x85, 0xd3, 0x10, 0x2a, 0x20, + 0x6c, 0xe5, 0xd0, 0x25, 0xc9, 0x12, 0xd0, 0x04, + 0xa9, 0x00, 0x85, 0xc7, 0xc9, 0x1d, 0xd0, 0x12, + 0x98, 0xf0, 0x09, 0x20, 0xa1, 0xe8, 0x88, 0x84, + 0xd3, 0x4c, 0xa8, 0xe6, 0x20, 0x01, 0xe7, 0x4c, + 0xa8, 0xe6, 0xc9, 0x13, 0xd0, 0x06, 0x20, 0x44, + 0xe5, 0x4c, 0xa8, 0xe6, 0x09, 0x80, 0x20, 0xcb, + 0xe8, 0x4c, 0x4f, 0xec, 0x46, 0xc9, 0xa6, 0xd6, + 0xe8, 0xe0, 0x19, 0xd0, 0x03, 0x20, 0xea, 0xe8, + 0xb5, 0xd9, 0x10, 0xf4, 0x86, 0xd6, 0x4c, 0x6c, + 0xe5, 0xa2, 0x00, 0x86, 0xd8, 0x86, 0xc7, 0x86, + 0xd4, 0x86, 0xd3, 0x20, 0x7c, 0xe8, 0x4c, 0xa8, + 0xe6, 0xa2, 0x02, 0xa9, 0x00, 0xc5, 0xd3, 0xf0, + 0x07, 0x18, 0x69, 0x28, 0xca, 0xd0, 0xf6, 0x60, + 0xc6, 0xd6, 0x60, 0xa2, 0x02, 0xa9, 0x27, 0xc5, + 0xd3, 0xf0, 0x07, 0x18, 0x69, 0x28, 0xca, 0xd0, + 0xf6, 0x60, 0xa6, 0xd6, 0xe0, 0x19, 0xf0, 0x02, + 0xe6, 0xd6, 0x60, 0xa2, 0x0f, 0xdd, 0xda, 0xe8, + 0xf0, 0x04, 0xca, 0x10, 0xf8, 0x60, 0x8e, 0x86, + 0x02, 0x60, 0x90, 0x05, 0x1c, 0x9f, 0x9c, 0x1e, + 0x1f, 0x9e, 0x81, 0x95, 0x96, 0x97, 0x98, 0x99, + 0x9a, 0x9b, 0xa5, 0xac, 0x48, 0xa5, 0xad, 0x48, + 0xa5, 0xae, 0x48, 0xa5, 0xaf, 0x48, 0xa2, 0xff, + 0xc6, 0xd6, 0xc6, 0xc9, 0xce, 0xa5, 0x02, 0xe8, + 0x20, 0xf0, 0xe9, 0xe0, 0x18, 0xb0, 0x0c, 0xbd, + 0xf1, 0xec, 0x85, 0xac, 0xb5, 0xda, 0x20, 0xc8, + 0xe9, 0x30, 0xec, 0x20, 0xff, 0xe9, 0xa2, 0x00, + 0xb5, 0xd9, 0x29, 0x7f, 0xb4, 0xda, 0x10, 0x02, + 0x09, 0x80, 0x95, 0xd9, 0xe8, 0xe0, 0x18, 0xd0, + 0xef, 0xa5, 0xf1, 0x09, 0x80, 0x85, 0xf1, 0xa5, + 0xd9, 0x10, 0xc3, 0xe6, 0xd6, 0xee, 0xa5, 0x02, + 0xa9, 0x7f, 0x8d, 0x00, 0xdc, 0xad, 0x01, 0xdc, + 0xc9, 0xfb, 0x08, 0xa9, 0x7f, 0x8d, 0x00, 0xdc, + 0x28, 0xd0, 0x0b, 0xa0, 0x00, 0xea, 0xca, 0xd0, + 0xfc, 0x88, 0xd0, 0xf9, 0x84, 0xc6, 0xa6, 0xd6, + 0x68, 0x85, 0xaf, 0x68, 0x85, 0xae, 0x68, 0x85, + 0xad, 0x68, 0x85, 0xac, 0x60, 0xa6, 0xd6, 0xe8, + 0xb5, 0xd9, 0x10, 0xfb, 0x8e, 0xa5, 0x02, 0xe0, + 0x18, 0xf0, 0x0e, 0x90, 0x0c, 0x20, 0xea, 0xe8, + 0xae, 0xa5, 0x02, 0xca, 0xc6, 0xd6, 0x4c, 0xda, + 0xe6, 0xa5, 0xac, 0x48, 0xa5, 0xad, 0x48, 0xa5, + 0xae, 0x48, 0xa5, 0xaf, 0x48, 0xa2, 0x19, 0xca, + 0x20, 0xf0, 0xe9, 0xec, 0xa5, 0x02, 0x90, 0x0e, + 0xf0, 0x0c, 0xbd, 0xef, 0xec, 0x85, 0xac, 0xb5, + 0xd8, 0x20, 0xc8, 0xe9, 0x30, 0xe9, 0x20, 0xff, + 0xe9, 0xa2, 0x17, 0xec, 0xa5, 0x02, 0x90, 0x0f, + 0xb5, 0xda, 0x29, 0x7f, 0xb4, 0xd9, 0x10, 0x02, + 0x09, 0x80, 0x95, 0xda, 0xca, 0xd0, 0xec, 0xae, + 0xa5, 0x02, 0x20, 0xda, 0xe6, 0x4c, 0x58, 0xe9, + 0x29, 0x03, 0x0d, 0x88, 0x02, 0x85, 0xad, 0x20, + 0xe0, 0xe9, 0xa0, 0x27, 0xb1, 0xac, 0x91, 0xd1, + 0xb1, 0xae, 0x91, 0xf3, 0x88, 0x10, 0xf5, 0x60, + 0x20, 0x24, 0xea, 0xa5, 0xac, 0x85, 0xae, 0xa5, + 0xad, 0x29, 0x03, 0x09, 0xd8, 0x85, 0xaf, 0x60, + 0xbd, 0xf0, 0xec, 0x85, 0xd1, 0xb5, 0xd9, 0x29, + 0x03, 0x0d, 0x88, 0x02, 0x85, 0xd2, 0x60, 0xa0, + 0x27, 0x20, 0xf0, 0xe9, 0x20, 0x24, 0xea, 0x20, + 0xda, 0xe4, 0xa9, 0x20, 0x91, 0xd1, 0x88, 0x10, + 0xf6, 0x60, 0xea, 0xa8, 0xa9, 0x02, 0x85, 0xcd, + 0x20, 0x24, 0xea, 0x98, 0xa4, 0xd3, 0x91, 0xd1, + 0x8a, 0x91, 0xf3, 0x60, 0xa5, 0xd1, 0x85, 0xf3, + 0xa5, 0xd2, 0x29, 0x03, 0x09, 0xd8, 0x85, 0xf4, + 0x60, 0x20, 0xea, 0xff, 0xa5, 0xcc, 0xd0, 0x29, + 0xc6, 0xcd, 0xd0, 0x25, 0xa9, 0x14, 0x85, 0xcd, + 0xa4, 0xd3, 0x46, 0xcf, 0xae, 0x87, 0x02, 0xb1, + 0xd1, 0xb0, 0x11, 0xe6, 0xcf, 0x85, 0xce, 0x20, + 0x24, 0xea, 0xb1, 0xf3, 0x8d, 0x87, 0x02, 0xae, + 0x86, 0x02, 0xa5, 0xce, 0x49, 0x80, 0x20, 0x1c, + 0xea, 0xa5, 0x01, 0x29, 0x10, 0xf0, 0x0a, 0xa0, + 0x00, 0x84, 0xc0, 0xa5, 0x01, 0x09, 0x20, 0xd0, + 0x08, 0xa5, 0xc0, 0xd0, 0x06, 0xa5, 0x01, 0x29, + 0x1f, 0x85, 0x01, 0x20, 0x87, 0xea, 0xad, 0x0d, + 0xdc, 0x68, 0xa8, 0x68, 0xaa, 0x68, 0x40, 0xa9, + 0x00, 0x8d, 0x8d, 0x02, 0xa0, 0x40, 0x84, 0xcb, + 0x8d, 0x00, 0xdc, 0xae, 0x01, 0xdc, 0xe0, 0xff, + 0xf0, 0x61, 0xa8, 0xa9, 0x81, 0x85, 0xf5, 0xa9, + 0xeb, 0x85, 0xf6, 0xa9, 0xfe, 0x8d, 0x00, 0xdc, + 0xa2, 0x08, 0x48, 0xad, 0x01, 0xdc, 0xcd, 0x01, + 0xdc, 0xd0, 0xf8, 0x4a, 0xb0, 0x16, 0x48, 0xb1, + 0xf5, 0xc9, 0x05, 0xb0, 0x0c, 0xc9, 0x03, 0xf0, + 0x08, 0x0d, 0x8d, 0x02, 0x8d, 0x8d, 0x02, 0x10, + 0x02, 0x84, 0xcb, 0x68, 0xc8, 0xc0, 0x41, 0xb0, + 0x0b, 0xca, 0xd0, 0xdf, 0x38, 0x68, 0x2a, 0x8d, + 0x00, 0xdc, 0xd0, 0xcc, 0x68, 0x6c, 0x8f, 0x02, + 0xa4, 0xcb, 0xb1, 0xf5, 0xaa, 0xc4, 0xc5, 0xf0, + 0x07, 0xa0, 0x10, 0x8c, 0x8c, 0x02, 0xd0, 0x36, + 0x29, 0x7f, 0x2c, 0x8a, 0x02, 0x30, 0x16, 0x70, + 0x49, 0xc9, 0x7f, 0xf0, 0x29, 0xc9, 0x14, 0xf0, + 0x0c, 0xc9, 0x20, 0xf0, 0x08, 0xc9, 0x1d, 0xf0, + 0x04, 0xc9, 0x11, 0xd0, 0x35, 0xac, 0x8c, 0x02, + 0xf0, 0x05, 0xce, 0x8c, 0x02, 0xd0, 0x2b, 0xce, + 0x8b, 0x02, 0xd0, 0x26, 0xa0, 0x04, 0x8c, 0x8b, + 0x02, 0xa4, 0xc6, 0x88, 0x10, 0x1c, 0xa4, 0xcb, + 0x84, 0xc5, 0xac, 0x8d, 0x02, 0x8c, 0x8e, 0x02, + 0xe0, 0xff, 0xf0, 0x0e, 0x8a, 0xa6, 0xc6, 0xec, + 0x89, 0x02, 0xb0, 0x06, 0x9d, 0x77, 0x02, 0xe8, + 0x86, 0xc6, 0xa9, 0x7f, 0x8d, 0x00, 0xdc, 0x60, + 0xad, 0x8d, 0x02, 0xc9, 0x03, 0xd0, 0x15, 0xcd, + 0x8e, 0x02, 0xf0, 0xee, 0xad, 0x91, 0x02, 0x30, + 0x1d, 0xad, 0x18, 0xd0, 0x49, 0x02, 0x8d, 0x18, + 0xd0, 0x4c, 0x76, 0xeb, 0x0a, 0xc9, 0x08, 0x90, + 0x02, 0xa9, 0x06, 0xaa, 0xbd, 0x79, 0xeb, 0x85, + 0xf5, 0xbd, 0x7a, 0xeb, 0x85, 0xf6, 0x4c, 0xe0, + 0xea, 0x81, 0xeb, 0xc2, 0xeb, 0x03, 0xec, 0x78, + 0xec, 0x14, 0x0d, 0x1d, 0x88, 0x85, 0x86, 0x87, + 0x11, 0x33, 0x57, 0x41, 0x34, 0x5a, 0x53, 0x45, + 0x01, 0x35, 0x52, 0x44, 0x36, 0x43, 0x46, 0x54, + 0x58, 0x37, 0x59, 0x47, 0x38, 0x42, 0x48, 0x55, + 0x56, 0x39, 0x49, 0x4a, 0x30, 0x4d, 0x4b, 0x4f, + 0x4e, 0x2b, 0x50, 0x4c, 0x2d, 0x2e, 0x3a, 0x40, + 0x2c, 0x5c, 0x2a, 0x3b, 0x13, 0x01, 0x3d, 0x5e, + 0x2f, 0x31, 0x5f, 0x04, 0x32, 0x20, 0x02, 0x51, + 0x03, 0xff, 0x94, 0x8d, 0x9d, 0x8c, 0x89, 0x8a, + 0x8b, 0x91, 0x23, 0xd7, 0xc1, 0x24, 0xda, 0xd3, + 0xc5, 0x01, 0x25, 0xd2, 0xc4, 0x26, 0xc3, 0xc6, + 0xd4, 0xd8, 0x27, 0xd9, 0xc7, 0x28, 0xc2, 0xc8, + 0xd5, 0xd6, 0x29, 0xc9, 0xca, 0x30, 0xcd, 0xcb, + 0xcf, 0xce, 0xdb, 0xd0, 0xcc, 0xdd, 0x3e, 0x5b, + 0xba, 0x3c, 0xa9, 0xc0, 0x5d, 0x93, 0x01, 0x3d, + 0xde, 0x3f, 0x21, 0x5f, 0x04, 0x22, 0xa0, 0x02, + 0xd1, 0x83, 0xff, 0x94, 0x8d, 0x9d, 0x8c, 0x89, + 0x8a, 0x8b, 0x91, 0x96, 0xb3, 0xb0, 0x97, 0xad, + 0xae, 0xb1, 0x01, 0x98, 0xb2, 0xac, 0x99, 0xbc, + 0xbb, 0xa3, 0xbd, 0x9a, 0xb7, 0xa5, 0x9b, 0xbf, + 0xb4, 0xb8, 0xbe, 0x29, 0xa2, 0xb5, 0x30, 0xa7, + 0xa1, 0xb9, 0xaa, 0xa6, 0xaf, 0xb6, 0xdc, 0x3e, + 0x5b, 0xa4, 0x3c, 0xa8, 0xdf, 0x5d, 0x93, 0x01, + 0x3d, 0xde, 0x3f, 0x81, 0x5f, 0x04, 0x95, 0xa0, + 0x02, 0xab, 0x83, 0xff, 0xc9, 0x0e, 0xd0, 0x07, + 0xad, 0x18, 0xd0, 0x09, 0x02, 0xd0, 0x09, 0xc9, + 0x8e, 0xd0, 0x0b, 0xad, 0x18, 0xd0, 0x29, 0xfd, + 0x8d, 0x18, 0xd0, 0x4c, 0xa8, 0xe6, 0xc9, 0x08, + 0xd0, 0x07, 0xa9, 0x80, 0x0d, 0x91, 0x02, 0x30, + 0x09, 0xc9, 0x09, 0xd0, 0xee, 0xa9, 0x7f, 0x2d, + 0x91, 0x02, 0x8d, 0x91, 0x02, 0x4c, 0xa8, 0xe6, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x1c, 0x17, 0x01, 0x9f, 0x1a, 0x13, 0x05, 0xff, + 0x9c, 0x12, 0x04, 0x1e, 0x03, 0x06, 0x14, 0x18, + 0x1f, 0x19, 0x07, 0x9e, 0x02, 0x08, 0x15, 0x16, + 0x12, 0x09, 0x0a, 0x92, 0x0d, 0x0b, 0x0f, 0x0e, + 0xff, 0x10, 0x0c, 0xff, 0xff, 0x1b, 0x00, 0xff, + 0x1c, 0xff, 0x1d, 0xff, 0xff, 0x1f, 0x1e, 0xff, + 0x90, 0x06, 0xff, 0x05, 0xff, 0xff, 0x11, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x9b, 0x37, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x14, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0e, 0x06, 0x01, 0x02, 0x03, 0x04, 0x00, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x4c, + 0x4f, 0x41, 0x44, 0x0d, 0x52, 0x55, 0x4e, 0x0d, + 0x00, 0x28, 0x50, 0x78, 0xa0, 0xc8, 0xf0, 0x18, + 0x40, 0x68, 0x90, 0xb8, 0xe0, 0x08, 0x30, 0x58, + 0x80, 0xa8, 0xd0, 0xf8, 0x20, 0x48, 0x70, 0x98, + 0xc0, 0x09, 0x40, 0x2c, 0x09, 0x20, 0x20, 0xa4, + 0xf0, 0x48, 0x24, 0x94, 0x10, 0x0a, 0x38, 0x66, + 0xa3, 0x20, 0x40, 0xed, 0x46, 0x94, 0x46, 0xa3, + 0x68, 0x85, 0x95, 0x78, 0x20, 0x97, 0xee, 0xc9, + 0x3f, 0xd0, 0x03, 0x20, 0x85, 0xee, 0xad, 0x00, + 0xdd, 0x09, 0x08, 0x8d, 0x00, 0xdd, 0x78, 0x20, + 0x8e, 0xee, 0x20, 0x97, 0xee, 0x20, 0xb3, 0xee, + 0x78, 0x20, 0x97, 0xee, 0x20, 0xa9, 0xee, 0xb0, + 0x64, 0x20, 0x85, 0xee, 0x24, 0xa3, 0x10, 0x0a, + 0x20, 0xa9, 0xee, 0x90, 0xfb, 0x20, 0xa9, 0xee, + 0xb0, 0xfb, 0x20, 0xa9, 0xee, 0x90, 0xfb, 0x20, + 0x8e, 0xee, 0xa9, 0x08, 0x85, 0xa5, 0xad, 0x00, + 0xdd, 0xcd, 0x00, 0xdd, 0xd0, 0xf8, 0x0a, 0x90, + 0x3f, 0x66, 0x95, 0xb0, 0x05, 0x20, 0xa0, 0xee, + 0xd0, 0x03, 0x20, 0x97, 0xee, 0x20, 0x85, 0xee, + 0xea, 0xea, 0xea, 0xea, 0xad, 0x00, 0xdd, 0x29, + 0xdf, 0x09, 0x10, 0x8d, 0x00, 0xdd, 0xc6, 0xa5, + 0xd0, 0xd4, 0xa9, 0x04, 0x8d, 0x07, 0xdc, 0xa9, + 0x19, 0x8d, 0x0f, 0xdc, 0xad, 0x0d, 0xdc, 0xad, + 0x0d, 0xdc, 0x29, 0x02, 0xd0, 0x0a, 0x20, 0xa9, + 0xee, 0xb0, 0xf4, 0x58, 0x60, 0xa9, 0x80, 0x2c, + 0xa9, 0x03, 0x20, 0x1c, 0xfe, 0x58, 0x18, 0x90, + 0x4a, 0x85, 0x95, 0x20, 0x36, 0xed, 0xad, 0x00, + 0xdd, 0x29, 0xf7, 0x8d, 0x00, 0xdd, 0x60, 0x85, + 0x95, 0x20, 0x36, 0xed, 0x78, 0x20, 0xa0, 0xee, + 0x20, 0xbe, 0xed, 0x20, 0x85, 0xee, 0x20, 0xa9, + 0xee, 0x30, 0xfb, 0x58, 0x60, 0x24, 0x94, 0x30, + 0x05, 0x38, 0x66, 0x94, 0xd0, 0x05, 0x48, 0x20, + 0x40, 0xed, 0x68, 0x85, 0x95, 0x18, 0x60, 0x78, + 0x20, 0x8e, 0xee, 0xad, 0x00, 0xdd, 0x09, 0x08, + 0x8d, 0x00, 0xdd, 0xa9, 0x5f, 0x2c, 0xa9, 0x3f, + 0x20, 0x11, 0xed, 0x20, 0xbe, 0xed, 0x8a, 0xa2, + 0x0a, 0xca, 0xd0, 0xfd, 0xaa, 0x20, 0x85, 0xee, + 0x4c, 0x97, 0xee, 0x78, 0xa9, 0x00, 0x85, 0xa5, + 0x20, 0x85, 0xee, 0x20, 0xa9, 0xee, 0x10, 0xfb, + 0xa9, 0x01, 0x8d, 0x07, 0xdc, 0xa9, 0x19, 0x8d, + 0x0f, 0xdc, 0x20, 0x97, 0xee, 0xad, 0x0d, 0xdc, + 0xad, 0x0d, 0xdc, 0x29, 0x02, 0xd0, 0x07, 0x20, + 0xa9, 0xee, 0x30, 0xf4, 0x10, 0x18, 0xa5, 0xa5, + 0xf0, 0x05, 0xa9, 0x02, 0x4c, 0xb2, 0xed, 0x20, + 0xa0, 0xee, 0x20, 0x85, 0xee, 0xa9, 0x40, 0x20, + 0x1c, 0xfe, 0xe6, 0xa5, 0xd0, 0xca, 0xa9, 0x08, + 0x85, 0xa5, 0xad, 0x00, 0xdd, 0xcd, 0x00, 0xdd, + 0xd0, 0xf8, 0x0a, 0x10, 0xf5, 0x66, 0xa4, 0xad, + 0x00, 0xdd, 0xcd, 0x00, 0xdd, 0xd0, 0xf8, 0x0a, + 0x30, 0xf5, 0xc6, 0xa5, 0xd0, 0xe4, 0x20, 0xa0, + 0xee, 0x24, 0x90, 0x50, 0x03, 0x20, 0x06, 0xee, + 0xa5, 0xa4, 0x58, 0x18, 0x60, 0xad, 0x00, 0xdd, + 0x29, 0xef, 0x8d, 0x00, 0xdd, 0x60, 0xad, 0x00, + 0xdd, 0x09, 0x10, 0x8d, 0x00, 0xdd, 0x60, 0xad, + 0x00, 0xdd, 0x29, 0xdf, 0x8d, 0x00, 0xdd, 0x60, + 0xad, 0x00, 0xdd, 0x09, 0x20, 0x8d, 0x00, 0xdd, + 0x60, 0xad, 0x00, 0xdd, 0xcd, 0x00, 0xdd, 0xd0, + 0xf8, 0x0a, 0x60, 0x8a, 0xa2, 0xb8, 0xca, 0xd0, + 0xfd, 0xaa, 0x60, 0xa5, 0xb4, 0xf0, 0x47, 0x30, + 0x3f, 0x46, 0xb6, 0xa2, 0x00, 0x90, 0x01, 0xca, + 0x8a, 0x45, 0xbd, 0x85, 0xbd, 0xc6, 0xb4, 0xf0, + 0x06, 0x8a, 0x29, 0x04, 0x85, 0xb5, 0x60, 0xa9, + 0x20, 0x2c, 0x94, 0x02, 0xf0, 0x14, 0x30, 0x1c, + 0x70, 0x14, 0xa5, 0xbd, 0xd0, 0x01, 0xca, 0xc6, + 0xb4, 0xad, 0x93, 0x02, 0x10, 0xe3, 0xc6, 0xb4, + 0xd0, 0xdf, 0xe6, 0xb4, 0xd0, 0xf0, 0xa5, 0xbd, + 0xf0, 0xed, 0xd0, 0xea, 0x70, 0xe9, 0x50, 0xe6, + 0xe6, 0xb4, 0xa2, 0xff, 0xd0, 0xcb, 0xad, 0x94, + 0x02, 0x4a, 0x90, 0x07, 0x2c, 0x01, 0xdd, 0x10, + 0x1d, 0x50, 0x1e, 0xa9, 0x00, 0x85, 0xbd, 0x85, + 0xb5, 0xae, 0x98, 0x02, 0x86, 0xb4, 0xac, 0x9d, + 0x02, 0xcc, 0x9e, 0x02, 0xf0, 0x13, 0xb1, 0xf9, + 0x85, 0xb6, 0xee, 0x9d, 0x02, 0x60, 0xa9, 0x40, + 0x2c, 0xa9, 0x10, 0x0d, 0x97, 0x02, 0x8d, 0x97, + 0x02, 0xa9, 0x01, 0x8d, 0x0d, 0xdd, 0x4d, 0xa1, + 0x02, 0x09, 0x80, 0x8d, 0xa1, 0x02, 0x8d, 0x0d, + 0xdd, 0x60, 0xa2, 0x09, 0xa9, 0x20, 0x2c, 0x93, + 0x02, 0xf0, 0x01, 0xca, 0x50, 0x02, 0xca, 0xca, + 0x60, 0xa6, 0xa9, 0xd0, 0x33, 0xc6, 0xa8, 0xf0, + 0x36, 0x30, 0x0d, 0xa5, 0xa7, 0x45, 0xab, 0x85, + 0xab, 0x46, 0xa7, 0x66, 0xaa, 0x60, 0xc6, 0xa8, + 0xa5, 0xa7, 0xf0, 0x67, 0xad, 0x93, 0x02, 0x0a, + 0xa9, 0x01, 0x65, 0xa8, 0xd0, 0xef, 0xa9, 0x90, + 0x8d, 0x0d, 0xdd, 0x0d, 0xa1, 0x02, 0x8d, 0xa1, + 0x02, 0x85, 0xa9, 0xa9, 0x02, 0x4c, 0x3b, 0xef, + 0xa5, 0xa7, 0xd0, 0xea, 0x4c, 0xd3, 0xe4, 0xac, + 0x9b, 0x02, 0xc8, 0xcc, 0x9c, 0x02, 0xf0, 0x2a, + 0x8c, 0x9b, 0x02, 0x88, 0xa5, 0xaa, 0xae, 0x98, + 0x02, 0xe0, 0x09, 0xf0, 0x04, 0x4a, 0xe8, 0xd0, + 0xf8, 0x91, 0xf7, 0xa9, 0x20, 0x2c, 0x94, 0x02, + 0xf0, 0xb4, 0x30, 0xb1, 0xa5, 0xa7, 0x45, 0xab, + 0xf0, 0x03, 0x70, 0xa9, 0x2c, 0x50, 0xa6, 0xa9, + 0x01, 0x2c, 0xa9, 0x04, 0x2c, 0xa9, 0x80, 0x2c, + 0xa9, 0x02, 0x0d, 0x97, 0x02, 0x8d, 0x97, 0x02, + 0x4c, 0x7e, 0xef, 0xa5, 0xaa, 0xd0, 0xf1, 0xf0, + 0xec, 0x85, 0x9a, 0xad, 0x94, 0x02, 0x4a, 0x90, + 0x29, 0xa9, 0x02, 0x2c, 0x01, 0xdd, 0x10, 0x1d, + 0xd0, 0x20, 0xad, 0xa1, 0x02, 0x29, 0x02, 0xd0, + 0xf9, 0x2c, 0x01, 0xdd, 0x70, 0xfb, 0xad, 0x01, + 0xdd, 0x09, 0x02, 0x8d, 0x01, 0xdd, 0x2c, 0x01, + 0xdd, 0x70, 0x07, 0x30, 0xf9, 0xa9, 0x40, 0x8d, + 0x97, 0x02, 0x18, 0x60, 0x20, 0x28, 0xf0, 0xac, + 0x9e, 0x02, 0xc8, 0xcc, 0x9d, 0x02, 0xf0, 0xf4, + 0x8c, 0x9e, 0x02, 0x88, 0xa5, 0x9e, 0x91, 0xf9, + 0xad, 0xa1, 0x02, 0x4a, 0xb0, 0x1e, 0xa9, 0x10, + 0x8d, 0x0e, 0xdd, 0xad, 0x99, 0x02, 0x8d, 0x04, + 0xdd, 0xad, 0x9a, 0x02, 0x8d, 0x05, 0xdd, 0xa9, + 0x81, 0x20, 0x3b, 0xef, 0x20, 0x06, 0xef, 0xa9, + 0x11, 0x8d, 0x0e, 0xdd, 0x60, 0x85, 0x99, 0xad, + 0x94, 0x02, 0x4a, 0x90, 0x28, 0x29, 0x08, 0xf0, + 0x24, 0xa9, 0x02, 0x2c, 0x01, 0xdd, 0x10, 0xad, + 0xf0, 0x22, 0xad, 0xa1, 0x02, 0x4a, 0xb0, 0xfa, + 0xad, 0x01, 0xdd, 0x29, 0xfd, 0x8d, 0x01, 0xdd, + 0xad, 0x01, 0xdd, 0x29, 0x04, 0xf0, 0xf9, 0xa9, + 0x90, 0x18, 0x4c, 0x3b, 0xef, 0xad, 0xa1, 0x02, + 0x29, 0x12, 0xf0, 0xf3, 0x18, 0x60, 0xad, 0x97, + 0x02, 0xac, 0x9c, 0x02, 0xcc, 0x9b, 0x02, 0xf0, + 0x0b, 0x29, 0xf7, 0x8d, 0x97, 0x02, 0xb1, 0xf7, + 0xee, 0x9c, 0x02, 0x60, 0x09, 0x08, 0x8d, 0x97, + 0x02, 0xa9, 0x00, 0x60, 0x48, 0xad, 0xa1, 0x02, + 0xf0, 0x11, 0xad, 0xa1, 0x02, 0x29, 0x03, 0xd0, + 0xf9, 0xa9, 0x10, 0x8d, 0x0d, 0xdd, 0xa9, 0x00, + 0x8d, 0xa1, 0x02, 0x68, 0x60, 0x0d, 0x49, 0x2f, + 0x4f, 0x20, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x20, + 0xa3, 0x0d, 0x53, 0x45, 0x41, 0x52, 0x43, 0x48, + 0x49, 0x4e, 0x47, 0xa0, 0x46, 0x4f, 0x52, 0xa0, + 0x0d, 0x50, 0x52, 0x45, 0x53, 0x53, 0x20, 0x50, + 0x4c, 0x41, 0x59, 0x20, 0x4f, 0x4e, 0x20, 0x54, + 0x41, 0x50, 0xc5, 0x50, 0x52, 0x45, 0x53, 0x53, + 0x20, 0x52, 0x45, 0x43, 0x4f, 0x52, 0x44, 0x20, + 0x26, 0x20, 0x50, 0x4c, 0x41, 0x59, 0x20, 0x4f, + 0x4e, 0x20, 0x54, 0x41, 0x50, 0xc5, 0x0d, 0x4c, + 0x4f, 0x41, 0x44, 0x49, 0x4e, 0xc7, 0x0d, 0x53, + 0x41, 0x56, 0x49, 0x4e, 0x47, 0xa0, 0x0d, 0x56, + 0x45, 0x52, 0x49, 0x46, 0x59, 0x49, 0x4e, 0xc7, + 0x0d, 0x46, 0x4f, 0x55, 0x4e, 0x44, 0xa0, 0x0d, + 0x4f, 0x4b, 0x8d, 0x24, 0x9d, 0x10, 0x0d, 0xb9, + 0xbd, 0xf0, 0x08, 0x29, 0x7f, 0x20, 0xd2, 0xff, + 0xc8, 0x28, 0x10, 0xf3, 0x18, 0x60, 0xa5, 0x99, + 0xd0, 0x08, 0xa5, 0xc6, 0xf0, 0x0f, 0x78, 0x4c, + 0xb4, 0xe5, 0xc9, 0x02, 0xd0, 0x18, 0x84, 0x97, + 0x20, 0x86, 0xf0, 0xa4, 0x97, 0x18, 0x60, 0xa5, + 0x99, 0xd0, 0x0b, 0xa5, 0xd3, 0x85, 0xca, 0xa5, + 0xd6, 0x85, 0xc9, 0x4c, 0x32, 0xe6, 0xc9, 0x03, + 0xd0, 0x09, 0x85, 0xd0, 0xa5, 0xd5, 0x85, 0xc8, + 0x4c, 0x32, 0xe6, 0xb0, 0x38, 0xc9, 0x02, 0xf0, + 0x3f, 0x86, 0x97, 0x20, 0x99, 0xf1, 0xb0, 0x16, + 0x48, 0x20, 0x99, 0xf1, 0xb0, 0x0d, 0xd0, 0x05, + 0xa9, 0x40, 0x20, 0x1c, 0xfe, 0xc6, 0xa6, 0xa6, + 0x97, 0x68, 0x60, 0xaa, 0x68, 0x8a, 0xa6, 0x97, + 0x60, 0x20, 0x0d, 0xf8, 0xd0, 0x0b, 0x20, 0x41, + 0xf8, 0xb0, 0x11, 0xa9, 0x00, 0x85, 0xa6, 0xf0, + 0xf0, 0xb1, 0xb2, 0x18, 0x60, 0xa5, 0x90, 0xf0, + 0x04, 0xa9, 0x0d, 0x18, 0x60, 0x4c, 0x13, 0xee, + 0x20, 0x4e, 0xf1, 0xb0, 0xf7, 0xc9, 0x00, 0xd0, + 0xf2, 0xad, 0x97, 0x02, 0x29, 0x60, 0xd0, 0xe9, + 0xf0, 0xee, 0x48, 0xa5, 0x9a, 0xc9, 0x03, 0xd0, + 0x04, 0x68, 0x4c, 0x16, 0xe7, 0x90, 0x04, 0x68, + 0x4c, 0xdd, 0xed, 0x4a, 0x68, 0x85, 0x9e, 0x8a, + 0x48, 0x98, 0x48, 0x90, 0x23, 0x20, 0x0d, 0xf8, + 0xd0, 0x0e, 0x20, 0x64, 0xf8, 0xb0, 0x0e, 0xa9, + 0x02, 0xa0, 0x00, 0x91, 0xb2, 0xc8, 0x84, 0xa6, + 0xa5, 0x9e, 0x91, 0xb2, 0x18, 0x68, 0xa8, 0x68, + 0xaa, 0xa5, 0x9e, 0x90, 0x02, 0xa9, 0x00, 0x60, + 0x20, 0x17, 0xf0, 0x4c, 0xfc, 0xf1, 0x20, 0x0f, + 0xf3, 0xf0, 0x03, 0x4c, 0x01, 0xf7, 0x20, 0x1f, + 0xf3, 0xa5, 0xba, 0xf0, 0x16, 0xc9, 0x03, 0xf0, + 0x12, 0xb0, 0x14, 0xc9, 0x02, 0xd0, 0x03, 0x4c, + 0x4d, 0xf0, 0xa6, 0xb9, 0xe0, 0x60, 0xf0, 0x03, + 0x4c, 0x0a, 0xf7, 0x85, 0x99, 0x18, 0x60, 0xaa, + 0x20, 0x09, 0xed, 0xa5, 0xb9, 0x10, 0x06, 0x20, + 0xcc, 0xed, 0x4c, 0x48, 0xf2, 0x20, 0xc7, 0xed, + 0x8a, 0x24, 0x90, 0x10, 0xe6, 0x4c, 0x07, 0xf7, + 0x20, 0x0f, 0xf3, 0xf0, 0x03, 0x4c, 0x01, 0xf7, + 0x20, 0x1f, 0xf3, 0xa5, 0xba, 0xd0, 0x03, 0x4c, + 0x0d, 0xf7, 0xc9, 0x03, 0xf0, 0x0f, 0xb0, 0x11, + 0xc9, 0x02, 0xd0, 0x03, 0x4c, 0xe1, 0xef, 0xa6, + 0xb9, 0xe0, 0x60, 0xf0, 0xea, 0x85, 0x9a, 0x18, + 0x60, 0xaa, 0x20, 0x0c, 0xed, 0xa5, 0xb9, 0x10, + 0x05, 0x20, 0xbe, 0xed, 0xd0, 0x03, 0x20, 0xb9, + 0xed, 0x8a, 0x24, 0x90, 0x10, 0xe7, 0x4c, 0x07, + 0xf7, 0x20, 0x14, 0xf3, 0xf0, 0x02, 0x18, 0x60, + 0x20, 0x1f, 0xf3, 0x8a, 0x48, 0xa5, 0xba, 0xf0, + 0x50, 0xc9, 0x03, 0xf0, 0x4c, 0xb0, 0x47, 0xc9, + 0x02, 0xd0, 0x1d, 0x68, 0x20, 0xf2, 0xf2, 0x20, + 0x83, 0xf4, 0x20, 0x27, 0xfe, 0xa5, 0xf8, 0xf0, + 0x01, 0xc8, 0xa5, 0xfa, 0xf0, 0x01, 0xc8, 0xa9, + 0x00, 0x85, 0xf8, 0x85, 0xfa, 0x4c, 0x7d, 0xf4, + 0xa5, 0xb9, 0x29, 0x0f, 0xf0, 0x23, 0x20, 0xd0, + 0xf7, 0xa9, 0x00, 0x38, 0x20, 0xdd, 0xf1, 0x20, + 0x64, 0xf8, 0x90, 0x04, 0x68, 0xa9, 0x00, 0x60, + 0xa5, 0xb9, 0xc9, 0x62, 0xd0, 0x0b, 0xa9, 0x05, + 0x20, 0x6a, 0xf7, 0x4c, 0xf1, 0xf2, 0x20, 0x42, + 0xf6, 0x68, 0xaa, 0xc6, 0x98, 0xe4, 0x98, 0xf0, + 0x14, 0xa4, 0x98, 0xb9, 0x59, 0x02, 0x9d, 0x59, + 0x02, 0xb9, 0x63, 0x02, 0x9d, 0x63, 0x02, 0xb9, + 0x6d, 0x02, 0x9d, 0x6d, 0x02, 0x18, 0x60, 0xa9, + 0x00, 0x85, 0x90, 0x8a, 0xa6, 0x98, 0xca, 0x30, + 0x15, 0xdd, 0x59, 0x02, 0xd0, 0xf8, 0x60, 0xbd, + 0x59, 0x02, 0x85, 0xb8, 0xbd, 0x63, 0x02, 0x85, + 0xba, 0xbd, 0x6d, 0x02, 0x85, 0xb9, 0x60, 0xa9, + 0x00, 0x85, 0x98, 0xa2, 0x03, 0xe4, 0x9a, 0xb0, + 0x03, 0x20, 0xfe, 0xed, 0xe4, 0x99, 0xb0, 0x03, + 0x20, 0xef, 0xed, 0x86, 0x9a, 0xa9, 0x00, 0x85, + 0x99, 0x60, 0xa6, 0xb8, 0xd0, 0x03, 0x4c, 0x0a, + 0xf7, 0x20, 0x0f, 0xf3, 0xd0, 0x03, 0x4c, 0xfe, + 0xf6, 0xa6, 0x98, 0xe0, 0x0a, 0x90, 0x03, 0x4c, + 0xfb, 0xf6, 0xe6, 0x98, 0xa5, 0xb8, 0x9d, 0x59, + 0x02, 0xa5, 0xb9, 0x09, 0x60, 0x85, 0xb9, 0x9d, + 0x6d, 0x02, 0xa5, 0xba, 0x9d, 0x63, 0x02, 0xf0, + 0x5a, 0xc9, 0x03, 0xf0, 0x56, 0x90, 0x05, 0x20, + 0xd5, 0xf3, 0x90, 0x4f, 0xc9, 0x02, 0xd0, 0x03, + 0x4c, 0x09, 0xf4, 0x20, 0xd0, 0xf7, 0xb0, 0x03, + 0x4c, 0x13, 0xf7, 0xa5, 0xb9, 0x29, 0x0f, 0xd0, + 0x1f, 0x20, 0x17, 0xf8, 0xb0, 0x36, 0x20, 0xaf, + 0xf5, 0xa5, 0xb7, 0xf0, 0x0a, 0x20, 0xea, 0xf7, + 0x90, 0x18, 0xf0, 0x28, 0x4c, 0x04, 0xf7, 0x20, + 0x2c, 0xf7, 0xf0, 0x20, 0x90, 0x0c, 0xb0, 0xf4, + 0x20, 0x38, 0xf8, 0xb0, 0x17, 0xa9, 0x04, 0x20, + 0x6a, 0xf7, 0xa9, 0xbf, 0xa4, 0xb9, 0xc0, 0x60, + 0xf0, 0x07, 0xa0, 0x00, 0xa9, 0x02, 0x91, 0xb2, + 0x98, 0x85, 0xa6, 0x18, 0x60, 0xa5, 0xb9, 0x30, + 0xfa, 0xa4, 0xb7, 0xf0, 0xf6, 0xa9, 0x00, 0x85, + 0x90, 0xa5, 0xba, 0x20, 0x0c, 0xed, 0xa5, 0xb9, + 0x09, 0xf0, 0x20, 0xb9, 0xed, 0xa5, 0x90, 0x10, + 0x05, 0x68, 0x68, 0x4c, 0x07, 0xf7, 0xa5, 0xb7, + 0xf0, 0x0c, 0xa0, 0x00, 0xb1, 0xbb, 0x20, 0xdd, + 0xed, 0xc8, 0xc4, 0xb7, 0xd0, 0xf6, 0x4c, 0x54, + 0xf6, 0x20, 0x83, 0xf4, 0x8c, 0x97, 0x02, 0xc4, + 0xb7, 0xf0, 0x0a, 0xb1, 0xbb, 0x99, 0x93, 0x02, + 0xc8, 0xc0, 0x04, 0xd0, 0xf2, 0x20, 0x4a, 0xef, + 0x8e, 0x98, 0x02, 0xad, 0x93, 0x02, 0x29, 0x0f, + 0xf0, 0x1c, 0x0a, 0xaa, 0xad, 0xa6, 0x02, 0xd0, + 0x09, 0xbc, 0xc1, 0xfe, 0xbd, 0xc0, 0xfe, 0x4c, + 0x40, 0xf4, 0xbc, 0xeb, 0xe4, 0xbd, 0xea, 0xe4, + 0x8c, 0x96, 0x02, 0x8d, 0x95, 0x02, 0xad, 0x95, + 0x02, 0x0a, 0x20, 0x2e, 0xff, 0xad, 0x94, 0x02, + 0x4a, 0x90, 0x09, 0xad, 0x01, 0xdd, 0x0a, 0xb0, + 0x03, 0x20, 0x0d, 0xf0, 0xad, 0x9b, 0x02, 0x8d, + 0x9c, 0x02, 0xad, 0x9e, 0x02, 0x8d, 0x9d, 0x02, + 0x20, 0x27, 0xfe, 0xa5, 0xf8, 0xd0, 0x05, 0x88, + 0x84, 0xf8, 0x86, 0xf7, 0xa5, 0xfa, 0xd0, 0x05, + 0x88, 0x84, 0xfa, 0x86, 0xf9, 0x38, 0xa9, 0xf0, + 0x4c, 0x2d, 0xfe, 0xa9, 0x7f, 0x8d, 0x0d, 0xdd, + 0xa9, 0x06, 0x8d, 0x03, 0xdd, 0x8d, 0x01, 0xdd, + 0xa9, 0x04, 0x0d, 0x00, 0xdd, 0x8d, 0x00, 0xdd, + 0xa0, 0x00, 0x8c, 0xa1, 0x02, 0x60, 0x86, 0xc3, + 0x84, 0xc4, 0x6c, 0x30, 0x03, 0x85, 0x93, 0xa9, + 0x00, 0x85, 0x90, 0xa5, 0xba, 0xd0, 0x03, 0x4c, + 0x13, 0xf7, 0xc9, 0x03, 0xf0, 0xf9, 0x90, 0x7b, + 0xa4, 0xb7, 0xd0, 0x03, 0x4c, 0x10, 0xf7, 0xa6, + 0xb9, 0x20, 0xaf, 0xf5, 0xa9, 0x60, 0x85, 0xb9, + 0x20, 0xd5, 0xf3, 0xa5, 0xba, 0x20, 0x09, 0xed, + 0xa5, 0xb9, 0x20, 0xc7, 0xed, 0x20, 0x13, 0xee, + 0x85, 0xae, 0xa5, 0x90, 0x4a, 0x4a, 0xb0, 0x50, + 0x20, 0x13, 0xee, 0x85, 0xaf, 0x8a, 0xd0, 0x08, + 0xa5, 0xc3, 0x85, 0xae, 0xa5, 0xc4, 0x85, 0xaf, + 0x20, 0xd2, 0xf5, 0xa9, 0xfd, 0x25, 0x90, 0x85, + 0x90, 0x20, 0xe1, 0xff, 0xd0, 0x03, 0x4c, 0x33, + 0xf6, 0x20, 0x13, 0xee, 0xaa, 0xa5, 0x90, 0x4a, + 0x4a, 0xb0, 0xe8, 0x8a, 0xa4, 0x93, 0xf0, 0x0c, + 0xa0, 0x00, 0xd1, 0xae, 0xf0, 0x08, 0xa9, 0x10, + 0x20, 0x1c, 0xfe, 0x2c, 0x91, 0xae, 0xe6, 0xae, + 0xd0, 0x02, 0xe6, 0xaf, 0x24, 0x90, 0x50, 0xcb, + 0x20, 0xef, 0xed, 0x20, 0x42, 0xf6, 0x90, 0x79, + 0x4c, 0x04, 0xf7, 0x4a, 0xb0, 0x03, 0x4c, 0x13, + 0xf7, 0x20, 0xd0, 0xf7, 0xb0, 0x03, 0x4c, 0x13, + 0xf7, 0x20, 0x17, 0xf8, 0xb0, 0x68, 0x20, 0xaf, + 0xf5, 0xa5, 0xb7, 0xf0, 0x09, 0x20, 0xea, 0xf7, + 0x90, 0x0b, 0xf0, 0x5a, 0xb0, 0xda, 0x20, 0x2c, + 0xf7, 0xf0, 0x53, 0xb0, 0xd3, 0xa5, 0x90, 0x29, + 0x10, 0x38, 0xd0, 0x4a, 0xe0, 0x01, 0xf0, 0x11, + 0xe0, 0x03, 0xd0, 0xdd, 0xa0, 0x01, 0xb1, 0xb2, + 0x85, 0xc3, 0xc8, 0xb1, 0xb2, 0x85, 0xc4, 0xb0, + 0x04, 0xa5, 0xb9, 0xd0, 0xef, 0xa0, 0x03, 0xb1, + 0xb2, 0xa0, 0x01, 0xf1, 0xb2, 0xaa, 0xa0, 0x04, + 0xb1, 0xb2, 0xa0, 0x02, 0xf1, 0xb2, 0xa8, 0x18, + 0x8a, 0x65, 0xc3, 0x85, 0xae, 0x98, 0x65, 0xc4, + 0x85, 0xaf, 0xa5, 0xc3, 0x85, 0xc1, 0xa5, 0xc4, + 0x85, 0xc2, 0x20, 0xd2, 0xf5, 0x20, 0x4a, 0xf8, + 0x24, 0x18, 0xa6, 0xae, 0xa4, 0xaf, 0x60, 0xa5, + 0x9d, 0x10, 0x1e, 0xa0, 0x0c, 0x20, 0x2f, 0xf1, + 0xa5, 0xb7, 0xf0, 0x15, 0xa0, 0x17, 0x20, 0x2f, + 0xf1, 0xa4, 0xb7, 0xf0, 0x0c, 0xa0, 0x00, 0xb1, + 0xbb, 0x20, 0xd2, 0xff, 0xc8, 0xc4, 0xb7, 0xd0, + 0xf6, 0x60, 0xa0, 0x49, 0xa5, 0x93, 0xf0, 0x02, + 0xa0, 0x59, 0x4c, 0x2b, 0xf1, 0x86, 0xae, 0x84, + 0xaf, 0xaa, 0xb5, 0x00, 0x85, 0xc1, 0xb5, 0x01, + 0x85, 0xc2, 0x6c, 0x32, 0x03, 0xa5, 0xba, 0xd0, + 0x03, 0x4c, 0x13, 0xf7, 0xc9, 0x03, 0xf0, 0xf9, + 0x90, 0x5f, 0xa9, 0x61, 0x85, 0xb9, 0xa4, 0xb7, + 0xd0, 0x03, 0x4c, 0x10, 0xf7, 0x20, 0xd5, 0xf3, + 0x20, 0x8f, 0xf6, 0xa5, 0xba, 0x20, 0x0c, 0xed, + 0xa5, 0xb9, 0x20, 0xb9, 0xed, 0xa0, 0x00, 0x20, + 0x8e, 0xfb, 0xa5, 0xac, 0x20, 0xdd, 0xed, 0xa5, + 0xad, 0x20, 0xdd, 0xed, 0x20, 0xd1, 0xfc, 0xb0, + 0x16, 0xb1, 0xac, 0x20, 0xdd, 0xed, 0x20, 0xe1, + 0xff, 0xd0, 0x07, 0x20, 0x42, 0xf6, 0xa9, 0x00, + 0x38, 0x60, 0x20, 0xdb, 0xfc, 0xd0, 0xe5, 0x20, + 0xfe, 0xed, 0x24, 0xb9, 0x30, 0x11, 0xa5, 0xba, + 0x20, 0x0c, 0xed, 0xa5, 0xb9, 0x29, 0xef, 0x09, + 0xe0, 0x20, 0xb9, 0xed, 0x20, 0xfe, 0xed, 0x18, + 0x60, 0x4a, 0xb0, 0x03, 0x4c, 0x13, 0xf7, 0x20, + 0xd0, 0xf7, 0x90, 0x8d, 0x20, 0x38, 0xf8, 0xb0, + 0x25, 0x20, 0x8f, 0xf6, 0xa2, 0x03, 0xa5, 0xb9, + 0x29, 0x01, 0xd0, 0x02, 0xa2, 0x01, 0x8a, 0x20, + 0x6a, 0xf7, 0xb0, 0x12, 0x20, 0x67, 0xf8, 0xb0, + 0x0d, 0xa5, 0xb9, 0x29, 0x02, 0xf0, 0x06, 0xa9, + 0x05, 0x20, 0x6a, 0xf7, 0x24, 0x18, 0x60, 0xa5, + 0x9d, 0x10, 0xfb, 0xa0, 0x51, 0x20, 0x2f, 0xf1, + 0x4c, 0xc1, 0xf5, 0xa2, 0x00, 0xe6, 0xa2, 0xd0, + 0x06, 0xe6, 0xa1, 0xd0, 0x02, 0xe6, 0xa0, 0x38, + 0xa5, 0xa2, 0xe9, 0x01, 0xa5, 0xa1, 0xe9, 0x1a, + 0xa5, 0xa0, 0xe9, 0x4f, 0x90, 0x06, 0x86, 0xa0, + 0x86, 0xa1, 0x86, 0xa2, 0xad, 0x01, 0xdc, 0xcd, + 0x01, 0xdc, 0xd0, 0xf8, 0xaa, 0x30, 0x13, 0xa2, + 0xbd, 0x8e, 0x00, 0xdc, 0xae, 0x01, 0xdc, 0xec, + 0x01, 0xdc, 0xd0, 0xf8, 0x8d, 0x00, 0xdc, 0xe8, + 0xd0, 0x02, 0x85, 0x91, 0x60, 0x78, 0xa5, 0xa2, + 0xa6, 0xa1, 0xa4, 0xa0, 0x78, 0x85, 0xa2, 0x86, + 0xa1, 0x84, 0xa0, 0x58, 0x60, 0xa5, 0x91, 0xc9, + 0x7f, 0xd0, 0x07, 0x08, 0x20, 0xcc, 0xff, 0x85, + 0xc6, 0x28, 0x60, 0xa9, 0x01, 0x2c, 0xa9, 0x02, + 0x2c, 0xa9, 0x03, 0x2c, 0xa9, 0x04, 0x2c, 0xa9, + 0x05, 0x2c, 0xa9, 0x06, 0x2c, 0xa9, 0x07, 0x2c, + 0xa9, 0x08, 0x2c, 0xa9, 0x09, 0x48, 0x20, 0xcc, + 0xff, 0xa0, 0x00, 0x24, 0x9d, 0x50, 0x0a, 0x20, + 0x2f, 0xf1, 0x68, 0x48, 0x09, 0x30, 0x20, 0xd2, + 0xff, 0x68, 0x38, 0x60, 0xa5, 0x93, 0x48, 0x20, + 0x41, 0xf8, 0x68, 0x85, 0x93, 0xb0, 0x32, 0xa0, + 0x00, 0xb1, 0xb2, 0xc9, 0x05, 0xf0, 0x2a, 0xc9, + 0x01, 0xf0, 0x08, 0xc9, 0x03, 0xf0, 0x04, 0xc9, + 0x04, 0xd0, 0xe1, 0xaa, 0x24, 0x9d, 0x10, 0x17, + 0xa0, 0x63, 0x20, 0x2f, 0xf1, 0xa0, 0x05, 0xb1, + 0xb2, 0x20, 0xd2, 0xff, 0xc8, 0xc0, 0x15, 0xd0, + 0xf6, 0xa5, 0xa1, 0x20, 0xe0, 0xe4, 0xea, 0x18, + 0x88, 0x60, 0x85, 0x9e, 0x20, 0xd0, 0xf7, 0x90, + 0x5e, 0xa5, 0xc2, 0x48, 0xa5, 0xc1, 0x48, 0xa5, + 0xaf, 0x48, 0xa5, 0xae, 0x48, 0xa0, 0xbf, 0xa9, + 0x20, 0x91, 0xb2, 0x88, 0xd0, 0xfb, 0xa5, 0x9e, + 0x91, 0xb2, 0xc8, 0xa5, 0xc1, 0x91, 0xb2, 0xc8, + 0xa5, 0xc2, 0x91, 0xb2, 0xc8, 0xa5, 0xae, 0x91, + 0xb2, 0xc8, 0xa5, 0xaf, 0x91, 0xb2, 0xc8, 0x84, + 0x9f, 0xa0, 0x00, 0x84, 0x9e, 0xa4, 0x9e, 0xc4, + 0xb7, 0xf0, 0x0c, 0xb1, 0xbb, 0xa4, 0x9f, 0x91, + 0xb2, 0xe6, 0x9e, 0xe6, 0x9f, 0xd0, 0xee, 0x20, + 0xd7, 0xf7, 0xa9, 0x69, 0x85, 0xab, 0x20, 0x6b, + 0xf8, 0xa8, 0x68, 0x85, 0xae, 0x68, 0x85, 0xaf, + 0x68, 0x85, 0xc1, 0x68, 0x85, 0xc2, 0x98, 0x60, + 0xa6, 0xb2, 0xa4, 0xb3, 0xc0, 0x02, 0x60, 0x20, + 0xd0, 0xf7, 0x8a, 0x85, 0xc1, 0x18, 0x69, 0xc0, + 0x85, 0xae, 0x98, 0x85, 0xc2, 0x69, 0x00, 0x85, + 0xaf, 0x60, 0x20, 0x2c, 0xf7, 0xb0, 0x1d, 0xa0, + 0x05, 0x84, 0x9f, 0xa0, 0x00, 0x84, 0x9e, 0xc4, + 0xb7, 0xf0, 0x10, 0xb1, 0xbb, 0xa4, 0x9f, 0xd1, + 0xb2, 0xd0, 0xe7, 0xe6, 0x9e, 0xe6, 0x9f, 0xa4, + 0x9e, 0xd0, 0xec, 0x18, 0x60, 0x20, 0xd0, 0xf7, + 0xe6, 0xa6, 0xa4, 0xa6, 0xc0, 0xc0, 0x60, 0x20, + 0x2e, 0xf8, 0xf0, 0x1a, 0xa0, 0x1b, 0x20, 0x2f, + 0xf1, 0x20, 0xd0, 0xf8, 0x20, 0x2e, 0xf8, 0xd0, + 0xf8, 0xa0, 0x6a, 0x4c, 0x2f, 0xf1, 0xa9, 0x10, + 0x24, 0x01, 0xd0, 0x02, 0x24, 0x01, 0x18, 0x60, + 0x20, 0x2e, 0xf8, 0xf0, 0xf9, 0xa0, 0x2e, 0xd0, + 0xdd, 0xa9, 0x00, 0x85, 0x90, 0x85, 0x93, 0x20, + 0xd7, 0xf7, 0x20, 0x17, 0xf8, 0xb0, 0x1f, 0x78, + 0xa9, 0x00, 0x85, 0xaa, 0x85, 0xb4, 0x85, 0xb0, + 0x85, 0x9e, 0x85, 0x9f, 0x85, 0x9c, 0xa9, 0x90, + 0xa2, 0x0e, 0xd0, 0x11, 0x20, 0xd7, 0xf7, 0xa9, + 0x14, 0x85, 0xab, 0x20, 0x38, 0xf8, 0xb0, 0x6c, + 0x78, 0xa9, 0x82, 0xa2, 0x08, 0xa0, 0x7f, 0x8c, + 0x0d, 0xdc, 0x8d, 0x0d, 0xdc, 0xad, 0x0e, 0xdc, + 0x09, 0x19, 0x8d, 0x0f, 0xdc, 0x29, 0x91, 0x8d, + 0xa2, 0x02, 0x20, 0xa4, 0xf0, 0xad, 0x11, 0xd0, + 0x29, 0xef, 0x8d, 0x11, 0xd0, 0xad, 0x14, 0x03, + 0x8d, 0x9f, 0x02, 0xad, 0x15, 0x03, 0x8d, 0xa0, + 0x02, 0x20, 0xbd, 0xfc, 0xa9, 0x02, 0x85, 0xbe, + 0x20, 0x97, 0xfb, 0xa5, 0x01, 0x29, 0x1f, 0x85, + 0x01, 0x85, 0xc0, 0xa2, 0xff, 0xa0, 0xff, 0x88, + 0xd0, 0xfd, 0xca, 0xd0, 0xf8, 0x58, 0xad, 0xa0, + 0x02, 0xcd, 0x15, 0x03, 0x18, 0xf0, 0x15, 0x20, + 0xd0, 0xf8, 0x20, 0xbc, 0xf6, 0x4c, 0xbe, 0xf8, + 0x20, 0xe1, 0xff, 0x18, 0xd0, 0x0b, 0x20, 0x93, + 0xfc, 0x38, 0x68, 0x68, 0xa9, 0x00, 0x8d, 0xa0, + 0x02, 0x60, 0x86, 0xb1, 0xa5, 0xb0, 0x0a, 0x0a, + 0x18, 0x65, 0xb0, 0x18, 0x65, 0xb1, 0x85, 0xb1, + 0xa9, 0x00, 0x24, 0xb0, 0x30, 0x01, 0x2a, 0x06, + 0xb1, 0x2a, 0x06, 0xb1, 0x2a, 0xaa, 0xad, 0x06, + 0xdc, 0xc9, 0x16, 0x90, 0xf9, 0x65, 0xb1, 0x8d, + 0x04, 0xdc, 0x8a, 0x6d, 0x07, 0xdc, 0x8d, 0x05, + 0xdc, 0xad, 0xa2, 0x02, 0x8d, 0x0e, 0xdc, 0x8d, + 0xa4, 0x02, 0xad, 0x0d, 0xdc, 0x29, 0x10, 0xf0, + 0x09, 0xa9, 0xf9, 0x48, 0xa9, 0x2a, 0x48, 0x4c, + 0x43, 0xff, 0x58, 0x60, 0xae, 0x07, 0xdc, 0xa0, + 0xff, 0x98, 0xed, 0x06, 0xdc, 0xec, 0x07, 0xdc, + 0xd0, 0xf2, 0x86, 0xb1, 0xaa, 0x8c, 0x06, 0xdc, + 0x8c, 0x07, 0xdc, 0xa9, 0x19, 0x8d, 0x0f, 0xdc, + 0xad, 0x0d, 0xdc, 0x8d, 0xa3, 0x02, 0x98, 0xe5, + 0xb1, 0x86, 0xb1, 0x4a, 0x66, 0xb1, 0x4a, 0x66, + 0xb1, 0xa5, 0xb0, 0x18, 0x69, 0x3c, 0xc5, 0xb1, + 0xb0, 0x4a, 0xa6, 0x9c, 0xf0, 0x03, 0x4c, 0x60, + 0xfa, 0xa6, 0xa3, 0x30, 0x1b, 0xa2, 0x00, 0x69, + 0x30, 0x65, 0xb0, 0xc5, 0xb1, 0xb0, 0x1c, 0xe8, + 0x69, 0x26, 0x65, 0xb0, 0xc5, 0xb1, 0xb0, 0x17, + 0x69, 0x2c, 0x65, 0xb0, 0xc5, 0xb1, 0x90, 0x03, + 0x4c, 0x10, 0xfa, 0xa5, 0xb4, 0xf0, 0x1d, 0x85, + 0xa8, 0xd0, 0x19, 0xe6, 0xa9, 0xb0, 0x02, 0xc6, + 0xa9, 0x38, 0xe9, 0x13, 0xe5, 0xb1, 0x65, 0x92, + 0x85, 0x92, 0xa5, 0xa4, 0x49, 0x01, 0x85, 0xa4, + 0xf0, 0x2b, 0x86, 0xd7, 0xa5, 0xb4, 0xf0, 0x22, + 0xad, 0xa3, 0x02, 0x29, 0x01, 0xd0, 0x05, 0xad, + 0xa4, 0x02, 0xd0, 0x16, 0xa9, 0x00, 0x85, 0xa4, + 0x8d, 0xa4, 0x02, 0xa5, 0xa3, 0x10, 0x30, 0x30, + 0xbf, 0xa2, 0xa6, 0x20, 0xe2, 0xf8, 0xa5, 0x9b, + 0xd0, 0xb9, 0x4c, 0xbc, 0xfe, 0xa5, 0x92, 0xf0, + 0x07, 0x30, 0x03, 0xc6, 0xb0, 0x2c, 0xe6, 0xb0, + 0xa9, 0x00, 0x85, 0x92, 0xe4, 0xd7, 0xd0, 0x0f, + 0x8a, 0xd0, 0xa0, 0xa5, 0xa9, 0x30, 0xbd, 0xc9, + 0x10, 0x90, 0xb9, 0x85, 0x96, 0xb0, 0xb5, 0x8a, + 0x45, 0x9b, 0x85, 0x9b, 0xa5, 0xb4, 0xf0, 0xd2, + 0xc6, 0xa3, 0x30, 0xc5, 0x46, 0xd7, 0x66, 0xbf, + 0xa2, 0xda, 0x20, 0xe2, 0xf8, 0x4c, 0xbc, 0xfe, + 0xa5, 0x96, 0xf0, 0x04, 0xa5, 0xb4, 0xf0, 0x07, + 0xa5, 0xa3, 0x30, 0x03, 0x4c, 0x97, 0xf9, 0x46, + 0xb1, 0xa9, 0x93, 0x38, 0xe5, 0xb1, 0x65, 0xb0, + 0x0a, 0xaa, 0x20, 0xe2, 0xf8, 0xe6, 0x9c, 0xa5, + 0xb4, 0xd0, 0x11, 0xa5, 0x96, 0xf0, 0x26, 0x85, + 0xa8, 0xa9, 0x00, 0x85, 0x96, 0xa9, 0x81, 0x8d, + 0x0d, 0xdc, 0x85, 0xb4, 0xa5, 0x96, 0x85, 0xb5, + 0xf0, 0x09, 0xa9, 0x00, 0x85, 0xb4, 0xa9, 0x01, + 0x8d, 0x0d, 0xdc, 0xa5, 0xbf, 0x85, 0xbd, 0xa5, + 0xa8, 0x05, 0xa9, 0x85, 0xb6, 0x4c, 0xbc, 0xfe, + 0x20, 0x97, 0xfb, 0x85, 0x9c, 0xa2, 0xda, 0x20, + 0xe2, 0xf8, 0xa5, 0xbe, 0xf0, 0x02, 0x85, 0xa7, + 0xa9, 0x0f, 0x24, 0xaa, 0x10, 0x17, 0xa5, 0xb5, + 0xd0, 0x0c, 0xa6, 0xbe, 0xca, 0xd0, 0x0b, 0xa9, + 0x08, 0x20, 0x1c, 0xfe, 0xd0, 0x04, 0xa9, 0x00, + 0x85, 0xaa, 0x4c, 0xbc, 0xfe, 0x70, 0x31, 0xd0, + 0x18, 0xa5, 0xb5, 0xd0, 0xf5, 0xa5, 0xb6, 0xd0, + 0xf1, 0xa5, 0xa7, 0x4a, 0xa5, 0xbd, 0x30, 0x03, + 0x90, 0x18, 0x18, 0xb0, 0x15, 0x29, 0x0f, 0x85, + 0xaa, 0xc6, 0xaa, 0xd0, 0xdd, 0xa9, 0x40, 0x85, + 0xaa, 0x20, 0x8e, 0xfb, 0xa9, 0x00, 0x85, 0xab, + 0xf0, 0xd0, 0xa9, 0x80, 0x85, 0xaa, 0xd0, 0xca, + 0xa5, 0xb5, 0xf0, 0x0a, 0xa9, 0x04, 0x20, 0x1c, + 0xfe, 0xa9, 0x00, 0x4c, 0x4a, 0xfb, 0x20, 0xd1, + 0xfc, 0x90, 0x03, 0x4c, 0x48, 0xfb, 0xa6, 0xa7, + 0xca, 0xf0, 0x2d, 0xa5, 0x93, 0xf0, 0x0c, 0xa0, + 0x00, 0xa5, 0xbd, 0xd1, 0xac, 0xf0, 0x04, 0xa9, + 0x01, 0x85, 0xb6, 0xa5, 0xb6, 0xf0, 0x4b, 0xa2, + 0x3d, 0xe4, 0x9e, 0x90, 0x3e, 0xa6, 0x9e, 0xa5, + 0xad, 0x9d, 0x01, 0x01, 0xa5, 0xac, 0x9d, 0x00, + 0x01, 0xe8, 0xe8, 0x86, 0x9e, 0x4c, 0x3a, 0xfb, + 0xa6, 0x9f, 0xe4, 0x9e, 0xf0, 0x35, 0xa5, 0xac, + 0xdd, 0x00, 0x01, 0xd0, 0x2e, 0xa5, 0xad, 0xdd, + 0x01, 0x01, 0xd0, 0x27, 0xe6, 0x9f, 0xe6, 0x9f, + 0xa5, 0x93, 0xf0, 0x0b, 0xa5, 0xbd, 0xa0, 0x00, + 0xd1, 0xac, 0xf0, 0x17, 0xc8, 0x84, 0xb6, 0xa5, + 0xb6, 0xf0, 0x07, 0xa9, 0x10, 0x20, 0x1c, 0xfe, + 0xd0, 0x09, 0xa5, 0x93, 0xd0, 0x05, 0xa8, 0xa5, + 0xbd, 0x91, 0xac, 0x20, 0xdb, 0xfc, 0xd0, 0x43, + 0xa9, 0x80, 0x85, 0xaa, 0x78, 0xa2, 0x01, 0x8e, + 0x0d, 0xdc, 0xae, 0x0d, 0xdc, 0xa6, 0xbe, 0xca, + 0x30, 0x02, 0x86, 0xbe, 0xc6, 0xa7, 0xf0, 0x08, + 0xa5, 0x9e, 0xd0, 0x27, 0x85, 0xbe, 0xf0, 0x23, + 0x20, 0x93, 0xfc, 0x20, 0x8e, 0xfb, 0xa0, 0x00, + 0x84, 0xab, 0xb1, 0xac, 0x45, 0xab, 0x85, 0xab, + 0x20, 0xdb, 0xfc, 0x20, 0xd1, 0xfc, 0x90, 0xf2, + 0xa5, 0xab, 0x45, 0xbd, 0xf0, 0x05, 0xa9, 0x20, + 0x20, 0x1c, 0xfe, 0x4c, 0xbc, 0xfe, 0xa5, 0xc2, + 0x85, 0xad, 0xa5, 0xc1, 0x85, 0xac, 0x60, 0xa9, + 0x08, 0x85, 0xa3, 0xa9, 0x00, 0x85, 0xa4, 0x85, + 0xa8, 0x85, 0x9b, 0x85, 0xa9, 0x60, 0xa5, 0xbd, + 0x4a, 0xa9, 0x60, 0x90, 0x02, 0xa9, 0xb0, 0xa2, + 0x00, 0x8d, 0x06, 0xdc, 0x8e, 0x07, 0xdc, 0xad, + 0x0d, 0xdc, 0xa9, 0x19, 0x8d, 0x0f, 0xdc, 0xa5, + 0x01, 0x49, 0x08, 0x85, 0x01, 0x29, 0x08, 0x60, + 0x38, 0x66, 0xb6, 0x30, 0x3c, 0xa5, 0xa8, 0xd0, + 0x12, 0xa9, 0x10, 0xa2, 0x01, 0x20, 0xb1, 0xfb, + 0xd0, 0x2f, 0xe6, 0xa8, 0xa5, 0xb6, 0x10, 0x29, + 0x4c, 0x57, 0xfc, 0xa5, 0xa9, 0xd0, 0x09, 0x20, + 0xad, 0xfb, 0xd0, 0x1d, 0xe6, 0xa9, 0xd0, 0x19, + 0x20, 0xa6, 0xfb, 0xd0, 0x14, 0xa5, 0xa4, 0x49, + 0x01, 0x85, 0xa4, 0xf0, 0x0f, 0xa5, 0xbd, 0x49, + 0x01, 0x85, 0xbd, 0x29, 0x01, 0x45, 0x9b, 0x85, + 0x9b, 0x4c, 0xbc, 0xfe, 0x46, 0xbd, 0xc6, 0xa3, + 0xa5, 0xa3, 0xf0, 0x3a, 0x10, 0xf3, 0x20, 0x97, + 0xfb, 0x58, 0xa5, 0xa5, 0xf0, 0x12, 0xa2, 0x00, + 0x86, 0xd7, 0xc6, 0xa5, 0xa6, 0xbe, 0xe0, 0x02, + 0xd0, 0x02, 0x09, 0x80, 0x85, 0xbd, 0xd0, 0xd9, + 0x20, 0xd1, 0xfc, 0x90, 0x0a, 0xd0, 0x91, 0xe6, + 0xad, 0xa5, 0xd7, 0x85, 0xbd, 0xb0, 0xca, 0xa0, + 0x00, 0xb1, 0xac, 0x85, 0xbd, 0x45, 0xd7, 0x85, + 0xd7, 0x20, 0xdb, 0xfc, 0xd0, 0xbb, 0xa5, 0x9b, + 0x49, 0x01, 0x85, 0xbd, 0x4c, 0xbc, 0xfe, 0xc6, + 0xbe, 0xd0, 0x03, 0x20, 0xca, 0xfc, 0xa9, 0x50, + 0x85, 0xa7, 0xa2, 0x08, 0x78, 0x20, 0xbd, 0xfc, + 0xd0, 0xea, 0xa9, 0x78, 0x20, 0xaf, 0xfb, 0xd0, + 0xe3, 0xc6, 0xa7, 0xd0, 0xdf, 0x20, 0x97, 0xfb, + 0xc6, 0xab, 0x10, 0xd8, 0xa2, 0x0a, 0x20, 0xbd, + 0xfc, 0x58, 0xe6, 0xab, 0xa5, 0xbe, 0xf0, 0x30, + 0x20, 0x8e, 0xfb, 0xa2, 0x09, 0x86, 0xa5, 0x86, + 0xb6, 0xd0, 0x83, 0x08, 0x78, 0xad, 0x11, 0xd0, + 0x09, 0x10, 0x8d, 0x11, 0xd0, 0x20, 0xca, 0xfc, + 0xa9, 0x7f, 0x8d, 0x0d, 0xdc, 0x20, 0xdd, 0xfd, + 0xad, 0xa0, 0x02, 0xf0, 0x09, 0x8d, 0x15, 0x03, + 0xad, 0x9f, 0x02, 0x8d, 0x14, 0x03, 0x28, 0x60, + 0x20, 0x93, 0xfc, 0xf0, 0x97, 0xbd, 0x93, 0xfd, + 0x8d, 0x14, 0x03, 0xbd, 0x94, 0xfd, 0x8d, 0x15, + 0x03, 0x60, 0xa5, 0x01, 0x09, 0x20, 0x85, 0x01, + 0x60, 0x38, 0xa5, 0xac, 0xe5, 0xae, 0xa5, 0xad, + 0xe5, 0xaf, 0x60, 0xe6, 0xac, 0xd0, 0x02, 0xe6, + 0xad, 0x60, 0xa2, 0xff, 0x78, 0x9a, 0xd8, 0x20, + 0x02, 0xfd, 0xd0, 0x03, 0x6c, 0x00, 0x80, 0x8e, + 0x16, 0xd0, 0x20, 0xa3, 0xfd, 0x20, 0x50, 0xfd, + 0x20, 0x15, 0xfd, 0x20, 0x5b, 0xff, 0x58, 0x6c, + 0x00, 0xa0, 0xa2, 0x05, 0xbd, 0x0f, 0xfd, 0xdd, + 0x03, 0x80, 0xd0, 0x03, 0xca, 0xd0, 0xf5, 0x60, + 0xc3, 0xc2, 0xcd, 0x38, 0x30, 0xa2, 0x30, 0xa0, + 0xfd, 0x18, 0x86, 0xc3, 0x84, 0xc4, 0xa0, 0x1f, + 0xb9, 0x14, 0x03, 0xb0, 0x02, 0xb1, 0xc3, 0x91, + 0xc3, 0x99, 0x14, 0x03, 0x88, 0x10, 0xf1, 0x60, + 0x31, 0xea, 0x66, 0xfe, 0x47, 0xfe, 0x4a, 0xf3, + 0x91, 0xf2, 0x0e, 0xf2, 0x50, 0xf2, 0x33, 0xf3, + 0x57, 0xf1, 0xca, 0xf1, 0xed, 0xf6, 0x3e, 0xf1, + 0x2f, 0xf3, 0x66, 0xfe, 0xa5, 0xf4, 0xed, 0xf5, + 0xa9, 0x00, 0xa8, 0x99, 0x02, 0x00, 0x99, 0x00, + 0x02, 0x99, 0x00, 0x03, 0xc8, 0xd0, 0xf4, 0xa2, + 0x3c, 0xa0, 0x03, 0x86, 0xb2, 0x84, 0xb3, 0xa8, + 0xa9, 0x03, 0x85, 0xc2, 0xe6, 0xc2, 0xb1, 0xc1, + 0xaa, 0xa9, 0x55, 0x91, 0xc1, 0xd1, 0xc1, 0xd0, + 0x0f, 0x2a, 0x91, 0xc1, 0xd1, 0xc1, 0xd0, 0x08, + 0x8a, 0x91, 0xc1, 0xc8, 0xd0, 0xe8, 0xf0, 0xe4, + 0x98, 0xaa, 0xa4, 0xc2, 0x18, 0x20, 0x2d, 0xfe, + 0xa9, 0x08, 0x8d, 0x82, 0x02, 0xa9, 0x04, 0x8d, + 0x88, 0x02, 0x60, 0x6a, 0xfc, 0xcd, 0xfb, 0x31, + 0xea, 0x2c, 0xf9, 0xa9, 0x7f, 0x8d, 0x0d, 0xdc, + 0x8d, 0x0d, 0xdd, 0x8d, 0x00, 0xdc, 0xa9, 0x08, + 0x8d, 0x0e, 0xdc, 0x8d, 0x0e, 0xdd, 0x8d, 0x0f, + 0xdc, 0x8d, 0x0f, 0xdd, 0xa2, 0x00, 0x8e, 0x03, + 0xdc, 0x8e, 0x03, 0xdd, 0x8e, 0x18, 0xd4, 0xca, + 0x8e, 0x02, 0xdc, 0xa9, 0x07, 0x8d, 0x00, 0xdd, + 0xa9, 0x3f, 0x8d, 0x02, 0xdd, 0xa9, 0xe7, 0x85, + 0x01, 0xa9, 0x2f, 0x85, 0x00, 0xad, 0xa6, 0x02, + 0xf0, 0x0a, 0xa9, 0x25, 0x8d, 0x04, 0xdc, 0xa9, + 0x40, 0x4c, 0xf3, 0xfd, 0xa9, 0x95, 0x8d, 0x04, + 0xdc, 0xa9, 0x42, 0x8d, 0x05, 0xdc, 0x4c, 0x6e, + 0xff, 0x85, 0xb7, 0x86, 0xbb, 0x84, 0xbc, 0x60, + 0x85, 0xb8, 0x86, 0xba, 0x84, 0xb9, 0x60, 0xa5, + 0xba, 0xc9, 0x02, 0xd0, 0x0d, 0xad, 0x97, 0x02, + 0x48, 0xa9, 0x00, 0x8d, 0x97, 0x02, 0x68, 0x60, + 0x85, 0x9d, 0xa5, 0x90, 0x05, 0x90, 0x85, 0x90, + 0x60, 0x8d, 0x85, 0x02, 0x60, 0x90, 0x06, 0xae, + 0x83, 0x02, 0xac, 0x84, 0x02, 0x8e, 0x83, 0x02, + 0x8c, 0x84, 0x02, 0x60, 0x90, 0x06, 0xae, 0x81, + 0x02, 0xac, 0x82, 0x02, 0x8e, 0x81, 0x02, 0x8c, + 0x82, 0x02, 0x60, 0x78, 0x6c, 0x18, 0x03, 0x48, + 0x8a, 0x48, 0x98, 0x48, 0xa9, 0x7f, 0x8d, 0x0d, + 0xdd, 0xac, 0x0d, 0xdd, 0x30, 0x1c, 0x20, 0x02, + 0xfd, 0xd0, 0x03, 0x6c, 0x02, 0x80, 0x20, 0xbc, + 0xf6, 0x20, 0xe1, 0xff, 0xd0, 0x0c, 0x20, 0x15, + 0xfd, 0x20, 0xa3, 0xfd, 0x20, 0x18, 0xe5, 0x6c, + 0x02, 0xa0, 0x98, 0x2d, 0xa1, 0x02, 0xaa, 0x29, + 0x01, 0xf0, 0x28, 0xad, 0x00, 0xdd, 0x29, 0xfb, + 0x05, 0xb5, 0x8d, 0x00, 0xdd, 0xad, 0xa1, 0x02, + 0x8d, 0x0d, 0xdd, 0x8a, 0x29, 0x12, 0xf0, 0x0d, + 0x29, 0x02, 0xf0, 0x06, 0x20, 0xd6, 0xfe, 0x4c, + 0x9d, 0xfe, 0x20, 0x07, 0xff, 0x20, 0xbb, 0xee, + 0x4c, 0xb6, 0xfe, 0x8a, 0x29, 0x02, 0xf0, 0x06, + 0x20, 0xd6, 0xfe, 0x4c, 0xb6, 0xfe, 0x8a, 0x29, + 0x10, 0xf0, 0x03, 0x20, 0x07, 0xff, 0xad, 0xa1, + 0x02, 0x8d, 0x0d, 0xdd, 0x68, 0xa8, 0x68, 0xaa, + 0x68, 0x40, 0xc1, 0x27, 0x3e, 0x1a, 0xc5, 0x11, + 0x74, 0x0e, 0xed, 0x0c, 0x45, 0x06, 0xf0, 0x02, + 0x46, 0x01, 0xb8, 0x00, 0x71, 0x00, 0xad, 0x01, + 0xdd, 0x29, 0x01, 0x85, 0xa7, 0xad, 0x06, 0xdd, + 0xe9, 0x1c, 0x6d, 0x99, 0x02, 0x8d, 0x06, 0xdd, + 0xad, 0x07, 0xdd, 0x6d, 0x9a, 0x02, 0x8d, 0x07, + 0xdd, 0xa9, 0x11, 0x8d, 0x0f, 0xdd, 0xad, 0xa1, + 0x02, 0x8d, 0x0d, 0xdd, 0xa9, 0xff, 0x8d, 0x06, + 0xdd, 0x8d, 0x07, 0xdd, 0x4c, 0x59, 0xef, 0xad, + 0x95, 0x02, 0x8d, 0x06, 0xdd, 0xad, 0x96, 0x02, + 0x8d, 0x07, 0xdd, 0xa9, 0x11, 0x8d, 0x0f, 0xdd, + 0xa9, 0x12, 0x4d, 0xa1, 0x02, 0x8d, 0xa1, 0x02, + 0xa9, 0xff, 0x8d, 0x06, 0xdd, 0x8d, 0x07, 0xdd, + 0xae, 0x98, 0x02, 0x86, 0xa8, 0x60, 0xaa, 0xad, + 0x96, 0x02, 0x2a, 0xa8, 0x8a, 0x69, 0xc8, 0x8d, + 0x99, 0x02, 0x98, 0x69, 0x00, 0x8d, 0x9a, 0x02, + 0x60, 0xea, 0xea, 0x08, 0x68, 0x29, 0xef, 0x48, + 0x48, 0x8a, 0x48, 0x98, 0x48, 0xba, 0xbd, 0x04, + 0x01, 0x29, 0x10, 0xf0, 0x03, 0x6c, 0x16, 0x03, + 0x6c, 0x14, 0x03, 0x20, 0x18, 0xe5, 0xad, 0x12, + 0xd0, 0xd0, 0xfb, 0xad, 0x19, 0xd0, 0x29, 0x01, + 0x8d, 0xa6, 0x02, 0x4c, 0xdd, 0xfd, 0xa9, 0x81, + 0x8d, 0x0d, 0xdc, 0xad, 0x0e, 0xdc, 0x29, 0x80, + 0x09, 0x11, 0x8d, 0x0e, 0xdc, 0x4c, 0x8e, 0xee, + 0x03, 0x4c, 0x5b, 0xff, 0x4c, 0xa3, 0xfd, 0x4c, + 0x50, 0xfd, 0x4c, 0x15, 0xfd, 0x4c, 0x1a, 0xfd, + 0x4c, 0x18, 0xfe, 0x4c, 0xb9, 0xed, 0x4c, 0xc7, + 0xed, 0x4c, 0x25, 0xfe, 0x4c, 0x34, 0xfe, 0x4c, + 0x87, 0xea, 0x4c, 0x21, 0xfe, 0x4c, 0x13, 0xee, + 0x4c, 0xdd, 0xed, 0x4c, 0xef, 0xed, 0x4c, 0xfe, + 0xed, 0x4c, 0x0c, 0xed, 0x4c, 0x09, 0xed, 0x4c, + 0x07, 0xfe, 0x4c, 0x00, 0xfe, 0x4c, 0xf9, 0xfd, + 0x6c, 0x1a, 0x03, 0x6c, 0x1c, 0x03, 0x6c, 0x1e, + 0x03, 0x6c, 0x20, 0x03, 0x6c, 0x22, 0x03, 0x6c, + 0x24, 0x03, 0x6c, 0x26, 0x03, 0x4c, 0x9e, 0xf4, + 0x4c, 0xdd, 0xf5, 0x4c, 0xe4, 0xf6, 0x4c, 0xdd, + 0xf6, 0x6c, 0x28, 0x03, 0x6c, 0x2a, 0x03, 0x6c, + 0x2c, 0x03, 0x4c, 0x9b, 0xf6, 0x4c, 0x05, 0xe5, + 0x4c, 0x0a, 0xe5, 0x4c, 0x00, 0xe5, 0x52, 0x52, + 0x42, 0x59, 0x43, 0xfe, 0xe2, 0xfc, 0x48, 0xff +}; diff --git a/Src/Makefile.Amiga b/Src/Makefile.Amiga new file mode 100644 index 0000000..29e476b --- /dev/null +++ b/Src/Makefile.Amiga @@ -0,0 +1,86 @@ +# Makefile for Frodo (AmigaOS with GCC) + +## Version information +VERSION = 4 +REVISION = 2 + +CXX = gcc +CFLAGS = -O2 -I./ -fomit-frame-pointer -m68040 -m68881 +LIBRARIES = + +.SUFFIXES: .o .cpp .c .h + +## Files +OBJS = main.o Display.o Prefs.o AmigaGUI.o SID.o REU.o IEC.o 1541fs.o \ + 1541d64.o 1541t64.o 1541job.o SAM.o +SLOBJS = $(OBJS) C64.o CPUC64.o VIC.o CIA.o CPU1541.o +SLFLAGS = -DPRECISE_CPU_CYCLES=1 -DPRECISE_CIA_CYCLES=1 -DPC_IS_POINTER=0 +SCOBJS = $(OBJS) C64_SC.o CPUC64_SC.o VIC_SC.o CIA_SC.o CPU1541_SC.o CPU_common.o +SCFLAGS = -DFRODO_SC + +Frodo: $(SLOBJS) + $(CXX) -o Frodo $(SLOBJS) $(LDFLAGS) $(LIBRARIES) + copy Frodo / + +FrodoSC: $(SCOBJS) + $(CXX) -o FrodoSC $(SCOBJS) $(LDFLAGS) $(LIBRARIES) + copy FrodoSC / + +all: Frodo FrodoSC + +clean: + @-delete $(SLOBJS) $(SCOBJS) + @-delete -f Frodo FrodoSC + +.cpp.o: + $(CXX) $(INCLUDES) $(CFLAGS) -o $@ -c $*.cpp + +.c.o: + $(CXX) $(INCLUDES) $(CFLAGS) -o $@ -c $*.c + +C64.o: C64.cpp + $(CXX) $(INCLUDES) $(CFLAGS) $(SLFLAGS) -o $@ -c $*.cpp + +C64_SC.o: C64.cpp + $(CXX) $(INCLUDES) $(CFLAGS) $(SCFLAGS) -o $@ -c $*.cpp + +CPUC64.o: CPUC64.cpp + $(CXX) $(INCLUDES) $(CFLAGS) $(SLFLAGS) -o $@ -c $*.cpp + +CPUC64_SC.o: CPUC64_SC.cpp + $(CXX) $(INCLUDES) $(CFLAGS) $(SCFLAGS) -o $@ -c $*.cpp + +CPU1541.o: CPU1541.cpp + $(CXX) $(INCLUDES) $(CFLAGS) $(SLFLAGS) -o $@ -c $*.cpp + +CPU1541_SC.o: CPU1541_SC.cpp + $(CXX) $(INCLUDES) $(CFLAGS) $(SCFLAGS) -o $@ -c $*.cpp + +VIC_SC.o: VIC_SC.cpp + $(CXX) $(INCLUDES) $(CFLAGS) $(SCFLAGS) -o $@ -c $*.cpp + +CIA_SC.o: CIA_SC.cpp + $(CXX) $(INCLUDES) $(CFLAGS) $(SCFLAGS) -o $@ -c $*.cpp + +## Dependencies +main.o: main.cpp main.h main_Amiga.h C64.h Display.h Prefs.h SAM.h +C64.o: C64.cpp C64.h C64_Amiga.h CmdPipe.h CPUC64.h CPU1541.h VIC.h SID.h CIA.h REU.h IEC.h 1541job.h Display.h Prefs.h +CmdPipe.o: CmdPipe.h +Display.o: Display.cpp Display.h Display_Amiga.h main.h Prefs.h Version.h +Prefs.o: Prefs.cpp Prefs.h Prefs_Amiga.h Display.h main.h +CPUC64.o: CPUC64.cpp CPUC64.h CPU_emulline.h C64.h VIC.h SID.h CIA.h REU.h IEC.h Display.h Version.h +CPU1541.o: CPU1541.cpp CPU1541.h CPU_emulline.h 1541job.h C64.h CIA.h Display.h +CPU_common.o: CPU_common.cpp CPU_common.h +VIC.o: VIC.cpp VIC.h C64.h CPUC64.h Display.h Prefs.h +SID.o: SID.cpp SID.h SID_Amiga.h Prefs.h +CIA.o: CIA.cpp CIA.h CPUC64.h CPU1541.h VIC.h Prefs.h +IEC.o: IEC.cpp IEC.h 1541fs.h 1541d64.h 1541t64.h Prefs.h Display.h +1541fs.o: 1541fs.cpp 1541fs.h IEC.h main.h Prefs.h +1541fs.h: IEC.h +1541d64.o: 1541d64.cpp 1541d64.h IEC.h Prefs.h +1541d64.h: IEC.h +1541t64.o: 1541t64.cpp 1541t64.h IEC.h Prefs.h +1541t64.h: IEC.h +1541job.o: 1541job.cpp 1541job.h CPU1541.h Prefs.h +REU.o: REU.cpp REU.h CPUC64.h Prefs.h +SAM.o: SAM.cpp SAM.h C64.h CPUC64.h CPU1541.h VIC.h SID.h CIA.h diff --git a/Src/Makefile.BeOS b/Src/Makefile.BeOS new file mode 100644 index 0000000..ec7ed10 --- /dev/null +++ b/Src/Makefile.BeOS @@ -0,0 +1,117 @@ +## BeOS Generic Makefile v2.1 ## + +## Fill in this file to specify the project being created, and the referenced +## makefile-engine will do all of the hard work for you. This handles both +## Intel and PowerPC builds of the BeOS. + +## Application Specific Settings --------------------------------------------- + +# specify the name of the binary +NAME= Frodo + +# specify the type of binary +# APP: Application +# SHARED: Shared library or add-on +# STATIC: Static library archive +# DRIVER: Kernel Driver +TYPE= APP + +# add support for new Pe and Eddie features +# to fill in generic makefile + +#%{ +# @src->@ + +# specify the source files to use +# full paths or paths relative to the makefile can be included +# all files, regardless of directory, will have their object +# files created in the common object directory. +# Note that this means this makefile will not work correctly +# if two source files with the same name (source.c or source.cpp) +# are included from different directories. Also note that spaces +# in folder names do not work well with this makefile. +SRCS = main.cpp Prefs.cpp SAM.cpp Display.cpp C64.cpp CPUC64.cpp CPU1541.cpp \ + VIC.cpp SID.cpp CIA.cpp REU.cpp IEC.cpp 1541fs.cpp 1541d64.cpp 1541t64.cpp 1541job.cpp + +# specify the resource files to use +# full path or a relative path to the resource file can be used. +RSRCS= Frodo.rsrc + +# @<-src@ +#%} + +# end support for Pe and Eddie + +# specify additional libraries to link against +# there are two acceptable forms of library specifications +# - if your library follows the naming pattern of: +# libXXX.so or libXXX.a you can simply specify XXX +# library: libbe.so entry: be +# +# - if your library does not follow the standard library +# naming scheme you need to specify the path to the library +# and it's name +# library: my_lib.a entry: my_lib.a or path/my_lib.a +LIBS=be media game device tracker + +# specify additional paths to directories following the standard +# libXXX.so or libXXX.a naming scheme. You can specify full paths +# or paths relative to the makefile. The paths included may not +# be recursive, so include all of the paths where libraries can +# be found. Directories where source files are found are +# automatically included. +LIBPATHS= + +# additional paths to look for system headers +# thes use the form: #include

+# source file directories are NOT auto-included here +SYSTEM_INCLUDE_PATHS = + +# additional paths to look for local headers +# thes use the form: #include "header" +# source file directories are automatically included +LOCAL_INCLUDE_PATHS = + +# specify the level of optimization that you desire +# NONE, SOME, FULL +OPTIMIZE= FULL + +# specify any preprocessor symbols to be defined. The symbols will not +# have their values set automatically; you must supply the value (if any) +# to use. For example, setting DEFINES to "DEBUG=1" will cause the +# compiler option "-DDEBUG=1" to be used. Setting DEFINES to "DEBUG" +# would pass "-DDEBUG" on the compiler's command line. +DEFINES= PRECISE_CPU_CYCLES=1 PRECISE_CIA_CYCLES=1 PC_IS_POINTER=0 + +# specify special warning levels +# if unspecified default warnings will be used +# NONE = supress all warnings +# ALL = enable all warnings +WARNINGS = + +# specify whether image symbols will be created +# so that stack crawls in the debugger are meaningful +# if TRUE symbols will be created +SYMBOLS = + +# specify debug settings +# if TRUE will allow application to be run from +# a source-level debugger +DEBUGGER = + +# specify additional compiler flags for all files +COMPILER_FLAGS = + +# specify additional linker flags +LINKER_FLAGS = + + +## include the makefile-engine +include /boot/develop/etc/makefile-engine + + +## Custom stuff for Frodo +sysconfig.h: sysconfig.h.Be + ln -s sysconfig.h.Be sysconfig.h + +main.cpp: sysconfig.h diff --git a/Src/Makefile.BeOS.SC b/Src/Makefile.BeOS.SC new file mode 100644 index 0000000..6af4235 --- /dev/null +++ b/Src/Makefile.BeOS.SC @@ -0,0 +1,118 @@ +## BeOS Generic Makefile v2.1 ## + +## Fill in this file to specify the project being created, and the referenced +## makefile-engine will do all of the hard work for you. This handles both +## Intel and PowerPC builds of the BeOS. + +## Application Specific Settings --------------------------------------------- + +# specify the name of the binary +NAME= FrodoSC + +# specify the type of binary +# APP: Application +# SHARED: Shared library or add-on +# STATIC: Static library archive +# DRIVER: Kernel Driver +TYPE= APP + +# add support for new Pe and Eddie features +# to fill in generic makefile + +#%{ +# @src->@ + +# specify the source files to use +# full paths or paths relative to the makefile can be included +# all files, regardless of directory, will have their object +# files created in the common object directory. +# Note that this means this makefile will not work correctly +# if two source files with the same name (source.c or source.cpp) +# are included from different directories. Also note that spaces +# in folder names do not work well with this makefile. +SRCS = main.cpp Prefs.cpp SAM.cpp Display.cpp C64_SC.cpp CPUC64_SC.cpp CPU1541_SC.cpp \ + CPU_common.cpp VIC_SC.cpp SID.cpp CIA_SC.cpp REU.cpp IEC.cpp 1541fs.cpp 1541d64.cpp \ + 1541t64.cpp 1541job.cpp + +# specify the resource files to use +# full path or a relative path to the resource file can be used. +RSRCS= Frodo.rsrc + +# @<-src@ +#%} + +# end support for Pe and Eddie + +# specify additional libraries to link against +# there are two acceptable forms of library specifications +# - if your library follows the naming pattern of: +# libXXX.so or libXXX.a you can simply specify XXX +# library: libbe.so entry: be +# +# - if your library does not follow the standard library +# naming scheme you need to specify the path to the library +# and it's name +# library: my_lib.a entry: my_lib.a or path/my_lib.a +LIBS=be media game device tracker + +# specify additional paths to directories following the standard +# libXXX.so or libXXX.a naming scheme. You can specify full paths +# or paths relative to the makefile. The paths included may not +# be recursive, so include all of the paths where libraries can +# be found. Directories where source files are found are +# automatically included. +LIBPATHS= + +# additional paths to look for system headers +# thes use the form: #include
+# source file directories are NOT auto-included here +SYSTEM_INCLUDE_PATHS = + +# additional paths to look for local headers +# thes use the form: #include "header" +# source file directories are automatically included +LOCAL_INCLUDE_PATHS = + +# specify the level of optimization that you desire +# NONE, SOME, FULL +OPTIMIZE= FULL + +# specify any preprocessor symbols to be defined. The symbols will not +# have their values set automatically; you must supply the value (if any) +# to use. For example, setting DEFINES to "DEBUG=1" will cause the +# compiler option "-DDEBUG=1" to be used. Setting DEFINES to "DEBUG" +# would pass "-DDEBUG" on the compiler's command line. +DEFINES= FRODO_SC + +# specify special warning levels +# if unspecified default warnings will be used +# NONE = supress all warnings +# ALL = enable all warnings +WARNINGS = + +# specify whether image symbols will be created +# so that stack crawls in the debugger are meaningful +# if TRUE symbols will be created +SYMBOLS = + +# specify debug settings +# if TRUE will allow application to be run from +# a source-level debugger +DEBUGGER = + +# specify additional compiler flags for all files +COMPILER_FLAGS = + +# specify additional linker flags +LINKER_FLAGS = + + +## include the makefile-engine +include /boot/develop/etc/makefile-engine + + +## Custom stuff for Frodo +sysconfig.h: sysconfig.h.Be + ln -s sysconfig.h.Be sysconfig.h + +main.cpp: sysconfig.h diff --git a/Src/Makefile.WIN32 b/Src/Makefile.WIN32 new file mode 100644 index 0000000..43aaced --- /dev/null +++ b/Src/Makefile.WIN32 @@ -0,0 +1,111 @@ +# Makefile for Frodo (WIN32 DirectX with MSVC++) + +## Version information +VERSION = 4 +REVISION = 2 + +# Choose static (L) or dynamic (D) version of C library +LIBC = L +#LIBC = D + +# Choose debug or optimized build +#CFLAGS = -W2 -Zi -DDEBUG -D_DEBUG -M$(LIBC)d +CFLAGS = -Ox2 -G5 -Gi- -FAsc -M$(LIBC) + +CXX = cl +RC = rc +ALLCFLAGS = -I. -DWIN32 -DSTRICT -D__i386 $(CFLAGS) +ALLSCCFLAGS = -DFRODO_SC -DBATCH_CIA_CYCLES $(ALLCFLAGS) +#ALLSCCFLAGS = -DFRODO_SC $(ALLCFLAGS) +ALLPCCFLAGS = -DPRECISE_CPU_CYCLES=1 -DPRECISE_CIA_CYCLES=1 -DPC_IS_POINTER=0 $(ALLCFLAGS) +LDFLAGS = $(ALLCFLAGS) +LIBRARIES = user32.lib kernel32.lib gdi32.lib ddraw.lib dsound.lib winmm.lib comctl32.lib comdlg32.lib shell32.lib +#PROFILE = /link /profile + +## Files +OBJS = main.obj Display.obj Prefs.obj SID.obj REU.obj IEC.obj 1541fs.obj \ + 1541d64.obj 1541t64.obj 1541job.obj SAM.obj ndir.obj Frodo.res +SLOBJS = $(OBJS) C64.obj CPUC64.obj VIC.obj CIA.obj CPU1541.obj +SCOBJS = $(OBJS) C64_SC.obj CPUC64_SC.obj VIC_SC.obj CIA_SC.obj CPU1541_SC.obj CPU_common.obj +PCOBJS = $(OBJS) C64_PC.obj CPUC64_PC.obj VIC.obj CIA.obj CPU1541_PC.obj + +Frodo: Frodo.exe + +Frodo.exe: $(SLOBJS) + $(CXX) -FeFrodo.exe $(SLOBJS) $(LDFLAGS) $(LIBRARIES) + copy Frodo.exe .. + +FrodoSC: FrodoSC.exe + +FrodoSC.exe: $(SCOBJS) + $(CXX) -FeFrodoSC.exe $(SCOBJS) $(LDFLAGS) $(LIBRARIES) $(PROFILE) + copy FrodoSC.exe .. + +FrodoPC: FrodoPC.exe + +FrodoPC.exe: $(PCOBJS) + $(CXX) -FeFrodoPC.exe $(PCOBJS) $(LDFLAGS) $(LIBRARIES) + copy FrodoPC.exe .. + +Frodo.res: Frodo.rc Frodo.ico + $(RC) Frodo.rc + +all: Frodo FrodoSC FrodoPC + +clean: + rm -f $(SLOBJS) $(SCOBJS) $(PCOBJS) + rm -f Frodo.exe FrodoSC.exe FrodoPC.exe + rm -f *.ilk *.pdb *.cod *.nms *.res *.aps + +.cpp.obj: + $(CXX) $(ALLCFLAGS) -o $@ -c $*.cpp + +.c.obj: + $(CXX) $(ALLCFLAGS) -o $@ -c $*.c + +C64_SC.obj: C64.cpp C64.h C64_WIN32.h CmdPipe.h CPUC64.h CPU1541.h VIC.h SID.h CIA.h REU.h IEC.h 1541job.h Display.h Prefs.h + $(CXX) $(ALLSCCFLAGS) -Fo$@ -c $*.cpp + +CPUC64_SC.obj: CPUC64_SC.cpp CPUC64.h CPU_emulcycle.h CPU_common.h C64.h VIC.h SID.h CIA.h REU.h IEC.h Display.h Version.h + $(CXX) $(ALLSCCFLAGS) -Fo$@ -c $*.cpp + +CPU1541_SC.obj: CPU1541_SC.cpp CPU1541.h CPU_emulcycle.h CPU_common.h 1541job.h C64.h CIA.h Display.h + $(CXX) $(ALLSCCFLAGS) -Fo$@ -c $*.cpp + +VIC_SC.obj: VIC_SC.cpp VIC.h C64.h CPUC64.h Display.h Prefs.h + $(CXX) $(ALLSCCFLAGS) -Fo$@ -c $*.cpp + +CIA_SC.obj: CIA_SC.cpp CIA.h CPUC64.h CPU1541.h VIC.h Prefs.h + $(CXX) $(ALLSCCFLAGS) -Fo$@ -c $*.cpp + +C64_PC.obj: C64.cpp C64.h C64_WIN32.h CmdPipe.h CPUC64.h CPU1541.h VIC.h SID.h CIA.h REU.h IEC.h 1541job.h Display.h Prefs.h + $(CXX) $(ALLPCCFLAGS) -Fo$@ -c $*.cpp + +CPUC64_PC.obj: CPUC64.cpp CPUC64.h CPU_emulline.h CPU_common.h C64.h VIC.h SID.h CIA.h REU.h IEC.h Display.h Version.h + $(CXX) $(ALLPCCFLAGS) -DPC_IS_POINTER=0 -Fo$@ -c $*.cpp + +CPU1541_PC.obj: CPU1541_PC.cpp CPU1541.h CPU_emulline.h CPU_common.h 1541job.h C64.h CIA.h Display.h + $(CXX) $(ALLPCCFLAGS) -Fo$@ -c $*.cpp + +## Dependencies +main.obj: main.cpp main.h main_WIN32.h C64.h Display.h Prefs.h SAM.h +C64.obj: C64.cpp C64.h C64_WIN32.h CmdPipe.h CPUC64.h CPU1541.h VIC.h SID.h CIA.h REU.h IEC.h 1541job.h Display.h Prefs.h +CmdPipe.obj: CmdPipe.h +Display.obj: Display.cpp Display.h Display_WIN32.h main.h Prefs.h Version.h resource.h +Prefs.obj: Prefs.cpp Prefs.h Prefs_WIN32.h Display.h main.h resource.h +CPUC64.obj: CPUC64.cpp CPUC64.h CPU_emulline.h C64.h VIC.h SID.h CIA.h REU.h IEC.h Display.h Version.h +CPU1541.obj: CPU1541.cpp CPU1541.h CPU_emulline.h 1541job.h C64.h CIA.h Display.h +CPU_common.obj: CPU_common.cpp CPU_common.h +VIC.obj: VIC.cpp VIC.h C64.h CPUC64.h Display.h Prefs.h +SID.obj: SID.cpp SID.h SID_WIN32.h Prefs.h main.h +CIA.obj: CIA.cpp CIA.h CPUC64.h CPU1541.h VIC.h Prefs.h +IEC.obj: IEC.cpp IEC.h 1541fs.h 1541d64.h 1541t64.h Prefs.h Display.h +1541fs.obj: 1541fs.cpp 1541fs.h IEC.h main.h Prefs.h +1541fs.h: IEC.h +1541d64.obj: 1541d64.cpp 1541d64.h IEC.h Prefs.h +1541d64.h: IEC.h +1541t64.obj: 1541t64.cpp 1541t64.h IEC.h Prefs.h +1541t64.h: IEC.h +1541job.obj: 1541job.cpp 1541job.h CPU1541.h Prefs.h +REU.obj: REU.cpp REU.h CPUC64.h Prefs.h +SAM.obj: SAM.cpp SAM.h C64.h CPUC64.h CPU1541.h VIC.h SID.h CIA.h diff --git a/Src/Makefile.in b/Src/Makefile.in new file mode 100644 index 0000000..95a2030 --- /dev/null +++ b/Src/Makefile.in @@ -0,0 +1,118 @@ +# Makefile.in for Frodo (generic Unix/X11) + +## Version information +VERSION = 4 +REVISION = 3 + +## System specific configuration +@SET_MAKE@ +SHELL = /bin/sh + +prefix = @prefix@ +exec_prefix = @exec_prefix@ +bindir = @bindir@ +datadir = @datadir@ + +DESTDIR = + +CXX = @CXX@ +CFLAGS = @CFLAGS@ @GLADE_CFLAGS@ @OSSO_CFLAGS@ -I./ -DKBD_LANG=@KBD_LANG@ +DEFS = @DEFS@ -DDATADIR=\"$(datadir)/frodo/\" -DBINDIR=\"$(bindir)/\" +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ @GLADE_LIBS@ @OSSO_LIBS@ +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ + +## Files +GUIOBJS = @GUIOBJS@ +OBJS = $(GUIOBJS) main.o Display.o Prefs.o SID.o REU.o IEC.o 1541fs.o \ + 1541d64.o 1541t64.o 1541job.o SAM.o CmdPipe.o +SLOBJS = $(OBJS) C64.o CPUC64.o VIC.o CIA.o CPU1541.o +SLFLAGS = -DPRECISE_CPU_CYCLES=1 -DPRECISE_CIA_CYCLES=1 -DPC_IS_POINTER=0 +SCOBJS = $(OBJS) C64_SC.o CPUC64_SC.o VIC_SC.o CIA_SC.o CPU1541_SC.o CPU_common.o +SCFLAGS = -DFRODO_SC +GUISRCS = @GUISRCS@ +SRCS = $(GUISRCS) main.cpp Display.cpp Prefs.cpp SID.cpp REU.cpp IEC.cpp 1541fs.cpp \ + 1541d64.cpp 1541t64.cpp 1541job.cpp SAM.cpp CmdPipe.cpp C64.cpp \ + C64_SC.cpp CPUC64.cpp CPUC64_SC.cpp VIC.cpp VIC_SC.cpp CIA.cpp \ + CIA_SC.cpp CPU1541.cpp CPU1541_PC.cpp CPU1541_SC.cpp CPU_common.cpp + +## Rules +.PHONY: install installdirs uninstall clean distclean depend dep +.SUFFIXES: +.SUFFIXES: .o .cpp .h + +all: Frodo FrodoSC + +Frodo: $(SLOBJS) + $(CXX) -o Frodo $(LDFLAGS) $(SLOBJS) $(LIBS) + +FrodoSC: $(SCOBJS) + $(CXX) -o FrodoSC $(LDFLAGS) $(SCOBJS) $(LIBS) + +install: Frodo FrodoSC installdirs + $(INSTALL_PROGRAM) Frodo $(DESTDIR)$(bindir)/Frodo + $(INSTALL_PROGRAM) FrodoSC $(DESTDIR)$(bindir)/FrodoSC + $(INSTALL_PROGRAM) Frodo_GUI.tcl $(DESTDIR)$(bindir)/Frodo_GUI.tcl + $(INSTALL_DATA) ../Kernal\ ROM $(DESTDIR)$(datadir)/frodo/Kernal\ ROM + $(INSTALL_DATA) glade/Frodo.glade $(DESTDIR)$(datadir)/frodo/Frodo.glade + $(INSTALL_DATA) maemo/frodo.desktop $(DESTDIR)$(datadir)/applications/hildon/frodo.desktop + $(INSTALL_DATA) maemo/frodo.service $(DESTDIR)$(datadir)/dbus-1/services/frodo.service + $(INSTALL_DATA) maemo/frodo.service $(DESTDIR)$(datadir)/dbus-1/services/frodo.service + $(INSTALL_DATA) maemo/Frodo_26_26.png $(DESTDIR)$(datadir)/icons/hicolor/26x26/hildon/frodo.png + $(INSTALL_DATA) maemo/Frodo_40_40.png $(DESTDIR)$(datadir)/icons/hicolor/40x40/hildon/frodo.png + $(INSTALL_DATA) maemo/Frodo_64_64.png $(DESTDIR)$(datadir)/icons/hicolor/scalable/hildon/frodo.png + +installdirs: + $(SHELL) mkinstalldirs $(DESTDIR)$(bindir) $(DESTDIR)$(datadir)/frodo $(DESTDIR)$(datadir)/applications/hildon $(DESTDIR)$(datadir)/dbus-1/services $(DESTDIR)$(datadir)/icons/hicolor/26x26/hildon $(DESTDIR)$(datadir)/icons/hicolor/40x40/hildon $(DESTDIR)$(datadir)/icons/hicolor/scalable/hildon + +uninstall: + rm -f $(DESTDIR)$(bindir)/Frodo + rm -f $(DESTDIR)$(bindir)/FrodoSC + rm -f $(DESTDIR)$(bindir)/Frodo_GUI.tcl + rm -f $(DESTDIR)$(datadir)/frodo/Kernal\ ROM + rmdir $(DESTDIR)$(datadir)/frodo + +clean: + rm -f $(SLOBJS) $(SCOBJS) + rm -f Frodo FrodoSC + rm -f core* *.core *~ *.bak + +distclean: clean + rm -rf autom4te.cache + rm -f Makefile sysconfig.h + rm -f config.cache config.log config.status config.h + +depend dep: + makedepend $(CPPFLAGS) -Y. $(SRCS) 2>/dev/null + +.cpp.o: + $(CXX) $(DEFS) $(CFLAGS) -o $@ -c $*.cpp + +C64.o: C64.cpp + $(CXX) $(DEFS) $(CFLAGS) $(SLFLAGS) -o $@ -c $*.cpp + +C64_SC.o: C64_SC.cpp + $(CXX) $(DEFS) $(CFLAGS) $(SCFLAGS) -o $@ -c $*.cpp + +CPUC64.o: CPUC64.cpp + $(CXX) $(DEFS) $(CFLAGS) $(SLFLAGS) -o $@ -c $*.cpp + +CPUC64_SC.o: CPUC64_SC.cpp + $(CXX) $(DEFS) $(CFLAGS) $(SCFLAGS) -o $@ -c $*.cpp + +CPU1541.o: CPU1541.cpp + $(CXX) $(DEFS) $(CFLAGS) $(SLFLAGS) -o $@ -c $*.cpp + +CPU1541_SC.o: CPU1541_SC.cpp + $(CXX) $(DEFS) $(CFLAGS) $(SCFLAGS) -o $@ -c $*.cpp + +VIC_SC.o: VIC_SC.cpp + $(CXX) $(DEFS) $(CFLAGS) $(SCFLAGS) -o $@ -c $*.cpp + +CIA_SC.o: CIA_SC.cpp + $(CXX) $(DEFS) $(CFLAGS) $(SCFLAGS) -o $@ -c $*.cpp + +#------------------------------------------------------------------------- +# DO NOT DELETE THIS LINE -- make depend depends on it. diff --git a/Src/MakefileRO b/Src/MakefileRO new file mode 100644 index 0000000..7927d0c --- /dev/null +++ b/Src/MakefileRO @@ -0,0 +1,132 @@ +# Makefile for Frodo (RISC OS with GCC) + +## Version information +VERSION = 4 +REVISION = 2 + +CXX = gcc +CFLAGS = -O2 +CFLAGSO = -O3 +SCFLAGS = -O2 -DFRODO_SC +SCFLAGSO = -O3 -DFRODO_SC + +LIBRARIES = GCC:o.libgcc C:o.stubs +LINK = drlink +ASS = as + +## Files +OBJS = o.Prefs o.REU o.IEC o.1541fs o.1541d64 o.1541t64 o.1541job o.SAM o.ROlib +SLOBJS = $(OBJS) o.C64 o.CPUC64 o.VIC o.CIA o.CPU1541 o.Display o.SID o.main o.AcornGUI +SCOBJS = $(OBJS) o.C64_SC o.CPUC64_SC o.VIC_SC o.CIA_SC o.CPU1541_SC o.CPU_common o.Display_SC\ + o.main_SC o.SID_SC o.AcornGUI_SC + +all: Frodo FrodoSC + +Frodo: $(SLOBJS) + $(LINK) -o Frodo $(LIBRARIES) $(SLOBJS) $(LDFLAGS) + +FrodoPC: $(PCOBJS) + $(LINK) -o FrodoPC $(LIBRARIES) $(PCOBJS) $(LDFLAGS) + + +# SC objects +o.C64_SC: cc.C64_SC h.C64 i.C64_Acorn h.CPUC64 h.CPU1541 h.VIC h.SID h.CIA h.REU \ + h.IEC h.1541job h.Display h.Prefs h.ROlib h.AcornGUI i.OldSnap + $(CXX) $(INCLUDES) $(SCFLAGS) -c C64_SC.cc + +o.CPUC64_SC: cc.CPUC64_SC h.CPUC64 i.CPU_emulcycle h.CPU_common h.C64 h.VIC h.SID h.CIA \ + h.REU h.IEC h.Display h.Version h.ROlib + $(CXX) $(INCLUDES) $(SCFLAGSO) -c CPUC64_SC.cc + +o.CPU1541_SC: cc.CPU1541_SC h.CPU1541 i.CPU_emulcycle h.CPU_common h.1541job h.C64 h.CIA \ + h.Display h.ROlib + $(CXX) $(INCLUDES) $(SCFLAGSO) -c CPU1541_SC.cc + +o.VIC_SC: cc.VIC_SC h.VIC h.C64 h.CPUC64 h.Display h.Prefs h.ROlib + $(CXX) $(INCLUDES) $(SCFLAGSO) -c VIC_SC.cc + +o.CIA_SC: cc.CIA_SC h.CIA h.CPUC64 h.CPU1541 h.VIC h.Prefs + $(CXX) $(INCLUDES) $(SCFLAGSO) -c CIA_SC.cc + +## These were added for RISC OS -- same source code, but different object files needed! +o.main_SC: cc.main_SC cc.main h.main i.main_Acorn h.C64 h.Display h.Prefs h.SAM h.ROlib\ + h.AcornGUI + $(CXX) $(INCLUDES) $(SCFLAGS) -c main_SC.cc + +o.Display_SC: cc.Display_SC cc.Display h.Display i.Display_Acorn h.main h.Prefs h.Version\ + h.ROlib h.C64 h.AcornGUI h.VIC + $(CXX) $(INCLUDES) $(SCFLAGS) -c Display_SC.cc + +o.SID_SC: cc.SID_SC cc.SID h.SID i.SID_Acorn h.Prefs h.ROlib h.C64 i.FixPoint + $(CXX) $(INCLUDES) $(SCFLAGSO) -c SID_SC.cc + +o.AcornGUI_SC: cc.AcornGUI_SC cc.AcornGUI h.AcornGUI h.ROlib h.main h.Prefs h.C64 h.VIC\ + h.Version + $(CXX) $(INCLUDES) $(SCFLAGS) -c AcornGUI_SC.cc + +## Dependencies +o.main: cc.main h.main i.main_Acorn h.C64 h.Display h.Prefs h.SAM h.ROlib h.AcornGUI + $(CXX) $(INCLUDES) $(CFLAGS) -c main.cc + +o.C64: cc.C64 h.C64 i.C64_Acorn h.CPUC64 h.CPU1541 h.VIC h.SID h.CIA \ + h.REU h.IEC h.1541job h.Display h.Prefs h.ROlib h.AcornGUI i.OldSnap + $(CXX) $(INCLUDES) $(CFLAGS) -c C64.cc + +o.Display: cc.Display h.Display i.Display_Acorn h.main h.Prefs h.Version h.ROlib h.C64\ + h.AcornGUI h.VIC + $(CXX) $(INCLUDES) $(CFLAGS) -c Display.cc + +o.Prefs: cc.Prefs h.Prefs h.Display h.main h.ROlib + $(CXX) $(INCLUDES) $(CFLAGS) -c Prefs.cc + +o.CPUC64: cc.CPUC64 h.CPUC64 i.CPU_emulline h.C64 h.VIC h.SID h.CIA h.REU h.IEC \ + h.Display h.Version h.ROlib + $(CXX) $(INCLUDES) $(CFLAGSO) -c CPUC64.cc + +o.CPU1541: cc.CPU1541 h.CPU1541 i.CPU_emulline h.1541job h.C64 h.CIA h.Display h.ROlib + $(CXX) $(INCLUDES) $(CFLAGSO) -c CPU1541.cc + +o.CPU_common: cc.CPU_common h.CPU_common + $(CXX) $(INCLUDES) $(CFLAGS) -c CPU_common.cc + +o.VIC: cc.VIC h.VIC h.C64 h.CPUC64 h.Display h.Prefs h.ROlib i.el_Acorn + $(CXX) $(INCLUDES) $(CFLAGSO) -c VIC.cc + +o.SID: cc.SID h.SID i.SID_Acorn h.Prefs h.ROlib h.C64 i.FixPoint + $(CXX) $(INCLUDES) $(CFLAGSO) -c SID.cc + +o.CIA: cc.CIA h.CIA h.CPUC64 h.CPU1541 h.VIC h.Prefs + $(CXX) $(INCLUDES) $(CFLAGSO) -c CIA.cc + +o.IEC: cc.IEC h.IEC h.1541fs h.1541d64 h.1541t64 h.Prefs h.Display h.ROlib + $(CXX) $(INCLUDES) $(CFLAGS) -c IEC.cc + +o.1541fs: cc.1541fs h.1541fs h.IEC h.main h.Prefs h.ROlib + $(CXX) $(INCLUDES) $(CFLAGS) -c 1541fs.cc + +h.1541fs: h.IEC + +o.1541d64: cc.1541d64 h.1541d64 h.IEC h.Prefs + $(CXX) $(INCLUDES) $(CFLAGS) -c 1541d64.cc + +h.1541d64: h.IEC + +o.1541t64: cc.1541t64 h.1541t64 h.IEC h.Prefs + $(CXX) $(INCLUDES) $(CFLAGS) -c 1541t64.cc + +h.1541t64: h.IEC + +o.1541job: cc.1541job h.1541job h.CPU1541 h.Prefs + $(CXX) $(INCLUDES) $(CFLAGS) -c 1541job.cc + +o.REU: cc.REU h.REU h.CPUC64 h.Prefs + $(CXX) $(INCLUDES) $(CFLAGS) -c REU.cc + +o.SAM: cc.SAM h.SAM h.C64 h.CPUC64 h.CPU1541 h.VIC h.SID h.CIA + $(CXX) $(INCLUDES) $(CFLAGS) -c SAM.cc + +o.ROlib: s.ROlib + $(ASS) -o o.ROlib s.ROlib + +o.AcornGUI: cc.AcornGUI h.AcornGUI h.ROlib h.main h.Display h.Prefs h.Version h.C64 h.VIC + $(CXX) $(INCLUDES) $(CFLAGS) -c AcornGUI.cc diff --git a/Src/Prefs.cpp b/Src/Prefs.cpp new file mode 100644 index 0000000..c6658bf --- /dev/null +++ b/Src/Prefs.cpp @@ -0,0 +1,438 @@ +/* + * Prefs.cpp - Global preferences + * + * Frodo (C) 1994-1997,2002-2005 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "sysdeps.h" + +#include "Prefs.h" +#include "Display.h" +#include "C64.h" +#include "main.h" + + +// These are the active preferences +Prefs ThePrefs; + +// These are the preferences on disk +Prefs ThePrefsOnDisk; + + +/* + * Constructor: Set up preferences with defaults + */ + +Prefs::Prefs() +{ + NormalCycles = 63; + BadLineCycles = 23; + CIACycles = 63; + FloppyCycles = 64; + SkipFrames = 1; + LatencyMin = 80; + LatencyMax = 120; + LatencyAvg = 280; + ScalingNumerator = 2; + ScalingDenominator = 2; + + strcpy(DrivePath[0], "64prgs"); + strcpy(DrivePath[1], ""); + strcpy(DrivePath[2], ""); + strcpy(DrivePath[3], ""); + + strcpy(ViewPort, "Default"); + strcpy(DisplayMode, "Default"); + + SIDType = SIDTYPE_DIGITAL; + REUSize = REU_NONE; + DisplayType = DISPTYPE_WINDOW; + Joystick1Port = 0; + Joystick2Port = 0; + + SpritesOn = true; + SpriteCollisions = true; + JoystickSwap = false; + LimitSpeed = false; + FastReset = false; + CIAIRQHack = false; + MapSlash = true; + Emul1541Proc = false; + SIDFilters = true; + DoubleScan = true; + HideCursor = false; + DirectSound = true; + ExclusiveSound = false; + AutoPause = false; + PrefsAtStartup = false; + SystemMemory = false; + AlwaysCopy = false; + SystemKeys = true; + ShowLEDs = true; + +#ifdef HAVE_SDL + for (int i = 0; i < N_WIIMOTE_BINDINGS; i++) + this->JoystickKeyBinding[i] = -1; + + this->DisplayOption = 0; + this->MsPerFrame = 38; +#endif +} + + +/* + * Check if two Prefs structures are equal + */ + +bool Prefs::operator==(const Prefs &rhs) const +{ + return (1 + && NormalCycles == rhs.NormalCycles + && BadLineCycles == rhs.BadLineCycles + && CIACycles == rhs.CIACycles + && FloppyCycles == rhs.FloppyCycles + && SkipFrames == rhs.SkipFrames + && LatencyMin == rhs.LatencyMin + && LatencyMax == rhs.LatencyMax + && LatencyAvg == rhs.LatencyAvg + && ScalingNumerator == rhs.ScalingNumerator + && ScalingDenominator == rhs.ScalingNumerator + && strcmp(DrivePath[0], rhs.DrivePath[0]) == 0 + && strcmp(DrivePath[1], rhs.DrivePath[1]) == 0 + && strcmp(DrivePath[2], rhs.DrivePath[2]) == 0 + && strcmp(DrivePath[3], rhs.DrivePath[3]) == 0 + && strcmp(ViewPort, rhs.ViewPort) == 0 + && strcmp(DisplayMode, rhs.DisplayMode) == 0 + && SIDType == rhs.SIDType + && REUSize == rhs.REUSize + && DisplayType == rhs.DisplayType + && SpritesOn == rhs.SpritesOn + && SpriteCollisions == rhs.SpriteCollisions + && Joystick1Port == rhs.Joystick1Port + && Joystick2Port == rhs.Joystick2Port + && JoystickSwap == rhs.JoystickSwap + && LimitSpeed == rhs.LimitSpeed + && FastReset == rhs.FastReset + && CIAIRQHack == rhs.CIAIRQHack + && MapSlash == rhs.MapSlash + && Emul1541Proc == rhs.Emul1541Proc + && SIDFilters == rhs.SIDFilters + && DoubleScan == rhs.DoubleScan + && HideCursor == rhs.HideCursor + && DirectSound == rhs.DirectSound + && ExclusiveSound == rhs.ExclusiveSound + && AutoPause == rhs.AutoPause + && PrefsAtStartup == rhs.PrefsAtStartup + && SystemMemory == rhs.SystemMemory + && AlwaysCopy == rhs.AlwaysCopy + && SystemKeys == rhs.SystemKeys + && ShowLEDs == rhs.ShowLEDs +#ifdef HAVE_SDL + && this->JoystickKeyBinding[0] == rhs.JoystickKeyBinding[0] + && this->JoystickKeyBinding[1] == rhs.JoystickKeyBinding[1] + && this->JoystickKeyBinding[2] == rhs.JoystickKeyBinding[2] + && this->JoystickKeyBinding[3] == rhs.JoystickKeyBinding[3] + && this->JoystickKeyBinding[4] == rhs.JoystickKeyBinding[4] + && this->JoystickKeyBinding[5] == rhs.JoystickKeyBinding[5] + && this->JoystickKeyBinding[6] == rhs.JoystickKeyBinding[6] + && this->JoystickKeyBinding[7] == rhs.JoystickKeyBinding[7] + && this->JoystickKeyBinding[8] == rhs.JoystickKeyBinding[8] + && this->JoystickKeyBinding[9] == rhs.JoystickKeyBinding[9] + && this->JoystickKeyBinding[10] == rhs.JoystickKeyBinding[10] + && this->JoystickKeyBinding[11] == rhs.JoystickKeyBinding[11] + && this->JoystickKeyBinding[12] == rhs.JoystickKeyBinding[12] + && this->DisplayOption == rhs.DisplayOption + && this->MsPerFrame == rhs.MsPerFrame +#endif + ); +} + +bool Prefs::operator!=(const Prefs &rhs) const +{ + return !operator==(rhs); +} + + +/* + * Check preferences for validity and correct if necessary + */ + +void Prefs::Check(void) +{ + if (SkipFrames <= 0) SkipFrames = 1; + + if (SIDType < SIDTYPE_NONE || SIDType > SIDTYPE_SIDCARD) + SIDType = SIDTYPE_NONE; + + if (REUSize < REU_NONE || REUSize > REU_512K) + REUSize = REU_NONE; + + if (DisplayType < DISPTYPE_WINDOW || DisplayType > DISPTYPE_SCREEN) + DisplayType = DISPTYPE_WINDOW; +} + + +/* + * Load preferences from file + */ + +void Prefs::Load(char *filename) +{ + FILE *file; + char line[256], keyword[256], value[256]; + + if ((file = fopen(filename, "r")) != NULL) { + while(fgets(line, 255, file)) { + if (sscanf(line, "%s = %s\n", keyword, value) == 2) { + if (!strcmp(keyword, "NormalCycles")) + NormalCycles = atoi(value); + else if (!strcmp(keyword, "BadLineCycles")) + BadLineCycles = atoi(value); + else if (!strcmp(keyword, "CIACycles")) + CIACycles = atoi(value); + else if (!strcmp(keyword, "FloppyCycles")) + FloppyCycles = atoi(value); + else if (!strcmp(keyword, "SkipFrames")) + SkipFrames = atoi(value); + else if (!strcmp(keyword, "LatencyMin")) + LatencyMin = atoi(value); + else if (!strcmp(keyword, "LatencyMax")) + LatencyMax = atoi(value); + else if (!strcmp(keyword, "LatencyAvg")) + LatencyAvg = atoi(value); + else if (!strcmp(keyword, "ScalingNumerator")) + ScalingNumerator = atoi(value); + else if (!strcmp(keyword, "ScalingDenominator")) + ScalingDenominator = atoi(value); + else if (!strcmp(keyword, "DrivePath8")) + strcpy(DrivePath[0], value); + else if (!strcmp(keyword, "DrivePath9")) + strcpy(DrivePath[1], value); + else if (!strcmp(keyword, "DrivePath10")) + strcpy(DrivePath[2], value); + else if (!strcmp(keyword, "DrivePath11")) + strcpy(DrivePath[3], value); + else if (!strcmp(keyword, "ViewPort")) + strcpy(ViewPort, value); + else if (!strcmp(keyword, "DisplayMode")) + strcpy(DisplayMode, value); + else if (!strcmp(keyword, "SIDType")) + if (!strcmp(value, "DIGITAL")) + SIDType = SIDTYPE_DIGITAL; + else if (!strcmp(value, "SIDCARD")) + SIDType = SIDTYPE_SIDCARD; + else + SIDType = SIDTYPE_NONE; + else if (!strcmp(keyword, "REUSize")) { + if (!strcmp(value, "128K")) + REUSize = REU_128K; + else if (!strcmp(value, "256K")) + REUSize = REU_256K; + else if (!strcmp(value, "512K")) + REUSize = REU_512K; + else + REUSize = REU_NONE; + } else if (!strcmp(keyword, "DisplayType")) + DisplayType = strcmp(value, "SCREEN") ? DISPTYPE_WINDOW : DISPTYPE_SCREEN; + else if (!strcmp(keyword, "Joystick1Port")) + Joystick1Port = atoi(value); + else if (!strcmp(keyword, "Joystick2Port")) + Joystick2Port = atoi(value); + else if (!strcmp(keyword, "SpritesOn")) + SpritesOn = !strcmp(value, "TRUE"); + else if (!strcmp(keyword, "SpriteCollisions")) + SpriteCollisions = !strcmp(value, "TRUE"); + else if (!strcmp(keyword, "JoystickSwap")) + JoystickSwap = !strcmp(value, "TRUE"); + else if (!strcmp(keyword, "LimitSpeed")) + LimitSpeed = !strcmp(value, "TRUE"); + else if (!strcmp(keyword, "FastReset")) + FastReset = !strcmp(value, "TRUE"); + else if (!strcmp(keyword, "CIAIRQHack")) + CIAIRQHack = !strcmp(value, "TRUE"); + else if (!strcmp(keyword, "MapSlash")) + MapSlash = !strcmp(value, "TRUE"); + else if (!strcmp(keyword, "Emul1541Proc")) + Emul1541Proc = !strcmp(value, "TRUE"); + else if (!strcmp(keyword, "SIDFilters")) + SIDFilters = !strcmp(value, "TRUE"); + else if (!strcmp(keyword, "DoubleScan")) + DoubleScan = !strcmp(value, "TRUE"); + else if (!strcmp(keyword, "HideCursor")) + HideCursor = !strcmp(value, "TRUE"); + else if (!strcmp(keyword, "DirectSound")) + DirectSound = !strcmp(value, "TRUE"); + else if (!strcmp(keyword, "ExclusiveSound")) + ExclusiveSound = !strcmp(value, "TRUE"); + else if (!strcmp(keyword, "AutoPause")) + AutoPause = !strcmp(value, "TRUE"); + else if (!strcmp(keyword, "PrefsAtStartup")) + PrefsAtStartup = !strcmp(value, "TRUE"); + else if (!strcmp(keyword, "SystemMemory")) + SystemMemory = !strcmp(value, "TRUE"); + else if (!strcmp(keyword, "AlwaysCopy")) + AlwaysCopy = !strcmp(value, "TRUE"); + else if (!strcmp(keyword, "SystemKeys")) + SystemKeys = !strcmp(value, "TRUE"); + else if (!strcmp(keyword, "ShowLEDs")) + ShowLEDs = !strcmp(value, "TRUE"); +#if defined(HAVE_SDL) + else if (!strcmp(keyword, "JoystickKeyBinding0")) + JoystickKeyBinding[0] = atoi(value); + else if (!strcmp(keyword, "JoystickKeyBinding1")) + JoystickKeyBinding[1] = atoi(value); + else if (!strcmp(keyword, "JoystickKeyBinding2")) + JoystickKeyBinding[2] = atoi(value); + else if (!strcmp(keyword, "JoystickKeyBinding3")) + JoystickKeyBinding[3] = atoi(value); + else if (!strcmp(keyword, "JoystickKeyBinding4")) + JoystickKeyBinding[4] = atoi(value); + else if (!strcmp(keyword, "JoystickKeyBinding5")) + JoystickKeyBinding[5] = atoi(value); + else if (!strcmp(keyword, "JoystickKeyBinding6")) + JoystickKeyBinding[6] = atoi(value); + else if (!strcmp(keyword, "JoystickKeyBinding7")) + JoystickKeyBinding[7] = atoi(value); + else if (!strcmp(keyword, "JoystickKeyBinding8")) + JoystickKeyBinding[8] = atoi(value); + else if (!strcmp(keyword, "JoystickKeyBinding9")) + JoystickKeyBinding[9] = atoi(value); + else if (!strcmp(keyword, "JoystickKeyBinding10")) + JoystickKeyBinding[10] = atoi(value); + else if (!strcmp(keyword, "JoystickKeyBinding11")) + JoystickKeyBinding[11] = atoi(value); + else if (!strcmp(keyword, "JoystickKeyBinding12")) + JoystickKeyBinding[12] = atoi(value); + else if (!strcmp(keyword, "DisplayOption")) + DisplayOption = atoi(value); + else if (!strcmp(keyword, "MsPerFrame")) + MsPerFrame = atoi(value); +#endif + } + } + fclose(file); + } + Check(); + ThePrefsOnDisk = *this; +} + + +/* + * Save preferences to file + * true: success, false: error + */ + +bool Prefs::Save(char *filename) +{ + FILE *file; + + Check(); + if ((file = fopen(filename, "w")) != NULL) { + fprintf(file, "NormalCycles = %d\n", NormalCycles); + fprintf(file, "BadLineCycles = %d\n", BadLineCycles); + fprintf(file, "CIACycles = %d\n", CIACycles); + fprintf(file, "FloppyCycles = %d\n", FloppyCycles); + fprintf(file, "SkipFrames = %d\n", SkipFrames); + fprintf(file, "LatencyMin = %d\n", LatencyMin); + fprintf(file, "LatencyMax = %d\n", LatencyMax); + fprintf(file, "LatencyAvg = %d\n", LatencyAvg); + fprintf(file, "ScalingNumerator = %d\n", ScalingNumerator); + fprintf(file, "ScalingDenominator = %d\n", ScalingDenominator); + for (int i=0; i<4; i++) + fprintf(file, "DrivePath%d = %s\n", i+8, DrivePath[i]); + fprintf(file, "ViewPort = %s\n", ViewPort); + fprintf(file, "DisplayMode = %s\n", DisplayMode); + fprintf(file, "SIDType = "); + switch (SIDType) { + case SIDTYPE_NONE: + fprintf(file, "NONE\n"); + break; + case SIDTYPE_DIGITAL: + fprintf(file, "DIGITAL\n"); + break; + case SIDTYPE_SIDCARD: + fprintf(file, "SIDCARD\n"); + break; + } + fprintf(file, "REUSize = "); + switch (REUSize) { + case REU_NONE: + fprintf(file, "NONE\n"); + break; + case REU_128K: + fprintf(file, "128K\n"); + break; + case REU_256K: + fprintf(file, "256K\n"); + break; + case REU_512K: + fprintf(file, "512K\n"); + break; + }; + fprintf(file, "DisplayType = %s\n", DisplayType == DISPTYPE_WINDOW ? "WINDOW" : "SCREEN"); + fprintf(file, "Joystick1Port = %d\n", Joystick1Port); + fprintf(file, "Joystick2Port = %d\n", Joystick2Port); + fprintf(file, "SpritesOn = %s\n", SpritesOn ? "TRUE" : "FALSE"); + fprintf(file, "SpriteCollisions = %s\n", SpriteCollisions ? "TRUE" : "FALSE"); + fprintf(file, "JoystickSwap = %s\n", JoystickSwap ? "TRUE" : "FALSE"); + fprintf(file, "LimitSpeed = %s\n", LimitSpeed ? "TRUE" : "FALSE"); + fprintf(file, "FastReset = %s\n", FastReset ? "TRUE" : "FALSE"); + fprintf(file, "CIAIRQHack = %s\n", CIAIRQHack ? "TRUE" : "FALSE"); + fprintf(file, "MapSlash = %s\n", MapSlash ? "TRUE" : "FALSE"); + fprintf(file, "Emul1541Proc = %s\n", Emul1541Proc ? "TRUE" : "FALSE"); + fprintf(file, "SIDFilters = %s\n", SIDFilters ? "TRUE" : "FALSE"); + fprintf(file, "DoubleScan = %s\n", DoubleScan ? "TRUE" : "FALSE"); + fprintf(file, "HideCursor = %s\n", HideCursor ? "TRUE" : "FALSE"); + fprintf(file, "DirectSound = %s\n", DirectSound ? "TRUE" : "FALSE"); + fprintf(file, "ExclusiveSound = %s\n", ExclusiveSound ? "TRUE" : "FALSE"); + fprintf(file, "AutoPause = %s\n", AutoPause ? "TRUE" : "FALSE"); + fprintf(file, "PrefsAtStartup = %s\n", PrefsAtStartup ? "TRUE" : "FALSE"); + fprintf(file, "SystemMemory = %s\n", SystemMemory ? "TRUE" : "FALSE"); + fprintf(file, "AlwaysCopy = %s\n", AlwaysCopy ? "TRUE" : "FALSE"); + fprintf(file, "SystemKeys = %s\n", SystemKeys ? "TRUE" : "FALSE"); + fprintf(file, "ShowLEDs = %s\n", ShowLEDs ? "TRUE" : "FALSE"); +#if defined(HAVE_SDL) + for (int i = 0; i < N_WIIMOTE_BINDINGS; i++) + fprintf(file, "JoystickKeyBinding%d = %d\n", + i, JoystickKeyBinding[i]); + + fprintf(file, "DisplayOption = %d\n", DisplayOption); + fprintf(file, "MsPerFrame = %d\n", MsPerFrame); +#endif + fclose(file); + ThePrefsOnDisk = *this; + return true; + } + return false; +} + + +#ifdef __BEOS__ +#include "Prefs_Be.h" +#endif + +#ifdef AMIGA +#include "Prefs_Amiga.h" +#endif + +#ifdef WIN32 +#include "Prefs_WIN32.h" +#endif + +#ifdef HAVE_GLADE +#include "Prefs_glade.h" +#endif diff --git a/Src/Prefs.h b/Src/Prefs.h new file mode 100644 index 0000000..a7c4b4b --- /dev/null +++ b/Src/Prefs.h @@ -0,0 +1,155 @@ +/* + * Prefs.h - Global preferences + * + * Frodo (C) 1994-1997,2002-2005 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _PREFS_H +#define _PREFS_H + + +// SID types +enum { + SIDTYPE_NONE, // SID emulation off + SIDTYPE_DIGITAL, // Digital SID emulation + SIDTYPE_SIDCARD // SID card +}; + + +// REU sizes +enum { + REU_NONE, // No REU + REU_128K, // 128K + REU_256K, // 256K + REU_512K // 512K +}; + + +// Display types +enum { + DISPTYPE_WINDOW, // Window + DISPTYPE_SCREEN // Fullscreen +}; + +// Key bindings (WII) +enum { + WIIMOTE_A, + WIIMOTE_B, + WIIMOTE_PLUS, + WIIMOTE_MINUS, + WIIMOTE_1, + CLASSIC_X, + CLASSIC_Y, + CLASSIC_B, + CLASSIC_L, + CLASSIC_R, + CLASSIC_ZR, + CLASSIC_ZL, + N_WIIMOTE_BINDINGS +}; + + + +// Preferences data +class Prefs { +public: + Prefs(); + bool ShowEditor(bool startup, char *prefs_name); + void Check(void); + void Load(char *filename); + bool Save(char *filename); + + bool operator==(const Prefs &rhs) const; + bool operator!=(const Prefs &rhs) const; + + int NormalCycles; // Available CPU cycles in normal raster lines + int BadLineCycles; // Available CPU cycles in Bad Lines + int CIACycles; // CIA timer ticks per raster line + int FloppyCycles; // Available 1541 CPU cycles per line + int SkipFrames; // Draw every n-th frame + + char DrivePath[4][256]; // Path for drive 8..11 + + char ViewPort[256]; // Size of the C64 screen to display (Win32) + char DisplayMode[256]; // Video mode to use for full screen (Win32) + + int SIDType; // SID emulation type + int REUSize; // Size of REU + int DisplayType; // Display type (BeOS) + int Joystick1Port; // Port that joystick 1 is connected to (0 = no joystick, all other values are system dependant) + int Joystick2Port; // Port that joystick 2 is connected to + int LatencyMin; // Min msecs ahead of sound buffer (Win32) + int LatencyMax; // Max msecs ahead of sound buffer (Win32) + int LatencyAvg; // Averaging interval in msecs (Win32) + int ScalingNumerator; // Window scaling numerator (Win32) + int ScalingDenominator; // Window scaling denominator (Win32) + + bool SpritesOn; // Sprite display is on + bool SpriteCollisions; // Sprite collision detection is on + bool JoystickSwap; // Swap joysticks 1<->2 + bool LimitSpeed; // Limit speed to 100% + bool FastReset; // Skip RAM test on reset + bool CIAIRQHack; // Write to CIA ICR clears IRQ + bool MapSlash; // Map '/' in C64 filenames + bool Emul1541Proc; // Enable processor-level 1541 emulation + bool SIDFilters; // Emulate SID filters + bool DoubleScan; // Double scan lines (BeOS, if DisplayType == DISPTYPE_SCREEN) + bool JoystickGeekPort; // Enable GeekPort joystick adapter + bool HideCursor; // Hide mouse cursor when visible (Win32) + bool DirectSound; // Use direct sound (instead of wav) (Win32) + bool ExclusiveSound; // Use exclusive mode with direct sound (Win32) + bool AutoPause; // Auto pause when not foreground app (Win32) + bool PrefsAtStartup; // Show prefs dialog at startup (Win32) + bool SystemMemory; // Put view work surface in system mem (Win32) + bool AlwaysCopy; // Always use a work surface (Win32) + bool SystemKeys; // Enable system keys and menu keys (Win32) + bool ShowLEDs; // Show LEDs (Win32) + +#ifdef __mac__ + void ChangeDisks(void); +#endif + +#ifdef WIN32 +private: + static BOOL CALLBACK StandardDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); + static BOOL CALLBACK WIN32DialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); + BOOL DialogProc(int page, HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); + void SetupControls(int page); + void SetValues(int page); + void GetValues(int page); + void BrowseForDevice(int id); + + static Prefs *edit_prefs; + static char *edit_prefs_name; + static HWND hDlg; +#endif + +#ifdef HAVE_SDL + int JoystickKeyBinding[N_WIIMOTE_BINDINGS]; + int DisplayOption; + uint32 MsPerFrame; +#endif +}; + + +// These are the active preferences +extern Prefs ThePrefs; + +// Theses are the preferences on disk +extern Prefs ThePrefsOnDisk; + +#endif diff --git a/Src/Prefs_Amiga.h b/Src/Prefs_Amiga.h new file mode 100644 index 0000000..a1248f8 --- /dev/null +++ b/Src/Prefs_Amiga.h @@ -0,0 +1,397 @@ +/* + * Prefs_Amiga.h - Global preferences, Amiga specific stuff + * + * Frodo (C) 1994-1997,2002-2005 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include + +extern "C" { +#include "AmigaGUI.h" +} + + +// Flag: All done, close prefs window +static bool done, result; + +// Pointer to preferences being edited +static Prefs *prefs; + +// Pointer to prefs file name +static char *path; + +// File requesters +struct FileRequester *open_req, *save_req, *drive_req; + +// Prototypes +static void set_values(void); +static void get_values(void); +static void ghost_gadgets(void); +static void get_drive(int i); + + +/* + * Show preferences editor (synchronously) + * prefs_name points to the file name of the preferences (which may be changed) + */ + +bool Prefs::ShowEditor(bool startup, char *prefs_name) +{ + done = result = FALSE; + prefs = this; + path = prefs_name; + open_req = save_req = NULL; + + // Open prefs window + if (!SetupScreen()) { + if (!OpenPrefsWindow()) { + + // Allocate file requesters + open_req = (struct FileRequester *)AllocAslRequestTags(ASL_FileRequest, + ASLFR_Window, (ULONG)PrefsWnd, + ASLFR_SleepWindow, TRUE, + ASLFR_TitleText, (ULONG)"Frodo: Open preferences...", + ASLFR_RejectIcons, TRUE, + TAG_DONE); + save_req = (struct FileRequester *)AllocAslRequestTags(ASL_FileRequest, + ASLFR_Window, (ULONG)PrefsWnd, + ASLFR_SleepWindow, TRUE, + ASLFR_TitleText, (ULONG)"Frodo: Save preferences as...", + ASLFR_DoSaveMode, TRUE, + ASLFR_RejectIcons, TRUE, + TAG_DONE); + drive_req = (struct FileRequester *)AllocAslRequestTags(ASL_FileRequest, + ASLFR_Window, (ULONG)PrefsWnd, + ASLFR_SleepWindow, TRUE, + ASLFR_RejectIcons, TRUE, + TAG_DONE); + + // Handle prefs window + set_values(); + while (!done) { + WaitPort(PrefsWnd->UserPort); + HandlePrefsIDCMP(); + } + + // Free file requesters + FreeAslRequest(open_req); + FreeAslRequest(save_req); + FreeAslRequest(drive_req); + } + ClosePrefsWindow(); + } + CloseDownScreen(); + + return result; +} + + +/* + * Set the values of the gadgets + */ + +static void set_values(void) +{ + prefs->Check(); + + GT_SetGadgetAttrs(PrefsGadgets[GDX_NormalCycles], PrefsWnd, NULL, GTIN_Number, prefs->NormalCycles, TAG_DONE); + GT_SetGadgetAttrs(PrefsGadgets[GDX_BadLineCycles], PrefsWnd, NULL, GTIN_Number, prefs->BadLineCycles, TAG_DONE); + GT_SetGadgetAttrs(PrefsGadgets[GDX_CIACycles], PrefsWnd, NULL, GTIN_Number, prefs->CIACycles, TAG_DONE); + GT_SetGadgetAttrs(PrefsGadgets[GDX_FloppyCycles], PrefsWnd, NULL, GTIN_Number, prefs->FloppyCycles, TAG_DONE); + GT_SetGadgetAttrs(PrefsGadgets[GDX_SkipFrames], PrefsWnd, NULL, GTIN_Number, prefs->SkipFrames, TAG_DONE); + + GT_SetGadgetAttrs(PrefsGadgets[GDX_SIDType], PrefsWnd, NULL, GTCY_Active, prefs->SIDType, TAG_DONE); + GT_SetGadgetAttrs(PrefsGadgets[GDX_REUSize], PrefsWnd, NULL, GTCY_Active, prefs->REUSize, TAG_DONE); + + GT_SetGadgetAttrs(PrefsGadgets[GDX_SpritesOn], PrefsWnd, NULL, GTCB_Checked, prefs->SpritesOn, TAG_DONE); + GT_SetGadgetAttrs(PrefsGadgets[GDX_SpriteCollisions], PrefsWnd, NULL, GTCB_Checked, prefs->SpriteCollisions, TAG_DONE); + GT_SetGadgetAttrs(PrefsGadgets[GDX_Joystick2On], PrefsWnd, NULL, GTCB_Checked, prefs->Joystick2On, TAG_DONE); + GT_SetGadgetAttrs(PrefsGadgets[GDX_JoystickSwap], PrefsWnd, NULL, GTCB_Checked, prefs->JoystickSwap, TAG_DONE); + GT_SetGadgetAttrs(PrefsGadgets[GDX_LimitSpeed], PrefsWnd, NULL, GTCB_Checked, prefs->LimitSpeed, TAG_DONE); + GT_SetGadgetAttrs(PrefsGadgets[GDX_FastReset], PrefsWnd, NULL, GTCB_Checked, prefs->FastReset, TAG_DONE); + GT_SetGadgetAttrs(PrefsGadgets[GDX_CIAIRQHack], PrefsWnd, NULL, GTCB_Checked, prefs->CIAIRQHack, TAG_DONE); + GT_SetGadgetAttrs(PrefsGadgets[GDX_SIDFilters], PrefsWnd, NULL, GTCB_Checked, prefs->SIDFilters, TAG_DONE); + + GT_SetGadgetAttrs(PrefsGadgets[GDX_DrivePath8], PrefsWnd, NULL, GTST_String, (ULONG)prefs->DrivePath[0], TAG_DONE); + GT_SetGadgetAttrs(PrefsGadgets[GDX_DrivePath9], PrefsWnd, NULL, GTST_String, (ULONG)prefs->DrivePath[1], TAG_DONE); + GT_SetGadgetAttrs(PrefsGadgets[GDX_DrivePath10], PrefsWnd, NULL, GTST_String, (ULONG)prefs->DrivePath[2], TAG_DONE); + GT_SetGadgetAttrs(PrefsGadgets[GDX_DrivePath11], PrefsWnd, NULL, GTST_String, (ULONG)prefs->DrivePath[3], TAG_DONE); + + GT_SetGadgetAttrs(PrefsGadgets[GDX_MapSlash], PrefsWnd, NULL, GTCB_Checked, prefs->MapSlash, TAG_DONE); + GT_SetGadgetAttrs(PrefsGadgets[GDX_Emul1541Proc], PrefsWnd, NULL, GTCB_Checked, prefs->Emul1541Proc, TAG_DONE); + + ghost_gadgets(); +} + + +/* + * Get the values of the gadgets + */ + +static void get_values(void) +{ + prefs->NormalCycles = GetNumber(PrefsGadgets[GDX_NormalCycles]); + prefs->BadLineCycles = GetNumber(PrefsGadgets[GDX_BadLineCycles]); + prefs->CIACycles = GetNumber(PrefsGadgets[GDX_CIACycles]); + prefs->FloppyCycles = GetNumber(PrefsGadgets[GDX_FloppyCycles]); + prefs->SkipFrames = GetNumber(PrefsGadgets[GDX_SkipFrames]); + + strcpy(prefs->DrivePath[0], GetString(PrefsGadgets[GDX_DrivePath8])); + strcpy(prefs->DrivePath[1], GetString(PrefsGadgets[GDX_DrivePath9])); + strcpy(prefs->DrivePath[2], GetString(PrefsGadgets[GDX_DrivePath10])); + strcpy(prefs->DrivePath[3], GetString(PrefsGadgets[GDX_DrivePath11])); + + prefs->Check(); +} + + +/* + * Enable/disable certain gadgets + */ + +static void ghost_gadgets(void) +{ + GT_SetGadgetAttrs(PrefsGadgets[GDX_NormalCycles], PrefsWnd, NULL, GA_Disabled, IsFrodoSC, TAG_DONE); + GT_SetGadgetAttrs(PrefsGadgets[GDX_BadLineCycles], PrefsWnd, NULL, GA_Disabled, IsFrodoSC, TAG_DONE); + GT_SetGadgetAttrs(PrefsGadgets[GDX_CIACycles], PrefsWnd, NULL, GA_Disabled, IsFrodoSC, TAG_DONE); + GT_SetGadgetAttrs(PrefsGadgets[GDX_FloppyCycles], PrefsWnd, NULL, GA_Disabled, IsFrodoSC, TAG_DONE); + GT_SetGadgetAttrs(PrefsGadgets[GDX_CIAIRQHack], PrefsWnd, NULL, GA_Disabled, IsFrodoSC, TAG_DONE); +} + + +/* + * Handle gadgets + */ + +int SpritesOnClicked(void) +{ + prefs->SpritesOn = !prefs->SpritesOn; +} + +int SpriteCollisionsClicked(void) +{ + prefs->SpriteCollisions = !prefs->SpriteCollisions; +} + +int Joystick2OnClicked(void) +{ + prefs->Joystick2On = !prefs->Joystick2On; +} + +int JoystickSwapClicked(void) +{ + prefs->JoystickSwap = !prefs->JoystickSwap; +} + +int LimitSpeedClicked(void) +{ + prefs->LimitSpeed = !prefs->LimitSpeed; +} + +int FastResetClicked(void) +{ + prefs->FastReset = !prefs->FastReset; +} + +int CIAIRQHackClicked(void) +{ + prefs->CIAIRQHack = !prefs->CIAIRQHack; +} + +int SIDFiltersClicked(void) +{ + prefs->SIDFilters = !prefs->SIDFilters; +} + +int NormalCyclesClicked(void) {} +int BadLineCyclesClicked(void) {} +int CIACyclesClicked(void) {} +int FloppyCyclesClicked(void) {} +int SkipFramesClicked(void) {} +int DrivePath8Clicked(void) {} +int DrivePath9Clicked(void) {} +int DrivePath10Clicked(void) {} +int DrivePath11Clicked(void) {} + +int SIDTypeClicked(void) +{ + prefs->SIDType = PrefsMsg.Code; +} + +int REUSizeClicked(void) +{ + prefs->REUSize = PrefsMsg.Code; +} + +void get_drive(int i) +{ + bool result = FALSE; + + if (drive_req == NULL) + return; + + get_values(); + + if (prefs->DriveType[i] == DRVTYPE_DIR) + result = AslRequestTags(drive_req, + ASLFR_TitleText, (ULONG)"Frodo: Select directory", + ASLFR_DrawersOnly, TRUE, + ASLFR_DoPatterns, FALSE, + ASLFR_InitialPattern, (ULONG)"#?", + ASLFR_InitialDrawer, (ULONG)prefs->DrivePath[i], + ASLFR_InitialFile, (ULONG)"", + TAG_DONE); + else { + + // Separate path and file + char dir[256], file[256]; + strncpy(dir, prefs->DrivePath[i], 255); + char *s = FilePart(dir); + strncpy(file, s, 255); + *s = 0; + + result = AslRequestTags(drive_req, + ASLFR_TitleText, (ULONG)"Frodo: Select disk image or archive file", + ASLFR_DrawersOnly, FALSE, + ASLFR_DoPatterns, TRUE, + ASLFR_InitialPattern, (ULONG)"#?.(d64|x64|t64|LNX|P00)", + ASLFR_InitialDrawer, (ULONG)dir, + ASLFR_InitialFile, (ULONG)file, + TAG_DONE); + } + + if (result) { + strncpy(prefs->DrivePath[i], drive_req->fr_Drawer, 255); + if (prefs->DriveType[i] != DRVTYPE_DIR) + AddPart(prefs->DrivePath[i], drive_req->fr_File, 255); + set_values(); + } +} + +int GetDrive8Clicked(void) +{ + get_drive(0); +} + +int GetDrive9Clicked(void) +{ + get_drive(1); +} + +int GetDrive10Clicked(void) +{ + get_drive(2); +} + +int GetDrive11Clicked(void) +{ + get_drive(3); +} + +int MapSlashClicked(void) +{ + prefs->MapSlash = !prefs->MapSlash; +} + +int Emul1541ProcClicked(void) +{ + prefs->Emul1541Proc = !prefs->Emul1541Proc; +} + +int OKClicked(void) +{ + get_values(); + done = result = TRUE; +} + +int CancelClicked(void) +{ + done = TRUE; + result = FALSE; +} + + +/* + * Handle menus + */ + +int PrefsOpen(void) +{ + if (open_req != NULL && AslRequest(open_req, NULL)) { + strncpy(path, open_req->fr_Drawer, 255); + AddPart(path, open_req->fr_File, 255); + + get_values(); // Useful if Load() is unsuccessful + prefs->Load(path); + set_values(); + } +} + +int PrefsRevert(void) +{ + get_values(); // Useful if Load() is unsuccessful + prefs->Load(path); + set_values(); +} + +int PrefsSaveAs(void) +{ + if (save_req != NULL && AslRequest(save_req, NULL)) { + strncpy(path, save_req->fr_Drawer, 255); + AddPart(path, save_req->fr_File, 255); + + get_values(); + prefs->Save(path); + } +} + +int PrefsSave(void) +{ + get_values(); + prefs->Save(path); +} + +int PrefsOK(void) +{ + return OKClicked(); +} + +int PrefsCancel(void) +{ + return CancelClicked(); +} + + +/* + * Handle keys + */ + +int PrefsVanillaKey(void) +{ + switch (PrefsMsg.Code) { + case 'o': case 'O': + return OKClicked(); + case 'c': case 'C': + return CancelClicked(); + } +} diff --git a/Src/Prefs_Be.h b/Src/Prefs_Be.h new file mode 100644 index 0000000..dfe18a5 --- /dev/null +++ b/Src/Prefs_Be.h @@ -0,0 +1,779 @@ +/* + * Prefs_Be.h - Global preferences, Be specific stuff + * + * Frodo (C) 1994-1997,2002-2005 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include + + +// Special colors +const rgb_color light_color = {255, 255, 255, 0}; +const rgb_color fill_color = {216, 216, 216, 0}; +const rgb_color dark_color = {184, 184, 184, 0}; + + +// Window thread messages +const uint32 MSG_OK = 'okok'; +const uint32 MSG_CANCEL = 'cncl'; +const uint32 MSG_SPRITES_ON = 'spon'; +const uint32 MSG_SPRITE_COLLISIONS = 'scol'; +const uint32 MSG_JOYSTICK_1_PORT = 'joy1'; +const uint32 MSG_JOYSTICK_2_PORT = 'joy2'; +const uint32 MSG_JOYSTICK_SWAP = 'jswp'; +const uint32 MSG_LIMIT_SPEED = 'lmit'; +const uint32 MSG_FAST_RESET = 'frst'; +const uint32 MSG_CIA_IRQ_HACK = 'cirq'; +const uint32 MSG_SID_FILTERS = 'filt'; +const uint32 MSG_DOUBLE_SCAN = 'dbls'; +const uint32 MSG_MAP_SLASH = 'mpsl'; +const uint32 MSG_EMUL_1541_PROC = '15pr'; +const uint32 MSG_GETDRIVE_8 = 'gtd8'; +const uint32 MSG_GETDRIVE_9 = 'gtd9'; +const uint32 MSG_GETDRIVE_10 = 'gtd:'; +const uint32 MSG_GETDRIVE_11 = 'gtd;'; +const uint32 MSG_DRIVE_PANEL_RETURNED = 'gdr8'; +const uint32 MSG_SID_TYPE = 'sidt'; +const uint32 MSG_REU_SIZE = 'reus'; +const uint32 MSG_DISPLAY_TYPE = 'dspt'; + +const uint32 MSG_OPEN = 'open'; +const uint32 MSG_SAVE = 'save'; +const uint32 MSG_SAVE_AS = 'svas'; +const uint32 MSG_REVERT = 'rvrt'; +const uint32 MSG_OPEN_PANEL_RETURNED = 'oprt'; +const uint32 MSG_SAVE_PANEL_RETURNED = 'svrt'; + + +/* + * Preferences window class + */ + +class NumberControl; +class PathControl; + +class PrefsWindow : public BWindow { +public: + PrefsWindow(Prefs *p, bool start, char *path); + virtual void MessageReceived(BMessage *msg); + virtual bool QuitRequested(void); + virtual bool FilterKeyDown(uint32 *aChar, BView **target); + +private: + BCheckBox *make_checkbox(BRect frame, char *label, uint32 what, BView *parent); + NumberControl *make_number_entry(BRect frame, char *label_text, BView *parent); + PathControl *make_path_entry(BRect frame, char *label, BView *parent); + BPopUpMenu *make_sidtype_popup(BRect frame, char *label_text, uint32 what, BView *parent); + BPopUpMenu *make_reusize_popup(BRect frame, char *label_text, uint32 what, BView *parent); + BPopUpMenu *make_disptype_popup(BRect frame, char *label_text, uint32 what, BView *parent); + BPopUpMenu *make_joystick_popup(BRect frame, char *label_text, uint32 what, BView *parent); + void set_values(void); + void get_values(void); + void ghost_controls(void); + + Prefs *prefs; + + BMessenger this_messenger; + BFilePanel *open_panel; // For opening prefs + BFilePanel *save_panel; // For saving prefs + BFilePanel *file_panel; // For D64/T64 drives + BFilePanel *dir_panel; // For directory drives + int panel_drive_num; // Drive number (0..3) of the file panel + + BButton *g_ok; + BButton *g_cancel; + NumberControl *g_normal_cycles; + NumberControl *g_bad_line_cycles; + NumberControl *g_cia_cycles; + NumberControl *g_floppy_cycles; + NumberControl *g_skip_frames; + PathControl *g_drive_path[4]; + BPopUpMenu *g_sid_type; + BPopUpMenu *g_reu_size; + BPopUpMenu *g_display_type; + BPopUpMenu *g_joystick_1_port; + BPopUpMenu *g_joystick_2_port; + BCheckBox *g_sprites_on; + BCheckBox *g_sprite_collisions; + BCheckBox *g_joystick_swap; + BCheckBox *g_limit_speed; + BCheckBox *g_fast_reset; + BCheckBox *g_cia_irq_hack; + BCheckBox *g_sid_filters; + BCheckBox *g_double_scan; + BCheckBox *g_map_slash; + BCheckBox *g_emul_1541_proc; + + char *prefs_path; + bool startup; +}; + + +/* + * Start preferences editor (asynchronously) + * startup = false: Send MSG_PREFS_DONE to application on close + * startup = true : Send MSG_STARTUP to application on close if not canceled, + * B_QUIT_REQUESTED otherwise + * prefs_name points to the file name of the preferences (which may be changed) + */ + +bool Prefs::ShowEditor(bool startup, char *prefs_name) +{ + PrefsWindow *win = new PrefsWindow(this, startup, prefs_name); + win->Show(); + return true; +} + + +/* + * Number-only BTextControl + */ + +// Class definition +class NumberControl : public BTextControl { +public: + NumberControl(BRect frame, float divider, const char *name, const char *label, const char *text, BMessage *message); + void SetValue(long value); + long Value(void); +}; + +// Constructor: Allow only digits +NumberControl::NumberControl(BRect frame, float divider, const char *name, const char *label, const char *text, BMessage *message) + : BTextControl(frame, name, label, text, message, B_FOLLOW_LEFT | B_FOLLOW_TOP, B_WILL_DRAW | B_NAVIGABLE) +{ + SetDivider(divider); + for (int c=0; c<256; c++) + if (!isdigit(c) && c != B_BACKSPACE && c != B_LEFT_ARROW && c != B_RIGHT_ARROW) + ((BTextView *)ChildAt(0))->DisallowChar(c); +} + +// Set integer value +void NumberControl::SetValue(long value) +{ + char str[32]; + sprintf(str, "%ld", value); + SetText(str); +} + +// Get integer value +long NumberControl::Value(void) +{ + return atol(Text()); +} + + +/* + * Path-entry BTextControl + */ + +// Class definition +class PathControl : public BTextControl { +public: + PathControl(BRect frame, float divider, const char *name, const char *label, const char *text, BMessage *message); + virtual void MessageReceived(BMessage *msg); +}; + +// Constructor: Disable some keys +PathControl::PathControl(BRect frame, float divider, const char *name, const char *label, const char *text, BMessage *message) + : BTextControl(frame, name, label, text, message, B_FOLLOW_LEFT | B_FOLLOW_TOP, B_WILL_DRAW | B_NAVIGABLE) +{ + SetDivider(divider); + for (int c=0; c<' '; c++) + if (c != B_BACKSPACE && c != B_LEFT_ARROW && c != B_RIGHT_ARROW) + ((BTextView *)ChildAt(0))->DisallowChar(c); +} + +// Message received: Look out for dropped refs +void PathControl::MessageReceived(BMessage *msg) +{ + if (msg->what == B_SIMPLE_DATA) { + entry_ref the_ref; + BEntry the_entry; + + // First look for refs + if (msg->FindRef("refs", &the_ref) == B_NO_ERROR) { + if (the_entry.SetTo(&the_ref) == B_NO_ERROR) { + BPath the_path; + the_entry.GetPath(&the_path); + SetText(the_path.Path()); + } + } else + BTextControl::MessageReceived(msg); + + MakeFocus(); + } else + BTextControl::MessageReceived(msg); +} + + +/* + * Open preferences window + */ + +PrefsWindow::PrefsWindow(Prefs *p, bool start, char *path) : BWindow(BRect(0, 0, 400, 349), "Frodo Preferences", B_TITLED_WINDOW, B_NOT_CLOSABLE | B_NOT_RESIZABLE | B_NOT_ZOOMABLE), this_messenger(this) +{ + int i; + prefs = p; + startup = start; + prefs_path = path; + + // Move window to right position + Lock(); + MoveTo(80, 80); + + // Set up menus + BMenuBar *bar = new BMenuBar(Bounds(), ""); + BMenu *menu = new BMenu("Preferences"); + BMenuItem *item; + menu->AddItem(item = new BMenuItem("About Frodo" B_UTF8_ELLIPSIS, new BMessage(B_ABOUT_REQUESTED))); + item->SetTarget(be_app); + menu->AddItem(new BSeparatorItem); + menu->AddItem(new BMenuItem("Open" B_UTF8_ELLIPSIS, new BMessage(MSG_OPEN), 'O')); + menu->AddItem(new BMenuItem("Save", new BMessage(MSG_SAVE), 'S')); + menu->AddItem(new BMenuItem("Save As" B_UTF8_ELLIPSIS, new BMessage(MSG_SAVE_AS), 'A')); + menu->AddItem(new BMenuItem("Revert", new BMessage(MSG_REVERT))); + menu->AddItem(new BSeparatorItem); + menu->AddItem(item = new BMenuItem("Quit Frodo", new BMessage(B_QUIT_REQUESTED), 'Q')); + item->SetTarget(be_app); + bar->AddItem(menu); + AddChild(bar); + SetKeyMenuBar(bar); + int mbar_height = int(bar->Frame().bottom) + 1; + + // Resize window to fit menu bar + ResizeBy(0, mbar_height); + + // Light gray background + BRect b = Bounds(); + BView *top = new BView(BRect(0, mbar_height, b.right, b.bottom), "top", B_FOLLOW_NONE, B_WILL_DRAW); + AddChild(top); + top->SetViewColor(fill_color); + + // Checkboxes + g_sprites_on = make_checkbox(BRect(10, 10, 180, 21), "Sprite display", MSG_SPRITES_ON, top); + g_sprite_collisions = make_checkbox(BRect(10, 25, 180, 36), "Sprite collisions", MSG_SPRITE_COLLISIONS, top); + g_limit_speed = make_checkbox(BRect(10, 40, 180, 51), "Limit speed", MSG_LIMIT_SPEED, top); + g_fast_reset = make_checkbox(BRect(10, 55, 180, 66), "Fast reset", MSG_FAST_RESET, top); + g_cia_irq_hack = make_checkbox(BRect(10, 70, 180, 81), "Clear CIA ICR on write", MSG_CIA_IRQ_HACK, top); + g_sid_filters = make_checkbox(BRect(10, 85, 180, 96), "SID filters", MSG_SID_FILTERS, top); + g_double_scan = make_checkbox(BRect(10, 100, 180, 111), "Doublescan lines", MSG_DOUBLE_SCAN, top); + g_joystick_swap = make_checkbox(BRect(10, 115, 180, 126), "Swap joysticks", MSG_JOYSTICK_SWAP, top); + g_joystick_1_port = make_joystick_popup(BRect(10, 130, 180, 145), "Joystick 1", MSG_JOYSTICK_1_PORT, top); + g_joystick_2_port = make_joystick_popup(BRect(10, 150, 180, 165), "Joystick 2", MSG_JOYSTICK_2_PORT, top); + + // Number entry fields + g_normal_cycles = make_number_entry(BRect(180, 10, 390, 26), "Cycles per line (CPU)", top); + g_bad_line_cycles = make_number_entry(BRect(180, 30, 390, 46), "Cycles per Bad Line (CPU)", top); + g_cia_cycles = make_number_entry(BRect(180, 50, 390, 66), "Cycles per line (CIA)", top); + g_floppy_cycles = make_number_entry(BRect(180, 70, 390, 86), "Cycles per line (1541)", top); + g_skip_frames = make_number_entry(BRect(180, 90, 390, 106), "Draw every n-th frame", top); + + // Popup fields + g_display_type = make_disptype_popup(BRect(180, 110, 390, 126), "Display type", MSG_DISPLAY_TYPE, top); + g_sid_type = make_sidtype_popup(BRect(180, 130, 390, 146), "SID emulation type", MSG_SID_TYPE, top); + g_reu_size = make_reusize_popup(BRect(180, 150, 390, 166), "REU size", MSG_REU_SIZE, top); + + // Prepare on/off pictures for file panel buttons + BView *view = new BView(BRect(0, 0, 19, 15), "", B_FOLLOW_NONE, 0); + AddChild(view); + view->SetViewColor(fill_color); + + view->BeginPicture(new BPicture); + view->SetHighColor(fill_color); + view->FillRect(BRect(0, 0, 19, 15)); + view->SetHighColor(light_color); + view->StrokeRect(BRect(0, 0, 18, 0)); + view->StrokeRect(BRect(0, 0, 0, 14)); + view->SetHighColor(dark_color); + view->StrokeRect(BRect(0, 15, 19, 15)); + view->StrokeRect(BRect(19, 0, 19, 15)); + view->SetFont(be_plain_font); + view->SetHighColor(0, 0, 0); + view->SetLowColor(fill_color); + view->MovePenTo(7, 11); + view->DrawString("B"); + BPicture *on = view->EndPicture(); + + view->BeginPicture(new BPicture); + view->SetHighColor(dark_color); + view->FillRect(BRect(0, 0, 19, 15)); + view->SetHighColor(128, 128, 128); + view->StrokeRect(BRect(0, 0, 18, 0)); + view->StrokeRect(BRect(0, 0, 0, 14)); + view->SetHighColor(light_color); + view->StrokeRect(BRect(0, 15, 19, 15)); + view->StrokeRect(BRect(19, 0, 19, 15)); + view->SetFont(be_plain_font); + view->SetHighColor(0, 0, 0); + view->SetLowColor(dark_color); + view->MovePenTo(7, 11); + view->DrawString("B"); + BPicture *off = view->EndPicture(); + + RemoveChild(view); + delete view; + + // Drive settings + BBox *drvbox = new BBox(BRect(10, 173, 390, 304)); + top->AddChild(drvbox); + drvbox->SetViewColor(fill_color); + drvbox->SetLowColor(fill_color); + drvbox->SetLabel("Drives"); + + for (i=0; i<4; i++) { + char str[4]; + sprintf(str, "%d", i+8); + g_drive_path[i] = make_path_entry(BRect(10, 14+i*20, 299, 30+i*20), str, drvbox); + drvbox->AddChild(new BPictureButton(BRect(304, 16+i*20, 323, 31+i*20), "", new BPicture(*on), new BPicture(*off), new BMessage(MSG_GETDRIVE_8 + i))); + } + + g_map_slash = make_checkbox(BRect(10, 94, 300, 110), "Map '/'<->'\\' in filenames", MSG_MAP_SLASH, drvbox); + g_emul_1541_proc = make_checkbox(BRect(10, 109, 300, 125), "Enable 1541 processor emulation", MSG_EMUL_1541_PROC, drvbox); + + // "OK" button + top->AddChild(g_ok = new BButton(BRect(20, 315, 90, 340), "", startup ? "Start" : "OK", new BMessage(MSG_OK))); + SetDefaultButton(g_ok); + + // "Cancel" button + top->AddChild(g_cancel = new BButton(BRect(b.right-90, 315, b.right-20, 340), "", startup ? "Quit" : "Cancel", new BMessage(MSG_CANCEL))); + + // Set the values of all controls to reflect the preferences + set_values(); + g_normal_cycles->MakeFocus(); + + // Create file panels + open_panel = new BFilePanel(B_OPEN_PANEL, &this_messenger, NULL, 0, false, new BMessage(MSG_OPEN_PANEL_RETURNED)); + open_panel->Window()->SetTitle("Frodo: Open preferences"); + save_panel = new BFilePanel(B_SAVE_PANEL, &this_messenger, NULL, 0, false, new BMessage(MSG_SAVE_PANEL_RETURNED)); + save_panel->Window()->SetTitle("Frodo: Save preferences"); + file_panel = new BFilePanel(B_OPEN_PANEL, &this_messenger, NULL, 0, false, new BMessage(MSG_DRIVE_PANEL_RETURNED)); + file_panel->SetPanelDirectory(&AppDirectory); + file_panel->Window()->SetTitle("Frodo: Select disk image or archive file"); + dir_panel = new BFilePanel(B_OPEN_PANEL, &this_messenger, NULL, B_DIRECTORY_NODE, false, new BMessage(MSG_DRIVE_PANEL_RETURNED)); + dir_panel->SetPanelDirectory(&AppDirectory); + dir_panel->Window()->SetTitle("Frodo: Select directory"); + dir_panel->SetButtonLabel(B_DEFAULT_BUTTON, "Select"); + + Unlock(); +} + + +/* + * Create checkbox + */ + +BCheckBox *PrefsWindow::make_checkbox(BRect frame, char *label, uint32 what, BView *parent) +{ + BCheckBox *checkbox = new BCheckBox(frame, "", label, new BMessage(what)); + parent->AddChild(checkbox); + return checkbox; +} + + +/* + * Create number entry field + */ + +NumberControl *PrefsWindow::make_number_entry(BRect frame, char *label_text, BView *parent) +{ + NumberControl *num = new NumberControl(frame, frame.right-frame.left-55, "", label_text, NULL, NULL); + parent->AddChild(num); + + num->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_RIGHT); + num->SetFont(be_plain_font); + num->ChildAt(0)->SetFont(be_plain_font); + + return num; +} + + +/* + * Create path entry field + */ + +PathControl *PrefsWindow::make_path_entry(BRect frame, char *label, BView *parent) +{ + PathControl *path = new PathControl(frame, 16, "", label, NULL, NULL); + parent->AddChild(path); + + path->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_LEFT); + path->SetFont(be_plain_font); + path->ChildAt(0)->SetFont(be_plain_font); + ((BTextView *)(path->ChildAt(0)))->SetMaxBytes(255); + + return path; +} + + +/* + * Create display type popup + */ + +BPopUpMenu *PrefsWindow::make_disptype_popup(BRect frame, char *label_text, uint32 what, BView *parent) +{ + BPopUpMenu *popup = new BPopUpMenu("display_type popup", true, true); + popup->AddItem(new BMenuItem("Window", new BMessage(what))); + popup->AddItem(new BMenuItem("Screen", new BMessage(what))); + popup->SetTargetForItems(this); + BMenuField *menu_field = new BMenuField(frame, "display_type", label_text, popup); + menu_field->SetDivider(frame.Width()-75); + menu_field->SetAlignment(B_ALIGN_RIGHT); + parent->AddChild(menu_field); + return popup; +} + + +/* + * Create SID type popup + */ + +BPopUpMenu *PrefsWindow::make_sidtype_popup(BRect frame, char *label_text, uint32 what, BView *parent) +{ + BPopUpMenu *popup = new BPopUpMenu("sid_type popup", true, true); + popup->AddItem(new BMenuItem("None", new BMessage(what))); + popup->AddItem(new BMenuItem("Digital", new BMessage(what))); + popup->SetTargetForItems(this); + BMenuField *menu_field = new BMenuField(frame, "sid_type", label_text, popup); + menu_field->SetDivider(frame.Width()-75); + menu_field->SetAlignment(B_ALIGN_RIGHT); + parent->AddChild(menu_field); + return popup; +} + + +/* + * Create REU size popup + */ + +BPopUpMenu *PrefsWindow::make_reusize_popup(BRect frame, char *label_text, uint32 what, BView *parent) +{ + BPopUpMenu *popup = new BPopUpMenu("reu_size popup", true, true); + popup->AddItem(new BMenuItem("None", new BMessage(what))); + popup->AddItem(new BMenuItem("128K", new BMessage(what))); + popup->AddItem(new BMenuItem("256K", new BMessage(what))); + popup->AddItem(new BMenuItem("512K", new BMessage(what))); + popup->SetTargetForItems(this); + BMenuField *menu_field = new BMenuField(frame, "reu_size", label_text, popup); + menu_field->SetDivider(frame.Width()-75); + menu_field->SetAlignment(B_ALIGN_RIGHT); + parent->AddChild(menu_field); + return popup; +} + + +/* + * Create joystick port popup + */ + +BPopUpMenu *PrefsWindow::make_joystick_popup(BRect frame, char *label_text, uint32 what, BView *parent) +{ + BPopUpMenu *popup = new BPopUpMenu("joystick popup", true, true); + popup->AddItem(new BMenuItem("None", new BMessage(what))); + popup->AddItem(new BMenuItem("Joystick Port 1", new BMessage(what))); + popup->AddItem(new BMenuItem("Joystick Port 2", new BMessage(what))); + popup->AddItem(new BMenuItem("GeekPort A", new BMessage(what))); + popup->AddItem(new BMenuItem("GeekPort B", new BMessage(what))); + popup->SetTargetForItems(this); + BMenuField *menu_field = new BMenuField(frame, "joystick", label_text, popup); + menu_field->SetDivider(60); + menu_field->SetAlignment(B_ALIGN_RIGHT); + parent->AddChild(menu_field); + return popup; +} + + +/* + * Set the values of the controls + */ + +void PrefsWindow::set_values(void) +{ + prefs->Check(); + + g_normal_cycles->SetValue(prefs->NormalCycles); + g_bad_line_cycles->SetValue(prefs->BadLineCycles); + g_cia_cycles->SetValue(prefs->CIACycles); + g_floppy_cycles->SetValue(prefs->FloppyCycles); + g_skip_frames->SetValue(prefs->SkipFrames); + + for (int i=0; i<4; i++) + g_drive_path[i]->SetText(prefs->DrivePath[i]); + + g_sid_type->ItemAt(prefs->SIDType)->SetMarked(true); + g_reu_size->ItemAt(prefs->REUSize)->SetMarked(true); + g_display_type->ItemAt(prefs->DisplayType)->SetMarked(true); + + g_sprites_on->SetValue(prefs->SpritesOn ? B_CONTROL_ON : B_CONTROL_OFF); + g_sprite_collisions->SetValue(prefs->SpriteCollisions ? B_CONTROL_ON : B_CONTROL_OFF); + g_joystick_1_port->ItemAt(prefs->Joystick1Port)->SetMarked(true); + g_joystick_2_port->ItemAt(prefs->Joystick2Port)->SetMarked(true); + g_joystick_swap->SetValue(prefs->JoystickSwap ? B_CONTROL_ON : B_CONTROL_OFF); + g_limit_speed->SetValue(prefs->LimitSpeed ? B_CONTROL_ON : B_CONTROL_OFF); + g_fast_reset->SetValue(prefs->FastReset ? B_CONTROL_ON : B_CONTROL_OFF); + g_cia_irq_hack->SetValue(prefs->CIAIRQHack ? B_CONTROL_ON : B_CONTROL_OFF); + g_sid_filters->SetValue(prefs->SIDFilters ? B_CONTROL_ON : B_CONTROL_OFF); + g_double_scan->SetValue(prefs->DoubleScan ? B_CONTROL_ON : B_CONTROL_OFF); + + g_map_slash->SetValue(prefs->MapSlash ? B_CONTROL_ON : B_CONTROL_OFF); + g_emul_1541_proc->SetValue(prefs->Emul1541Proc ? B_CONTROL_ON : B_CONTROL_OFF); + + ghost_controls(); +} + + +/* + * Get the values of the controls + */ + +void PrefsWindow::get_values(void) +{ + prefs->NormalCycles = g_normal_cycles->Value(); + prefs->BadLineCycles = g_bad_line_cycles->Value(); + prefs->CIACycles = g_cia_cycles->Value(); + prefs->FloppyCycles = g_floppy_cycles->Value(); + prefs->SkipFrames = g_skip_frames->Value(); + + for (int i=0; i<4; i++) + strcpy(prefs->DrivePath[i], g_drive_path[i]->Text()); + + prefs->Check(); +} + + +/* + * Enable/disable certain controls + */ + +void PrefsWindow::ghost_controls(void) +{ + g_normal_cycles->SetEnabled(!IsFrodoSC); + g_bad_line_cycles->SetEnabled(!IsFrodoSC); + g_cia_cycles->SetEnabled(!IsFrodoSC); + g_floppy_cycles->SetEnabled(!IsFrodoSC); + g_cia_irq_hack->SetEnabled(!IsFrodoSC); + g_double_scan->SetEnabled(prefs->DisplayType == DISPTYPE_SCREEN); +} + + +/* + * Message from controls/menus received + */ + +void PrefsWindow::MessageReceived(BMessage *msg) +{ + switch (msg->what) { + case MSG_OK: + get_values(); + if (startup) + be_app->PostMessage(MSG_STARTUP); + else { + BMessage msg(MSG_PREFS_DONE); + msg.AddBool("canceled", false); + msg.AddPointer("prefs", prefs); + be_app->PostMessage(&msg); + } + PostMessage(B_QUIT_REQUESTED); + break; + + case MSG_CANCEL: + if (startup) + be_app->PostMessage(B_QUIT_REQUESTED); + else { + BMessage msg(MSG_PREFS_DONE); + msg.AddBool("canceled", true); + msg.AddPointer("prefs", prefs); + be_app->PostMessage(&msg); + } + PostMessage(B_QUIT_REQUESTED); + break; + + case MSG_SPRITES_ON: + prefs->SpritesOn = !prefs->SpritesOn; + break; + + case MSG_SPRITE_COLLISIONS: + prefs->SpriteCollisions = !prefs->SpriteCollisions; + break; + + case MSG_JOYSTICK_1_PORT: + prefs->Joystick1Port = msg->FindInt32("index"); + break; + + case MSG_JOYSTICK_2_PORT: + prefs->Joystick2Port = msg->FindInt32("index"); + break; + + case MSG_JOYSTICK_SWAP: + prefs->JoystickSwap = !prefs->JoystickSwap; + break; + + case MSG_LIMIT_SPEED: + prefs->LimitSpeed = !prefs->LimitSpeed; + break; + + case MSG_FAST_RESET: + prefs->FastReset = !prefs->FastReset; + break; + + case MSG_CIA_IRQ_HACK: + prefs->CIAIRQHack = !prefs->CIAIRQHack; + break; + + case MSG_SID_FILTERS: + prefs->SIDFilters = !prefs->SIDFilters; + break; + + case MSG_DOUBLE_SCAN: + prefs->DoubleScan = !prefs->DoubleScan; + break; + + case MSG_SID_TYPE: + prefs->SIDType = msg->FindInt32("index"); + break; + + case MSG_REU_SIZE: + prefs->REUSize = msg->FindInt32("index"); + break; + + case MSG_DISPLAY_TYPE: + prefs->DisplayType = msg->FindInt32("index"); + g_double_scan->SetEnabled(prefs->DisplayType == DISPTYPE_SCREEN); + break; + + case MSG_MAP_SLASH: + prefs->MapSlash = !prefs->MapSlash; + break; + + case MSG_EMUL_1541_PROC: + prefs->Emul1541Proc = !prefs->Emul1541Proc; + break; + + case MSG_GETDRIVE_8: + case MSG_GETDRIVE_9: + case MSG_GETDRIVE_10: + case MSG_GETDRIVE_11: + panel_drive_num = msg->what & 3; +// file_panel->Hide(); +// dir_panel->Hide(); + file_panel->Show(); +// dir_panel->Show(); + break; + + case MSG_DRIVE_PANEL_RETURNED: { // Drive path file panel returned + entry_ref the_ref; + BEntry the_entry; + if (msg->FindRef("refs", &the_ref) == B_NO_ERROR) + if (the_entry.SetTo(&the_ref) == B_NO_ERROR) { + BPath the_path; + the_entry.GetPath(&the_path); + strncpy(prefs->DrivePath[panel_drive_num], the_path.Path(), 255); + prefs->DrivePath[panel_drive_num][255] = 0; + set_values(); + } + break; + } + + case MSG_OPEN: + open_panel->Show(); + break; + + case MSG_OPEN_PANEL_RETURNED: { // Open file panel returned + get_values(); // Useful if Load() is unsuccessful + + entry_ref the_ref; + BEntry the_entry; + if (msg->FindRef("refs", &the_ref) == B_NO_ERROR) + if (the_entry.SetTo(&the_ref) == B_NO_ERROR) + if (the_entry.IsFile()) { + BPath the_path; + the_entry.GetPath(&the_path); + strncpy(prefs_path, the_path.Path(), 1023); + prefs_path[1023] = 0; + prefs->Load(prefs_path); + set_values(); + } + } + + case MSG_SAVE: + get_values(); + prefs->Save(prefs_path); + break; + + case MSG_SAVE_AS: + save_panel->Show(); + break; + + case MSG_SAVE_PANEL_RETURNED: { // Save file panel returned + entry_ref the_ref; + BEntry the_entry; + if (msg->FindRef("directory", &the_ref) == B_NO_ERROR) + if (the_entry.SetTo(&the_ref) == B_NO_ERROR) { + BPath the_path; + the_entry.GetPath(&the_path); + strncpy(prefs_path, the_path.Path(), 1023); + strncat(prefs_path, "/", 1023); + strncat(prefs_path, msg->FindString("name"), 1023); + prefs_path[1023] = 0; + get_values(); + if (!prefs->Save(prefs_path)) + ShowRequester("Couldn't save preferences.", "Too bad"); + } + break; + } + + case MSG_REVERT: + get_values(); // Useful if Load() is unsuccessful + prefs->Load(prefs_path); + set_values(); + break; + + default: + BWindow::MessageReceived(msg); + } +} + + +/* + * Intercept ESC key (works as clicking the Cancel button) + */ + +bool PrefsWindow::FilterKeyDown(uint32 *aChar, BView **target) +{ + if (*aChar == B_ESCAPE) { + // Flash Cancel button + g_cancel->SetValue(B_CONTROL_ON); + snooze(100000); + PostMessage(MSG_CANCEL); + } + return true; +} + + +/* + * Quit requested + */ + +bool PrefsWindow::QuitRequested(void) +{ + delete open_panel; + delete save_panel; + delete file_panel; + delete dir_panel; + return true; +} diff --git a/Src/Prefs_WIN32.h b/Src/Prefs_WIN32.h new file mode 100644 index 0000000..91671b7 --- /dev/null +++ b/Src/Prefs_WIN32.h @@ -0,0 +1,403 @@ +/* + * Prefs_WIN32.h - Global preferences, WIN32 specific stuff + * + * Frodo (C) 1994-1997,2002-2005 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include "resource.h" + +Prefs *Prefs::edit_prefs; +char *Prefs::edit_prefs_name; +HWND Prefs::hDlg; + +#define STANDARD_PAGE 0 +#define WIN32_PAGE 1 + +bool Prefs::ShowEditor(bool /* startup */, char *prefs_name) +{ + edit_prefs = this; + edit_prefs_name = prefs_name; + + PROPSHEETPAGE psp[2]; + + // Set up standard preferences property page. + psp[0].dwSize = sizeof(PROPSHEETPAGE); + psp[0].dwFlags = PSP_HASHELP; + psp[0].hInstance = hInstance; + psp[0].pszTemplate = MAKEINTRESOURCE(IDD_PREFERENCES_STANDARD); + psp[0].pszIcon = NULL; + psp[0].pfnDlgProc = StandardDialogProc; + psp[0].pszTitle = NULL; + psp[0].lParam = 0; + psp[0].pfnCallback = NULL; + + // Set up WIN32 preferences property page. + psp[1].dwSize = sizeof(PROPSHEETPAGE); + psp[1].dwFlags = PSP_HASHELP; + psp[1].hInstance = hInstance; + psp[1].pszTemplate = MAKEINTRESOURCE(IDD_PREFERENCES_WIN32); + psp[1].pszIcon = NULL; + psp[1].pfnDlgProc = WIN32DialogProc; + psp[1].pszTitle = NULL; + psp[1].lParam = 0; + psp[1].pfnCallback = NULL; + + // Setup property sheet. + PROPSHEETHEADER psh; + psh.dwSize = sizeof(PROPSHEETHEADER); + psh.dwFlags = PSH_PROPSHEETPAGE | PSH_NOAPPLYNOW; + psh.hwndParent = hwnd; + psh.hInstance = hInstance; + psh.pszIcon = NULL; + psh.pszCaption = "Preferences"; + psh.nPages = sizeof(psp) / sizeof(PROPSHEETPAGE); + psh.nStartPage = 0; + psh.ppsp = psp; + psh.pfnCallback = NULL; + + int result = PropertySheet(&psh); + + if (result == -1) + return FALSE; + return result; +} + +BOOL CALLBACK Prefs::StandardDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + return edit_prefs->DialogProc(STANDARD_PAGE, hDlg, message, wParam, lParam); +} + +BOOL CALLBACK Prefs::WIN32DialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + return edit_prefs->DialogProc(WIN32_PAGE, hDlg, message, wParam, lParam); +} + +BOOL Prefs::DialogProc(int page, HWND hDlg_arg, UINT message, WPARAM wParam, LPARAM lParam) +{ + hDlg = hDlg_arg; + + switch (message) { + + case WM_INITDIALOG: + SetupControls(page); + SetValues(page); + return TRUE; + + case WM_COMMAND: + switch (LOWORD(wParam)) { + + case IDC_BROWSE8: + BrowseForDevice(IDC_DEVICE8); + break; + + case IDC_BROWSE9: + BrowseForDevice(IDC_DEVICE9); + break; + + case IDC_BROWSE10: + BrowseForDevice(IDC_DEVICE10); + break; + + case IDC_BROWSE11: + BrowseForDevice(IDC_DEVICE11); + break; + + } + return TRUE; + + case WM_NOTIFY: + { + NMHDR *pnmhdr = (NMHDR *) lParam; + switch (pnmhdr->code) { + + case PSN_KILLACTIVE: + SetWindowLong(hDlg, DWL_MSGRESULT, FALSE); + break; + + case PSN_APPLY: + GetValues(page); + break; + + case PSN_HELP: + PostMessage(hwnd, WM_COMMAND, ID_HELP_SETTINGS, 0); + break; + } + } + return TRUE; + } + + return FALSE; +} + +#define SetupSpin(id, upper, lower) SendMessage(GetDlgItem(hDlg, id), UDM_SETRANGE, 0, MAKELONG(upper, lower)) +#define SetupSpinIncrement(id, increment) (udaccel.nSec = 0, udaccel.nInc = increment, SendMessage(GetDlgItem(hDlg, id), UDM_SETACCEL, 1, (LPARAM) &udaccel)) +#define SetupComboClear(id) SendMessage(GetDlgItem(hDlg, id), CB_RESETCONTENT, 0, 0) +#define SetupComboAdd(id, string) SendMessage(GetDlgItem(hDlg, id), CB_ADDSTRING, 0, (LPARAM) string) + +void Prefs::SetupControls(int page) +{ + UDACCEL udaccel; + switch (page) { + + case STANDARD_PAGE: + SetupSpin(IDC_NORMAL_SPIN, 200, 1); + SetupSpin(IDC_BADLINES_SPIN, 200, 1); + SetupSpin(IDC_CIA_SPIN, 200, 1); + SetupSpin(IDC_FLOPPY_SPIN, 200, 1); + SetupSpin(IDC_DRAWEVERY_SPIN, 10, 1); + SetupComboClear(IDC_REUSIZE); + SetupComboAdd(IDC_REUSIZE, "None"); + SetupComboAdd(IDC_REUSIZE, "128k"); + SetupComboAdd(IDC_REUSIZE, "256k"); + SetupComboAdd(IDC_REUSIZE, "512k"); + break; + + case WIN32_PAGE: + SetupComboClear(IDC_VIEWPORT); + SetupComboAdd(IDC_VIEWPORT, "Default"); + SetupComboAdd(IDC_VIEWPORT, "320x200"); + SetupComboAdd(IDC_VIEWPORT, "336x216"); + SetupComboAdd(IDC_VIEWPORT, "384x272"); + SetupComboClear(IDC_DISPLAYMODE); + SetupComboAdd(IDC_DISPLAYMODE, "Default"); + { + C64Display *TheDisplay = TheApp->TheC64->TheDisplay; + int n = TheDisplay->GetNumDisplayModes(); + const C64Display::DisplayMode *modes = + TheDisplay->GetDisplayModes(); + for (int i = 0; i < n; i++) { + char mode[64]; + sprintf(mode, "%dx%dx%d%s", + modes[i].x, modes[i].y, modes[i].depth, + modes[i].modex ? " (ModeX)" : ""); + SetupComboAdd(IDC_DISPLAYMODE, mode); + } + } + SetupSpin(IDC_SCALINGNUMERATOR_SPIN, 16, 1); + SetupSpin(IDC_SCALINGDENOMINATOR_SPIN, 4, 1); + SetupSpin(IDC_LATENCYMIN_SPIN, 1000, 20); + SetupSpinIncrement(IDC_LATENCYMIN_SPIN, 20); + SetupSpin(IDC_LATENCYMAX_SPIN, 1000, 20); + SetupSpinIncrement(IDC_LATENCYMAX_SPIN, 20); + SetupSpin(IDC_LATENCYAVG_SPIN, 1000, 20); + SetupSpinIncrement(IDC_LATENCYAVG_SPIN, 20); + break; + } +} + +#define SetText(id, val) SetDlgItemText(hDlg, id, val) +#define SetInteger(id, val) SetDlgItemInt(hDlg, id, val, FALSE) +#define SetCheckBox(id, val) CheckDlgButton(hDlg, id, (val) ? BST_CHECKED : BST_UNCHECKED) +#define SetCombo(id, val) SendMessage(GetDlgItem(hDlg, id), CB_SELECTSTRING, 0, (LPARAM) val) + +void Prefs::SetValues(int page) +{ + const char *str; + switch (page) { + + case STANDARD_PAGE: + SetText(IDC_DEVICE8, DrivePath[0]); + SetText(IDC_DEVICE9, DrivePath[1]); + SetText(IDC_DEVICE10, DrivePath[2]); + SetText(IDC_DEVICE11, DrivePath[3]); + + SetInteger(IDC_NORMAL, NormalCycles); + SetInteger(IDC_BADLINES, BadLineCycles); + SetInteger(IDC_CIA, CIACycles); + SetInteger(IDC_FLOPPY, FloppyCycles); + SetInteger(IDC_DRAWEVERY, SkipFrames); + switch (REUSize) { + case REU_NONE: str = "None"; break; + case REU_128K: str = "128k"; break; + case REU_256K: str = "256k"; break; + case REU_512K: str = "512k"; break; + } + SetCombo(IDC_REUSIZE, str); + + SetCheckBox(IDC_LIMITSPEED, LimitSpeed); + SetCheckBox(IDC_SPRITES, SpritesOn); + SetCheckBox(IDC_SPRITECOLLISIONS, SpriteCollisions); + SetCheckBox(IDC_JOYSTICK1, Joystick1On); + SetCheckBox(IDC_JOYSTICK2, Joystick2On); + SetCheckBox(IDC_SWAPJOYSTICKS, JoystickSwap); + SetCheckBox(IDC_FASTRESET, FastReset); + SetCheckBox(IDC_CIAIRQHACK, CIAIRQHack); + SetCheckBox(IDC_MAPSLASH, MapSlash); + SetCheckBox(IDC_SIDEMULATION, SIDType == SIDTYPE_DIGITAL); + SetCheckBox(IDC_SIDFILTERS, SIDFilters); + SetCheckBox(IDC_1541EMULATION, Emul1541Proc); + break; + + case WIN32_PAGE: + SetCheckBox(IDC_FULLSCREEN, DisplayType == DISPTYPE_SCREEN); + SetCheckBox(IDC_SYSTEMMEMORY, SystemMemory); + SetCheckBox(IDC_ALWAYSCOPY, AlwaysCopy); + SetText(IDC_VIEWPORT, ViewPort); + SetText(IDC_DISPLAYMODE, DisplayMode); + + SetCheckBox(IDC_HIDECURSOR, HideCursor); + SetCheckBox(IDC_SYSTEMKEYS, SystemKeys); + SetInteger(IDC_SCALINGNUMERATOR, ScalingNumerator); + SetInteger(IDC_SCALINGDENOMINATOR, ScalingDenominator); + + SetCheckBox(IDC_DIRECTSOUND, DirectSound); + SetCheckBox(IDC_EXCLUSIVESOUND, ExclusiveSound); + SetInteger(IDC_LATENCYMIN, LatencyMin); + SetInteger(IDC_LATENCYMAX, LatencyMax); + SetInteger(IDC_LATENCYAVG, LatencyAvg); + + SetCheckBox(IDC_AUTOPAUSE, AutoPause); + SetCheckBox(IDC_PREFSATSTARTUP, PrefsAtStartup); + SetCheckBox(IDC_SHOWLEDS, ShowLEDs); + break; + } +} + +#define GetCheckBox(id, val) (val = IsDlgButtonChecked(hDlg, id) == BST_CHECKED) +#define GetInteger(id, val) (val = GetDlgItemInt(hDlg, id, NULL, FALSE)) +#define GetText(id, val) GetDlgItemText(hDlg, id, val, sizeof(val)) + +void Prefs::GetValues(int page) +{ + BOOL temp; + char str[256]; + switch (page) { + + case STANDARD_PAGE: + GetText(IDC_DEVICE8, DrivePath[0]); + GetText(IDC_DEVICE9, DrivePath[1]); + GetText(IDC_DEVICE10, DrivePath[2]); + GetText(IDC_DEVICE11, DrivePath[3]); + + GetInteger(IDC_NORMAL, NormalCycles); + GetInteger(IDC_BADLINES, BadLineCycles); + GetInteger(IDC_CIA, CIACycles); + GetInteger(IDC_FLOPPY, FloppyCycles); + GetInteger(IDC_DRAWEVERY, SkipFrames); + GetText(IDC_REUSIZE, str); + if (strcmp(str, "None") == 0) + REUSize = REU_NONE; + else if (strcmp(str, "128k") == 0) + REUSize = REU_128K; + else if (strcmp(str, "256k") == 0) + REUSize = REU_256K; + else if (strcmp(str, "512k") == 0) + REUSize = REU_512K; + + GetCheckBox(IDC_LIMITSPEED, LimitSpeed); + GetCheckBox(IDC_SPRITES, SpritesOn); + GetCheckBox(IDC_SPRITECOLLISIONS, SpriteCollisions); + GetCheckBox(IDC_JOYSTICK1, Joystick1On); + GetCheckBox(IDC_JOYSTICK2, Joystick2On); + GetCheckBox(IDC_SWAPJOYSTICKS, JoystickSwap); + GetCheckBox(IDC_FASTRESET, FastReset); + GetCheckBox(IDC_CIAIRQHACK, CIAIRQHack); + GetCheckBox(IDC_MAPSLASH, MapSlash); + GetCheckBox(IDC_SIDEMULATION, temp); + SIDType = temp ? SIDTYPE_DIGITAL : SIDTYPE_NONE; + GetCheckBox(IDC_SIDFILTERS, SIDFilters); + GetCheckBox(IDC_1541EMULATION, Emul1541Proc); + break; + + case WIN32_PAGE: + GetCheckBox(IDC_FULLSCREEN, temp); + DisplayType = temp ? DISPTYPE_SCREEN : DISPTYPE_WINDOW; + GetCheckBox(IDC_SYSTEMMEMORY, SystemMemory); + GetCheckBox(IDC_ALWAYSCOPY, AlwaysCopy); + GetText(IDC_VIEWPORT, ViewPort); + GetText(IDC_DISPLAYMODE, DisplayMode); + + GetCheckBox(IDC_HIDECURSOR, HideCursor); + GetCheckBox(IDC_SYSTEMKEYS, SystemKeys); + GetInteger(IDC_SCALINGNUMERATOR, ScalingNumerator); + GetInteger(IDC_SCALINGDENOMINATOR, ScalingDenominator); + + GetCheckBox(IDC_DIRECTSOUND, DirectSound); + GetCheckBox(IDC_EXCLUSIVESOUND, ExclusiveSound); + GetInteger(IDC_LATENCYMIN, LatencyMin); + GetInteger(IDC_LATENCYMAX, LatencyMax); + GetInteger(IDC_LATENCYAVG, LatencyAvg); + + GetCheckBox(IDC_AUTOPAUSE, AutoPause); + GetCheckBox(IDC_PREFSATSTARTUP, PrefsAtStartup); + GetCheckBox(IDC_SHOWLEDS, ShowLEDs); + break; + } + + for (int i = 0; i < 4; i++) { + DriveType[i] = DRVTYPE_DIR; + int length = strlen(DrivePath[i]); + if (length >= 4) { + char *suffix = DrivePath[i] + length - 4; + if (stricmp(suffix, ".t64") == 0) + DriveType[i] = DRVTYPE_T64; + else if (stricmp(suffix, ".d64") == 0) + DriveType[i] = DRVTYPE_D64; + } + } +} + +void Prefs::BrowseForDevice(int id) +{ + char filename[256]; + GetDlgItemText(hDlg, id, filename, sizeof(filename)); + OPENFILENAME ofn; + memset(&ofn, 0, sizeof(ofn)); + ofn.lStructSize = sizeof(ofn); + ofn.hwndOwner = hDlg; + ofn.hInstance = hInstance; + ofn.lpstrFilter = + "All Files (*.*)\0*.*\0" + "All C64 Files (*.d64;*.t64)\0*.d64;*.t64\0" + "D64 Files (*.d64)\0*.d64\0" + "T64 Files (*.t64)\0*.t64\0" + ; + ofn.lpstrCustomFilter = NULL; + ofn.nMaxCustFilter = 0; + ofn.nFilterIndex = 1; + ofn.lpstrFile = filename; + ofn.nMaxFile = sizeof(filename); + ofn.lpstrFileTitle = NULL; + ofn.nMaxFileTitle = 0; + ofn.lpstrInitialDir = NULL; + ofn.lpstrTitle = "Set Device"; + ofn.Flags = OFN_EXPLORER | OFN_HIDEREADONLY | OFN_NOTESTFILECREATE | + OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR | OFN_SHAREAWARE; + ofn.nFileOffset = 0; + ofn.nFileExtension = 0; + ofn.lpstrDefExt = NULL; + ofn.lpfnHook = NULL; + ofn.lpTemplateName = NULL; + BOOL result = GetOpenFileName(&ofn); + if (result) { + const char *ext = filename + ofn.nFileExtension; + if (stricmp(ext, "d64") != 0 && stricmp(ext, "t64") != 0) + filename[ofn.nFileOffset - 1] = '\0'; + char cwd[256]; + GetCurrentDirectory(sizeof(cwd), cwd); + int cwd_len = strlen(cwd); + if (cwd_len > 0 && cwd[cwd_len - 1] != '\\') { + strcat(cwd, "\\"); + cwd_len++; + } + if (strnicmp(filename, cwd, cwd_len) == 0) + SetDlgItemText(hDlg, id, filename + cwd_len); + else + SetDlgItemText(hDlg, id, filename); + } +} diff --git a/Src/Prefs_glade.h b/Src/Prefs_glade.h new file mode 100644 index 0000000..13fabdb --- /dev/null +++ b/Src/Prefs_glade.h @@ -0,0 +1,250 @@ +/* + * Prefs_glade.h - Global preferences, Glade/Gnome/Gtk+ specific stuff + * + * Frodo (C) 1994-1997,2002-2005 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include + +#include + + +// Glade XML tree +static GladeXML *xml = NULL; + +// Result of ShowEditor() +static bool result = false; + +// Pointer to preferences being edited +static Prefs *prefs = NULL; + +// Prefs file name +static char *prefs_path = NULL; + +// Prototypes +static void set_values(); +static void get_values(); +static void ghost_widgets(); + + +/* + * Show preferences editor (synchronously) + * prefs_name points to the file name of the preferences (which may be changed) + */ + +bool Prefs::ShowEditor(bool startup, char *path) +{ + prefs = this; + prefs_path = path; + + // Load XML user interface file on startup + if (startup) { + xml = glade_xml_new(DATADIR "Frodo.glade", NULL, NULL); + if (xml) { + glade_xml_signal_autoconnect(xml); + set_values(); + } + } + + // No XML means no prefs editor + if (!xml) + return startup; + + // Run editor + result = false; + gtk_main(); + return result; +} + + +/* + * Set the values of the widgets + */ + +static void create_joystick_menu(const char *widget_name) +{ + GtkWidget *w = glade_xml_get_widget(xml, widget_name); + gtk_option_menu_remove_menu(GTK_OPTION_MENU(w)); + + GtkWidget *menu = gtk_menu_new(); + + for (int i = -1; i < SDL_NumJoysticks(); ++i) { + GtkWidget *item = gtk_menu_item_new_with_label(i < 0 ? "None" : SDL_JoystickName(i)); + gtk_widget_show(item); + gtk_menu_append(GTK_MENU(menu), item); + } + + gtk_option_menu_set_menu(GTK_OPTION_MENU(w), menu); +} + +static void set_values() +{ + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(glade_xml_get_widget(xml, "emul1541_proc")), prefs->Emul1541Proc); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(glade_xml_get_widget(xml, "map_slash")), prefs->MapSlash); + + gtk_entry_set_text(GTK_ENTRY(gnome_file_entry_gtk_entry(GNOME_FILE_ENTRY(glade_xml_get_widget(xml, "drive8_path")))), prefs->DrivePath[0]); + gtk_entry_set_text(GTK_ENTRY(gnome_file_entry_gtk_entry(GNOME_FILE_ENTRY(glade_xml_get_widget(xml, "drive9_path")))), prefs->DrivePath[1]); + gtk_entry_set_text(GTK_ENTRY(gnome_file_entry_gtk_entry(GNOME_FILE_ENTRY(glade_xml_get_widget(xml, "drive10_path")))), prefs->DrivePath[2]); + gtk_entry_set_text(GTK_ENTRY(gnome_file_entry_gtk_entry(GNOME_FILE_ENTRY(glade_xml_get_widget(xml, "drive11_path")))), prefs->DrivePath[3]); + + gtk_option_menu_set_history(GTK_OPTION_MENU(glade_xml_get_widget(xml, "display_type")), prefs->DisplayType); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(glade_xml_get_widget(xml, "sprites_on")), prefs->SpritesOn); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(glade_xml_get_widget(xml, "sprite_collisions")), prefs->SpriteCollisions); + + gtk_option_menu_set_history(GTK_OPTION_MENU(glade_xml_get_widget(xml, "sid_type")), prefs->SIDType); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(glade_xml_get_widget(xml, "sid_filters")), prefs->SIDFilters); + + create_joystick_menu("joystick1_port"); + create_joystick_menu("joystick2_port"); + + gtk_option_menu_set_history(GTK_OPTION_MENU(glade_xml_get_widget(xml, "joystick1_port")), prefs->Joystick1Port); + gtk_option_menu_set_history(GTK_OPTION_MENU(glade_xml_get_widget(xml, "joystick2_port")), prefs->Joystick2Port); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(glade_xml_get_widget(xml, "joystick_swap")), prefs->JoystickSwap); + + gtk_spin_button_set_value(GTK_SPIN_BUTTON(glade_xml_get_widget(xml, "skip_frames")), prefs->SkipFrames); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(glade_xml_get_widget(xml, "limit_speed")), prefs->LimitSpeed); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(glade_xml_get_widget(xml, "fast_reset")), prefs->FastReset); + + gtk_option_menu_set_history(GTK_OPTION_MENU(glade_xml_get_widget(xml, "reu_size")), prefs->REUSize); + + gtk_spin_button_set_value(GTK_SPIN_BUTTON(glade_xml_get_widget(xml, "normal_cycles")), prefs->NormalCycles); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(glade_xml_get_widget(xml, "bad_line_cycles")), prefs->BadLineCycles); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(glade_xml_get_widget(xml, "cia_cycles")), prefs->CIACycles); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(glade_xml_get_widget(xml, "floppy_cycles")), prefs->FloppyCycles); + + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(glade_xml_get_widget(xml, "cia_irq_hack")), prefs->CIAIRQHack); + + ghost_widgets(); +} + + +/* + * Get the values of the widgets + */ + +static void get_drive_path(int num, const char *widget_name) +{ + prefs->DrivePath[num][0] = 0; + const char *path = gnome_file_entry_get_full_path(GNOME_FILE_ENTRY(glade_xml_get_widget(xml, widget_name)), false); + if (path) + strncpy(prefs->DrivePath[num], path, 255); + prefs->DrivePath[num][255] = 0; +} + +static void get_values() +{ + prefs->Emul1541Proc = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(glade_xml_get_widget(xml, "emul1541_proc"))); + prefs->MapSlash = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(glade_xml_get_widget(xml, "map_slash"))); + + get_drive_path(0, "drive8_path"); + get_drive_path(1, "drive9_path"); + get_drive_path(2, "drive10_path"); + get_drive_path(3, "drive11_path"); + + prefs->DisplayType = gtk_option_menu_get_history(GTK_OPTION_MENU(glade_xml_get_widget(xml, "display_type"))); + prefs->SpritesOn = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(glade_xml_get_widget(xml, "sprites_on"))); + prefs->SpriteCollisions = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(glade_xml_get_widget(xml, "sprite_collisions"))); + + prefs->SIDType = gtk_option_menu_get_history(GTK_OPTION_MENU(glade_xml_get_widget(xml, "sid_type"))); + prefs->SIDFilters = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(glade_xml_get_widget(xml, "sid_filters"))); + + prefs->Joystick1Port = gtk_option_menu_get_history(GTK_OPTION_MENU(glade_xml_get_widget(xml, "joystick1_port"))); + prefs->Joystick2Port = gtk_option_menu_get_history(GTK_OPTION_MENU(glade_xml_get_widget(xml, "joystick2_port"))); + prefs->JoystickSwap = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(glade_xml_get_widget(xml, "joystick_swap"))); + + prefs->SkipFrames = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(glade_xml_get_widget(xml, "skip_frames"))); + prefs->LimitSpeed = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(glade_xml_get_widget(xml, "limit_speed"))); + prefs->FastReset = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(glade_xml_get_widget(xml, "fast_reset"))); + + prefs->REUSize = gtk_option_menu_get_history(GTK_OPTION_MENU(glade_xml_get_widget(xml, "reu_size"))); + + prefs->NormalCycles = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(glade_xml_get_widget(xml, "normal_cycles"))); + prefs->BadLineCycles = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(glade_xml_get_widget(xml, "bad_line_cycles"))); + prefs->CIACycles = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(glade_xml_get_widget(xml, "cia_cycles"))); + prefs->FloppyCycles = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(glade_xml_get_widget(xml, "floppy_cycles"))); + + prefs->CIAIRQHack = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(glade_xml_get_widget(xml, "cia_irq_hack"))); +} + + +/* + * Ghost/unghost widgets + */ + +static void ghost_widget(const char *name, bool ghosted) +{ + gtk_widget_set_sensitive(glade_xml_get_widget(xml, name), !ghosted); +} + +static void ghost_widgets() +{ + ghost_widget("drive9_path", prefs->Emul1541Proc); + ghost_widget("drive10_path", prefs->Emul1541Proc); + ghost_widget("drive11_path", prefs->Emul1541Proc); + + ghost_widget("sid_filters", prefs->SIDType != SIDTYPE_DIGITAL); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(glade_xml_get_widget(xml, "sid_filters")), prefs->SIDType == SIDTYPE_DIGITAL ? prefs->SIDFilters : (prefs->SIDType == SIDTYPE_SIDCARD ? true : false)); +} + + +/* + * Signal handlers + */ + +extern "C" gboolean on_prefs_win_delete_event(GtkWidget *widget, GdkEvent *event, gpointer user_data) +{ + return false; +} + +extern "C" void on_ok_clicked(GtkButton *button, gpointer user_data) +{ + result = true; + get_values(); + prefs->Save(prefs_path); + gtk_main_quit(); +} + +extern "C" void on_quit_clicked(GtkButton *button, gpointer user_data) +{ + result = false; + gtk_main_quit(); +} + +extern "C" void on_about_activate(GtkMenuItem *menuitem, gpointer user_data) +{ + GladeXML *about_xml = glade_xml_new(DATADIR "Frodo.glade", "about_win", NULL); + if (about_xml) + gtk_widget_show(glade_xml_get_widget(about_xml, "about_win")); +} + +extern "C" void on_emul1541_proc_toggled(GtkToggleButton *button, gpointer user_data) +{ + prefs->Emul1541Proc = gtk_toggle_button_get_active(button); + ghost_widgets(); +} + +extern "C" void on_sid_type_activate(GtkMenuItem *menuitem, gpointer user_data) +{ + prefs->SIDType = gtk_option_menu_get_history(GTK_OPTION_MENU(glade_xml_get_widget(xml, "sid_type"))); + ghost_widgets(); +} + +extern "C" void on_sid_filters_toggled(GtkToggleButton *button, gpointer user_data) +{ + prefs->SIDFilters = gtk_toggle_button_get_active(button); +} diff --git a/Src/REU.cpp b/Src/REU.cpp new file mode 100644 index 0000000..01ad360 --- /dev/null +++ b/Src/REU.cpp @@ -0,0 +1,276 @@ +/* + * REU.cpp - 17xx REU emulation + * + * Frodo (C) 1994-1997,2002-2005 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * Incompatibilities: + * ------------------ + * + * - REU interrupts are not emulated + * - Transfer time is not accounted for, all transfers + * are done in 0 cycles + */ + +#include "sysdeps.h" + +#include "REU.h" +#include "CPUC64.h" +#include "Prefs.h" + + +/* + * Constructor + */ + +REU::REU(MOS6510 *CPU) : the_cpu(CPU) +{ + int i; + + // Init registers + regs[0] = 0x40; + for (i=1; i<11; i++) + regs[i] = 0; + for (i=11; i<16; i++) + regs[i] = 0xff; + + ex_ram = NULL; + ram_size = ram_mask = 0; + + // Allocate RAM + open_close_reu(REU_NONE, ThePrefs.REUSize); +} + + +/* + * Destructor + */ + +REU::~REU() +{ + // Free RAM + open_close_reu(ThePrefs.REUSize, REU_NONE); +} + + +/* + * Prefs may have changed, reallocate expansion RAM + */ + +void REU::NewPrefs(Prefs *prefs) +{ + open_close_reu(ThePrefs.REUSize, prefs->REUSize); +} + + +/* + * Allocate/free expansion RAM + */ + +void REU::open_close_reu(int old_size, int new_size) +{ + if (old_size == new_size) + return; + + // Free old RAM + if (old_size != REU_NONE) { + delete[] ex_ram; + ex_ram = NULL; + } + + // Allocate new RAM + if (new_size != REU_NONE) { + switch (new_size) { + case REU_128K: + ram_size = 0x20000; + break; + case REU_256K: + ram_size = 0x40000; + break; + case REU_512K: + ram_size = 0x80000; + break; + } + ram_mask = ram_size - 1; + ex_ram = new uint8[ram_size]; + + // Set size bit in status register + if (ram_size > 0x20000) + regs[0] |= 0x10; + else + regs[0] &= 0xef; + } +} + + +/* + * Reset the REU + */ + +void REU::Reset(void) +{ + int i; + + for (i=1; i<11; i++) + regs[i] = 0; + for (i=11; i<16; i++) + regs[i] = 0xff; + + if (ram_size > 0x20000) + regs[0] = 0x50; + else + regs[0] = 0x40; +} + + +/* + * Read from REU register + */ + +uint8 REU::ReadRegister(uint16 adr) +{ + if (ex_ram == NULL) + return rand(); + + switch (adr) { + case 0:{ + uint8 ret = regs[0]; + regs[0] &= 0x1f; + return ret; + } + case 6: + return regs[6] | 0xf8; + case 9: + return regs[9] | 0x1f; + case 10: + return regs[10] | 0x3f; + default: + return regs[adr]; + } +} + + +/* + * Write to REU register + */ + +void REU::WriteRegister(uint16 adr, uint8 byte) +{ + if (ex_ram == NULL) + return; + + switch (adr) { + case 0: // Status register is read-only + case 11: // Unconnected registers + case 12: + case 13: + case 14: + case 15: + break; + case 1: // Command register + regs[1] = byte; + if ((byte & 0x90) == 0x90) + execute_dma(); + break; + default: + regs[adr] = byte; + break; + } +} + + +/* + * CPU triggered REU by writing to $ff00 + */ + +void REU::FF00Trigger(void) +{ + if (ex_ram == NULL) + return; + + if ((regs[1] & 0x90) == 0x80) + execute_dma(); +} + + +/* + * Execute REU DMA transfer + */ + +void REU::execute_dma(void) +{ + // Get C64 and REU transfer base addresses + uint16 c64_adr = regs[2] | (regs[3] << 8); + uint32 reu_adr = regs[4] | (regs[5] << 8) | (regs[6] << 16); + + // Calculate transfer length + int length = regs[7] | (regs[8] << 8); + if (!length) + length = 0x10000; + + // Calculate address increments + uint32 c64_inc = (regs[10] & 0x80) ? 0 : 1; + uint32 reu_inc = (regs[10] & 0x40) ? 0 : 1; + + // Do transfer + switch (regs[1] & 3) { + + case 0: // C64 -> REU + for (; length--; c64_adr+=c64_inc, reu_adr+=reu_inc) + ex_ram[reu_adr & ram_mask] = the_cpu->REUReadByte(c64_adr); + break; + + case 1: // C64 <- REU + for (; length--; c64_adr+=c64_inc, reu_adr+=reu_inc) + the_cpu->REUWriteByte(c64_adr, ex_ram[reu_adr & ram_mask]); + break; + + case 2: // C64 <-> REU + for (; length--; c64_adr+=c64_inc, reu_adr+=reu_inc) { + uint8 tmp = the_cpu->REUReadByte(c64_adr); + the_cpu->REUWriteByte(c64_adr, ex_ram[reu_adr & ram_mask]); + ex_ram[reu_adr & ram_mask] = tmp; + } + break; + + case 3: // Compare + for (; length--; c64_adr+=c64_inc, reu_adr+=reu_inc) + if (ex_ram[reu_adr & ram_mask] != the_cpu->REUReadByte(c64_adr)) { + regs[0] |= 0x20; + break; + } + break; + } + + // Update address and length registers if autoload is off + if (!(regs[1] & 0x20)) { + regs[2] = c64_adr; + regs[3] = c64_adr >> 8; + regs[4] = reu_adr; + regs[5] = reu_adr >> 8; + regs[6] = reu_adr >> 16; + regs[7] = length + 1; + regs[8] = (length + 1) >> 8; + } + + // Set complete bit in status register + regs[0] |= 0x40; + + // Clear execute bit in command register + regs[1] &= 0x7f; +} diff --git a/Src/REU.h b/Src/REU.h new file mode 100644 index 0000000..482971a --- /dev/null +++ b/Src/REU.h @@ -0,0 +1,53 @@ +/* + * REU.h - 17xx REU emulation + * + * Frodo (C) 1994-1997,2002-2005 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _REU_H +#define _REU_H + + +class MOS6510; +class Prefs; + +class REU { +public: + REU(MOS6510 *CPU); + ~REU(); + + void NewPrefs(Prefs *prefs); + void Reset(void); + uint8 ReadRegister(uint16 adr); + void WriteRegister(uint16 adr, uint8 byte); + void FF00Trigger(void); + +private: + void open_close_reu(int old_size, int new_size); + void execute_dma(void); + + MOS6510 *the_cpu; // Pointer to 6510 + + uint8 *ex_ram; // REU expansion RAM + + uint32 ram_size; // Size of expansion RAM + uint32 ram_mask; // Expansion RAM address bit mask + + uint8 regs[16]; // REU registers +}; + +#endif diff --git a/Src/ROlib.h b/Src/ROlib.h new file mode 100644 index 0000000..89877f5 --- /dev/null +++ b/Src/ROlib.h @@ -0,0 +1,533 @@ +/* + * ROlib.h - Defines Classes, variables and OS interface calls for Acorn + * RISC OS computers + * + * (C) 1997 Andreas Dehmel + * + * Frodo (C) 1994-1997,2002-2005 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef RO_CUSTOM_LIB +#define RO_CUSTOM_LIB + +#include + + +#define TASKNAME "Frodo" +#define TASK_WORD 0x4b534154 + +/* Scrap-file for 1541fs directory function */ +#define RO_TEMPFILE ".FrodoDIR" + + + +/* Icon Flags */ +#define IFlg_Text 1 +#define IFlg_Sprite 2 +#define IFlg_Border 4 +#define IFlg_HCenter 8 +#define IFlg_VCenter 16 +#define IFlg_Filled 32 +#define IFlg_AntiA 64 +#define IFlg_AutoRdrw 128 +#define IFlg_Indir 256 +#define IFlg_RAdjust 512 +#define IFlg_Slct (1<<21) +#define IFlg_Grey (1<<22) + +/* Menu Flags */ +#define MFlg_Tick 1 +#define MFlg_Dotted 2 +#define MFlg_Writable 4 +#define MFlg_Warning 8 +#define MFlg_LastItem 128 + +/* Joystick stuff */ +#define JoyDir_Thresh 32 +#define JoyButton1 (1<<16) +#define JoyButton2 (1<<17) + + + +/* Size of WIMP data types */ +#define RO_WINDOW_WORDS 23 +#define RO_WINDOW_BYTES 92 +#define RO_ICON_WORDS 8 +#define RO_ICON_BYTES 32 +#define RO_MHEAD_WORDS 7 +#define RO_MHEAD_BYTES 28 +#define RO_MITEM_WORDS 6 +#define RO_MITEM_BYTES 24 + + + + +/* Structures for directory scanning (mainly 1541fs) */ +/* May hold entire pathname, so DON'T REDUCE!!! */ +#define FILENAME_MAX_CHARS 256 /* must be >= NAMEBUF_LENGTH (256) */ + +typedef struct { + unsigned int load, exec, length, attrib, otype; + char name[FILENAME_MAX_CHARS]; +} dir_full_info; + +typedef struct { + int readno, offset, buffsize; + char *match; +} dir_env; + + + +/* WIMP structures: */ +typedef struct { + int *tit, *val; + int len; +} WIdatI; + +/* data type for window / icon data */ +typedef union { + char strg[12]; + WIdatI ind; +} WIdata; + + +/* Window descriptor - 23 words = 92 bytes */ +typedef struct { + int Handle; + int vminx,vminy,vmaxx,vmaxy; + int scrollx,scrolly; + int stackpos; + unsigned int wflags; + char col_tfg, col_tbg, col_wfg, col_wbg; /* title/work_area fore/background colour */ + char col_sbo, col_sbi, col_tsl, reserved;/* scroll bar inner/outer, title if input focus */ + int wminx, wminy, wmaxx, wmaxy; + unsigned int tflags, waflags; + int SpriteAreaPtr; + short min_width, min_height; + WIdata dat; + int icon_no; +} RO_Window; + + +/* Icon descriptor */ +typedef struct { + int minx, miny, maxx, maxy; + int iflags; + WIdata dat; +} RO_Icon; + + +typedef struct { + int WindowHandle; + int minx, miny, maxx, maxy; + int iflags; + WIdata dat; +} RO_IconDesc; + + +typedef struct { + char title[12]; + char col_tfg, col_tbg, col_wfg, col_wbg; + int width, height, vgap; +} RO_MenuHead; + + +typedef struct { + unsigned int mflags; + RO_MenuHead *submenu; + unsigned int iflags; + WIdata dat; +} RO_MenuItem; + + +/* Caret descriptor */ +typedef struct { + int WHandle; + int IHandle; + int offx, offy; + int height, index; +} RO_Caret; + + + +/* Joystick key descriptor */ +typedef struct { + unsigned char up, down, left, right, fire; +} Joy_Keys; + + + + +/* Declare classes that are needed in the following new classes */ +class C64Display; +class C64; + + + +/* Very simple class to read the resolution and eigen values */ +class RORes +{ + public: + RORes(void); + ~RORes(void); + + int resx,resy,eigx,eigy; +}; + + +/* Handle current screenmode */ +class ROScreen +{ + private: + _kernel_oserror ModeError; + + public: + ROScreen(void); + ~ROScreen(void); + int ReadMode(void); + + int resx, resy, ldbpp, eigx, eigy, ladd; + char *scrbase; +}; + + +class Icon +{ + private: + RO_IconDesc icon; + + public: + Icon(int IconHandle, const RO_IconDesc *IDesc); + ~Icon(void); + void setstate(unsigned int eor, unsigned int clear); + void getstate(void); + + int IHandle; +}; + + +class Window +{ + private: + RO_Window *wind; + bool isopen; + + public: + Window(const int *WDesc, const char *Title); + ~Window(void); + int MyHandle(void); + void GetWorkArea(int *Dest); + void RedrawAWindow(int *Block, uint8 *Bitmap, C64Display *Disp); + RO_Window *Descriptor(void); + RO_Icon *GetIcon(unsigned int number); + void SetIconState(unsigned int number, unsigned int eor, unsigned int clear); + void GetIconState(unsigned int number, int *Block); + void WriteIconText(unsigned int number, const char *text); + void WriteIconTextU(unsigned int number, const char *text); // update instead of force redrw + void WriteIconNumber(unsigned int number, int value); + void WriteIconNumberU(unsigned int number, int value); + char *ReadIconText(unsigned int number); + int ReadIconNumber(unsigned int number); + void ForceIconRedraw(unsigned int number); + void UpdateIcon(unsigned int number); + void WriteTitle(const char *title); + char *ReadTitle(void); + void UpdateTitle(void); + bool HaveInput(void); + bool OpenStatus(void); + bool OpenStatus(int *Block); + void open(void); + void open(int *Block); + void close(void); + void forceredraw(int minx, int miny, int maxx, int maxy); + void update(uint8 *Bitmap, C64Display *Disp); + void update(int *Block, uint8 *Bitmap, C64Display *Disp); + void redraw(int *Block, uint8 *Bitmap, C64Display *Disp); + void extent(int minx, int miny, int maxx, int maxy); + void getstate(void); + void getstate(int *dest); // read window definition to external block +}; + + +class WIMP +{ + private: + _kernel_oserror WimpError; + int Block[64], AuxBlock[64]; // two WIMP blocks for convenience + int Mask; + int DragType, CMOS_DragType; + bool UseScrap; // Scrap file used (data transfer protocol!) + bool EmuPaused; + int UseNULL; // Number of clients that need NULL events + int RAMsize, RAMtransfered, RAMpartner; // for RAM transfer + int LastMenu, LastClick, LastDrag, MenuType, LastIcon, SaveType; + int *SpriteArea; + RO_Caret LastCaret; + int EmuZoom; + Joy_Keys NewJoyKeys[2]; + C64 *the_c64; + + public: + WIMP(C64 *my_c64); + ~WIMP(void); + + // On startup + bool LoadATemplate(char *Name, Window **Which); + + // To make window and pane work as a team + void OpenEmuWindow(void); + void OpenEmuWindow(int *Block); // open at pos + void CloseEmuWindow(void); + void UpdateEmuWindow(void); // update window and pane + void ThePrefsToWindow(void); // write ThePrefs into the window + void WindowToThePrefs(void); // update ThePrefs from window + void SysConfToWindow(void); + void WindowToSysConf(void); + void PollSysConfWindow(void); // low-level koyboard scan + void DragIconSprite(Window *host, unsigned int number); + int CalculateVolume(int *Block); + int CheckFilename(char *name); + void SnapshotSaved(bool OK); + void IssueSnapshotRequest(void); + void SetLEDIcons(bool FloppyEmulation); + void SetEmuWindowSize(void); + void ToggleEmuWindowSize(void); + int ReadEmuWindowSize(void); + void NewDriveImage(int DrNum, int *MsgBlock, bool SetNow); + void SetSpeedLimiter(bool LimitSpeed); + + // Standard WIMP functions + void Poll(bool Paused); + void Redraw(void); + void OpenWindow(void); + void CloseWindow(void); + void MouseClick(void); + void UserDrag(void); + void KeyPressed(void); + void MenuSelection(void); + void UserMessage(void); + void UserMessageAck(void); + + // WIMP's Windows and Icons + Icon *IBicon; + Window *EmuWindow, *EmuPane, *PrefsWindow, *ConfigWindow, *InfoWindow, *SoundWindow, *SaveBox; + char SnapFile[256], RAMFile[256]; +}; + + + +/* system-specific Variables */ + +extern RO_IconDesc IBarIcon; +extern unsigned int TaskHandle; +extern int WimpMessages[]; + + +/* Functions available via ROlib (not RISC OS Lib, mind you) + All SWIs are called in the X-Form! */ + +extern "C" +{ + +/* WIMP FUNCTIONS */ + +/* returns handle or 0 if error */ +extern int Wimp_Initialise(int Version, int tw, const char *TaskName, const int *Messages); + +extern _kernel_oserror *Wimp_CloseDown(int Handle, int tw); + +/* returns handle or 0 if error */ +extern int Wimp_CreateWindow(const int *Window); + +extern int Wimp_CreateIcon(int Priority, const RO_IconDesc *Icon); + +extern _kernel_oserror *Wimp_DeleteWindow(const int *Window); + +extern _kernel_oserror *Wimp_DeleteIcon(int *Block); + +extern _kernel_oserror *Wimp_OpenWindow(const int *Window); + +extern _kernel_oserror *Wimp_CloseWindow(const int *Window); + +extern int Wimp_Poll(unsigned int Mask, int *Block, int *PollWord); + +/* returns 0 if no more to do or error */ +extern int Wimp_RedrawWindow(int *Block); + +extern int Wimp_UpdateWindow(int *Block); + +extern int Wimp_GetRectangle(int *Block); + +extern _kernel_oserror *Wimp_GetWindowState(int *Block); + +extern _kernel_oserror *Wimp_GetWindowInfo(int *Block); + +extern _kernel_oserror *Wimp_SetIconState(int *Block); + +extern _kernel_oserror *Wimp_GetIconState(int *Block); + +extern _kernel_oserror *Wimp_GetPointerInfo(int *Block); + +extern _kernel_oserror *Wimp_DragBox(int *Block); + +extern _kernel_oserror *Wimp_ForceRedraw(int Handle, int minx, int miny, int maxx, int maxy); + +extern _kernel_oserror *Wimp_SetCaretPosition(int WHandle, int IHandle, int xoff, int yoff, int height, int index); + +extern _kernel_oserror *Wimp_GetCaretPosition(RO_Caret *Caret); + +extern _kernel_oserror *Wimp_CreateMenu(const int *Menu, int cx, int cy); + +extern _kernel_oserror *Wimp_SetExtent(int Handle, int *Block); + +extern _kernel_oserror *Wimp_OpenTemplate(char *Name); + +extern _kernel_oserror *Wimp_CloseTemplate(void); + +extern _kernel_oserror *Wimp_LoadTemplate(char **Template, char **Indirect, char *IndirLimit, char *Fonts, char *Name, int *Position); + +extern _kernel_oserror *Wimp_ProcessKey(int Key); + +extern int Wimp_StartTask(char *command); + +extern _kernel_oserror *Wimp_ReportError(const _kernel_oserror *Error, unsigned int Flags, const char *AppName); + +extern _kernel_oserror *Wimp_GetWindowOutline(int *Block); + +extern int Wimp_PollIdle(unsigned int Mask, int *Block, int MinTime, int *PollWord); + +extern _kernel_oserror *Wimp_PlotIcon(int *Block); + +extern _kernel_oserror *Wimp_SendMessage(int Event, int *Block, int THandle, int IHandle); + +extern _kernel_oserror *Wimp_CreateSubMenu(int *MenuBlock, int cx, int xy); + +extern _kernel_oserror *Wimp_SpriteOp(int, int, int, int, int, int, int, int); + +extern _kernel_oserror *Wimp_BaseOfSprites(int *ROM, int *RAM); + +extern _kernel_oserror *Wimp_CommandWindow(int Action); + +extern _kernel_oserror *Wimp_TransferBlock(int SHandle, char *SBuff, int DHandle, char *DBuff, int BuffSize); + +extern _kernel_oserror *Wimp_SpriteInfo(char *name, int *width, int *height, int *mode); + +extern _kernel_oserror *DragASprite_Start(unsigned int Flags, int SpriteArea, char *SpriteName, int *Box, int *BBox); + +extern _kernel_oserror *DragASprite_Stop(void); + +extern _kernel_oserror *ColourTrans_SelectTable(int SMode, int SPal, int DMode, int DPal, char **Buffer, unsigned int Flags, int *TransWork, int *TransFunc); + +extern _kernel_oserror *ColourTrans_SetFontColours(int Handle, int BPal, int FPal, int Offset); + +extern _kernel_oserror *ColourTrans_SetColour(int GCOL, unsigned int Flags, int Action); + +extern _kernel_oserror *ColourTrans_SetGCOL(int Palette, unsigned int Flags, int Action); + + + +/* OS FUNCTIONS */ + +extern int OS_ReadModeVariable(int mode, int var); + +extern int OS_ReadDynamicArea(int area); + +extern int ScanKeys(int keys); + +extern int ReadKeyboardStatus(void); + +extern int ReadDragType(void); + +extern int SetMousePointer(int NewShape); + + +/* Generic sprite op call */ +extern _kernel_oserror *OS_SpriteOp(int, int, int, int, int, int, int, int); + +extern _kernel_oserror *OS_Plot(int Command, int x, int y); + +extern _kernel_oserror *MouseBoundingBox(char Box[8]); + +extern int OS_ReadMonotonicTime(void); + +extern _kernel_oserror *OS_ReadC(char *Code); + +/* returns length of characters read; if length negative ==> terminated by escape */ +extern int OS_ReadLine(char *Buffer, int BuffSize, int minasc, int maxasc, int Echo); + +/* true ==> escape */ +extern bool OS_ReadEscapeState(void); + +/* File related calls */ + +/* Returns object type */ +extern int ReadCatalogueInfo(char *Name, int Result[4]); + +/* Read the next name in the directory */ +extern _kernel_oserror *ReadDirName(const char *dirname, char *buffer, dir_env *env); + +/* Read the next entry (name, length, type,...) in the directory */ +extern _kernel_oserror *ReadDirNameInfo(const char *dirname,dir_full_info *buffer,dir_env *env); + +extern _kernel_oserror *DeleteFile(char *name); + +extern _kernel_oserror *OS_FlushBuffer(int BuffNum); + +/* These functions are more secure than using sprintf because they allow buffersize */ +/* Return value is a pointer to the terminating null */ +extern char *ConvertInteger1(int value, char *buffer, int buffsize); +extern char *ConvertInteger2(int value, char *buffer, int buffsize); +extern char *ConvertInteger3(int value, char *buffer, int buffsize); +extern char *ConvertInteger4(int value, char *buffer, int buffsize); + + +/* Misc */ + +extern unsigned int ModeColourNumber(unsigned int pal_entry); + +/* Returns -1 if error in joystick module, -2 if SWI unknown, joystate otherwise */ +extern int Joystick_Read(int joyno); + + +/* Sound stuff */ + +#define DRState_Active 1 +#define DRState_NeedData 2 +#define DRState_Overflow 4 + +/* Sound calls */ + +extern int Sound_Volume(int volume); + +/* Digital Renderer SWI calls */ +extern _kernel_oserror *DigitalRenderer_Activate(int Channels, int Length, int SamPeriod); + +extern _kernel_oserror *DigitalRenderer_Deactivate(void); + +extern _kernel_oserror *DigitalRenderer_Pause(void); + +extern _kernel_oserror *DigitalRenderer_Resume(void); + +extern _kernel_oserror *DigitalRenderer_GetTables(uint8 **LinToLog, uint8 **LogScale); + +extern int DigitalRenderer_ReadState(void); + +extern _kernel_oserror *DigitalRenderer_NewSample(uint8 *Sample); + +} + +#endif diff --git a/Src/ROlib.s b/Src/ROlib.s new file mode 100644 index 0000000..de8f291 --- /dev/null +++ b/Src/ROlib.s @@ -0,0 +1,1167 @@ +;* +;* ROlib.h +;* +;* Assembler interface to RISC OS +;* (C) 1997 Andreas Dehmel +;* +;* Frodo (C) 1994-1997,2002-2005 Christian Bauer +;* +;* 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 2 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, write to the Free Software +;* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +a1 rn 0 +a2 rn 1 +a3 rn 2 +a4 rn 3 +v1 rn 4 +v2 rn 5 +v3 rn 6 +v4 rn 7 +v5 rn 8 +v6 rn 9 +sl rn 10 +fp rn 11 +ip rn 12 +sp rn 13 +lr rn 14 +pc rn 15 + + + idfn (C) 1997 by Andreas Dehmel + +; ************ WIMP STUFF **************** + + AREA CODE, READONLY + align 4 + export |Wimp_Initialise| + = "Wimp_Initialise" + align 4 + +|Wimp_Initialise|: + swi 0x600c0 ;XWimp_Initialise + movvs a1,#0 ;return 0 if error + movvc a1,a2 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_CloseDown| + = "Wimp_CloseDown" + align 4 + +|Wimp_CloseDown| + swi 0x600dd ;XWimp_CloseDown + movvc a1,#0 ;return pointer to error block + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_CreateWindow| + = "Wimp_CreateWindow" + align 4 + +|Wimp_CreateWindow|: + mov a2,a1 + swi 0x600c1 + movvs a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_CreateIcon| + = "Wimp_CreateIcon" + align 4 + +|Wimp_CreateIcon|: + swi 0x600c2 + movvs a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_DeleteWindow| + = "Wimp_DeleteWindow" + align 4 + +|Wimp_DeleteWindow|: + mov a2,a1 + swi 0x600c3 + movvc a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_DeleteIcon| + = "Wimp_DeleteIcon" + align 4 + +|Wimp_DeleteIcon|: + mov a2,a1 + swi 0x600c4 + movvc a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_OpenWindow| + = "Wimp_OpenWindow" + align 4 + +|Wimp_OpenWindow|: + mov a2,a1 + swi 0x600c5 + movvc a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_CloseWindow| + = "Wimp_CloseWindow" + align 4 + +|Wimp_CloseWindow|: + mov a2,a1 + swi 0x600c6 + movvc a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_Poll| + = "Wimp_Poll" + align 4 + +|Wimp_Poll|: + mov a4,a3 + swi 0x600c7 + mvnvs a1,#0 ;return -1 if error + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_RedrawWindow| + = "Wimp_RedrawWindow" + align 4 + +|Wimp_RedrawWindow|: + mov a2,a1 + swi 0x600c8 + movvs a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_UpdateWindow| + = "Wimp_UpdateWindow" + align 4 + +|Wimp_UpdateWindow| + mov a2,a1 + swi 0x600c9 + movvs a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_GetRectangle| + = "Wimp_GetRectangle" + align 4 + +|Wimp_GetRectangle|: + mov a2,a1 + swi 0x600ca + movvs a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_GetWindowState| + = "Wimp_GetWindowState" + align 4 + +|Wimp_GetWindowState|: + mov a2,a1 + swi 0x600cb + movvc a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_GetWindowInfo| + = "Wimp_GetWindowInfo" + align 4 + +|Wimp_GetWindowInfo|: + mov a2,a1 + swi 0x600cc + movvc a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_SetIconState| + = "Wimp_SetIconState" + align 4 + +|Wimp_SetIconState|: + mov a2,a1 + swi 0x600cd + movvc a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_GetIconState| + = "Wimp_GetIconState" + align 4 + +|Wimp_GetIconState|: + mov a2,a1 + swi 0x600ce + movvc a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_GetPointerInfo| + = "Wimp_GetPointerInfo" + align 4 + +|Wimp_GetPointerInfo|: + mov a2,a1 + swi 0x600cf + movvc a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_DragBox| + = "Wimp_DragBox" + align 4 + +|Wimp_DragBox|: + mov a2,a1 + swi 0x600d0 + movvc a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_ForceRedraw| + = "Wimp_ForceRedraw" + align 4 + +|Wimp_ForceRedraw|: + stmdb sp!,{v1} + ldr v1,[sp,#4] + swi 0x600d1 + movvc a1,#0 + ldmia sp!,{v1} + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_SetCaretPosition| + = "Wimp_SetCaretPosition" + align 4 + +|Wimp_SetCaretPosition|: + stmdb sp!,{v1,v2} + add v1,sp,#8 + ldmia v1,{v1,v2} + swi 0x600d2 + movvc a1,#0 + ldmia sp!,{v1,v2} + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_GetCaretPosition| + = "Wimp_GetCaretPosition" + align 4 + +|Wimp_GetCaretPosition|: + mov a2,a1 + swi 0x600d3 + movvc a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_CreateMenu| + = "Wimp_CreateMenu" + align 4 + +|Wimp_CreateMenu|: + mov a4,a3 + mov a3,a2 + mov a2,a1 + swi 0x600d4 + movvc a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_SetExtent| + = "Wimp_SetExtent" + align 4 + +|Wimp_SetExtent|: + swi 0x600d7 + movvc a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_OpenTemplate| + = "Wimp_OpenTemplate" + align 4 + +|Wimp_OpenTemplate|: + mov a2,a1 + swi 0x600d9 + movvc a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_CloseTemplate| + = "Wimp_CloseTemplate" + align 4 + +|Wimp_CloseTemplate|: + swi 0x600da + movvc a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_LoadTemplate| + = "Wimp_LoadTemplate" + align 4 + +|Wimp_LoadTemplate|: + stmdb sp!,{a1,a2,v1-v4,lr} ;7 registers + mov v1,a4 ;Fonts + mov a4,a3 ;IndirectLimit + ldr a3,[a2] ;*Indirect + ldr a2,[a1] ;*Template + add v2,sp,#28 + ldmia v2,{v2,v4} + ldr v3,[v4] ;Position + swi 0x600db + addvs sp,sp,#8 + bvs |WLTexit| + str v3,[v4] ;store Position + ldmia sp!,{v1,v2} + str a2,[v1] + str a3,[v2] + mov a1,#0 +|WLTexit|: + ldmia sp!,{v1-v4,pc}^ + + + AREA CODE, READONLY + align 4 + export |Wimp_ProcessKey| + = "Wimp_ProcessKey" + align 4 + +|Wimp_ProcessKey|: + swi 0x600dc + movvc a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_StartTask| + = "Wimp_StartTask" + align 4 + +|Wimp_StartTask|: + swi 0x600de + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_ReportError| + = "Wimp_ReportError" + align 4 + +|Wimp_ReportError|: + swi 0x600df + movvs a1,#0 + movvc a1,a2 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_GetWindowOutline| + = "Wimp_GetWindowOutline" + align 4 + +|Wimp_GetWindowOutline|: + mov a2,a1 + swi 0x600e0 + movvc a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_PollIdle| + = "Wimp_PollIdle" + align 4 + +|Wimp_PollIdle|: + swi 0x600e1 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_PlotIcon| + = "Wimp_PlotIcon" + align 4 + +|Wimp_PlotIcon|: + mov a2,a1 + swi 0x600e2 + movvc a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_SendMessage| + = "Wimp_SendMessage" + align 4 + +|Wimp_SendMessage|: + swi 0x600e7 + movvc a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_CreateSubMenu| + = "Wimp_CreateSubMenu" + align 4 + +|Wimp_CreateSubMenu|: + mov a4,a3 + mov a3,a2 + mov a2,a1 + swi 0x600e8 + movvc a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_SpriteOp| + = "Wimp_SpriteOp" + align 4 + +|Wimp_SpriteOp|: + stmdb sp!,{v1-v4,lr} + add v1,sp,#20 + ldmia v1,{v1-v4} + swi 0x600e9 + movvc a1,#0 + ldmia sp!,{v1-v4,pc}^ + + + AREA CODE, READONLY + align 4 + export |Wimp_BaseOfSprites| + = "Wimp_BaseOfSprites" + align 4 + +|Wimp_BaseOfSprites|: + mov a3,a1 + mov a4,a2 + swi 0x600ea + strvc a1,[a3] + strvc a2,[a4] + movvc a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_CommandWindow| + = "Wimp_CommandWindow" + align 4 + +|Wimp_CommandWindow|: + swi 0x600ef + movvc a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_TransferBlock| + = "Wimp_TransferBlock" + align 4 + +|Wimp_TransferBlock|: + str v1,[sp,#-4]! + ldr v1,[sp,#4] + swi 0x600f1 + ldr v1,[sp],#4 + movvc a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |Wimp_SpriteInfo| + = "Wimp_SpriteInfo" + align 4 + +|Wimp_SpriteInfo|: + stmdb sp!,{v1-v6,lr} + mov v4,a2 + mov v5,a3 + mov v6,a4 + mov a3,a1 + mov a1,#40 + movvc a1,#0 + swi 0x600e9 ;Wimp_SpriteOp + str a4,[v4] + str v1,[v5] + str v3,[v6] + ldmia sp!,{v1-v6,pc}^ + + + AREA CODE, READONLY + align 4 + export |DragASprite_Start| + = "DragASprite_Start" + align 4 + +|DragASprite_Start|: + swi 0x62400 + movvc a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |DragASprite_Stop| + = "DragASprite_Stop" + align 4 + +|DragASprite_Stop|: + swi 0x62401 + movvc a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |ColourTrans_SelectTable| + = "ColourTrans_SelectTable" + align 4 + +|ColourTrans_SelectTable|: + stmdb sp!,{v1-v4,lr} + add v1,sp,#20 + ldmia v1,{v1-v4} + ldr v1,[v1] + swi 0x60740 + addvs sp,sp,#4 + bvs |CTSTexit| + mov a1,v1 + ldr v1,[sp],#4 + str a1,[v1] + mov a1,#0 +|CTSTexit|: + ldmia sp!,{v2-v4,pc}^ + + + AREA CODE, READONLY + align 4 + export |ColourTrans_SetFontColours| + = "ColourTrans_SetFontColours" + align 4 + +|ColourTrans_SetFontColours|: + swi 0x6074f + movvc a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |ColourTrans_SetColour| + = "ColourTrans_SetColour" + align 4 + +|ColourTrans_SetColour|: + str v1,[sp,#-4]! + mov v1,a3 + mov a4,a2 + swi 0x6075e + movvc a1,#0 + ldr v1,[sp],#4 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |ColourTrans_SetGCOL| + = "ColourTrans_SetGCOL" + align 4 + +|ColourTrans_SetGCOL|: + str v1,[sp,#-4]! + mov v1,a3 + mov a4,a2 + swi 0x60743 + movvc a1,#0 + ldr v1,[sp],#4 + movs pc,lr + + + + + +; ************* OS STUFF *************** + + AREA CODE, READONLY + align 4 + export |OS_ReadModeVariable| + = "OS_ReadModeVariable" + align 4 + +|OS_ReadModeVariable|: + swi 0x20035 + mvncs a1,#0 + movcc a1,a3 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |OS_ReadDynamicArea| + = "OS_ReadDynamicArea" + align 4 + +|OS_ReadDynamicArea|: + swi 0x2005c + movvs a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |ModeColourNumber| + = "ModeColourNumber" + align 4 + +|ModeColourNumber|: + swi 0x60744 ;XColourTrans_ReturnColourNumber + mvnvs a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |ScanKeys| + = "ScanKeys" + align 4 + +|ScanKeys|: + mov a2,a1 + mov a1,#121 + swi 0x20006 ;XOS_Byte 121 + mov a1,a2 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |ReadKeyboardStatus| + = "ReadKeyboardStatus" + align 4 + +|ReadKeyboardStatus|: + mov a1,#202 + mov a2,#0 + mov a3,#255 + swi 0x20006 + mov a1,a2 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |ReadDragType| + = "ReadDragType" + align 4 + +|ReadDragType|: + mov a1,#161 + mov a2,#28 + swi 0x20006 + and a1,a3,#2 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |SetMousePointer| + = "SetMousePointer" + align 4 + +|SetMousePointer|: + mov a2,a1 + mov a1,#106 + swi 0x20006 + mov a1,a2 + movs pc,lr + + + AREA CODE, READONLY ;sprite ops (I have 52, 53 and 56 in mind) + align 4 + export |OS_SpriteOp| + = "OS_SpriteOp" + align 4 + +|OS_SpriteOp|: + stmdb sp!,{v1-v4,lr} + add v1,sp,#20 + ldmia v1,{v1-v4} + swi 0x2002e + movvc a1,#0 + ldmia sp!,{v1-v4,pc}^ + + + AREA CODE, READONLY + align 4 + export |OS_Plot| + = "OS_Plot" + align 4 + +|OS_Plot|: + swi 0x20045 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |MouseBoundingBox| + = "MouseBoundingBox" + align 4 + +|MouseBoundingBox|: + mov a2,a1 + mov a1,#21 + swi 0x20007 ;OS_Word21,1 + movvc a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |OS_ReadMonotonicTime| + = "OS_ReadMonotonicTime" + align 4 + +|OS_ReadMonotonicTime|: + swi 0x20042 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |OS_ReadC| + = "OS_ReadC" + align 4 + +|OS_ReadC|: + mov a2,a1 + swi 0x20004 + strccb a1,[a2] + movcc a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |OS_ReadLine| + = "OS_ReadLine" + align 4 + +|OS_ReadLine|: + stmdb sp!,{a1,v1,v2} + stmdb sp!,{a1-a3} + mov a1,#200 + mov a2,#1 + mvn a3,#1 + swi 0x20006 + mov v2,a2 + ldmia sp!,{a1-a3} + ldr v1,[sp,#8] + swi 0x2000e + mov v1,a2 + mov a1,#200 + mov a2,v2 + mvn a3,#0 + swi 0x20006 + mov a2,v1 + ldmia sp!,{a4,v1,v2} + bic a4,a4,#0xc0000000 + mov a1,#10 + strb a1,[a4,a2] ;make compatible with fgets (terminated by 10, not 13) + mov a1,a2 + rsbcs a1,a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |OS_ReadEscapeState| + = "OS_ReadEscapeState" + align 4 + +|OS_ReadEscapeState|: + swi 0x2002c + movcc a1,#0 + movcs a1,#1 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |ReadCatalogueInfo| + = "ReadCatalogueInfo" + align 4 + +|ReadCatalogueInfo|: + stmdb sp!,{v1-v3,lr} + mov v3,a2 + mov a2,a1 + mov a1,#17 + swi 0x20008 ;OS_File 17 + stmia v3,{a3-v2} + ldmia sp!,{v1-v3,pc}^ + + + AREA CODE, READONLY + align 4 + export |ReadDirName| + = "ReadDirName" + align 4 + +|ReadDirName|: + stmdb sp!,{v1-v4,lr} + mov v4,a3 ;preserve dir_env * + ldmia a3,{a4-v3} + mov a3,a2 + mov a2,a1 + mov a1,#9 + swi 0x2000c ;OS_GBPB 9 + stmia v4,{a4,v1} + movvc a1,#0 + ldmia sp!,{v1-v4,pc}^ + + + AREA CODE, READONLY + align 4 + export |ReadDirNameInfo| + = "ReadDirNameInfo" + align 4 + +|ReadDirNameInfo|: + stmdb sp!,{v1-v4,lr} + mov v4,a3 + ldmia a3,{a4-v3} + mov a3,a2 + mov a2,a1 + mov a1,#10 + swi 0x2000c ;OS_GBPB 10 + stmia v4,{a4,v1} + movvc a1,#0 + ldmia sp!,{v1-v4,pc}^ + + + AREA CODE, READONLY + align 4 + export |DeleteFile| + = "DeleteFile" + align 4 + +|DeleteFile|: + stmdb sp!,{v1,v2} + mov a2,a1 + mov a1,#6 + swi 0x20008 ;OS_File 6 + movvc a1,#0 + ldmia sp!,{v1,v2} + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |ConvertInteger1| + = "ConvertInteger1" + align 4 + +|ConvertInteger1|: + swi 0x200d9 + mov a1,a2 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |ConvertInteger2| + = "ConvertInteger2" + align 4 + +|ConvertInteger2|: + swi 0x200da + mov a1,a2 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |ConvertInteger3| + = "ConvertInteger3" + align 4 + +|ConvertInteger3|: + swi 0x200db + mov a1,a2 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |ConvertInteger4| + = "ConvertInteger4" + align 4 + +|ConvertInteger4|: + swi 0x200dc + mov a1,a2 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |OS_FlushBuffer| + = "OS_FlushBuffer" + align 4 + +|OS_FlushBuffer|: + mov a2,a1 + mov a1,#21 + swi 0x20006 + movvc a1,#0 + movs pc,lr + + + + + + +; ************ MISC STUFF *************** + + AREA CODE, READONLY + align 4 + export |Joystick_Read| + = "Joystick_Read" + align 4 + +|Joystick_Read|: + swi 0x63f40 ;XJoystick_Read (Acorn) + bvc |JRexit| + ldr a2,[a1] ;get error-number + eor a2,a2,#0xe6 + eors a2,a2,#0x100 + mvneq a1,#1 ;-2 ==> unknown SWI + mvnne a1,#0 ;-1 ==> error generated from joystick module +|JRexit|: + movs pc,lr + + + + + +; ************** SOUND SWIS ************* + + AREA CODE, READONLY + align 4 + export |Sound_Volume| + = "Sound_Volume" + align 4 + +|Sound_Volume|: + swi 0x60180 + mvnvs a1,#0 + movs pc,lr + + +; *********** DIGITAL RENDERER ********** + +DigiRendChunk equ 0x6F700 ;X flag set + + + AREA CODE, READONLY + align 4 + export |DigitalRenderer_Activate| + = "DigitalRenderer_Activate" + align 4 + +|DigitalRenderer_Activate|: + swi DigiRendChunk + movvc a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |DigitalRenderer_Deactivate| + = "DigitalRenderer_Deactivate" + align 4 + +|DigitalRenderer_Deactivate|: + swi DigiRendChunk+1 + movvc a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |DigitalRenderer_Pause| + = "DigitalRenderer_Pause" + align 4 + +|DigitalRenderer_Pause|: + swi DigiRendChunk+2 + movvc a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |DigitalRenderer_Resume| + = "DigitalRenderer_Resume" + align 4 + +|DigitalRenderer_Resume|: + swi DigiRendChunk+3 + movvc a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |DigitalRenderer_GetTables| + = "DigitalRenderer_GetTables" + align 4 + +|DigitalRenderer_GetTables|: + mov a3,a1 + mov a4,a2 + swi DigiRendChunk+4 + strvc a1,[a3] + strvc a2,[a4] + movvc a1,#0 + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |DigitalRenderer_ReadState| + = "DigitalRenderer_ReadState" + align 4 + +|DigitalRenderer_ReadState|: + swi DigiRendChunk+5 + mvnvs a1,#0 ;returns -1 on error + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |DigitalRenderer_NewSample| + = "DigitalRenderer_NewSample" + align 4 + +|DigitalRenderer_NewSample|: + swi DigiRendChunk+6 + movvc a1,#0 + movs pc,lr + + +; *********** PLOTTER ************** + +BPlotChunk equ 0x70100 ;X bit set + + AREA CODE, READONLY + align 4 + export |PlotZoom1| + = "PlotZoom1" + align 4 + +|PlotZoom1|: + swi BPlotChunk + movs pc,lr + + + AREA CODE, READONLY + align 4 + export |PlotZoom2| + = "PlotZoom2" + align 4 + +|PlotZoom2|: + swi BPlotChunk+1 diff --git a/Src/SAM.cpp b/Src/SAM.cpp new file mode 100644 index 0000000..6a8883b --- /dev/null +++ b/Src/SAM.cpp @@ -0,0 +1,2192 @@ +/* + * SAM.h - Simple Assembler and Monitor With Integrated System Explorer + * + * Frodo (C) 1994-1997,2002-2009 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "sysdeps.h" + +#include "SAM.h" +#include "C64.h" +#include "CPUC64.h" +#include "CPU1541.h" +#include "VIC.h" +#include "SID.h" +#include "CIA.h" + + +// Pointers to chips +static MOS6510 *TheCPU; +static MOS6502_1541 *TheCPU1541; +static MOS6569 *TheVIC; +static MOS6581 *TheSID; +static MOS6526_1 *TheCIA1; +static MOS6526_2 *TheCIA2; + +// 6510/6502 registers +static MOS6510State R64; +static MOS6502State R1541; + +static bool access_1541; // false: accessing C64, true: accessing 1541 + +// Access to 6510/6502 address space +static inline uint8 SAMReadByte(uint16 adr) +{ + if (access_1541) + return TheCPU1541->ExtReadByte(adr); + else + return TheCPU->ExtReadByte(adr); +} + +static inline void SAMWriteByte(uint16 adr, uint8 byte) +{ + if (access_1541) + TheCPU1541->ExtWriteByte(adr, byte); + else + TheCPU->ExtWriteByte(adr, byte); +} + + +// Streams for input, output and error messages +static FILE *fin, *fout, *ferr; + +// Input line +#define INPUT_LENGTH 80 +static char input[INPUT_LENGTH]; +static char *in_ptr; + +static uint16 address, end_address; + + +// Input tokens +enum Token { + T_NULL, // Invalid token + T_END, // End of line + T_NUMBER, // Hexadecimal number + T_STRING, // String enclosed in "" + T_LPAREN, // '(' + T_RPAREN, // ')' + T_ADD, // '+' + T_SUB, // '-' + T_MUL, // '*' + T_DIV, // '/' + T_COMMA, // ',' + T_IMMED, // '#' + T_X, // 'x' + T_Y, // 'y' + T_PC, // 'pc' + T_SP, // 'sp' + + T_A, // 'a' (get_reg_token() only) + T_DR, // 'dr' (get_reg_token() only) + T_PR // 'pr' (get_reg_token() only) +}; + +static enum Token the_token; // Last token read +static uint16 the_number; // Contains the number if the_token==T_NUMBER +static char the_string[INPUT_LENGTH]; // Contains the string if the_token==T_STRING + + +// Addressing modes +enum { + A_IMPL, + A_ACCU, // A + A_IMM, // #zz + A_REL, // Branches + A_ZERO, // zz + A_ZEROX, // zz,x + A_ZEROY, // zz,y + A_ABS, // zzzz + A_ABSX, // zzzz,x + A_ABSY, // zzzz,y + A_IND, // (zzzz) + A_INDX, // (zz,x) + A_INDY // (zz),y +}; + +// Mnemonics +enum { + M_ADC, M_AND, M_ASL, M_BCC, M_BCS, M_BEQ, M_BIT, M_BMI, M_BNE, M_BPL, + M_BRK, M_BVC, M_BVS, M_CLC, M_CLD, M_CLI, M_CLV, M_CMP, M_CPX, M_CPY, + M_DEC, M_DEX, M_DEY, M_EOR, M_INC, M_INX, M_INY, M_JMP, M_JSR, M_LDA, + M_LDX, M_LDY, M_LSR, M_NOP, M_ORA, M_PHA, M_PHP, M_PLA, M_PLP, M_ROL, + M_ROR, M_RTI, M_RTS, M_SBC, M_SEC, M_SED, M_SEI, M_STA, M_STX, M_STY, + M_TAX, M_TAY, M_TSX, M_TXA, M_TXS, M_TYA, + + M_ILLEGAL, // Undocumented opcodes start here + + M_IANC, M_IANE, M_IARR, M_IASR, M_IDCP, M_IISB, M_IJAM, M_INOP, M_ILAS, + M_ILAX, M_ILXA, M_IRLA, M_IRRA, M_ISAX, M_ISBC, M_ISBX, M_ISHA, M_ISHS, + M_ISHX, M_ISHY, M_ISLO, M_ISRE, + + M_MAXIMUM // Highest element +}; + +// Mnemonic for each opcode +static const char mnemonic[256] = { + M_BRK , M_ORA , M_IJAM, M_ISLO, M_INOP, M_ORA, M_ASL , M_ISLO, // 00 + M_PHP , M_ORA , M_ASL , M_IANC, M_INOP, M_ORA, M_ASL , M_ISLO, + M_BPL , M_ORA , M_IJAM, M_ISLO, M_INOP, M_ORA, M_ASL , M_ISLO, // 10 + M_CLC , M_ORA , M_INOP, M_ISLO, M_INOP, M_ORA, M_ASL , M_ISLO, + M_JSR , M_AND , M_IJAM, M_IRLA, M_BIT , M_AND, M_ROL , M_IRLA, // 20 + M_PLP , M_AND , M_ROL , M_IANC, M_BIT , M_AND, M_ROL , M_IRLA, + M_BMI , M_AND , M_IJAM, M_IRLA, M_INOP, M_AND, M_ROL , M_IRLA, // 30 + M_SEC , M_AND , M_INOP, M_IRLA, M_INOP, M_AND, M_ROL , M_IRLA, + M_RTI , M_EOR , M_IJAM, M_ISRE, M_INOP, M_EOR, M_LSR , M_ISRE, // 40 + M_PHA , M_EOR , M_LSR , M_IASR, M_JMP , M_EOR, M_LSR , M_ISRE, + M_BVC , M_EOR , M_IJAM, M_ISRE, M_INOP, M_EOR, M_LSR , M_ISRE, // 50 + M_CLI , M_EOR , M_INOP, M_ISRE, M_INOP, M_EOR, M_LSR , M_ISRE, + M_RTS , M_ADC , M_IJAM, M_IRRA, M_INOP, M_ADC, M_ROR , M_IRRA, // 60 + M_PLA , M_ADC , M_ROR , M_IARR, M_JMP , M_ADC, M_ROR , M_IRRA, + M_BVS , M_ADC , M_IJAM, M_IRRA, M_INOP, M_ADC, M_ROR , M_IRRA, // 70 + M_SEI , M_ADC , M_INOP, M_IRRA, M_INOP, M_ADC, M_ROR , M_IRRA, + M_INOP, M_STA , M_INOP, M_ISAX, M_STY , M_STA, M_STX , M_ISAX, // 80 + M_DEY , M_INOP, M_TXA , M_IANE, M_STY , M_STA, M_STX , M_ISAX, + M_BCC , M_STA , M_IJAM, M_ISHA, M_STY , M_STA, M_STX , M_ISAX, // 90 + M_TYA , M_STA , M_TXS , M_ISHS, M_ISHY, M_STA, M_ISHX, M_ISHA, + M_LDY , M_LDA , M_LDX , M_ILAX, M_LDY , M_LDA, M_LDX , M_ILAX, // a0 + M_TAY , M_LDA , M_TAX , M_ILXA, M_LDY , M_LDA, M_LDX , M_ILAX, + M_BCS , M_LDA , M_IJAM, M_ILAX, M_LDY , M_LDA, M_LDX , M_ILAX, // b0 + M_CLV , M_LDA , M_TSX , M_ILAS, M_LDY , M_LDA, M_LDX , M_ILAX, + M_CPY , M_CMP , M_INOP, M_IDCP, M_CPY , M_CMP, M_DEC , M_IDCP, // c0 + M_INY , M_CMP , M_DEX , M_ISBX, M_CPY , M_CMP, M_DEC , M_IDCP, + M_BNE , M_CMP , M_IJAM, M_IDCP, M_INOP, M_CMP, M_DEC , M_IDCP, // d0 + M_CLD , M_CMP , M_INOP, M_IDCP, M_INOP, M_CMP, M_DEC , M_IDCP, + M_CPX , M_SBC , M_INOP, M_IISB, M_CPX , M_SBC, M_INC , M_IISB, // e0 + M_INX , M_SBC , M_NOP , M_ISBC, M_CPX , M_SBC, M_INC , M_IISB, + M_BEQ , M_SBC , M_IJAM, M_IISB, M_INOP, M_SBC, M_INC , M_IISB, // f0 + M_SED , M_SBC , M_INOP, M_IISB, M_INOP, M_SBC, M_INC , M_IISB +}; + +// Addressing mode for each opcode +static const char adr_mode[256] = { + A_IMPL, A_INDX, A_IMPL, A_INDX, A_ZERO , A_ZERO , A_ZERO , A_ZERO, // 00 + A_IMPL, A_IMM , A_ACCU, A_IMM , A_ABS , A_ABS , A_ABS , A_ABS, + A_REL , A_INDY, A_IMPL, A_INDY, A_ZEROX, A_ZEROX, A_ZEROX, A_ZEROX, // 10 + A_IMPL, A_ABSY, A_IMPL, A_ABSY, A_ABSX , A_ABSX , A_ABSX , A_ABSX, + A_ABS , A_INDX, A_IMPL, A_INDX, A_ZERO , A_ZERO , A_ZERO , A_ZERO, // 20 + A_IMPL, A_IMM , A_ACCU, A_IMM , A_ABS , A_ABS , A_ABS , A_ABS, + A_REL , A_INDY, A_IMPL, A_INDY, A_ZEROX, A_ZEROX, A_ZEROX, A_ZEROX, // 30 + A_IMPL, A_ABSY, A_IMPL, A_ABSY, A_ABSX , A_ABSX , A_ABSX , A_ABSX, + A_IMPL, A_INDX, A_IMPL, A_INDX, A_ZERO , A_ZERO , A_ZERO , A_ZERO, // 40 + A_IMPL, A_IMM , A_ACCU, A_IMM , A_ABS , A_ABS , A_ABS , A_ABS, + A_REL , A_INDY, A_IMPL, A_INDY, A_ZEROX, A_ZEROX, A_ZEROX, A_ZEROX, // 50 + A_IMPL, A_ABSY, A_IMPL, A_ABSY, A_ABSX , A_ABSX , A_ABSX , A_ABSX, + A_IMPL, A_INDX, A_IMPL, A_INDX, A_ZERO , A_ZERO , A_ZERO , A_ZERO, // 60 + A_IMPL, A_IMM , A_ACCU, A_IMM , A_IND , A_ABS , A_ABS , A_ABS, + A_REL , A_INDY, A_IMPL, A_INDY, A_ZEROX, A_ZEROX, A_ZEROX, A_ZEROX, // 70 + A_IMPL, A_ABSY, A_IMPL, A_ABSY, A_ABSX , A_ABSX , A_ABSX , A_ABSX, + A_IMM , A_INDX, A_IMM , A_INDX, A_ZERO , A_ZERO , A_ZERO , A_ZERO, // 80 + A_IMPL, A_IMM , A_IMPL, A_IMM , A_ABS , A_ABS , A_ABS , A_ABS, + A_REL , A_INDY, A_IMPL, A_INDY, A_ZEROX, A_ZEROX, A_ZEROY, A_ZEROY, // 90 + A_IMPL, A_ABSY, A_IMPL, A_ABSY, A_ABSX , A_ABSX , A_ABSY , A_ABSY, + A_IMM , A_INDX, A_IMM , A_INDX, A_ZERO , A_ZERO , A_ZERO , A_ZERO, // a0 + A_IMPL, A_IMM , A_IMPL, A_IMM , A_ABS , A_ABS , A_ABS , A_ABS, + A_REL , A_INDY, A_IMPL, A_INDY, A_ZEROX, A_ZEROX, A_ZEROY, A_ZEROY, // b0 + A_IMPL, A_ABSY, A_IMPL, A_ABSY, A_ABSX , A_ABSX , A_ABSY , A_ABSY, + A_IMM , A_INDX, A_IMM , A_INDX, A_ZERO , A_ZERO , A_ZERO , A_ZERO, // c0 + A_IMPL, A_IMM , A_IMPL, A_IMM , A_ABS , A_ABS , A_ABS , A_ABS, + A_REL , A_INDY, A_IMPL, A_INDY, A_ZEROX, A_ZEROX, A_ZEROX, A_ZEROX, // d0 + A_IMPL, A_ABSY, A_IMPL, A_ABSY, A_ABSX , A_ABSX , A_ABSX , A_ABSX, + A_IMM , A_INDX, A_IMM , A_INDX, A_ZERO , A_ZERO , A_ZERO , A_ZERO, // e0 + A_IMPL, A_IMM , A_IMPL, A_IMM , A_ABS , A_ABS , A_ABS , A_ABS, + A_REL , A_INDY, A_IMPL, A_INDY, A_ZEROX, A_ZEROX, A_ZEROX, A_ZEROX, // f0 + A_IMPL, A_ABSY, A_IMPL, A_ABSY, A_ABSX , A_ABSX , A_ABSX , A_ABSX +}; + +// Chars for each mnemonic +static const char mnem_1[] = "aaabbbbbbbbbbcccccccdddeiiijjllllnopppprrrrssssssstttttt?aaaadijnlllrrsssssssss"; +static const char mnem_2[] = "dnscceimnprvvllllmppeeeonnnmsdddsorhhlloottbeeetttaasxxy?nnrscsaoaaxlrabbhhhhlr"; +static const char mnem_3[] = "cdlcsqtielkcscdivpxycxyrcxypraxyrpaapaplrisccdiaxyxyxasa?cerrpbmpsxaaaxcxasxyoe"; + +// Instruction length for each addressing mode +static const char adr_length[] = {1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 2, 2}; + + +// Prototypes +static void error(const char *s); +static void handle_abort(...); +static void init_abort(void); +static void exit_abort(void); +static bool aborted(void); + +static void read_line(void); // Scanner +static char get_char(void); +static void put_back(char c); +static enum Token get_token(void); +static enum Token get_reg_token(void); +static uint16 get_number(void); +static enum Token get_string(char *str); + +static bool expression(uint16 *number); // Parser +static bool term(uint16 *number); +static bool factor(uint16 *number); +static bool address_args(void); +static bool range_args(int def_range); +static bool instr_args(uint16 *number, char *mode); + +static void help(void); // Routines for commands +static void registers(void); +static void display_registers(void); +static void memory_dump(void); +static void ascii_dump(void); +static char conv_from_64(char c); +static void screen_dump(void); +static char conv_from_scode(char c); +static void binary_dump(void); +static void sprite_dump(void); +static void byte_to_bin(uint8 byte, char *str); +static void disassemble(void); +static int disass_line(uint16 adr, uint8 op, uint8 lo, uint8 hi); +static void assemble(void); +static char find_mnemonic(char op1, char op2, char op3); +static bool find_opcode(char mnem, char mode, uint8 *opcode); +static void mem_config(void); +static void fill(void); +static void compare(void); +static void transfer(void); +static void modify(void); +static void print_expr(void); +static void redir_output(void); +static void int_vectors(void); +static void view_state(void); +static void view_cia_state(void); +static void dump_cia_ints(uint8 i); +static void view_sid_state(void); +static void dump_sid_waveform(uint8 wave); +static void view_vic_state(void); +static void dump_spr_flags(uint8 f); +static void dump_vic_ints(uint8 i); +static void view_1541_state(void); +static void dump_via_ints(uint8 i); +static void load_data(void); +static void save_data(void); + + +/* + * Open and handle SAM + */ + +void SAM(C64 *the_c64) +{ + bool done = false; + char c; + + TheCPU = the_c64->TheCPU; + TheCPU1541 = the_c64->TheCPU1541; + TheVIC = the_c64->TheVIC; + TheSID = the_c64->TheSID; + TheCIA1 = the_c64->TheCIA1; + TheCIA2 = the_c64->TheCIA2; + + // Get CPU registers and current memory configuration + TheCPU->GetState(&R64); + TheCPU->ExtConfig = (~R64.ddr | R64.pr) & 7; + TheCPU1541->GetState(&R1541); + +#ifdef __riscos__ + Wimp_CommandWindow((int)"SAM"); +#endif + +#ifdef AMIGA + if (!(fin = fout = ferr = fopen("CON:0/0/640/480/SAM", "w+"))) + return; +#else + fin = stdin; + fout = stdout; + ferr = stdout; +#endif + + access_1541 = false; + address = R64.pc; + + fprintf(ferr, "\n *** SAM - Simple Assembler and Monitor ***\n *** Press 'h' for help ***\n\n"); + init_abort(); + display_registers(); + + while (!done) { + if (access_1541) + fprintf(ferr, "1541> "); + else + fprintf(ferr, "C64> "); + fflush(ferr); + read_line(); + while ((c = get_char()) == ' ') ; + + switch (c) { + case 'a': // Assemble + get_token(); + assemble(); + break; + + case 'b': // Binary dump + get_token(); + binary_dump(); + break; + + case 'c': // Compare + get_token(); + compare(); + break; + + case 'd': // Disassemble + get_token(); + disassemble(); + break; + + case 'e': // Interrupt vectors + int_vectors(); + break; + + case 'f': // Fill + get_token(); + fill(); + break; + + case 'h': // Help + help(); + break; + + case 'i': // ASCII dump + get_token(); + ascii_dump(); + break; + + case 'k': // Memory configuration + get_token(); + mem_config(); + break; + + case 'l': // Load data + get_token(); + load_data(); + break; + + case 'm': // Memory dump + get_token(); + memory_dump(); + break; + + case 'n': // Screen code dump + get_token(); + screen_dump(); + break; + + case 'o': // Redirect output + get_token(); + redir_output(); + break; + + case 'p': // Sprite dump + get_token(); + sprite_dump(); + break; + + case 'r': // Registers + get_reg_token(); + registers(); + break; + + case 's': // Save data + get_token(); + save_data(); + break; + + case 't': // Transfer + get_token(); + transfer(); + break; + + case 'v': // View machine state + view_state(); + break; + + case 'x': // Exit + done = true; + break; + + case ':': // Change memory + get_token(); + modify(); + break; + + case '1': // Switch to 1541 mode + access_1541 = true; + break; + + case '6': // Switch to C64 mode + access_1541 = false; + break; + + case '?': // Compute expression + get_token(); + print_expr(); + break; + + case '\n': // Blank line + break; + + default: // Unknown command + error("Unknown command"); + break; + } + } + + exit_abort(); + +#ifdef AMIGA + fclose(fin); +#endif + if (fout != ferr) + fclose(fout); + +#ifdef __riscos__ + Wimp_CommandWindow(-1); +#endif + + // Set CPU registers + TheCPU->SetState(&R64); + TheCPU1541->SetState(&R1541); +} + + +/* + * Print error message + */ + +static void error(const char *s) +{ + fprintf(ferr, "*** %s\n", s); +} + + +/* + * CTRL-C pressed? + */ + +static bool WasAborted; + +#ifdef HAVE_SIGACTION +struct sigaction my_sa; +#endif + +static void handle_abort(...) +{ + WasAborted = true; +#if !defined(HAVE_SIGACTION) && defined(HAVE_SIGNAL) +#ifdef __riscos__ + signal(SIGINT, (Handler*) handle_abort); +#else + signal(SIGINT, (sighandler_t) handle_abort); +#endif +#endif +} + +static void init_abort(void) +{ + WasAborted = false; +#if defined(HAVE_SIGACTION) + my_sa.sa_handler = (void (*)(int))handle_abort; + my_sa.sa_flags = 0; + sigemptyset(&my_sa.sa_mask); + sigaction(SIGINT, &my_sa, NULL); +#elif defined(HAVE_SIGNAL) +#ifdef __riscos__ + signal(SIGINT, (Handler*) handle_abort); +#else + signal(SIGINT, (sighandler_t) handle_abort); +#endif +#endif +} + +static void exit_abort(void) +{ +#if defined(HAVE_SIGACTION) + my_sa.sa_handler = SIG_DFL; + sigaction(SIGINT, &my_sa, NULL); +#elif defined(HAVE_SIGNAL) + signal(SIGINT, SIG_DFL); +#endif +} + +static bool aborted(void) +{ + bool ret = WasAborted; + + WasAborted = false; + return ret; +} + + +/* + * Read a line from the keyboard + */ + +static void read_line(void) +{ +#ifdef __riscos__ + OS_ReadLine(in_ptr = input, INPUT_LENGTH, 0, 255, 0); +#else + fgets(in_ptr = input, INPUT_LENGTH, fin); +#endif +} + + +/* + * Read a character from the input line + */ + +static char get_char(void) +{ + return *in_ptr++; +} + + +/* + * Stuff back a character into the input line + */ + +static void put_back(char c) +{ + *(--in_ptr) = c; +} + + +/* + * Scanner: Get a token from the input line + */ + +static enum Token get_token(void) +{ + char c; + + // Skip spaces + while ((c = get_char()) == ' ') ; + + switch (c) { + case '\n': + return the_token = T_END; + case '(': + return the_token = T_LPAREN; + case ')': + return the_token = T_RPAREN; + case '+': + return the_token = T_ADD; + case '-': + return the_token = T_SUB; + case '*': + return the_token = T_MUL; + case '/': + return the_token = T_DIV; + case ',': + return the_token = T_COMMA; + case '#': + return the_token = T_IMMED; + case 'x': + return the_token = T_X; + case 'y': + return the_token = T_Y; + case 'p': + if (get_char() == 'c') + return the_token = T_PC; + else { + error("Unrecognized token"); + return the_token = T_NULL; + } + case 's': + if (get_char() == 'p') + return the_token = T_SP; + else { + error("Unrecognized token"); + return the_token = T_NULL; + } + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': + put_back(c); + the_number = get_number(); + return the_token = T_NUMBER; + case '"': + return the_token = get_string(the_string); + default: + error("Unrecognized token"); + return the_token = T_NULL; + } +} + +static enum Token get_reg_token(void) +{ + char c; + + // Skip spaces + while ((c = get_char()) == ' ') ; + + switch (c) { + case '\n': + return the_token = T_END; + case 'a': + return the_token = T_A; + case 'd': + if (get_char() == 'r') + return the_token = T_DR; + else { + error("Unrecognized token"); + return the_token = T_NULL; + } + case 'p': + if ((c = get_char()) == 'c') + return the_token = T_PC; + else if (c == 'r') + return the_token = T_PR; + else { + error("Unrecognized token"); + return the_token = T_NULL; + } + case 's': + if (get_char() == 'p') + return the_token = T_SP; + else { + error("Unrecognized token"); + return the_token = T_NULL; + } + case 'x': + return the_token = T_X; + case 'y': + return the_token = T_Y; + default: + error("Unrecognized token"); + return the_token = T_NULL; + } +} + +static uint16 get_number(void) +{ + char c; + uint16 i = 0; + + while (((c = get_char()) >= '0') && (c <= '9') || (c >= 'a') && (c <= 'f')) + if (c < 'a') + i = (i << 4) + (c - '0'); + else + i = (i << 4) + (c - 'a' + 10); + + put_back(c); + return i; +} + +static enum Token get_string(char *str) +{ + char c; + + while ((c = get_char()) != '\n') { + if (c == '"') { + *str = 0; + return T_STRING; + } + *str++ = c; + } + + error("Unterminated string"); + return T_NULL; +} + + +/* + * expression = term {(ADD | SUB) term} + * true: OK, false: Error + */ + +static bool expression(uint16 *number) +{ + uint16 accu, trm; + + if (!term(&accu)) + return false; + + for (;;) + switch (the_token) { + case T_ADD: + get_token(); + if (!term(&trm)) + return false; + accu += trm; + break; + + case T_SUB: + get_token(); + if (!term(&trm)) + return false; + accu -= trm; + break; + + default: + *number = accu; + return true; + } +} + + +/* + * term = factor {(MUL | DIV) factor} + * true: OK, false: Error + */ + +static bool term(uint16 *number) +{ + uint16 accu, fact; + + if (!factor(&accu)) + return false; + + for (;;) + switch (the_token) { + case T_MUL: + get_token(); + if (!factor(&fact)) + return false; + accu *= fact; + break; + + case T_DIV: + get_token(); + if (!factor(&fact)) + return false; + if (fact == 0) { + error("Division by 0"); + return false; + } + accu /= fact; + break; + + default: + *number = accu; + return true; + } +} + + +/* + * factor = NUMBER | PC | SP | LPAREN expression RPAREN + * true: OK, false: Error + */ + +static bool factor(uint16 *number) +{ + switch (the_token) { + case T_NUMBER: + *number = the_number; + get_token(); + return true; + + case T_PC: + get_token(); + *number = access_1541 ? R1541.pc : R64.pc; + return true; + + case T_SP: + get_token(); + *number = access_1541 ? R1541.sp : R64.sp; + return true; + + case T_LPAREN: + get_token(); + if (expression(number)) + if (the_token == T_RPAREN) { + get_token(); + return true; + } else { + error("Missing ')'"); + return false; + } + else { + error("Error in expression"); + return false; + } + + case T_END: + error("Required argument missing"); + return false; + + default: + error("'pc', 'sp', '(' or number expected"); + return false; + } +} + + +/* + * address_args = [expression] END + * + * Read start to "address" + * + * true: OK, false: Error + */ + +static bool address_args(void) +{ + if (the_token == T_END) + return true; + else { + if (!expression(&address)) + return false; + return the_token == T_END; + } +} + + +/* + * range_args = [expression] [[COMMA] expression] END + * + * Read start address to "address", end address to "end_address" + * + * true: OK, false: Error + */ + +static bool range_args(int def_range) +{ + end_address = address + def_range; + + if (the_token == T_END) + return true; + else { + if (!expression(&address)) + return false; + end_address = address + def_range; + if (the_token == T_END) + return true; + else { + if (the_token == T_COMMA) get_token(); + if (!expression(&end_address)) + return false; + return the_token == T_END; + } + } +} + + +/* + * instr_args = END + * | IMMED NUMBER END + * | NUMBER [COMMA (X | Y)] END + * | LPAREN NUMBER (RPAREN [COMMA Y] | COMMA X RPAREN) END + * + * Read arguments of a 6510 instruction, determine address and addressing mode + * + * true: OK, false: Error + */ + +static bool instr_args(uint16 *number, char *mode) +{ + switch (the_token) { + + case T_END: + *mode = A_IMPL; + return true; + + case T_IMMED: + get_token(); + if (the_token == T_NUMBER) { + *number = the_number; + *mode = A_IMM; + get_token(); + return the_token == T_END; + } else { + error("Number expected"); + return false; + } + + case T_NUMBER: + *number = the_number; + get_token(); + switch (the_token) { + + case T_END: + if (*number < 0x100) + *mode = A_ZERO; + else + *mode = A_ABS; + return true; + + case T_COMMA: + get_token(); + switch (the_token) { + + case T_X: + get_token(); + if (*number < 0x100) + *mode = A_ZEROX; + else + *mode = A_ABSX; + return the_token == T_END; + + case T_Y: + get_token(); + if (*number < 0x100) + *mode = A_ZEROY; + else + *mode = A_ABSY; + return the_token == T_END; + + default: + error("Illegal index register"); + return false; + } + + default: + return false; + } + + case T_LPAREN: + get_token(); + if (the_token == T_NUMBER) { + *number = the_number; + get_token(); + switch (the_token) { + + case T_RPAREN: + get_token(); + switch (the_token) { + + case T_END: + *mode = A_IND; + return true; + + case T_COMMA: + get_token(); + if (the_token == T_Y) { + *mode = A_INDY; + get_token(); + return the_token == T_END; + } else { + error("Only 'y' index register allowed"); + return false; + } + + default: + error("Illegal characters after ')'"); + return false; + } + + case T_COMMA: + get_token(); + if (the_token == T_X) { + get_token(); + if (the_token == T_RPAREN) { + *mode = A_INDX; + get_token(); + return the_token == T_END; + } else { + error("')' expected"); + return false; + } + } else { + error("Only 'x' index register allowed"); + return false; + } + + default: + error("')' or ',' expected"); + return false; + } + } else { + error("Number expected"); + return false; + } + + default: + error("'(', '#' or number expected"); + return false; + } +} + + +/* + * Display help + * h + */ + +static void help(void) +{ + fprintf(fout, "a [start] Assemble\n" + "b [start] [end] Binary dump\n" + "c start end dest Compare memory\n" + "d [start] [end] Disassemble\n" + "e Show interrupt vectors\n" + "f start end byte Fill memory\n" + "i [start] [end] ASCII/PETSCII dump\n" + "k [config] Show/set C64 memory configuration\n" + "l start \"file\" Load data\n" + "m [start] [end] Memory dump\n" + "n [start] [end] Screen code dump\n" + "o [\"file\"] Redirect output\n" + "p [start] [end] Sprite dump\n" + "r [reg value] Show/set CPU registers\n" + "s start end \"file\" Save data\n" + "t start end dest Transfer memory\n" + "vc1 View CIA 1 state\n" + "vc2 View CIA 2 state\n" + "vf View 1541 state\n" + "vs View SID state\n" + "vv View VIC state\n" + "x Return to Frodo\n" + ": addr {byte} Modify memory\n" + "1541 Switch to 1541\n" + "64 Switch to C64\n" + "? expression Calculate expression\n"); +} + + +/* + * Display/change 6510 registers + * r [reg value] + */ + +static void registers(void) +{ + enum Token the_reg; + uint16 value; + + if (the_token != T_END) + switch (the_reg = the_token) { + case T_A: + case T_X: + case T_Y: + case T_PC: + case T_SP: + case T_DR: + case T_PR: + get_token(); + if (!expression(&value)) + return; + + switch (the_reg) { + case T_A: + if (access_1541) + R1541.a = value; + else + R64.a = value; + break; + case T_X: + if (access_1541) + R1541.x = value; + else + R64.x = value; + break; + case T_Y: + if (access_1541) + R1541.y = value; + else + R64.y = value; + break; + case T_PC: + if (access_1541) + R1541.pc = value; + else + R64.pc = value; + break; + case T_SP: + if (access_1541) + R1541.sp = (value & 0xff) | 0x0100; + else + R64.sp = (value & 0xff) | 0x0100; + break; + case T_DR: + if (!access_1541) + R64.ddr = value; + break; + case T_PR: + if (!access_1541) + R64.pr = value; + break; + default: + break; + } + break; + + default: + return; + } + + display_registers(); +} + +static void display_registers(void) +{ + if (access_1541) { + fprintf(fout, " PC A X Y SP NVDIZC Instruction\n"); + fprintf(fout, "%04lx %02lx %02lx %02lx %04lx %c%c%c%c%c%c ", + R1541.pc, R1541.a, R1541.x, R1541.y, R1541.sp, + R1541.p & 0x80 ? '1' : '0', R1541.p & 0x40 ? '1' : '0', R1541.p & 0x08 ? '1' : '0', + R1541.p & 0x04 ? '1' : '0', R1541.p & 0x02 ? '1' : '0', R1541.p & 0x01 ? '1' : '0'); + disass_line(R1541.pc, SAMReadByte(R1541.pc), SAMReadByte(R1541.pc+1), SAMReadByte(R1541.pc+2)); + } else { + fprintf(fout, " PC A X Y SP DR PR NVDIZC Instruction\n"); + fprintf(fout, "%04lx %02lx %02lx %02lx %04lx %02lx %02lx %c%c%c%c%c%c ", + R64.pc, R64.a, R64.x, R64.y, R64.sp, R64.ddr, R64.pr, + R64.p & 0x80 ? '1' : '0', R64.p & 0x40 ? '1' : '0', R64.p & 0x08 ? '1' : '0', + R64.p & 0x04 ? '1' : '0', R64.p & 0x02 ? '1' : '0', R64.p & 0x01 ? '1' : '0'); + disass_line(R64.pc, SAMReadByte(R64.pc), SAMReadByte(R64.pc+1), SAMReadByte(R64.pc+2)); + } +} + + +/* + * Memory dump + * m [start] [end] + */ + +#define MEMDUMP_BPL 16 // Bytes per line + +static void memory_dump(void) +{ + bool done = false; + short i; + uint8 mem[MEMDUMP_BPL + 2]; + uint8 byte; + + mem[MEMDUMP_BPL] = 0; + + if (!range_args(16 * MEMDUMP_BPL - 1)) // 16 lines unless end address specified + return; + + do { + fprintf(fout, "%04lx:", address); + for (i=0; i= ' ') && (byte <= '~')) + mem[i] = conv_from_64(byte); + else + mem[i] = '.'; + } + fprintf(fout, " '%s'\n", mem); + } while (!done && !aborted()); +} + + +/* + * ASCII dump + * i [start] [end] + */ + +#define ASCIIDUMP_BPL 64 // Bytes per line + +static void ascii_dump(void) +{ + bool done = false; + short i; + uint8 mem[ASCIIDUMP_BPL + 2]; + uint8 byte; + + mem[ASCIIDUMP_BPL] = 0; + + if (!range_args(16 * ASCIIDUMP_BPL - 1)) // 16 lines unless end address specified + return; + + do { + fprintf(fout, "%04lx:", address); + for (i=0; i= ' ') && (byte <= '~')) + mem[i] = conv_from_64(byte); + else + mem[i] = '.'; + } + fprintf(fout, " '%s'\n", mem); + } while (!done && !aborted()); +} + + +/* + * Convert PETSCII->ASCII + */ + +static char conv_from_64(char c) +{ + if ((c >= 'A') && (c <= 'Z') || (c >= 'a') && (c <= 'z')) + return c ^ 0x20; + else + return c; +} + + +/* + * Screen code dump + * n [start] [end] + */ + +#define SCRDUMP_BPL 64 // Bytes per line + +static void screen_dump(void) +{ + bool done = false; + short i; + uint8 mem[SCRDUMP_BPL + 2]; + uint8 byte; + + mem[SCRDUMP_BPL] = 0; + + if (!range_args(16 * SCRDUMP_BPL - 1)) // 16 Zeilen unless end address specified + return; + + do { + fprintf(fout, "%04lx:", address); + for (i=0; iASCII + */ + +static char conv_from_scode(char c) +{ + c &= 0x7f; + + if (c <= 31) + return c + 64; + else + if (c >= 64) + return c + 32; + else + return c; +} + + +/* + * Binary dump + * b [start] [end] + */ + +static void binary_dump(void) +{ + bool done = false; + char bin[10]; + + bin[8] = 0; + + if (!range_args(7)) // 8 lines unless end address specified + return; + + do { + if (address == end_address) done = true; + + byte_to_bin(SAMReadByte(address), bin); + fprintf(fout, "%04lx: %s\n", address++, bin); + } while (!done && !aborted()); +} + + +/* + * Sprite data dump + * p [start] [end] + */ + +static void sprite_dump(void) +{ + bool done = false; + short i; + char bin[10]; + + bin[8] = 0; + + if (!range_args(21 * 3 - 1)) // 21 lines unless end address specified + return; + + do { + fprintf(fout, "%04lx: ", address); + for (i=0; i<3; i++, address++) { + if (address == end_address) done = true; + + byte_to_bin(SAMReadByte(address), bin); + fprintf(fout, "%s", bin); + } + fputc('\n', fout); + } while (!done && !aborted()); +} + + +/* + * Convert byte to binary representation + */ + +static void byte_to_bin(uint8 byte, char *str) +{ + short i; + + for (i=0; i<8; i++, byte<<=1) + if (byte & 0x80) + str[i] = '#'; + else + str[i] = '.'; +} + + +/* + * Disassemble + * d [start] [end] + */ + +static void disassemble(void) +{ + bool done = false; + short i; + uint8 op[3]; + uint16 adr; + + if (!range_args(31)) // 32 bytes unless end address specified + return; + + do { + fprintf(fout, "%04lx:", adr = address); + for (i=0; i<3; i++, adr++) { + if (adr == end_address) done = true; + op[i] = SAMReadByte(adr); + } + address += disass_line(address, op[0], op[1], op[2]); + } while (!done && !aborted()); +} + + +/* + * Disassemble one instruction, return length + */ + +static int disass_line(uint16 adr, uint8 op, uint8 lo, uint8 hi) +{ + char mode = adr_mode[op], mnem = mnemonic[op]; + + // Display instruction bytes in hex + switch (adr_length[mode]) { + case 1: + fprintf(fout, " %02lx ", op); + break; + + case 2: + fprintf(fout, " %02lx %02lx ", op, lo); + break; + + case 3: + fprintf(fout, " %02lx %02lx %02lx ", op, lo, hi); + break; + } + + // Tag undocumented opcodes with an asterisk + if (mnem > M_ILLEGAL) + fputc('*', fout); + else + fputc(' ', fout); + + // Print mnemonic + fprintf(fout, "%c%c%c ", mnem_1[mnem], mnem_2[mnem], mnem_3[mnem]); + + // Pring argument + switch (mode) { + case A_IMPL: + break; + + case A_ACCU: + fprintf(fout, "a"); + break; + + case A_IMM: + fprintf(fout, "#%02lx", lo); + break; + + case A_REL: + fprintf(fout, "%04lx", ((adr + 2) + (int8)lo) & 0xffff); + break; + + case A_ZERO: + fprintf(fout, "%02lx", lo); + break; + + case A_ZEROX: + fprintf(fout, "%02lx,x", lo); + break; + + case A_ZEROY: + fprintf(fout, "%02lx,y", lo); + break; + + case A_ABS: + fprintf(fout, "%04lx", (hi << 8) | lo); + break; + + case A_ABSX: + fprintf(fout, "%04lx,x", (hi << 8) | lo); + break; + + case A_ABSY: + fprintf(fout, "%04lx,y", (hi << 8) | lo); + break; + + case A_IND: + fprintf(fout, "(%04lx)", (hi << 8) | lo); + break; + + case A_INDX: + fprintf(fout, "(%02lx,x)", lo); + break; + + case A_INDY: + fprintf(fout, "(%02lx),y", lo); + break; + } + + fputc('\n', fout); + return adr_length[mode]; +} + + +/* + * Assemble + * a [start] + */ + +static void assemble(void) +{ + bool done = false; + char c1, c2, c3; + char mnem, mode; + uint8 opcode; + uint16 arg; + int16 rel; + + // Read parameters + if (!address_args()) + return; + + do { + fprintf(fout, "%04lx> ", address); + fflush(ferr); + read_line(); + + c1 = get_char(); + c2 = get_char(); + c3 = get_char(); + + if (c1 != '\n') { + + if ((mnem = find_mnemonic(c1, c2, c3)) != M_ILLEGAL) { + + get_token(); + if (instr_args(&arg, &mode)) { + + // Convert A_IMPL -> A_ACCU if necessary + if ((mode == A_IMPL) && find_opcode(mnem, A_ACCU, &opcode)) + mode = A_ACCU; + + // Handle relative addressing seperately + if (((mode == A_ABS) || (mode == A_ZERO)) && find_opcode(mnem, A_REL, &opcode)) { + mode = A_REL; + rel = arg - (address + 2) & 0xffff; + if ((rel < -128) || (rel > 127)) { + error("Branch too long"); + continue; + } else + arg = rel & 0xff; + } + + if (find_opcode(mnem, mode, &opcode)) { + + // Print disassembled line + fprintf(fout, "\v%04lx:", address); + disass_line(address, opcode, arg & 0xff, arg >> 8); + + switch (adr_length[mode]) { + case 1: + SAMWriteByte(address++, opcode); + break; + + case 2: + SAMWriteByte(address++, opcode); + SAMWriteByte(address++, arg); + break; + + case 3: + SAMWriteByte(address++, opcode); + SAMWriteByte(address++, arg & 0xff); + SAMWriteByte(address++, arg >> 8); + break; + + default: + error("Internal error"); + break; + } + + } else + error("Addressing mode not supported by instruction"); + + } else + error("Unrecognized addressing mode"); + + } else + error("Unknown instruction"); + + } else // Input is terminated with a blank line + done = true; + } while (!done); +} + + +/* + * Find mnemonic code to three letters + * M_ILLEGAL: No matching mnemonic found + */ + +static char find_mnemonic(char op1, char op2, char op3) +{ + int i; + + for (i=0; iExtConfig = con; + else + con = TheCPU->ExtConfig; + + fprintf(fout, "Configuration: %ld\n", con & 7); + fprintf(fout, "A000-BFFF: %s\n", (con & 3) == 3 ? "Basic" : "RAM"); + fprintf(fout, "D000-DFFF: %s\n", (con & 3) ? ((con & 4) ? "I/O" : "Char") : "RAM"); + fprintf(fout, "E000-FFFF: %s\n", (con & 2) ? "Kernal" : "RAM"); +} + + +/* + * Fill + * f start end byte + */ + +static void fill(void) +{ + bool done = false; + uint16 adr, end_adr, value; + + if (!expression(&adr)) + return; + if (!expression(&end_adr)) + return; + if (!expression(&value)) + return; + + do { + if (adr == end_adr) done = true; + + SAMWriteByte(adr++, value); + } while (!done); +} + + +/* + * Compare + * c start end dest + */ + +static void compare(void) +{ + bool done = false; + uint16 adr, end_adr, dest; + int num = 0; + + if (!expression(&adr)) + return; + if (!expression(&end_adr)) + return; + if (!expression(&dest)) + return; + + do { + if (adr == end_adr) done = true; + + if (SAMReadByte(adr) != SAMReadByte(dest)) { + fprintf(fout, "%04lx ", adr); + num++; + if (!(num & 7)) + fputc('\n', fout); + } + adr++; dest++; + } while (!done && !aborted()); + + if (num & 7) + fputc('\n', fout); + fprintf(fout, "%ld byte(s) different\n", num); +} + + +/* + * Transfer memory + * t start end dest + */ + +static void transfer(void) +{ + bool done = false; + uint16 adr, end_adr, dest; + + if (!expression(&adr)) + return; + if (!expression(&end_adr)) + return; + if (!expression(&dest)) + return; + + if (dest < adr) + do { + if (adr == end_adr) done = true; + SAMWriteByte(dest++, SAMReadByte(adr++)); + } while (!done); + else { + dest += end_adr - adr; + do { + if (adr == end_adr) done = true; + SAMWriteByte(dest--, SAMReadByte(end_adr--)); + } while (!done); + } +} + + +/* + * Change memory + * : addr {byte} + */ + +static void modify(void) +{ + uint16 adr, val; + + if (!expression(&adr)) + return; + + while (the_token != T_END) + if (expression(&val)) + SAMWriteByte(adr++, val); + else + return; +} + + +/* + * Compute and display expression + * ? expression + */ + +static void print_expr(void) +{ + uint16 val; + + if (!expression(&val)) + return; + + fprintf(fout, "Hex: %04lx\nDec: %lu\n", val, val); +} + + +/* + * Redirect output + * o [file] + */ + +static void redir_output(void) +{ + // Close old file + if (fout != ferr) { + fclose(fout); + fout = ferr; + return; + } + + // No argument given? + if (the_token == T_END) + return; + + // Otherwise open file + if (the_token == T_STRING) { + if (!(fout = fopen(the_string, "w"))) + error("Unable to open file"); + } else + error("'\"' around file name expected"); +} + + +/* + * Display interrupt vectors + */ + +static void int_vectors(void) +{ + fprintf(fout, " IRQ BRK NMI\n"); + fprintf(fout, "%d : %04lx %04lx %04lx\n", + access_1541 ? 6502 : 6510, + SAMReadByte(0xffff) << 8 | SAMReadByte(0xfffe), + SAMReadByte(0xffff) << 8 | SAMReadByte(0xfffe), + SAMReadByte(0xfffb) << 8 | SAMReadByte(0xfffa)); + + if (!access_1541 && TheCPU->ExtConfig & 2) + fprintf(fout, "Kernal: %04lx %04lx %04lx\n", + SAMReadByte(0x0315) << 8 | SAMReadByte(0x0314), + SAMReadByte(0x0317) << 8 | SAMReadByte(0x0316), + SAMReadByte(0x0319) << 8 | SAMReadByte(0x0318)); +} + + +/* + * Display state of custom chips + */ + +static void view_state(void) +{ + switch (get_char()) { + case 'c': // CIA + view_cia_state(); + break; + + case 's': // SID + view_sid_state(); + break; + + case 'v': // VIC + view_vic_state(); + break; + + case 'f': // Floppy + view_1541_state(); + break; + + default: + error("Unknown command"); + break; + } +} + +static void view_cia_state(void) +{ + MOS6526State cs; + + switch (get_char()) { + case '1': + TheCIA1->GetState(&cs); + break; + case '2': + TheCIA2->GetState(&cs); + break; + default: + error("Unknown command"); + return; + } + + fprintf(fout, "Timer A : %s\n", cs.cra & 1 ? "On" : "Off"); + fprintf(fout, " Counter : %04lx Latch: %04lx\n", (cs.ta_hi << 8) | cs.ta_lo, cs.latcha); + fprintf(fout, " Run mode: %s\n", cs.cra & 8 ? "One-shot" : "Continuous"); + fprintf(fout, " Input : %s\n", cs.cra & 0x20 ? "CNT" : "Phi2"); + fprintf(fout, " Output : "); + if (cs.cra & 2) + if (cs.cra & 4) + fprintf(fout, "PB6 Toggle\n\n"); + else + fprintf(fout, "PB6 Pulse\n\n"); + else + fprintf(fout, "None\n\n"); + + fprintf(fout, "Timer B : %s\n", cs.crb & 1 ? "On" : "Off"); + fprintf(fout, " Counter : %04lx Latch: %04lx\n", (cs.tb_hi << 8) | cs.tb_lo, cs.latchb); + fprintf(fout, " Run mode: %s\n", cs.crb & 8 ? "One-shot" : "Continuous"); + fprintf(fout, " Input : "); + if (cs.crb & 0x40) + if (cs.crb & 0x20) + fprintf(fout, "Timer A underflow (CNT high)\n"); + else + fprintf(fout, "Timer A underflow\n"); + else + if (cs.crb & 0x20) + fprintf(fout, "CNT\n"); + else + fprintf(fout, "Phi2\n"); + fprintf(fout, " Output : "); + if (cs.crb & 2) + if (cs.crb & 4) + fprintf(fout, "PB7 Toggle\n\n"); + else + fprintf(fout, "PB7 Pulse\n\n"); + else + fprintf(fout, "None\n\n"); + + fprintf(fout, "TOD : %lx%lx:%lx%lx:%lx%lx.%lx %s\n", + (cs.tod_hr >> 4) & 1, cs.tod_hr & 0x0f, + (cs.tod_min >> 4) & 7, cs.tod_min & 0x0f, + (cs.tod_sec >> 4) & 7, cs.tod_sec & 0x0f, + cs.tod_10ths & 0x0f, cs.tod_hr & 0x80 ? "PM" : "AM"); + fprintf(fout, "Alarm : %lx%lx:%lx%lx:%lx%lx.%lx %s\n", + (cs.alm_hr >> 4) & 1, cs.alm_hr & 0x0f, + (cs.alm_min >> 4) & 7, cs.alm_min & 0x0f, + (cs.alm_sec >> 4) & 7, cs.alm_sec & 0x0f, + cs.alm_10ths & 0x0f, cs.alm_hr & 0x80 ? "PM" : "AM"); + fprintf(fout, "TOD input : %s\n", cs.cra & 0x80 ? "50Hz" : "60Hz"); + fprintf(fout, "Write to : %s registers\n\n", cs.crb & 0x80 ? "Alarm" : "TOD"); + + fprintf(fout, "Serial data : %02lx\n", cs.sdr); + fprintf(fout, "Serial mode : %s\n\n", cs.cra & 0x40 ? "Output" : "Input"); + + fprintf(fout, "Pending int.: "); + dump_cia_ints(cs.int_data); + fprintf(fout, "Enabled int.: "); + dump_cia_ints(cs.int_mask); +} + +static void dump_cia_ints(uint8 i) +{ + if (i & 0x1f) { + if (i & 1) fprintf(fout, "TA "); + if (i & 2) fprintf(fout, "TB "); + if (i & 4) fprintf(fout, "Alarm "); + if (i & 8) fprintf(fout, "Serial "); + if (i & 0x10) fprintf(fout, "Flag"); + } else + fprintf(fout, "None"); + fputc('\n', fout); +} + +static void view_sid_state(void) +{ + MOS6581State ss; + + TheSID->GetState(&ss); + + fprintf(fout, "Voice 1\n"); + fprintf(fout, " Frequency : %04lx\n", (ss.freq_hi_1 << 8) | ss.freq_lo_1); + fprintf(fout, " Pulse Width: %04lx\n", ((ss.pw_hi_1 & 0x0f) << 8) | ss.pw_lo_1); + fprintf(fout, " Env. (ADSR): %lx %lx %lx %lx\n", ss.AD_1 >> 4, ss.AD_1 & 0x0f, ss.SR_1 >> 4, ss.SR_1 & 0x0f); + fprintf(fout, " Waveform : "); + dump_sid_waveform(ss.ctrl_1); + fprintf(fout, " Gate : %s Ring mod.: %s\n", ss.ctrl_1 & 0x01 ? "On " : "Off", ss.ctrl_1 & 0x04 ? "On" : "Off"); + fprintf(fout, " Test bit : %s Synchron.: %s\n", ss.ctrl_1 & 0x08 ? "On " : "Off", ss.ctrl_1 & 0x02 ? "On" : "Off"); + fprintf(fout, " Filter : %s\n", ss.res_filt & 0x01 ? "On" : "Off"); + + fprintf(fout, "\nVoice 2\n"); + fprintf(fout, " Frequency : %04lx\n", (ss.freq_hi_2 << 8) | ss.freq_lo_2); + fprintf(fout, " Pulse Width: %04lx\n", ((ss.pw_hi_2 & 0x0f) << 8) | ss.pw_lo_2); + fprintf(fout, " Env. (ADSR): %lx %lx %lx %lx\n", ss.AD_2 >> 4, ss.AD_2 & 0x0f, ss.SR_2 >> 4, ss.SR_2 & 0x0f); + fprintf(fout, " Waveform : "); + dump_sid_waveform(ss.ctrl_2); + fprintf(fout, " Gate : %s Ring mod.: %s\n", ss.ctrl_2 & 0x01 ? "On " : "Off", ss.ctrl_2 & 0x04 ? "On" : "Off"); + fprintf(fout, " Test bit : %s Synchron.: %s\n", ss.ctrl_2 & 0x08 ? "On " : "Off", ss.ctrl_2 & 0x02 ? "On" : "Off"); + fprintf(fout, " Filter : %s\n", ss.res_filt & 0x02 ? "On" : "Off"); + + fprintf(fout, "\nVoice 3\n"); + fprintf(fout, " Frequency : %04lx\n", (ss.freq_hi_3 << 8) | ss.freq_lo_3); + fprintf(fout, " Pulse Width: %04lx\n", ((ss.pw_hi_3 & 0x0f) << 8) | ss.pw_lo_3); + fprintf(fout, " Env. (ADSR): %lx %lx %lx %lx\n", ss.AD_3 >> 4, ss.AD_3 & 0x0f, ss.SR_3 >> 4, ss.SR_3 & 0x0f); + fprintf(fout, " Waveform : "); + dump_sid_waveform(ss.ctrl_3); + fprintf(fout, " Gate : %s Ring mod.: %s\n", ss.ctrl_3 & 0x01 ? "On " : "Off", ss.ctrl_3 & 0x04 ? "On" : "Off"); + fprintf(fout, " Test bit : %s Synchron.: %s\n", ss.ctrl_3 & 0x08 ? "On " : "Off", ss.ctrl_3 & 0x02 ? "On" : "Off"); + fprintf(fout, " Filter : %s Mute : %s\n", ss.res_filt & 0x04 ? "On" : "Off", ss.mode_vol & 0x80 ? "Yes" : "No"); + + fprintf(fout, "\nFilters/Volume\n"); + fprintf(fout, " Frequency: %04lx\n", (ss.fc_hi << 3) | (ss.fc_lo & 0x07)); + fprintf(fout, " Resonance: %lx\n", ss.res_filt >> 4); + fprintf(fout, " Mode : "); + if (ss.mode_vol & 0x70) { + if (ss.mode_vol & 0x10) fprintf(fout, "Low-pass "); + if (ss.mode_vol & 0x20) fprintf(fout, "Band-pass "); + if (ss.mode_vol & 0x40) fprintf(fout, "High-pass"); + } else + fprintf(fout, "None"); + fprintf(fout, "\n Volume : %lx\n", ss.mode_vol & 0x0f); +} + +static void dump_sid_waveform(uint8 wave) +{ + if (wave & 0xf0) { + if (wave & 0x10) fprintf(fout, "Triangle "); + if (wave & 0x20) fprintf(fout, "Sawtooth "); + if (wave & 0x40) fprintf(fout, "Rectangle "); + if (wave & 0x80) fprintf(fout, "Noise"); + } else + fprintf(fout, "None"); + fputc('\n', fout); +} + +static void view_vic_state(void) +{ + MOS6569State vs; + short i; + + TheVIC->GetState(&vs); + + fprintf(fout, "Raster line : %04lx\n", vs.raster | ((vs.ctrl1 & 0x80) << 1)); + fprintf(fout, "IRQ raster line : %04lx\n\n", vs.irq_raster); + + fprintf(fout, "X scroll : %ld\n", vs.ctrl2 & 7); + fprintf(fout, "Y scroll : %ld\n", vs.ctrl1 & 7); + fprintf(fout, "Horizontal border : %ld columns\n", vs.ctrl2 & 8 ? 40 : 38); + fprintf(fout, "Vertical border : %ld rows\n\n", vs.ctrl1 & 8 ? 25 : 24); + + fprintf(fout, "Display mode : "); + switch (((vs.ctrl1 >> 4) & 6) | ((vs.ctrl2 >> 4) & 1)) { + case 0: + fprintf(fout, "Standard text\n"); + break; + case 1: + fprintf(fout, "Multicolor text\n"); + break; + case 2: + fprintf(fout, "Standard bitmap\n"); + break; + case 3: + fprintf(fout, "Multicolor bitmap\n"); + break; + case 4: + fprintf(fout, "ECM text\n"); + break; + case 5: + fprintf(fout, "Invalid text (ECM+MCM)\n"); + break; + case 6: + fprintf(fout, "Invalid bitmap (ECM+BMM)\n"); + break; + case 7: + fprintf(fout, "Invalid bitmap (ECM+BMM+ECM)\n"); + break; + } + fprintf(fout, "Sequencer state : %s\n", vs.display_state ? "Display" : "Idle"); + fprintf(fout, "Bad line state : %s\n", vs.bad_line ? "Yes" : "No"); + fprintf(fout, "Bad lines enabled : %s\n", vs.bad_line_enable ? "Yes" : "No"); + fprintf(fout, "Video counter : %04lx\n", vs.vc); + fprintf(fout, "Video counter base: %04lx\n", vs.vc_base); + fprintf(fout, "Row counter : %ld\n\n", vs.rc); + + fprintf(fout, "VIC bank : %04lx-%04lx\n", vs.bank_base, vs.bank_base + 0x3fff); + fprintf(fout, "Video matrix base : %04lx\n", vs.matrix_base); + fprintf(fout, "Character base : %04lx\n", vs.char_base); + fprintf(fout, "Bitmap base : %04lx\n\n", vs.bitmap_base); + + fprintf(fout, " Spr.0 Spr.1 Spr.2 Spr.3 Spr.4 Spr.5 Spr.6 Spr.7\n"); + fprintf(fout, "Enabled: "); dump_spr_flags(vs.me); + fprintf(fout, "Data : %04lx %04lx %04lx %04lx %04lx %04lx %04lx %04lx\n", + vs.sprite_base[0], vs.sprite_base[1], vs.sprite_base[2], vs.sprite_base[3], + vs.sprite_base[4], vs.sprite_base[5], vs.sprite_base[6], vs.sprite_base[7]); + fprintf(fout, "MC : %02lx %02lx %02lx %02lx %02lx %02lx %02lx %02lx\n", + vs.mc[0], vs.mc[1], vs.mc[2], vs.mc[3], vs.mc[4], vs.mc[5], vs.mc[6], vs.mc[7]); + + fprintf(fout, "Mode : "); + for (i=0; i<8; i++) + if (vs.mmc & (1< + +#include "SID.h" +#include "Prefs.h" + +#ifdef __BEOS__ +#include +#endif + +#ifdef AMIGA +#include +#include +#include +#define USE_FIXPOINT_MATHS +#define FIXPOINT_PREC 16 // number of fractional bits used in fixpoint representation +#define PRECOMPUTE_RESONANCE +#define ldSINTAB 9 // size of sinus table (0 to 90 degrees) +#endif + +#ifdef SUN +extern "C" { + #include +} +#endif + +#ifdef __hpux +extern "C" { + #include +} +#endif + +#ifdef __mac__ +#include +#define M_PI 3.14159265358979323846 +#endif + +#ifdef WIN32 +class DigitalPlayer; +#endif + +#ifdef __riscos__ +#include "ROLib.h" +# ifndef M_PI +# define M_PI 3.14159265358979323846 +# endif +#define USE_FIXPOINT_MATHS +#define FIXPOINT_PREC 16 // number of fractional bits used in fixpoint representation +#define PRECOMPUTE_RESONANCE +#define ldSINTAB 9 // size of sinus table (0 to 90 degrees) +#endif + + +#ifdef USE_FIXPOINT_MATHS +#include "FixPoint.h" +#endif + + +/* + * Resonance frequency polynomials + */ + +#define CALC_RESONANCE_LP(f) (227.755\ + - 1.7635 * f\ + - 0.0176385 * f * f\ + + 0.00333484 * f * f * f\ + - 9.05683E-6 * f * f * f * f) + +#define CALC_RESONANCE_HP(f) (366.374\ + - 14.0052 * f\ + + 0.603212 * f * f\ + - 0.000880196 * f * f * f) + + +/* + * Random number generator for noise waveform + */ + +static uint8 sid_random(void); +static uint8 sid_random(void) +{ + static uint32 seed = 1; + seed = seed * 1103515245 + 12345; + return seed >> 16; +} + + +/* + * Constructor + */ + +MOS6581::MOS6581(C64 *c64) : the_c64(c64) +{ + the_renderer = NULL; + for (int i=0; i<32; i++) + regs[i] = 0; + + // Open the renderer + open_close_renderer(SIDTYPE_NONE, ThePrefs.SIDType); +} + + +/* + * Destructor + */ + +MOS6581::~MOS6581() +{ + // Close the renderer + open_close_renderer(ThePrefs.SIDType, SIDTYPE_NONE); +} + + +/* + * Reset the SID + */ + +void MOS6581::Reset(void) +{ + for (int i=0; i<32; i++) + regs[i] = 0; + last_sid_byte = 0; + + // Reset the renderer + if (the_renderer != NULL) + the_renderer->Reset(); +} + + +/* + * Preferences may have changed + */ + +void MOS6581::NewPrefs(Prefs *prefs) +{ + open_close_renderer(ThePrefs.SIDType, prefs->SIDType); + if (the_renderer != NULL) + the_renderer->NewPrefs(prefs); +} + + +/* + * Pause sound output + */ + +void MOS6581::PauseSound(void) +{ + if (the_renderer != NULL) + the_renderer->Pause(); +} + + +/* + * Resume sound output + */ + +void MOS6581::ResumeSound(void) +{ + if (the_renderer != NULL) + the_renderer->Resume(); +} + + +/* + * Get SID state + */ + +void MOS6581::GetState(MOS6581State *ss) +{ + ss->freq_lo_1 = regs[0]; + ss->freq_hi_1 = regs[1]; + ss->pw_lo_1 = regs[2]; + ss->pw_hi_1 = regs[3]; + ss->ctrl_1 = regs[4]; + ss->AD_1 = regs[5]; + ss->SR_1 = regs[6]; + + ss->freq_lo_2 = regs[7]; + ss->freq_hi_2 = regs[8]; + ss->pw_lo_2 = regs[9]; + ss->pw_hi_2 = regs[10]; + ss->ctrl_2 = regs[11]; + ss->AD_2 = regs[12]; + ss->SR_2 = regs[13]; + + ss->freq_lo_3 = regs[14]; + ss->freq_hi_3 = regs[15]; + ss->pw_lo_3 = regs[16]; + ss->pw_hi_3 = regs[17]; + ss->ctrl_3 = regs[18]; + ss->AD_3 = regs[19]; + ss->SR_3 = regs[20]; + + ss->fc_lo = regs[21]; + ss->fc_hi = regs[22]; + ss->res_filt = regs[23]; + ss->mode_vol = regs[24]; + + ss->pot_x = 0xff; + ss->pot_y = 0xff; + ss->osc_3 = 0; + ss->env_3 = 0; +} + + +/* + * Restore SID state + */ + +void MOS6581::SetState(MOS6581State *ss) +{ + regs[0] = ss->freq_lo_1; + regs[1] = ss->freq_hi_1; + regs[2] = ss->pw_lo_1; + regs[3] = ss->pw_hi_1; + regs[4] = ss->ctrl_1; + regs[5] = ss->AD_1; + regs[6] = ss->SR_1; + + regs[7] = ss->freq_lo_2; + regs[8] = ss->freq_hi_2; + regs[9] = ss->pw_lo_2; + regs[10] = ss->pw_hi_2; + regs[11] = ss->ctrl_2; + regs[12] = ss->AD_2; + regs[13] = ss->SR_2; + + regs[14] = ss->freq_lo_3; + regs[15] = ss->freq_hi_3; + regs[16] = ss->pw_lo_3; + regs[17] = ss->pw_hi_3; + regs[18] = ss->ctrl_3; + regs[19] = ss->AD_3; + regs[20] = ss->SR_3; + + regs[21] = ss->fc_lo; + regs[22] = ss->fc_hi; + regs[23] = ss->res_filt; + regs[24] = ss->mode_vol; + + // Stuff the new register values into the renderer + if (the_renderer != NULL) + for (int i=0; i<25; i++) + the_renderer->WriteRegister(i, regs[i]); +} + + +/** + ** Renderer for digital SID emulation (SIDTYPE_DIGITAL) + **/ + +#if defined(AMIGA) || defined(__riscos__) +const uint32 SAMPLE_FREQ = 22050; // Sample output frequency in Hz +#elif defined(GEKKO) +const uint32 SAMPLE_FREQ = 48000; +#else +const uint32 SAMPLE_FREQ = 44100; // Sample output frequency in Hz +#endif +const uint32 SID_FREQ = 985248; // SID frequency in Hz +const uint32 CALC_FREQ = 50; // Frequency at which calc_buffer is called in Hz (should be 50Hz) +const uint32 SID_CYCLES = SID_FREQ/SAMPLE_FREQ; // # of SID clocks per sample frame +const int SAMPLE_BUF_SIZE = 0x138*2;// Size of buffer for sampled voice (double buffered) + +// SID waveforms (some of them :-) +enum { + WAVE_NONE, + WAVE_TRI, + WAVE_SAW, + WAVE_TRISAW, + WAVE_RECT, + WAVE_TRIRECT, + WAVE_SAWRECT, + WAVE_TRISAWRECT, + WAVE_NOISE +}; + +// EG states +enum { + EG_IDLE, + EG_ATTACK, + EG_DECAY, + EG_RELEASE +}; + +// Filter types +enum { + FILT_NONE, + FILT_LP, + FILT_BP, + FILT_LPBP, + FILT_HP, + FILT_NOTCH, + FILT_HPBP, + FILT_ALL +}; + +// Structure for one voice +struct DRVoice { + int wave; // Selected waveform + int eg_state; // Current state of EG + DRVoice *mod_by; // Voice that modulates this one + DRVoice *mod_to; // Voice that is modulated by this one + + uint32 count; // Counter for waveform generator, 8.16 fixed + uint32 add; // Added to counter in every frame + + uint16 freq; // SID frequency value + uint16 pw; // SID pulse-width value + + uint32 a_add; // EG parameters + uint32 d_sub; + uint32 s_level; + uint32 r_sub; + uint32 eg_level; // Current EG level, 8.16 fixed + + uint32 noise; // Last noise generator output value + + bool gate; // EG gate bit + bool ring; // Ring modulation bit + bool test; // Test bit + bool filter; // Flag: Voice filtered + + // The following bit is set for the modulating + // voice, not for the modulated one (as the SID bits) + bool sync; // Sync modulation bit + bool mute; // Voice muted (voice 3 only) +}; + +// Renderer class +class DigitalRenderer : public SIDRenderer { +public: +#if defined(__BEOS__) || defined(__riscos__) + DigitalRenderer(C64 *c64); +#else + DigitalRenderer(); +#endif + virtual ~DigitalRenderer(); + + virtual void Reset(void); + virtual void EmulateLine(void); + virtual void WriteRegister(uint16 adr, uint8 byte); + virtual void NewPrefs(Prefs *prefs); + virtual void Pause(void); + virtual void Resume(void); + +private: + void init_sound(void); + void calc_filter(void); +#ifdef __riscos__ + void calc_buffer(uint8 *buf, long count); +#else + void calc_buffer(int16 *buf, long count); +#endif + + bool ready; // Flag: Renderer has initialized and is ready + uint8 volume; // Master volume + + static uint16 TriTable[0x1000*2]; // Tables for certain waveforms + static const uint16 TriSawTable[0x100]; + static const uint16 TriRectTable[0x100]; + static const uint16 SawRectTable[0x100]; + static const uint16 TriSawRectTable[0x100]; + static const uint32 EGTable[16]; // Increment/decrement values for all A/D/R settings + static const uint8 EGDRShift[256]; // For exponential approximation of D/R + static const int16 SampleTab[16]; // Table for sampled voice + + DRVoice voice[3]; // Data for 3 voices + + uint8 f_type; // Filter type + uint8 f_freq; // SID filter frequency (upper 8 bits) + uint8 f_res; // Filter resonance (0..15) +#ifdef USE_FIXPOINT_MATHS + FixPoint f_ampl; + FixPoint d1, d2, g1, g2; + int32 xn1, xn2, yn1, yn2; // can become very large + FixPoint sidquot; +#ifdef PRECOMPUTE_RESONANCE + FixPoint resonanceLP[256]; + FixPoint resonanceHP[256]; +#endif +#else + float f_ampl; // IIR filter input attenuation + float d1, d2, g1, g2; // IIR filter coefficients + float xn1, xn2, yn1, yn2; // IIR filter previous input/output signal +#ifdef PRECOMPUTE_RESONANCE + float resonanceLP[256]; // shortcut for calc_filter + float resonanceHP[256]; +#endif +#endif + + uint8 sample_buf[SAMPLE_BUF_SIZE]; // Buffer for sampled voice + int sample_in_ptr; // Index in sample_buf for writing + +#ifdef __BEOS__ + static void buffer_proc(void *cookie, void *buffer, size_t size, const media_raw_audio_format &format); + C64 *the_c64; // Pointer to C64 object + BSoundPlayer *the_player; // Pointer to sound player + bool player_stopped; // Flag: player stopped +#endif + +#ifdef AMIGA + static void sub_invoc(void); // Sound sub-process + void sub_func(void); + struct Process *sound_process; + int quit_sig, pause_sig, + resume_sig, ahi_sig; // Sub-process signals + struct Task *main_task; // Main task + int main_sig; // Main task signals + static ULONG sound_func(void); // AHI callback + struct MsgPort *ahi_port; // Port and IORequest for AHI + struct AHIRequest *ahi_io; + struct AHIAudioCtrl *ahi_ctrl; // AHI control structure + struct AHISampleInfo sample[2]; // SampleInfos for double buffering + struct Hook sf_hook; // Hook for callback function + int play_buf; // Number of buffer currently playing +#endif + +#if defined(__linux__) || defined(GEKKO) + int devfd, sndbufsize, buffer_rate; + int16 *sound_buffer; +#endif + +#ifdef SUN + int fd; + audio_info status; + uint_t sent_samples,delta_samples; + int16 *sound_calc_buf; +#endif + +#ifdef __hpux + int fd; + audio_status status; + int16 *sound_calc_buf; + int linecnt; +#endif + +#ifdef __mac__ + SndChannelPtr chan1; + SndDoubleBufferHeader myDblHeader; + SndDoubleBufferPtr sampleBuffer1, sampleBuffer2; + SCStatus myStatus; + short sndbufsize; + OSErr err; + + static void doubleBackProc(SndChannelPtr chan, SndDoubleBufferPtr doubleBuffer); +#endif + +#ifdef WIN32 +public: + void VBlank(void); + +private: + void StartPlayer(void); + void StopPlayer(void); + + BOOL direct_sound; + DigitalPlayer *ThePlayer; + SWORD *sound_buffer; + int to_output; + int sb_pos; + int divisor; + int *lead; + int lead_pos; +#endif + +#ifdef __riscos__ + int linecnt, sndbufsize; + uint8 *sound_buffer; + C64 *the_c64; +#endif +}; + +// Static data members +uint16 DigitalRenderer::TriTable[0x1000*2]; + +#ifndef EMUL_MOS8580 +// Sampled from a 6581R4 +const uint16 DigitalRenderer::TriSawTable[0x100] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0808, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1010, 0x3C3C, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0808, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1010, 0x3C3C +}; + +const uint16 DigitalRenderer::TriRectTable[0x100] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x8080, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x8080, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x8080, 0xC0C0, + 0x0000, 0x8080, 0x8080, 0xE0E0, 0x8080, 0xE0E0, 0xF0F0, 0xFCFC, + 0xFFFF, 0xFCFC, 0xFAFA, 0xF0F0, 0xF6F6, 0xE0E0, 0xE0E0, 0x8080, + 0xEEEE, 0xE0E0, 0xE0E0, 0x8080, 0xC0C0, 0x0000, 0x0000, 0x0000, + 0xDEDE, 0xC0C0, 0xC0C0, 0x0000, 0x8080, 0x0000, 0x0000, 0x0000, + 0x8080, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0xBEBE, 0x8080, 0x8080, 0x0000, 0x8080, 0x0000, 0x0000, 0x0000, + 0x8080, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x7E7E, 0x4040, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 +}; + +const uint16 DigitalRenderer::SawRectTable[0x100] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x7878, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x7878 +}; + +const uint16 DigitalRenderer::TriSawRectTable[0x100] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 +}; +#else +// Sampled from an 8580R5 +const uint16 DigitalRenderer::TriSawTable[0x100] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0808, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1818, 0x3C3C, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1C1C, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x8080, 0x0000, 0x8080, 0x8080, + 0xC0C0, 0xC0C0, 0xC0C0, 0xC0C0, 0xC0C0, 0xC0C0, 0xC0C0, 0xE0E0, + 0xF0F0, 0xF0F0, 0xF0F0, 0xF0F0, 0xF8F8, 0xF8F8, 0xFCFC, 0xFEFE +}; + +const uint16 DigitalRenderer::TriRectTable[0x100] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0xFFFF, 0xFCFC, 0xF8F8, 0xF0F0, 0xF4F4, 0xF0F0, 0xF0F0, 0xE0E0, + 0xECEC, 0xE0E0, 0xE0E0, 0xC0C0, 0xE0E0, 0xC0C0, 0xC0C0, 0xC0C0, + 0xDCDC, 0xC0C0, 0xC0C0, 0xC0C0, 0xC0C0, 0xC0C0, 0x8080, 0x8080, + 0xC0C0, 0x8080, 0x8080, 0x8080, 0x8080, 0x8080, 0x0000, 0x0000, + 0xBEBE, 0xA0A0, 0x8080, 0x8080, 0x8080, 0x8080, 0x8080, 0x0000, + 0x8080, 0x8080, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x8080, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x7E7E, 0x7070, 0x6060, 0x0000, 0x4040, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 +}; + +const uint16 DigitalRenderer::SawRectTable[0x100] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x8080, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x8080, 0x8080, + 0x0000, 0x8080, 0x8080, 0x8080, 0x8080, 0x8080, 0xB0B0, 0xBEBE, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x8080, + 0x0000, 0x0000, 0x0000, 0x8080, 0x8080, 0x8080, 0x8080, 0xC0C0, + 0x0000, 0x8080, 0x8080, 0x8080, 0x8080, 0x8080, 0x8080, 0xC0C0, + 0x8080, 0x8080, 0xC0C0, 0xC0C0, 0xC0C0, 0xC0C0, 0xC0C0, 0xDCDC, + 0x8080, 0x8080, 0x8080, 0xC0C0, 0xC0C0, 0xC0C0, 0xC0C0, 0xC0C0, + 0xC0C0, 0xC0C0, 0xC0C0, 0xE0E0, 0xE0E0, 0xE0E0, 0xE0E0, 0xECEC, + 0xC0C0, 0xE0E0, 0xE0E0, 0xE0E0, 0xE0E0, 0xF0F0, 0xF0F0, 0xF4F4, + 0xF0F0, 0xF0F0, 0xF8F8, 0xF8F8, 0xF8F8, 0xFCFC, 0xFEFE, 0xFFFF +}; + +const uint16 DigitalRenderer::TriSawRectTable[0x100] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x8080, 0x8080, + 0x8080, 0x8080, 0x8080, 0x8080, 0x8080, 0x8080, 0xC0C0, 0xC0C0, + 0xC0C0, 0xC0C0, 0xE0E0, 0xE0E0, 0xE0E0, 0xF0F0, 0xF8F8, 0xFCFC +}; +#endif + +const uint32 DigitalRenderer::EGTable[16] = { + (SID_CYCLES << 16) / 9, (SID_CYCLES << 16) / 32, + (SID_CYCLES << 16) / 63, (SID_CYCLES << 16) / 95, + (SID_CYCLES << 16) / 149, (SID_CYCLES << 16) / 220, + (SID_CYCLES << 16) / 267, (SID_CYCLES << 16) / 313, + (SID_CYCLES << 16) / 392, (SID_CYCLES << 16) / 977, + (SID_CYCLES << 16) / 1954, (SID_CYCLES << 16) / 3126, + (SID_CYCLES << 16) / 3906, (SID_CYCLES << 16) / 11720, + (SID_CYCLES << 16) / 19531, (SID_CYCLES << 16) / 31251 +}; + +const uint8 DigitalRenderer::EGDRShift[256] = { + 5,5,5,5,5,5,5,5,4,4,4,4,4,4,4,4, + 3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +}; + +const int16 DigitalRenderer::SampleTab[16] = { + 0x8000, 0x9111, 0xa222, 0xb333, 0xc444, 0xd555, 0xe666, 0xf777, + 0x0888, 0x1999, 0x2aaa, 0x3bbb, 0x4ccc, 0x5ddd, 0x6eee, 0x7fff, +}; + + +/* + * Constructor + */ + +#if defined(__BEOS__) || defined(__riscos__) +DigitalRenderer::DigitalRenderer(C64 *c64) : the_c64(c64) +#else +DigitalRenderer::DigitalRenderer() +#endif +{ + // Link voices together + voice[0].mod_by = &voice[2]; + voice[1].mod_by = &voice[0]; + voice[2].mod_by = &voice[1]; + voice[0].mod_to = &voice[1]; + voice[1].mod_to = &voice[2]; + voice[2].mod_to = &voice[0]; + + // Calculate triangle table + for (int i=0; i<0x1000; i++) { + TriTable[i] = (i << 4) | (i >> 8); + TriTable[0x1fff-i] = (i << 4) | (i >> 8); + } + +#ifdef PRECOMPUTE_RESONANCE +#ifdef USE_FIXPOINT_MATHS + // slow floating point doesn't matter much on startup! + for (int i=0; i<256; i++) { + resonanceLP[i] = FixNo(CALC_RESONANCE_LP(i)); + resonanceHP[i] = FixNo(CALC_RESONANCE_HP(i)); + } + // Pre-compute the quotient. No problem since int-part is small enough + sidquot = (int32)((((double)SID_FREQ)*65536) / SAMPLE_FREQ); + // compute lookup table for sin and cos + InitFixSinTab(); +#else + for (int i=0; i<256; i++) { + resonanceLP[i] = CALC_RESONANCE_LP(i); + resonanceHP[i] = CALC_RESONANCE_HP(i); + } +#endif +#endif + + Reset(); + + // System specific initialization + init_sound(); +} + + +/* + * Reset emulation + */ + +void DigitalRenderer::Reset(void) +{ + volume = 0; + + for (int v=0; v<3; v++) { + voice[v].wave = WAVE_NONE; + voice[v].eg_state = EG_IDLE; + voice[v].count = voice[v].add = 0; + voice[v].freq = voice[v].pw = 0; + voice[v].eg_level = voice[v].s_level = 0; + voice[v].a_add = voice[v].d_sub = voice[v].r_sub = EGTable[0]; + voice[v].gate = voice[v].ring = voice[v].test = false; + voice[v].filter = voice[v].sync = voice[v].mute = false; + } + + f_type = FILT_NONE; + f_freq = f_res = 0; +#ifdef USE_FIXPOINT_MATHS + f_ampl = FixNo(1); + d1 = d2 = g1 = g2 = 0; + xn1 = xn2 = yn1 = yn2 = 0; +#else + f_ampl = 1.0; + d1 = d2 = g1 = g2 = 0.0; + xn1 = xn2 = yn1 = yn2 = 0.0; +#endif + + sample_in_ptr = 0; + memset(sample_buf, 0, SAMPLE_BUF_SIZE); +} + + +/* + * Write to register + */ + +void DigitalRenderer::WriteRegister(uint16 adr, uint8 byte) +{ + if (!ready) + return; + + int v = adr/7; // Voice number + + switch (adr) { + case 0: + case 7: + case 14: + voice[v].freq = (voice[v].freq & 0xff00) | byte; +#ifdef USE_FIXPOINT_MATHS + voice[v].add = sidquot.imul((int)voice[v].freq); +#else + voice[v].add = (uint32)((float)voice[v].freq * SID_FREQ / SAMPLE_FREQ); +#endif + break; + + case 1: + case 8: + case 15: + voice[v].freq = (voice[v].freq & 0xff) | (byte << 8); +#ifdef USE_FIXPOINT_MATHS + voice[v].add = sidquot.imul((int)voice[v].freq); +#else + voice[v].add = (uint32)((float)voice[v].freq * SID_FREQ / SAMPLE_FREQ); +#endif + break; + + case 2: + case 9: + case 16: + voice[v].pw = (voice[v].pw & 0x0f00) | byte; + break; + + case 3: + case 10: + case 17: + voice[v].pw = (voice[v].pw & 0xff) | ((byte & 0xf) << 8); + break; + + case 4: + case 11: + case 18: + voice[v].wave = (byte >> 4) & 0xf; + if ((byte & 1) != voice[v].gate) + if (byte & 1) // Gate turned on + voice[v].eg_state = EG_ATTACK; + else // Gate turned off + if (voice[v].eg_state != EG_IDLE) + voice[v].eg_state = EG_RELEASE; + voice[v].gate = byte & 1; + voice[v].mod_by->sync = byte & 2; + voice[v].ring = byte & 4; + if ((voice[v].test = byte & 8) != 0) + voice[v].count = 0; + break; + + case 5: + case 12: + case 19: + voice[v].a_add = EGTable[byte >> 4]; + voice[v].d_sub = EGTable[byte & 0xf]; + break; + + case 6: + case 13: + case 20: + voice[v].s_level = (byte >> 4) * 0x111111; + voice[v].r_sub = EGTable[byte & 0xf]; + break; + + case 22: + if (byte != f_freq) { + f_freq = byte; + if (ThePrefs.SIDFilters) + calc_filter(); + } + break; + + case 23: + voice[0].filter = byte & 1; + voice[1].filter = byte & 2; + voice[2].filter = byte & 4; + if ((byte >> 4) != f_res) { + f_res = byte >> 4; + if (ThePrefs.SIDFilters) + calc_filter(); + } + break; + + case 24: + volume = byte & 0xf; + voice[2].mute = byte & 0x80; + if (((byte >> 4) & 7) != f_type) { + f_type = (byte >> 4) & 7; +#ifdef USE_FIXPOINT_MATHS + xn1 = xn2 = yn1 = yn2 = 0; +#else + xn1 = xn2 = yn1 = yn2 = 0.0; +#endif + if (ThePrefs.SIDFilters) + calc_filter(); + } + break; + } +} + + +/* + * Preferences may have changed + */ + +void DigitalRenderer::NewPrefs(Prefs *prefs) +{ + calc_filter(); +} + + +/* + * Calculate IIR filter coefficients + */ + +void DigitalRenderer::calc_filter(void) +{ +#ifdef USE_FIXPOINT_MATHS + FixPoint fr, arg; + + if (f_type == FILT_ALL) + { + d1 = 0; d2 = 0; g1 = 0; g2 = 0; f_ampl = FixNo(1); return; + } + else if (f_type == FILT_NONE) + { + d1 = 0; d2 = 0; g1 = 0; g2 = 0; f_ampl = 0; return; + } +#else + float fr, arg; + + // Check for some trivial cases + if (f_type == FILT_ALL) { + d1 = 0.0; d2 = 0.0; + g1 = 0.0; g2 = 0.0; + f_ampl = 1.0; + return; + } else if (f_type == FILT_NONE) { + d1 = 0.0; d2 = 0.0; + g1 = 0.0; g2 = 0.0; + f_ampl = 0.0; + return; + } +#endif + + // Calculate resonance frequency + if (f_type == FILT_LP || f_type == FILT_LPBP) +#ifdef PRECOMPUTE_RESONANCE + fr = resonanceLP[f_freq]; +#else + fr = CALC_RESONANCE_LP(f_freq); +#endif + else +#ifdef PRECOMPUTE_RESONANCE + fr = resonanceHP[f_freq]; +#else + fr = CALC_RESONANCE_HP(f_freq); +#endif + +#ifdef USE_FIXPOINT_MATHS + // explanations see below. + arg = fr / (SAMPLE_FREQ >> 1); + if (arg > FixNo(0.99)) {arg = FixNo(0.99);} + if (arg < FixNo(0.01)) {arg = FixNo(0.01);} + + g2 = FixNo(0.55) + FixNo(1.2) * arg * (arg - 1) + FixNo(0.0133333333) * f_res; + g1 = FixNo(-2) * g2.sqrt() * fixcos(arg); + + if (f_type == FILT_LPBP || f_type == FILT_HPBP) {g2 += FixNo(0.1);} + + if (g1.abs() >= g2 + 1) + { + if (g1 > 0) {g1 = g2 + FixNo(0.99);} + else {g1 = -(g2 + FixNo(0.99));} + } + + switch (f_type) + { + case FILT_LPBP: + case FILT_LP: + d1 = FixNo(2); d2 = FixNo(1); f_ampl = FixNo(0.25) * (1 + g1 + g2); break; + case FILT_HPBP: + case FILT_HP: + d1 = FixNo(-2); d2 = FixNo(1); f_ampl = FixNo(0.25) * (1 - g1 + g2); break; + case FILT_BP: + d1 = 0; d2 = FixNo(-1); + f_ampl = FixNo(0.25) * (1 + g1 + g2) * (1 + fixcos(arg)) / fixsin(arg); + break; + case FILT_NOTCH: + d1 = FixNo(-2) * fixcos(arg); d2 = FixNo(1); + f_ampl = FixNo(0.25) * (1 + g1 + g2) * (1 + fixcos(arg)) / fixsin(arg); + break; + default: break; + } + +#else + + // Limit to <1/2 sample frequency, avoid div by 0 in case FILT_BP below + arg = fr / (float)(SAMPLE_FREQ >> 1); + if (arg > 0.99) + arg = 0.99; + if (arg < 0.01) + arg = 0.01; + + // Calculate poles (resonance frequency and resonance) + g2 = 0.55 + 1.2 * arg * arg - 1.2 * arg + (float)f_res * 0.0133333333; + g1 = -2.0 * sqrt(g2) * cos(M_PI * arg); + + // Increase resonance if LP/HP combined with BP + if (f_type == FILT_LPBP || f_type == FILT_HPBP) + g2 += 0.1; + + // Stabilize filter + if (fabs(g1) >= g2 + 1.0) + if (g1 > 0.0) + g1 = g2 + 0.99; + else + g1 = -(g2 + 0.99); + + // Calculate roots (filter characteristic) and input attenuation + switch (f_type) { + + case FILT_LPBP: + case FILT_LP: + d1 = 2.0; d2 = 1.0; + f_ampl = 0.25 * (1.0 + g1 + g2); + break; + + case FILT_HPBP: + case FILT_HP: + d1 = -2.0; d2 = 1.0; + f_ampl = 0.25 * (1.0 - g1 + g2); + break; + + case FILT_BP: + d1 = 0.0; d2 = -1.0; + f_ampl = 0.25 * (1.0 + g1 + g2) * (1 + cos(M_PI * arg)) / sin(M_PI * arg); + break; + + case FILT_NOTCH: + d1 = -2.0 * cos(M_PI * arg); d2 = 1.0; + f_ampl = 0.25 * (1.0 + g1 + g2) * (1 + cos(M_PI * arg)) / (sin(M_PI * arg)); + break; + + default: + break; + } +#endif +} + + +/* + * Fill one audio buffer with calculated SID sound + */ + +#ifdef __riscos__ +void DigitalRenderer::calc_buffer(uint8 *buf, long count) +#else +void DigitalRenderer::calc_buffer(int16 *buf, long count) +#endif +{ + // Get filter coefficients, so the emulator won't change + // them in the middle of our calculations +#ifdef USE_FIXPOINT_MATHS + FixPoint cf_ampl = f_ampl; + FixPoint cd1 = d1, cd2 = d2, cg1 = g1, cg2 = g2; +#else + float cf_ampl = f_ampl; + float cd1 = d1, cd2 = d2, cg1 = g1, cg2 = g2; +#endif + +#ifdef __riscos__ + uint8 *LinToLog, *LogScale; +#endif + + // Index in sample_buf for reading, 16.16 fixed + uint32 sample_count = (sample_in_ptr + SAMPLE_BUF_SIZE/2) << 16; + +#ifdef __riscos__ // on RISC OS we have 8 bit logarithmic sound + DigitalRenderer_GetTables(&LinToLog, &LogScale); // get translation tables +#else + count >>= 1; // 16 bit mono output, count is in bytes +#endif + while (count--) { + // Get current master volume from sample buffer, + // calculate sampled voice + uint8 master_volume = sample_buf[(sample_count >> 16) % SAMPLE_BUF_SIZE]; + sample_count += ((0x138 * 50) << 16) / SAMPLE_FREQ; + int32 sum_output = SampleTab[master_volume] << 8; + int32 sum_output_filter = 0; + + // Loop for all three voices + for (int j=0; j<3; j++) { + DRVoice *v = &voice[j]; + + // Envelope generators + uint16 envelope; + + switch (v->eg_state) { + case EG_ATTACK: + v->eg_level += v->a_add; + if (v->eg_level > 0xffffff) { + v->eg_level = 0xffffff; + v->eg_state = EG_DECAY; + } + break; + case EG_DECAY: + if (v->eg_level <= v->s_level || v->eg_level > 0xffffff) + v->eg_level = v->s_level; + else { + v->eg_level -= v->d_sub >> EGDRShift[v->eg_level >> 16]; + if (v->eg_level <= v->s_level || v->eg_level > 0xffffff) + v->eg_level = v->s_level; + } + break; + case EG_RELEASE: + v->eg_level -= v->r_sub >> EGDRShift[v->eg_level >> 16]; + if (v->eg_level > 0xffffff) { + v->eg_level = 0; + v->eg_state = EG_IDLE; + } + break; + case EG_IDLE: + v->eg_level = 0; + break; + } + envelope = (v->eg_level * master_volume) >> 20; + + // Waveform generator + if (v->mute) + continue; + uint16 output; + + if (!v->test) + v->count += v->add; + + if (v->sync && (v->count > 0x1000000)) + v->mod_to->count = 0; + + v->count &= 0xffffff; + + switch (v->wave) { + case WAVE_TRI: + if (v->ring) + output = TriTable[(v->count ^ (v->mod_by->count & 0x800000)) >> 11]; + else + output = TriTable[v->count >> 11]; + break; + case WAVE_SAW: + output = v->count >> 8; + break; + case WAVE_RECT: + if (v->count > (uint32)(v->pw << 12)) + output = 0xffff; + else + output = 0; + break; + case WAVE_TRISAW: + output = TriSawTable[v->count >> 16]; + break; + case WAVE_TRIRECT: + if (v->count > (uint32)(v->pw << 12)) + output = TriRectTable[v->count >> 16]; + else + output = 0; + break; + case WAVE_SAWRECT: + if (v->count > (uint32)(v->pw << 12)) + output = SawRectTable[v->count >> 16]; + else + output = 0; + break; + case WAVE_TRISAWRECT: + if (v->count > (uint32)(v->pw << 12)) + output = TriSawRectTable[v->count >> 16]; + else + output = 0; + break; + case WAVE_NOISE: + if (v->count > 0x100000) { + output = v->noise = sid_random() << 8; + v->count &= 0xfffff; + } else + output = v->noise; + break; + default: + output = 0x8000; + break; + } + if (v->filter) + sum_output_filter += (int16)(output ^ 0x8000) * envelope; + else + sum_output += (int16)(output ^ 0x8000) * envelope; + } + + // Filter + if (ThePrefs.SIDFilters) { +#ifdef USE_FIXPOINT_MATHS + int32 xn = cf_ampl.imul(sum_output_filter); + int32 yn = xn+cd1.imul(xn1)+cd2.imul(xn2)-cg1.imul(yn1)-cg2.imul(yn2); + yn2 = yn1; yn1 = yn; xn2 = xn1; xn1 = xn; + sum_output_filter = yn; +#else + float xn = (float)sum_output_filter * cf_ampl; + float yn = xn + cd1 * xn1 + cd2 * xn2 - cg1 * yn1 - cg2 * yn2; + yn2 = yn1; yn1 = yn; xn2 = xn1; xn1 = xn; + sum_output_filter = (int32)yn; +#endif + } + + // Write to buffer +#if defined(__riscos__) // lookup in 8k (13bit) translation table + *buf++ = LinToLog[((sum_output + sum_output_filter) >> 13) & 0x1fff]; +#else + *buf++ = (sum_output + sum_output_filter) >> 10; +#endif + } +} + + +// Manufacturer independent sound is still just a dream... +#if defined(__BEOS__) +#include "SID_Be.h" + +#elif defined(AMIGA) +#include "SID_Amiga.h" + +#elif defined(__linux__) +#include "SID_linux.h" + +#elif defined(SUN) +#include "SID_sun.h" + +#elif defined(__hpux) +#include "SID_hp.h" + +#elif defined(__mac__) +#include "SID_mac.h" + +#elif defined(WIN32) +#include "SID_WIN32.h" + +#elif defined(__riscos__) +#include "SID_Acorn.h" + +#elif defined(GEKKO) +#include "SID_wii.h" + +#else // No sound +void DigitalRenderer::init_sound(void) {ready = false;} +DigitalRenderer::~DigitalRenderer() {} +void DigitalRenderer::EmulateLine(void) {} +void DigitalRenderer::Pause(void) {} +void DigitalRenderer::Resume(void) {} +#endif + + +/* + * Open/close the renderer, according to old and new prefs + */ + +void MOS6581::open_close_renderer(int old_type, int new_type) +{ + if (old_type == new_type) + return; + + // Delete the old renderer + delete the_renderer; + + // Create new renderer + if (new_type == SIDTYPE_DIGITAL) +#if defined(__BEOS__) || defined(__riscos__) + the_renderer = new DigitalRenderer(the_c64); +#else + the_renderer = new DigitalRenderer; +#endif +#ifdef AMIGA + else if (new_type == SIDTYPE_SIDCARD) + the_renderer = new SIDCardRenderer; +#endif +#ifdef __linux__ + else if (new_type == SIDTYPE_SIDCARD) + the_renderer = new CatweaselRenderer; +#endif + else + the_renderer = NULL; + + // Stuff the current register values into the new renderer + if (the_renderer != NULL) + for (int i=0; i<25; i++) + the_renderer->WriteRegister(i, regs[i]); +} diff --git a/Src/SID.h b/Src/SID.h new file mode 100644 index 0000000..4286907 --- /dev/null +++ b/Src/SID.h @@ -0,0 +1,161 @@ +/* + * SID.h - 6581 emulation + * + * Frodo (C) 1994-1997,2002-2005 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _SID_H +#define _SID_H + +#include + + +// Define this if you want an emulation of an 8580 +// (affects combined waveforms) +#undef EMUL_MOS8580 + + +class Prefs; +class C64; +class SIDRenderer; +struct MOS6581State; + +// Class for administrative functions +class MOS6581 { +public: + MOS6581(C64 *c64); + ~MOS6581(); + + void Reset(void); + uint8 ReadRegister(uint16 adr); + void WriteRegister(uint16 adr, uint8 byte); + void NewPrefs(Prefs *prefs); + void PauseSound(void); + void ResumeSound(void); + void GetState(MOS6581State *ss); + void SetState(MOS6581State *ss); + void EmulateLine(void); + +private: + void open_close_renderer(int old_type, int new_type); + + C64 *the_c64; // Pointer to C64 object + SIDRenderer *the_renderer; // Pointer to current renderer + uint8 regs[32]; // Copies of the 25 write-only SID registers + uint8 last_sid_byte; // Last value written to SID +}; + + +// Renderers do the actual audio data processing +class SIDRenderer { +public: + virtual ~SIDRenderer() {} + virtual void Reset(void)=0; + virtual void EmulateLine(void)=0; + virtual void WriteRegister(uint16 adr, uint8 byte)=0; + virtual void NewPrefs(Prefs *prefs)=0; + virtual void Pause(void)=0; + virtual void Resume(void)=0; +}; + + +// SID state +struct MOS6581State { + uint8 freq_lo_1; + uint8 freq_hi_1; + uint8 pw_lo_1; + uint8 pw_hi_1; + uint8 ctrl_1; + uint8 AD_1; + uint8 SR_1; + + uint8 freq_lo_2; + uint8 freq_hi_2; + uint8 pw_lo_2; + uint8 pw_hi_2; + uint8 ctrl_2; + uint8 AD_2; + uint8 SR_2; + + uint8 freq_lo_3; + uint8 freq_hi_3; + uint8 pw_lo_3; + uint8 pw_hi_3; + uint8 ctrl_3; + uint8 AD_3; + uint8 SR_3; + + uint8 fc_lo; + uint8 fc_hi; + uint8 res_filt; + uint8 mode_vol; + + uint8 pot_x; + uint8 pot_y; + uint8 osc_3; + uint8 env_3; +}; + + +/* + * Fill buffer (for Unix sound routines), sample volume (for sampled voice) + */ + +inline void MOS6581::EmulateLine(void) +{ + if (the_renderer != NULL) + the_renderer->EmulateLine(); +} + + +/* + * Read from register + */ + +inline uint8 MOS6581::ReadRegister(uint16 adr) +{ + // A/D converters + if (adr == 0x19 || adr == 0x1a) { + last_sid_byte = 0; + return 0xff; + } + + // Voice 3 oscillator/EG readout + if (adr == 0x1b || adr == 0x1c) { + last_sid_byte = 0; + return rand(); + } + + // Write-only register: Return last value written to SID + return last_sid_byte; +} + + +/* + * Write to register + */ + +inline void MOS6581::WriteRegister(uint16 adr, uint8 byte) +{ + // Keep a local copy of the register values + last_sid_byte = regs[adr] = byte; + + if (the_renderer != NULL) + the_renderer->WriteRegister(adr, byte); +} + +#endif diff --git a/Src/SID_Acorn.h b/Src/SID_Acorn.h new file mode 100644 index 0000000..8b81eb2 --- /dev/null +++ b/Src/SID_Acorn.h @@ -0,0 +1,107 @@ +/* + * SID_Acorn.h - 6581 emulation, RISC OS specific stuff + * + * Frodo (C) 1994-1997,2002-2005 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "C64.h" + + +void DigitalRenderer::init_sound(void) +{ + _kernel_oserror *err; + + ready = false; sound_buffer = NULL; + if ((DigitalRenderer_ReadState() & DRState_Active) != 0) + { + _kernel_oserror dra; + + dra.errnum = 0; sprintf(dra.errmess,"Can't claim sound system -- already active!"); + Wimp_ReportError(&dra,1,TASKNAME); return; + } + // Try starting up the renderer + sndbufsize = 2*224; linecnt = 0; + if ((err = DigitalRenderer_Activate(1,sndbufsize,1000000/SAMPLE_FREQ)) != NULL) + { + Wimp_ReportError(err,1,TASKNAME); return; + } + sound_buffer = new uint8[sndbufsize]; + ready = true; +} + + + + +DigitalRenderer::~DigitalRenderer() +{ + if (ready) + { + _kernel_oserror *err; + + delete sound_buffer; + if ((err = DigitalRenderer_Deactivate()) != NULL) + { + Wimp_ReportError(err,1,TASKNAME); + } + } +} + + + + +void DigitalRenderer::EmulateLine(void) +{ + if (ready) + { + sample_buf[sample_in_ptr++] = volume; + // faster than modulo; usually there shouldn't be a loop (while)... + while (sample_in_ptr >= SAMPLE_BUF_SIZE) {sample_in_ptr -= SAMPLE_BUF_SIZE;} + + // A similar approach to the HP variant: check every of lines if + // new sample needed. + if (--linecnt < 0) + { + int status; + + linecnt = the_c64->PollSoundAfter; + if ((status = DigitalRenderer_ReadState()) > 0) + { + if ((status & DRState_NeedData) != 0) + { + calc_buffer(sound_buffer, sndbufsize); + DigitalRenderer_NewSample(sound_buffer); + } + } + } + } +} + + + + +void DigitalRenderer::Pause(void) +{ + if (ready) {DigitalRenderer_Pause();} +} + + + + +void DigitalRenderer::Resume(void) +{ + if (ready) {DigitalRenderer_Resume();} +} diff --git a/Src/SID_Amiga.h b/Src/SID_Amiga.h new file mode 100644 index 0000000..ae92bb7 --- /dev/null +++ b/Src/SID_Amiga.h @@ -0,0 +1,298 @@ +/* + * SID_Amiga.h - 6581 emulation, Amiga specific stuff + * + * Frodo (C) 1994-1997,2002-2005 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include + + +// Library bases +struct Library *AHIBase; + +// CIA-A base +extern struct CIA ciaa; + + +/* + * Initialization, create sub-process + */ + +void DigitalRenderer::init_sound(void) +{ + // Find our (main) task + main_task = FindTask(NULL); + + // Create signal for communication + main_sig = AllocSignal(-1); + + // Create sub-process and wait until it is ready + if ((sound_process = CreateNewProcTags( + NP_Entry, (ULONG)&sub_invoc, + NP_Name, (ULONG)"Frodo Sound Process", + NP_Priority, 1, + NP_ExitData, (ULONG)this, // Easiest way to supply sub_invoc with this pointer + TAG_DONE)) != NULL) + Wait(1 << main_sig); +} + + +/* + * Destructor, delete sub-process + */ + +DigitalRenderer::~DigitalRenderer() +{ + // Tell sub-process to quit and wait for completion + if (sound_process != NULL) { + Signal(&(sound_process->pr_Task), 1 << quit_sig); + Wait(1 << main_sig); + } + + // Free signal + FreeSignal(main_sig); +} + + +/* + * Sample volume (for sampled voice) + */ + +void DigitalRenderer::EmulateLine(void) +{ + sample_buf[sample_in_ptr] = volume; + sample_in_ptr = (sample_in_ptr + 1) % SAMPLE_BUF_SIZE; +} + + +/* + * Pause sound output + */ + +void DigitalRenderer::Pause(void) +{ + if (sound_process != NULL) + Signal(&(sound_process->pr_Task), 1 << pause_sig); +} + + +/* + * Resume sound output + */ + +void DigitalRenderer::Resume(void) +{ + if (sound_process != NULL) + Signal(&(sound_process->pr_Task), 1 << resume_sig); +} + + +/* + * Sound sub-process + */ + +void DigitalRenderer::sub_invoc(void) +{ + // Get pointer to the DigitalRenderer object and call sub_func() + DigitalRenderer *r = (DigitalRenderer *)((struct Process *)FindTask(NULL))->pr_ExitData; + r->sub_func(); +} + +void DigitalRenderer::sub_func(void) +{ + ahi_port = NULL; + ahi_io = NULL; + ahi_ctrl = NULL; + sample[0].ahisi_Address = sample[1].ahisi_Address = NULL; + ready = FALSE; + + // Create signals for communication + quit_sig = AllocSignal(-1); + pause_sig = AllocSignal(-1); + resume_sig = AllocSignal(-1); + ahi_sig = AllocSignal(-1); + + // Open AHI + if ((ahi_port = CreateMsgPort()) == NULL) + goto wait_for_quit; + if ((ahi_io = (struct AHIRequest *)CreateIORequest(ahi_port, sizeof(struct AHIRequest))) == NULL) + goto wait_for_quit; + ahi_io->ahir_Version = 2; + if (OpenDevice(AHINAME, AHI_NO_UNIT, (struct IORequest *)ahi_io, NULL)) + goto wait_for_quit; + AHIBase = (struct Library *)ahi_io->ahir_Std.io_Device; + + // Initialize callback hook + sf_hook.h_Entry = sound_func; + + // Open audio control structure + if ((ahi_ctrl = AHI_AllocAudio( + AHIA_AudioID, 0x0002000b, + AHIA_MixFreq, SAMPLE_FREQ, + AHIA_Channels, 1, + AHIA_Sounds, 2, + AHIA_SoundFunc, (ULONG)&sf_hook, + AHIA_UserData, (ULONG)this, + TAG_DONE)) == NULL) + goto wait_for_quit; + + // Prepare SampleInfos and load sounds (two sounds for double buffering) + sample[0].ahisi_Type = AHIST_M16S; + sample[0].ahisi_Length = SAMPLE_FREQ / CALC_FREQ; + sample[0].ahisi_Address = AllocVec(SAMPLE_FREQ / CALC_FREQ * 2, MEMF_PUBLIC | MEMF_CLEAR); + sample[1].ahisi_Type = AHIST_M16S; + sample[1].ahisi_Length = SAMPLE_FREQ / CALC_FREQ; + sample[1].ahisi_Address = AllocVec(SAMPLE_FREQ / CALC_FREQ * 2, MEMF_PUBLIC | MEMF_CLEAR); + if (sample[0].ahisi_Address == NULL || sample[1].ahisi_Address == NULL) + goto wait_for_quit; + AHI_LoadSound(0, AHIST_DYNAMICSAMPLE, &sample[0], ahi_ctrl); + AHI_LoadSound(1, AHIST_DYNAMICSAMPLE, &sample[1], ahi_ctrl); + + // Set parameters + play_buf = 0; + AHI_SetVol(0, 0x10000, 0x8000, ahi_ctrl, AHISF_IMM); + AHI_SetFreq(0, SAMPLE_FREQ, ahi_ctrl, AHISF_IMM); + AHI_SetSound(0, play_buf, 0, 0, ahi_ctrl, AHISF_IMM); + + // Start audio output + AHI_ControlAudio(ahi_ctrl, AHIC_Play, TRUE, TAG_DONE); + + // We are now ready for commands + ready = TRUE; + Signal(main_task, 1 << main_sig); + + // Accept and execute commands + for (;;) { + ULONG sigs = Wait((1 << quit_sig) | (1 << pause_sig) | (1 << resume_sig) | (1 << ahi_sig)); + + // Quit sub-process + if (sigs & (1 << quit_sig)) + goto quit; + + // Pause sound output + if (sigs & (1 << pause_sig)) + AHI_ControlAudio(ahi_ctrl, AHIC_Play, FALSE, TAG_DONE); + + // Resume sound output + if (sigs & (1 << resume_sig)) + AHI_ControlAudio(ahi_ctrl, AHIC_Play, TRUE, TAG_DONE); + + // Calculate next buffer + if (sigs & (1 << ahi_sig)) + calc_buffer((int16 *)(sample[play_buf].ahisi_Address), sample[play_buf].ahisi_Length * 2); + } + +wait_for_quit: + // Initialization failed, wait for quit signal + Wait(1 << quit_sig); + +quit: + // Free everything + if (ahi_ctrl != NULL) { + AHI_ControlAudio(ahi_ctrl, AHIC_Play, FALSE, TAG_DONE); + AHI_FreeAudio(ahi_ctrl); + CloseDevice((struct IORequest *)ahi_io); + } + + FreeVec(sample[0].ahisi_Address); + FreeVec(sample[1].ahisi_Address); + + if (ahi_io != NULL) + DeleteIORequest((struct IORequest *)ahi_io); + + if (ahi_port != NULL) + DeleteMsgPort(ahi_port); + + FreeSignal(quit_sig); + FreeSignal(pause_sig); + FreeSignal(resume_sig); + FreeSignal(ahi_sig); + + // Quit (synchronized with main task) + Forbid(); + Signal(main_task, 1 << main_sig); +} + + +/* + * AHI sound callback, play next buffer and signal sub-process + */ + +ULONG DigitalRenderer::sound_func(void) +{ + register struct AHIAudioCtrl *ahi_ctrl asm ("a2"); + DigitalRenderer *r = (DigitalRenderer *)ahi_ctrl->ahiac_UserData; + r->play_buf ^= 1; + AHI_SetSound(0, r->play_buf, 0, 0, ahi_ctrl, 0); + Signal(&(r->sound_process->pr_Task), 1 << (r->ahi_sig)); + return 0; +} + + +/* + * Renderer for SID card + */ + +// Renderer class +class SIDCardRenderer : public SIDRenderer { +public: + SIDCardRenderer(); + virtual ~SIDCardRenderer(); + + virtual void Reset(void); + virtual void EmulateLine(void) {} + virtual void WriteRegister(uint16 adr, uint8 byte); + virtual void NewPrefs(Prefs *prefs) {} + virtual void Pause(void) {} + virtual void Resume(void) {} + +private: + UBYTE *sid_base; // SID card base pointer +}; + +// Constructor: Reset SID +SIDCardRenderer::SIDCardRenderer() +{ + sid_base = (UBYTE *)0xa00001; + Reset(); +} + +// Destructor: Reset SID +SIDCardRenderer::~SIDCardRenderer() +{ + Reset(); +} + +// Reset SID +void SIDCardRenderer::Reset(void) +{ + WaitTOF(); + ciaa.ciapra |= CIAF_LED; + WaitTOF(); + ciaa.ciapra &= ~CIAF_LED; +} + +// Write to register +void SIDCardRenderer::WriteRegister(uint16 adr, uint8 byte) +{ + sid_base[adr << 1] = byte; +} diff --git a/Src/SID_Be.h b/Src/SID_Be.h new file mode 100644 index 0000000..7f8ac7f --- /dev/null +++ b/Src/SID_Be.h @@ -0,0 +1,102 @@ +/* + * SID_Be.h - 6581 emulation, Be specific stuff + * + * Frodo (C) 1994-1997,2002-2005 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "C64.h" + + +/* + * Initialization, open subscriber + */ + +#if B_HOST_IS_LENDIAN +const media_raw_audio_format audio_format = {SAMPLE_FREQ, 1, media_raw_audio_format::B_AUDIO_SHORT, B_MEDIA_LITTLE_ENDIAN, SAMPLE_FREQ / CALC_FREQ * 2}; +#else +const media_raw_audio_format audio_format = {SAMPLE_FREQ, 1, media_raw_audio_format::B_AUDIO_SHORT, B_MEDIA_BIG_ENDIAN, SAMPLE_FREQ / CALC_FREQ * 2}; +#endif + +void DigitalRenderer::init_sound(void) +{ + the_player = new BSoundPlayer(&audio_format, "Frodo", buffer_proc, NULL, this); + the_player->SetHasData(true); + the_player->Start(); + player_stopped = false; + ready = true; +} + + +/* + * Destructor, close subscriber + */ + +DigitalRenderer::~DigitalRenderer() +{ + if (the_player) { + the_player->Stop(); + delete the_player; + } +} + + +/* + * Sample volume (for sampled voice) + */ + +void DigitalRenderer::EmulateLine(void) +{ + sample_buf[sample_in_ptr] = volume; + sample_in_ptr = (sample_in_ptr + 1) % SAMPLE_BUF_SIZE; +} + + +/* + * Pause sound output + */ + +void DigitalRenderer::Pause(void) +{ + if (!player_stopped) { + the_player->Stop(); + player_stopped = true; + } +} + + +/* + * Resume sound output + */ + +void DigitalRenderer::Resume(void) +{ + if (player_stopped) { + the_player->Start(); + player_stopped = false; + } +} + + +/* + * Stream function + */ + +void DigitalRenderer::buffer_proc(void *cookie, void *buffer, size_t size, const media_raw_audio_format &format) +{ + ((DigitalRenderer *)cookie)->calc_buffer((int16 *)buffer, size); + ((DigitalRenderer *)cookie)->the_c64->SoundSync(); +} diff --git a/Src/SID_WIN32.h b/Src/SID_WIN32.h new file mode 100644 index 0000000..c6da330 --- /dev/null +++ b/Src/SID_WIN32.h @@ -0,0 +1,569 @@ +/* + * SID_WIN32.h - 6581 emulation, WIN32 specific stuff + * + * Frodo (C) 1994-1997,2002-2005 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#include "VIC.h" +#include "main.h" + +#define FRAG_FREQ SCREEN_FREQ // one frag per frame + +#define FRAGMENT_SIZE (SAMPLE_FREQ/FRAG_FREQ) // samples, not bytes +#define FRAG_INTERVAL (1000/FRAG_FREQ) // in milliseconds +#define BUFFER_FRAGS FRAG_FREQ // frags the in buffer +#define BUFFER_SIZE (2*FRAGMENT_SIZE*BUFFER_FRAGS) // bytes, not samples +#define MAX_LEAD_AVG BUFFER_FRAGS // lead average count + +// This won't hurt DirectX 2 but it will help when using the DirectX 3 runtime. +#if !defined(DSBCAPS_GETCURRENTPOSITION2) +#define DSBCAPS_GETCURRENTPOSITION2 0x00010000 +#endif + +class DigitalPlayer { + +public: + virtual ~DigitalPlayer() = 0; + virtual BOOL Ready() = 0; + virtual int GetCurrentPosition() = 0; + virtual void Write(void *buffer, int position, int length) = 0; + virtual void Pause() = 0; + virtual void Resume() = 0; +}; + +DigitalPlayer::~DigitalPlayer() +{ +} + +class DirectSound: public DigitalPlayer { + +public: + DirectSound(); + ~DirectSound(); + BOOL Ready(); + int GetCurrentPosition(); + void Write(void *buffer, int position, int length); + void Pause(); + void Resume(); + +private: + BOOL ready; + LPDIRECTSOUND pDS; + LPDIRECTSOUNDBUFFER pPrimaryBuffer; + LPDIRECTSOUNDBUFFER pSoundBuffer; +}; + +class WaveOut: public DigitalPlayer { + +public: + WaveOut(); + ~WaveOut(); + BOOL Ready(); + int GetCurrentPosition(); + void Write(void *buffer, int position, int length); + void Pause(); + void Resume(); + +private: + void UnprepareHeader(int index); + void UnprepareHeaders(); + +private: + BOOL ready; + HWAVEOUT hWaveOut; + char wave_buffer[BUFFER_SIZE]; + WAVEHDR wave_header[SCREEN_FREQ]; + int last_unprepared; +}; + +void DigitalRenderer::init_sound() +{ + ready = FALSE; + sound_buffer = new SWORD[2*FRAGMENT_SIZE]; + ThePlayer = 0; + to_output = 0; + divisor = 0; + lead = new int[MAX_LEAD_AVG]; + + StartPlayer(); +} + +DigitalRenderer::~DigitalRenderer() +{ + StopPlayer(); + + delete[] sound_buffer; + delete[] lead; +} + +void DigitalRenderer::StartPlayer() +{ + direct_sound = ThePrefs.DirectSound; + if (ThePrefs.DirectSound) + ThePlayer = new DirectSound; + else + ThePlayer = new WaveOut; + ready = ThePlayer->Ready(); + sb_pos = 0; + memset(lead, 0, sizeof(lead)); + lead_pos = 0; +} + +void DigitalRenderer::StopPlayer() +{ + delete ThePlayer; + ready = FALSE; +} + +void DigitalRenderer::EmulateLine() +{ + if (!ready) + return; + + sample_buf[sample_in_ptr] = volume; + sample_in_ptr = (sample_in_ptr + 1) % SAMPLE_BUF_SIZE; + +#if 0 + // Now see how many samples have to be added for this line. + // XXX: This is too much computation here, precompute it. + divisor += SAMPLE_FREQ; + while (divisor >= 0) + divisor -= TOTAL_RASTERS*SCREEN_FREQ, to_output++; + + // Calculate the sound data only when we have enough to fill + // the buffer entirely. + if (to_output < FRAGMENT_SIZE) + return; + to_output -= FRAGMENT_SIZE; + + VBlank(); +#endif +} + +void DigitalRenderer::VBlank() +{ + if (!ready) + return; + + // Delete and recreate the player if preferences have changed. + if (direct_sound != ThePrefs.DirectSound) { + StopPlayer(); + StartPlayer(); + } + + // Convert latency preferences from milliseconds to frags. + int lead_smooth = ThePrefs.LatencyAvg/FRAG_INTERVAL; + int lead_hiwater = ThePrefs.LatencyMax/FRAG_INTERVAL; + int lead_lowater = ThePrefs.LatencyMin/FRAG_INTERVAL; + + // Compute the current lead in frags. + int current_position = ThePlayer->GetCurrentPosition(); + if (current_position == -1) + return; + int lead_in_bytes = (sb_pos - current_position + BUFFER_SIZE) % BUFFER_SIZE; + if (lead_in_bytes >= BUFFER_SIZE/2) + lead_in_bytes -= BUFFER_SIZE; + int lead_in_frags = lead_in_bytes / int(2*FRAGMENT_SIZE); + lead[lead_pos++] = lead_in_frags; + if (lead_pos == lead_smooth) + lead_pos = 0; + + // Compute the average lead in frags. + int avg_lead = 0; + for (int i = 0; i < lead_smooth; i++) + avg_lead += lead[i]; + avg_lead /= lead_smooth; + //Debug("lead = %d, avg = %d\n", lead_in_frags, avg_lead); + + // If we're getting too far ahead of the audio skip a frag. + if (avg_lead > lead_hiwater) { + for (int i = 0; i < lead_smooth; i++) + lead[i]--; + Debug("Skipping a frag...\n"); + return; + } + + // Calculate one frag. + int nsamples = FRAGMENT_SIZE; + calc_buffer(sound_buffer, 2*FRAGMENT_SIZE); + + // If we're getting too far behind the audio add an extra frag. + if (avg_lead < lead_lowater) { + for (int i = 0; i < lead_smooth; i++) + lead[i]++; + Debug("Adding an extra frag...\n"); + calc_buffer(sound_buffer + FRAGMENT_SIZE, 2*FRAGMENT_SIZE); + nsamples += FRAGMENT_SIZE; + } + + // Write the frags to the player and update out write position. + ThePlayer->Write(sound_buffer, sb_pos, 2*nsamples); + sb_pos = (sb_pos + 2*nsamples) % BUFFER_SIZE; +} + +void DigitalRenderer::Pause() +{ + if (!ready) + return; + ThePlayer->Pause(); +} + +void DigitalRenderer::Resume() +{ + if (!ready) + return; + ThePlayer->Resume(); +} + +// Direct sound implemenation. + +DirectSound::DirectSound() +{ + ready = FALSE; + pDS = NULL; + pPrimaryBuffer = NULL; + pSoundBuffer = NULL; + + HRESULT dsrval = DirectSoundCreate(NULL, &pDS, NULL); + if (dsrval != DS_OK) { + DebugResult("DirectSoundCreate failed", dsrval); + return; + } + + // Set cooperative level trying to get exclusive or normal mode. + DWORD level = ThePrefs.ExclusiveSound ? DSSCL_EXCLUSIVE : DSSCL_NORMAL; + dsrval = pDS->SetCooperativeLevel(hwnd, level); + if (dsrval != DS_OK) { + DebugResult("SetCooperativeLevel failed", dsrval); + return; + } + + WAVEFORMATEX wfx; + memset(&wfx, 0, sizeof(wfx)); + wfx.wFormatTag = WAVE_FORMAT_PCM; + wfx.nChannels = 1; + wfx.nSamplesPerSec = 44100; + wfx.wBitsPerSample = 16; + wfx.nBlockAlign = wfx.nChannels*wfx.wBitsPerSample/8; + wfx.nAvgBytesPerSec = wfx.nBlockAlign*wfx.nSamplesPerSec; + wfx.cbSize = 0; + + DSBUFFERDESC dsbd; + memset(&dsbd, 0, sizeof(dsbd)); + dsbd.dwSize = sizeof(dsbd); + dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER; + dsbd.dwBufferBytes = 0; + dsbd.lpwfxFormat = NULL; + + dsrval = pDS->CreateSoundBuffer(&dsbd, &pPrimaryBuffer, NULL); + if (dsrval != DS_OK) { + DebugResult("CreateSoundBuffer for primary failed", dsrval); + return; + } + + dsrval = pPrimaryBuffer->SetFormat(&wfx); + if (dsrval != DS_OK) { + DebugResult("SetFormat on primary failed", dsrval); + //return; + } + + dsrval = pPrimaryBuffer->Play(0, 0, DSBPLAY_LOOPING); + if (dsrval != DS_OK) { + DebugResult("Play primary failed", dsrval); + return; + } + + dsbd.dwSize = sizeof(dsbd); + dsbd.dwFlags = DSBCAPS_GETCURRENTPOSITION2; + dsbd.dwBufferBytes = BUFFER_SIZE; + dsbd.lpwfxFormat = &wfx; + + dsrval = pDS->CreateSoundBuffer(&dsbd, &pSoundBuffer, NULL); + if (dsrval != DS_OK) { + DebugResult("CreateSoundBuffer failed", dsrval); + return; + } + + ready = TRUE; +} + +DirectSound::~DirectSound() +{ + if (pDS != NULL) { + if (pSoundBuffer != NULL) { + pSoundBuffer->Release(); + pSoundBuffer = NULL; + } + if (pPrimaryBuffer != NULL) { + pPrimaryBuffer->Release(); + pPrimaryBuffer = NULL; + } + pDS->Release(); + pDS = NULL; + } +} + +BOOL DirectSound::Ready() +{ + return ready; +} + +int DirectSound::GetCurrentPosition() +{ + DWORD dwPlayPos, dwWritePos; + HRESULT dsrval = pSoundBuffer->GetCurrentPosition(&dwPlayPos, &dwWritePos); + if (dsrval != DS_OK) { + DebugResult("GetCurrentPostion failed", dsrval); + return -1; + } + return dwWritePos; +} + +void DirectSound::Write(void *buffer, int position, int length) +{ + // Lock sound buffer. + LPVOID pMem1, pMem2; + DWORD dwSize1, dwSize2; + HRESULT dsrval = pSoundBuffer->Lock(position, length, + &pMem1, &dwSize1, &pMem2, &dwSize2, 0); + if (dsrval != DS_OK) { + DebugResult("Sound Lock failed", dsrval); + return; + } + + // Copy the sample buffer into the sound buffer. + BYTE *pSample = (BYTE *) buffer; + memcpy(pMem1, pSample, dwSize1); + if (dwSize2 != 0) + memcpy(pMem2, pSample + dwSize1, dwSize2); + + // Unlock the sound buffer. + dsrval = pSoundBuffer->Unlock(pMem1, dwSize1, pMem2, dwSize2); + if (dsrval != DS_OK) { + DebugResult("Unlock failed\n", dsrval); + return; + } + + // Play the sound buffer. + dsrval = pSoundBuffer->Play(0, 0, DSBPLAY_LOOPING); + if (dsrval != DS_OK) { + DebugResult("Play failed", dsrval); + return; + } +} + +void DirectSound::Pause() +{ + HRESULT dsrval = pSoundBuffer->Stop(); + if (dsrval != DS_OK) + DebugResult("Stop failed", dsrval); + dsrval = pPrimaryBuffer->Stop(); + if (dsrval != DS_OK) + DebugResult("Stop primary failed", dsrval); +} + +void DirectSound::Resume() +{ + HRESULT dsrval = pPrimaryBuffer->Play(0, 0, DSBPLAY_LOOPING); + if (dsrval != DS_OK) + DebugResult("Play primary failed", dsrval); + dsrval = pSoundBuffer->Play(0, 0, DSBPLAY_LOOPING); + if (dsrval != DS_OK) + DebugResult("Play failed", dsrval); +} + +// Wave output implemenation. + +WaveOut::WaveOut() +{ + ready = FALSE; + + WAVEFORMATEX wfx; + memset(&wfx, 0, sizeof(wfx)); + wfx.wFormatTag = WAVE_FORMAT_PCM; + wfx.nChannels = 1; + wfx.nSamplesPerSec = 44100; + wfx.wBitsPerSample = 16; + wfx.nBlockAlign = wfx.nChannels*wfx.wBitsPerSample/8; + wfx.nAvgBytesPerSec = wfx.nBlockAlign*wfx.nSamplesPerSec; + wfx.cbSize = 0; + MMRESULT worval = waveOutOpen(&hWaveOut, WAVE_MAPPER, &wfx, 0, 0, 0); + if (worval != MMSYSERR_NOERROR) { + Debug("waveOutOpen returned %d\n", worval); + return; + } + memset(wave_header, 0, sizeof(wave_header)); + + last_unprepared = 0; + + ready = TRUE; +} + +WaveOut::~WaveOut() +{ + waveOutReset(hWaveOut); + UnprepareHeaders(); + waveOutClose(hWaveOut); +} + +BOOL WaveOut::Ready() +{ + return ready; +} + +int WaveOut::GetCurrentPosition() +{ + MMTIME mmtime; + memset(&mmtime, 0, sizeof(mmtime)); + mmtime.wType = TIME_BYTES; + MMRESULT worval = waveOutGetPosition(hWaveOut, &mmtime, sizeof(mmtime)); + if (worval != MMSYSERR_NOERROR) { + Debug("waveOutGetPosition(%d) returned %d\n", worval); + return -1; + } + int current_position = mmtime.u.cb % BUFFER_SIZE; + return current_position; +} + +void WaveOut::Write(void *buffer, int position, int length) +{ + // If we are called for a double length buffer split it in half. + if (length == 4*FRAGMENT_SIZE) { + int half = length/2; + Write(buffer, position, half); + Write(((char *) buffer) + half, position + half, half); + return; + } + + // Free up as many previous frags as possible. + for (;;) { + WAVEHDR &wh = wave_header[last_unprepared]; + if (!(wh.dwFlags & WHDR_DONE)) + break; + UnprepareHeader(last_unprepared); + last_unprepared++; + if (last_unprepared == BUFFER_FRAGS) + last_unprepared = 0; + } + + // Make sure the current header isn't in use. + int index = position/(2*FRAGMENT_SIZE); + WAVEHDR &wh = wave_header[index]; + if (wh.dwFlags & WHDR_DONE) + UnprepareHeader(index); + if (wh.dwFlags != 0) { + Debug("wave header %d is in use!\n", index); + return; + } + + // Prepare the header and write the sound data. + wh.lpData = wave_buffer + position; + wh.dwBufferLength = length; + wh.dwFlags = 0; + memcpy(wh.lpData, buffer, length); + MMRESULT worval = waveOutPrepareHeader(hWaveOut, &wh, sizeof(wh)); + if (worval != MMSYSERR_NOERROR) { + Debug("waveOutPrepareHeader(%d) returned %d\n", index, worval); + return; + } + worval = waveOutWrite(hWaveOut, &wh, sizeof(wh)); + if (worval != MMSYSERR_NOERROR) { + Debug("waveOutWrite(%d) returned %d\n", index, worval); + return; + } +} + +void WaveOut::Pause() +{ + waveOutPause(hWaveOut); +} + +void WaveOut::Resume() +{ + waveOutRestart(hWaveOut); +} + +void WaveOut::UnprepareHeader(int index) +{ + WAVEHDR &wh = wave_header[index]; + MMRESULT worval = waveOutUnprepareHeader(hWaveOut, &wh, sizeof(wh)); + if (worval != MMSYSERR_NOERROR) { + Debug("waveOutUnprepareHeader(%d) returned %d\n", index, worval); + return; + } + memset(&wh, 0, sizeof(wh)); +} + +void WaveOut::UnprepareHeaders() +{ + for (int i = 0; i < BUFFER_FRAGS; i++) { + WAVEHDR &wh = wave_header[i]; + if (wh.dwFlags & WHDR_DONE) + UnprepareHeader(i); + } +} + +#if 0 + +// Log player implemenation. + +void Log::Write() +{ + // Dump the sound output to the log for debugging. + { + int last = sound_buffer[0]; + int count = 1; + for (int i = 1; i < nsamples; i++) { + if (sound_buffer[i] == last) { + count++; + continue; + } + Debug("[%dx%d] ", count, last); + count = 1; + last = sound_buffer[i]; + } + Debug("[%dx%d] ", count, last); + } +} + +#endif + +#if 0 + +// File player implemenation. + +void Log::Write() +{ + // Log the sound output to a file for debugging. + { + static FILE *ofp = NULL; + if (ofp == NULL) { + ofp = fopen("debug.sid", "wb"); + if (ofp == NULL) + Debug("fopen failed\n"); + } + if (ofp != NULL) { + Debug("Write sound data to file\n"); + if (fwrite(sound_buffer, 1, 2*nsamples, ofp) != 2*nsamples) + Debug("fwrite failed\n"); + } + } +} + +#endif diff --git a/Src/SID_hp.h b/Src/SID_hp.h new file mode 100644 index 0000000..b82cc7b --- /dev/null +++ b/Src/SID_hp.h @@ -0,0 +1,191 @@ +/* + * SID_hp.h - 6581 emulation, HP-UX specific stuff + * + * Frodo (C) 1994-1997,2002-2005 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +extern "C" { + #include + #include + #include + #include +} + +#define TXBUFSIZE 16384 // bytes, not samples +#define TXFRAGSIZE 1024 // samples, not bytes +#define TXLOWWATER (TXBUFSIZE-(TXFRAGSIZE*2)) // bytes, not samples + +/* + * Initialization + */ + +void DigitalRenderer::init_sound(void) +{ + ready = false; + + if ((fd = open("/dev/audio", O_WRONLY | O_NDELAY, 0)) < 0) { + fprintf(stderr, "unable to open /dev/audio -> no sound\n"); + return; + } + + int flags; + if ((flags = fcntl (fd, F_GETFL, 0)) < 0) { + fprintf(stderr, "unable to set non-blocking mode for /dev/audio -> no sound\n"); + return; + } + flags |= O_NDELAY; + if (fcntl (fd, F_SETFL, flags) < 0) { + fprintf(stderr, "unable to set non-blocking mode for /dev/audio -> no sound\n"); + return; + } + + if (ioctl(fd, AUDIO_SET_DATA_FORMAT, AUDIO_FORMAT_LINEAR16BIT)) { + fprintf(stderr, "unable to select 16bit-linear sample format -> no sound\n"); + return; + } + + if (ioctl(fd, AUDIO_SET_SAMPLE_RATE, 44100)) { + fprintf(stderr, "unable to select 44.1kHz sample-rate -> no sound\n"); + return; + } + + if (ioctl(fd, AUDIO_SET_CHANNELS, 1)) { + fprintf(stderr, "unable to select 1-channel playback -> no sound\n"); + return; + } + + // choose between: + // AUDIO_OUT_SPEAKER + // AUDIO_OUT_HEADPHONE + // AUDIO_OUT_LINE + if (ioctl(fd, AUDIO_SET_OUTPUT, AUDIO_OUT_SPEAKER)) { + fprintf(stderr, "unable to select audio output -> no sound\n"); + return; + } + + { + // set volume: + audio_describe description; + audio_gains gains; + if (ioctl(fd, AUDIO_DESCRIBE, &description)) { + fprintf(stderr, "unable to get audio description -> no sound\n"); + return; + } + if (ioctl (fd, AUDIO_GET_GAINS, &gains)) { + fprintf(stderr, "unable to get gain values -> no sound\n"); + return; + } + + float volume = 1.0; + gains.transmit_gain = (int)((float)description.min_transmit_gain + + (float)(description.max_transmit_gain + - description.min_transmit_gain) + * volume); + if (ioctl (fd, AUDIO_SET_GAINS, &gains)) { + fprintf(stderr, "unable to set gain values -> no sound\n"); + return; + } + } + + if (ioctl(fd, AUDIO_SET_TXBUFSIZE, TXBUFSIZE)) { + fprintf(stderr, "unable to set transmission buffer size -> no sound\n"); + return; + } + + sound_calc_buf = new int16[TXFRAGSIZE]; + + linecnt = 0; + + ready = true; + return; +} + + +/* + * Destructor + */ + +DigitalRenderer::~DigitalRenderer() +{ + if (fd > 0) { + if (ready) { + ioctl(fd, AUDIO_DRAIN); + } + ioctl(fd, AUDIO_RESET); + close(fd); + } +} + + +/* + * Pause sound output + */ + +void DigitalRenderer::Pause(void) +{ +} + + +/* + * Resume sound output + */ + +void DigitalRenderer::Resume(void) +{ +} + + +/* + * Fill buffer, sample volume (for sampled voice) + */ + +void DigitalRenderer::EmulateLine(void) +{ + sample_buf[sample_in_ptr] = volume; + sample_in_ptr = (sample_in_ptr + 1) % SAMPLE_BUF_SIZE; + + // testing the audio status at each raster-line is + // really not necessary. Let's do it every 50th one.. + // ought to be enough by far. + + linecnt--; + if (linecnt < 0 && ready) { + linecnt = 50; + + // check whether we should add some more data to the + // transmission buffer. + + if (ioctl(fd, AUDIO_GET_STATUS, &status)) { + fprintf(stderr,"fatal: unable to get audio status\n"); + exit(20); + } + + if (status.transmit_buffer_count < TXLOWWATER) { + // add one sound fragment.. + calc_buffer(sound_calc_buf, TXFRAGSIZE*2); + + // since we've checked for enough space in the transmission buffer, + // it is an error if the non-blocking write returns anything but + // TXFRAGSIZE*2 + if (TXFRAGSIZE*2 != write (fd, sound_calc_buf, TXFRAGSIZE*2)) { + fprintf(stderr,"fatal: write to audio-device failed\n"); + exit(20); + } + } + } +} + diff --git a/Src/SID_linux.h b/Src/SID_linux.h new file mode 100644 index 0000000..43f31d1 --- /dev/null +++ b/Src/SID_linux.h @@ -0,0 +1,217 @@ +/* + * SID_linux.h - 6581 emulation, Linux specific stuff + * + * Frodo (C) 1994-1997,2002-2005 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include + +// Catweasel ioctls (included here for convenience) +#include +#define CWSID_IOCTL_TYPE ('S') +#define CWSID_IOCTL_RESET _IO(CWSID_IOCTL_TYPE, 0) +#define CWSID_IOCTL_CARDTYPE _IOR(CWSID_IOCTL_TYPE, 4, int) +#define CWSID_IOCTL_PAL _IO(CWSID_IOCTL_TYPE, 0x11) +#define CWSID_IOCTL_NTSC _IO(CWSID_IOCTL_TYPE, 0x12) +#define CWSID_IOCTL_DOUBLEBUFFER _IOW(CWSID_IOCTL_TYPE, 0x21, int) +#define CWSID_IOCTL_DELAY _IOW(CWSID_IOCTL_TYPE, 0x22, int) +#define CWSID_MAGIC 0x100 +#define HAVE_CWSID 1 + +#include "VIC.h" + + +/* + * Initialization + */ + +void DigitalRenderer::init_sound(void) +{ + int arg; + unsigned long format; + + ready = false; + devfd = open("/dev/dsp", O_WRONLY); + if (devfd < 0) + return; + + ioctl(devfd, SNDCTL_DSP_GETFMTS, &format); + if (!(format & AFMT_S16_LE)) + return; + format = AFMT_S16_LE; + ioctl(devfd, SNDCTL_DSP_SETFMT, &format); + + // Buffer size: 2^9 == 512 bytes. Note that too large buffers will not work + // very well: The speed of the C64 is slowed down to an average speed of + // 100% by the blocking write() call in EmulateLine(). If you use a buffer + // of, say 4096 bytes, that will happen only about every 4 frames, which + // means that the emulation runs much faster in some frames, and much + // slower in others. + // On really fast machines, it might make sense to use an even smaller + // buffer size. + arg = 0x00100009; + ioctl(devfd, SNDCTL_DSP_SETFRAGMENT, &arg); + arg = 0; + ioctl(devfd, SNDCTL_DSP_STEREO, &arg); + arg = 44100; + ioctl(devfd, SNDCTL_DSP_SPEED, &arg); + ioctl(devfd, SOUND_PCM_READ_RATE, &arg); + if (arg < 43000 || arg > 45000) + return; + + ioctl(devfd, SNDCTL_DSP_GETBLKSIZE, &sndbufsize); + sound_buffer = new int16[sndbufsize]; + ready = true; +} + + +/* + * Destructor + */ + +DigitalRenderer::~DigitalRenderer() +{ + if (devfd >= 0) + close(devfd); +} + + +/* + * Pause sound output + */ + +void DigitalRenderer::Pause(void) +{ +} + + +/* + * Resume sound output + */ + +void DigitalRenderer::Resume(void) +{ +} + + +/* + * Fill buffer, sample volume (for sampled voice) + */ + +void DigitalRenderer::EmulateLine(void) +{ + static int divisor = 0; + static int to_output = 0; + static int buffer_pos = 0; + + if (!ready) + return; + + sample_buf[sample_in_ptr] = volume; + sample_in_ptr = (sample_in_ptr + 1) % SAMPLE_BUF_SIZE; + + // Now see how many samples have to be added for this line + divisor += SAMPLE_FREQ; + while (divisor >= 0) + divisor -= TOTAL_RASTERS*SCREEN_FREQ, to_output++; + + // Calculate the sound data only when we have enough to fill + // the buffer entirely + if ((buffer_pos + to_output) >= sndbufsize) { + int datalen = sndbufsize - buffer_pos; + to_output -= datalen; + calc_buffer(sound_buffer + buffer_pos, datalen*2); + write(devfd, sound_buffer, sndbufsize*2); + buffer_pos = 0; + } +} + + +/* + * Renderer for Catweasel card + */ + +// Renderer class +class CatweaselRenderer : public SIDRenderer { +public: + CatweaselRenderer(); + virtual ~CatweaselRenderer(); + + virtual void Reset(void); + virtual void EmulateLine(void) {} + virtual void WriteRegister(uint16 adr, uint8 byte); + virtual void NewPrefs(Prefs *prefs) {} + virtual void Pause(void) {} + virtual void Resume(void) {} + +private: + int cwsid_fh; // Catweasel device file handle +}; + +// Constructor: Open Catweasel device and reset SID +CatweaselRenderer::CatweaselRenderer() +{ + cwsid_fh = open("/dev/sid", O_WRONLY); + if (cwsid_fh >= 0) { + int i; + if (ioctl(cwsid_fh, CWSID_IOCTL_CARDTYPE, &i) < 0 || i != CWSID_MAGIC) { + close(cwsid_fh); + cwsid_fh = -1; + } else { + ioctl(cwsid_fh, CWSID_IOCTL_RESET); + ioctl(cwsid_fh, CWSID_IOCTL_DOUBLEBUFFER, 0); + } + } + + Reset(); +} + +// Destructor: Reset SID and close Catweasel device +CatweaselRenderer::~CatweaselRenderer() +{ + Reset(); + + if (cwsid_fh >= 0) { + close(cwsid_fh); + cwsid_fh = -1; + } +} + +// Reset SID +void CatweaselRenderer::Reset(void) +{ + if (cwsid_fh >= 0) { + uint8 zero = 0; + ioctl(cwsid_fh, CWSID_IOCTL_RESET); + lseek(cwsid_fh, 24, SEEK_SET); + write(cwsid_fh, &zero, 1); + } +} + +// Write to register +void CatweaselRenderer::WriteRegister(uint16 adr, uint8 byte) +{ + if (cwsid_fh >= 0 && adr < 0x1a) { + lseek(cwsid_fh, adr, SEEK_SET); + write(cwsid_fh, &byte, 1); + lseek(cwsid_fh, adr, SEEK_SET); + write(cwsid_fh, &byte, 1); + } +} diff --git a/Src/SID_wii.h b/Src/SID_wii.h new file mode 100644 index 0000000..cdd652a --- /dev/null +++ b/Src/SID_wii.h @@ -0,0 +1,93 @@ +/* + * SID_linux.i - 6581 emulation, Linux specific stuff + * + * Frodo (C) 1994-1997,2002 Christian Bauer + * Linux sound stuff by Bernd Schmidt + */ + +#include "gcaudio.h" + +#include "VIC.h" + +/* + * Initialization + */ + +void DigitalRenderer::init_sound(void) +{ + this->sndbufsize = 512; + + ready = false; + InitialiseAudio(); + ResetAudio(); + + this->sound_buffer = new int16[this->sndbufsize]; + memset(this->sound_buffer, 0, sizeof(int16) * this->sndbufsize); + ready = true; +} + + +/* + * Destructor + */ + +DigitalRenderer::~DigitalRenderer() +{ + StopAudio(); +} + + +/* + * Pause sound output + */ + +void DigitalRenderer::Pause(void) +{ + StopAudio(); +} + + +/* + * Resume sound output + */ + +void DigitalRenderer::Resume(void) +{ + /* Done by PlayAudio() */ +} + + +/* + * Fill buffer, sample volume (for sampled voice) + */ + +void DigitalRenderer::EmulateLine(void) +{ + static int divisor = 0; + static int to_output = 0; + + if (!ready) + return; + + sample_buf[sample_in_ptr] = volume; + sample_in_ptr = (sample_in_ptr + 1) % SAMPLE_BUF_SIZE; + + /* + * Now see how many samples have to be added for this line + */ + divisor += SAMPLE_FREQ; + while (divisor >= 0) + divisor -= TOTAL_RASTERS*SCREEN_FREQ, to_output++; + + /* + * Calculate the sound data only when we have enough to fill + * the buffer entirely. + */ + if (to_output >= sndbufsize) { + int datalen = sndbufsize; + to_output -= datalen; + calc_buffer(sound_buffer, datalen * 2); + + PlaySound(sound_buffer, datalen); + } +} diff --git a/Src/VIC.cpp b/Src/VIC.cpp new file mode 100644 index 0000000..bed31de --- /dev/null +++ b/Src/VIC.cpp @@ -0,0 +1,1875 @@ +/* + * VIC.cpp - 6569R5 emulation (line based) + * + * Frodo (C) 1994-1997,2002-2005 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * Notes: + * ------ + * + * - The EmulateLine() function is called for every emulated + * raster line. It computes one pixel row of the graphics + * according to the current VIC register settings and returns + * the number of cycles available for the CPU in that line. + * - The graphics are output into an 8 bit chunky bitmap + * - The sprite-graphics priority handling and collision + * detection is done in a bit-oriented way with masks. + * The foreground/background pixel mask for the graphics + * is stored in the fore_mask_buf[] array. Multicolor + * sprites are converted from their original chunky format + * to a bitplane representation (two bit masks) for easier + * handling of priorities and collisions. + * - The sprite-sprite priority handling and collision + * detection is done in with the byte array spr_coll_buf[], + * that is used to keep track of which sprites are already + * visible at certain X positions. + * + * Incompatibilities: + * ------------------ + * + * - Raster effects that are achieved by modifying VIC registers + * in the middle of a raster line cannot be emulated + * - Sprite collisions are only detected within the visible + * screen area (excluding borders) + * - Sprites are only drawn if they completely fit within the + * left/right limits of the chunky bitmap + * - The Char ROM is not visible in the bitmap displays at + * addresses $0000 and $8000 + * - The IRQ is cleared on every write access to the flag + * register. This is a hack for the RMW instructions of the + * 6510 that first write back the original value. + */ + +#include "sysdeps.h" + +#include "VIC.h" +#include "C64.h" +#include "CPUC64.h" +#include "Display.h" +#include "Prefs.h" + + +// Test alignment on run-time for processors that can't access unaligned: +#ifdef __riscos__ +#define ALIGNMENT_CHECK +#endif + +// First and last displayed line +const unsigned FIRST_DISP_LINE = 0x10; +const unsigned LAST_DISP_LINE = 0x11f; + +// First and last possible line for Bad Lines +const unsigned FIRST_DMA_LINE = 0x30; +const unsigned LAST_DMA_LINE = 0xf7; + +// Display window coordinates +const int ROW25_YSTART = 0x33; +const int ROW25_YSTOP = 0xfb; +const int ROW24_YSTART = 0x37; +const int ROW24_YSTOP = 0xf7; + +#if defined(SMALL_DISPLAY) +/* This does not work yet, the sprite code doesn't know about it. */ +const int COL40_XSTART = 0x14; +const int COL40_XSTOP = 0x154; +const int COL38_XSTART = 0x1B; +const int COL38_XSTOP = 0x14B; +#else +const int COL40_XSTART = 0x20; +const int COL40_XSTOP = 0x160; +const int COL38_XSTART = 0x27; +const int COL38_XSTOP = 0x157; +#endif + + +// Tables for sprite X expansion +uint16 ExpTable[256] = { + 0x0000, 0x0003, 0x000C, 0x000F, 0x0030, 0x0033, 0x003C, 0x003F, + 0x00C0, 0x00C3, 0x00CC, 0x00CF, 0x00F0, 0x00F3, 0x00FC, 0x00FF, + 0x0300, 0x0303, 0x030C, 0x030F, 0x0330, 0x0333, 0x033C, 0x033F, + 0x03C0, 0x03C3, 0x03CC, 0x03CF, 0x03F0, 0x03F3, 0x03FC, 0x03FF, + 0x0C00, 0x0C03, 0x0C0C, 0x0C0F, 0x0C30, 0x0C33, 0x0C3C, 0x0C3F, + 0x0CC0, 0x0CC3, 0x0CCC, 0x0CCF, 0x0CF0, 0x0CF3, 0x0CFC, 0x0CFF, + 0x0F00, 0x0F03, 0x0F0C, 0x0F0F, 0x0F30, 0x0F33, 0x0F3C, 0x0F3F, + 0x0FC0, 0x0FC3, 0x0FCC, 0x0FCF, 0x0FF0, 0x0FF3, 0x0FFC, 0x0FFF, + 0x3000, 0x3003, 0x300C, 0x300F, 0x3030, 0x3033, 0x303C, 0x303F, + 0x30C0, 0x30C3, 0x30CC, 0x30CF, 0x30F0, 0x30F3, 0x30FC, 0x30FF, + 0x3300, 0x3303, 0x330C, 0x330F, 0x3330, 0x3333, 0x333C, 0x333F, + 0x33C0, 0x33C3, 0x33CC, 0x33CF, 0x33F0, 0x33F3, 0x33FC, 0x33FF, + 0x3C00, 0x3C03, 0x3C0C, 0x3C0F, 0x3C30, 0x3C33, 0x3C3C, 0x3C3F, + 0x3CC0, 0x3CC3, 0x3CCC, 0x3CCF, 0x3CF0, 0x3CF3, 0x3CFC, 0x3CFF, + 0x3F00, 0x3F03, 0x3F0C, 0x3F0F, 0x3F30, 0x3F33, 0x3F3C, 0x3F3F, + 0x3FC0, 0x3FC3, 0x3FCC, 0x3FCF, 0x3FF0, 0x3FF3, 0x3FFC, 0x3FFF, + 0xC000, 0xC003, 0xC00C, 0xC00F, 0xC030, 0xC033, 0xC03C, 0xC03F, + 0xC0C0, 0xC0C3, 0xC0CC, 0xC0CF, 0xC0F0, 0xC0F3, 0xC0FC, 0xC0FF, + 0xC300, 0xC303, 0xC30C, 0xC30F, 0xC330, 0xC333, 0xC33C, 0xC33F, + 0xC3C0, 0xC3C3, 0xC3CC, 0xC3CF, 0xC3F0, 0xC3F3, 0xC3FC, 0xC3FF, + 0xCC00, 0xCC03, 0xCC0C, 0xCC0F, 0xCC30, 0xCC33, 0xCC3C, 0xCC3F, + 0xCCC0, 0xCCC3, 0xCCCC, 0xCCCF, 0xCCF0, 0xCCF3, 0xCCFC, 0xCCFF, + 0xCF00, 0xCF03, 0xCF0C, 0xCF0F, 0xCF30, 0xCF33, 0xCF3C, 0xCF3F, + 0xCFC0, 0xCFC3, 0xCFCC, 0xCFCF, 0xCFF0, 0xCFF3, 0xCFFC, 0xCFFF, + 0xF000, 0xF003, 0xF00C, 0xF00F, 0xF030, 0xF033, 0xF03C, 0xF03F, + 0xF0C0, 0xF0C3, 0xF0CC, 0xF0CF, 0xF0F0, 0xF0F3, 0xF0FC, 0xF0FF, + 0xF300, 0xF303, 0xF30C, 0xF30F, 0xF330, 0xF333, 0xF33C, 0xF33F, + 0xF3C0, 0xF3C3, 0xF3CC, 0xF3CF, 0xF3F0, 0xF3F3, 0xF3FC, 0xF3FF, + 0xFC00, 0xFC03, 0xFC0C, 0xFC0F, 0xFC30, 0xFC33, 0xFC3C, 0xFC3F, + 0xFCC0, 0xFCC3, 0xFCCC, 0xFCCF, 0xFCF0, 0xFCF3, 0xFCFC, 0xFCFF, + 0xFF00, 0xFF03, 0xFF0C, 0xFF0F, 0xFF30, 0xFF33, 0xFF3C, 0xFF3F, + 0xFFC0, 0xFFC3, 0xFFCC, 0xFFCF, 0xFFF0, 0xFFF3, 0xFFFC, 0xFFFF +}; + +uint16 MultiExpTable[256] = { + 0x0000, 0x0005, 0x000A, 0x000F, 0x0050, 0x0055, 0x005A, 0x005F, + 0x00A0, 0x00A5, 0x00AA, 0x00AF, 0x00F0, 0x00F5, 0x00FA, 0x00FF, + 0x0500, 0x0505, 0x050A, 0x050F, 0x0550, 0x0555, 0x055A, 0x055F, + 0x05A0, 0x05A5, 0x05AA, 0x05AF, 0x05F0, 0x05F5, 0x05FA, 0x05FF, + 0x0A00, 0x0A05, 0x0A0A, 0x0A0F, 0x0A50, 0x0A55, 0x0A5A, 0x0A5F, + 0x0AA0, 0x0AA5, 0x0AAA, 0x0AAF, 0x0AF0, 0x0AF5, 0x0AFA, 0x0AFF, + 0x0F00, 0x0F05, 0x0F0A, 0x0F0F, 0x0F50, 0x0F55, 0x0F5A, 0x0F5F, + 0x0FA0, 0x0FA5, 0x0FAA, 0x0FAF, 0x0FF0, 0x0FF5, 0x0FFA, 0x0FFF, + 0x5000, 0x5005, 0x500A, 0x500F, 0x5050, 0x5055, 0x505A, 0x505F, + 0x50A0, 0x50A5, 0x50AA, 0x50AF, 0x50F0, 0x50F5, 0x50FA, 0x50FF, + 0x5500, 0x5505, 0x550A, 0x550F, 0x5550, 0x5555, 0x555A, 0x555F, + 0x55A0, 0x55A5, 0x55AA, 0x55AF, 0x55F0, 0x55F5, 0x55FA, 0x55FF, + 0x5A00, 0x5A05, 0x5A0A, 0x5A0F, 0x5A50, 0x5A55, 0x5A5A, 0x5A5F, + 0x5AA0, 0x5AA5, 0x5AAA, 0x5AAF, 0x5AF0, 0x5AF5, 0x5AFA, 0x5AFF, + 0x5F00, 0x5F05, 0x5F0A, 0x5F0F, 0x5F50, 0x5F55, 0x5F5A, 0x5F5F, + 0x5FA0, 0x5FA5, 0x5FAA, 0x5FAF, 0x5FF0, 0x5FF5, 0x5FFA, 0x5FFF, + 0xA000, 0xA005, 0xA00A, 0xA00F, 0xA050, 0xA055, 0xA05A, 0xA05F, + 0xA0A0, 0xA0A5, 0xA0AA, 0xA0AF, 0xA0F0, 0xA0F5, 0xA0FA, 0xA0FF, + 0xA500, 0xA505, 0xA50A, 0xA50F, 0xA550, 0xA555, 0xA55A, 0xA55F, + 0xA5A0, 0xA5A5, 0xA5AA, 0xA5AF, 0xA5F0, 0xA5F5, 0xA5FA, 0xA5FF, + 0xAA00, 0xAA05, 0xAA0A, 0xAA0F, 0xAA50, 0xAA55, 0xAA5A, 0xAA5F, + 0xAAA0, 0xAAA5, 0xAAAA, 0xAAAF, 0xAAF0, 0xAAF5, 0xAAFA, 0xAAFF, + 0xAF00, 0xAF05, 0xAF0A, 0xAF0F, 0xAF50, 0xAF55, 0xAF5A, 0xAF5F, + 0xAFA0, 0xAFA5, 0xAFAA, 0xAFAF, 0xAFF0, 0xAFF5, 0xAFFA, 0xAFFF, + 0xF000, 0xF005, 0xF00A, 0xF00F, 0xF050, 0xF055, 0xF05A, 0xF05F, + 0xF0A0, 0xF0A5, 0xF0AA, 0xF0AF, 0xF0F0, 0xF0F5, 0xF0FA, 0xF0FF, + 0xF500, 0xF505, 0xF50A, 0xF50F, 0xF550, 0xF555, 0xF55A, 0xF55F, + 0xF5A0, 0xF5A5, 0xF5AA, 0xF5AF, 0xF5F0, 0xF5F5, 0xF5FA, 0xF5FF, + 0xFA00, 0xFA05, 0xFA0A, 0xFA0F, 0xFA50, 0xFA55, 0xFA5A, 0xFA5F, + 0xFAA0, 0xFAA5, 0xFAAA, 0xFAAF, 0xFAF0, 0xFAF5, 0xFAFA, 0xFAFF, + 0xFF00, 0xFF05, 0xFF0A, 0xFF0F, 0xFF50, 0xFF55, 0xFF5A, 0xFF5F, + 0xFFA0, 0xFFA5, 0xFFAA, 0xFFAF, 0xFFF0, 0xFFF5, 0xFFFA, 0xFFFF +}; + +#ifdef __POWERPC__ +static union { + struct { + uint8 a,b,c,d,e,f,g,h; + } a; + double b; +} TextColorTable[16][16][256]; +#else +static union { + struct { + uint8 a,b,c,d; + } a; + uint32 b; +} TextColorTable[16][16][256][2]; +#endif + +#ifdef GLOBAL_VARS +static uint16 mc_color_lookup[4]; +#ifndef CAN_ACCESS_UNALIGNED +static uint8 text_chunky_buf[40*8]; +#endif +static uint16 mx[8]; // VIC registers +static uint8 mx8; +static uint8 my[8]; +static uint8 ctrl1, ctrl2; +static uint8 lpx, lpy; +static uint8 me, mxe, mye, mdp, mmc; +static uint8 vbase; +static uint8 irq_flag, irq_mask; +static uint8 clx_spr, clx_bgr; +static uint8 ec, b0c, b1c, b2c, b3c, mm0, mm1; +static uint8 sc[8]; + +static uint8 *ram, *char_rom, *color_ram; // Pointers to RAM and ROM +static C64 *the_c64; // Pointer to C64 +static C64Display *the_display; // Pointer to C64Display +static MOS6510 *the_cpu; // Pointer to 6510 + +static uint8 colors[256]; // Indices of the 16 C64 colors (16 times mirrored to avoid "& 0x0f") + +static uint8 ec_color, b0c_color, b1c_color, b2c_color, b3c_color; // Indices for exterior/background colors +static uint8 mm0_color, mm1_color; // Indices for MOB multicolors +static uint8 spr_color[8]; // Indices for MOB colors + +static uint32 ec_color_long; // ec_color expanded to 32 bits + +static uint8 matrix_line[40]; // Buffer for video line, read in Bad Lines +static uint8 color_line[40]; // Buffer for color line, read in Bad Lines + +#ifdef __POWERPC__ +static double chunky_tmp[DISPLAY_X/8]; // Temporary line buffer for GameKit speedup +#endif +static uint8 *chunky_line_start; // Pointer to start of current line in bitmap buffer +static int xmod; // Number of bytes per row + +static uint8 *matrix_base; // Video matrix base +static uint8 *char_base; // Character generator base +static uint8 *bitmap_base; // Bitmap base + +static uint16 raster_y; // Current raster line +static uint16 irq_raster; // Interrupt raster line +static uint16 dy_start; // Comparison values for border logic +static uint16 dy_stop; +static uint16 rc; // Row counter +static uint16 vc; // Video counter +static uint16 vc_base; // Video counter base +static uint16 x_scroll; // X scroll value +static uint16 y_scroll; // Y scroll value +static uint16 cia_vabase; // CIA VA14/15 video base + +static int display_idx; // Index of current display mode +static int skip_counter; // Counter for frame-skipping + +static uint16 mc[8]; // Sprite data counters +static uint8 sprite_on; // 8 Flags: Sprite display/DMA active + +static uint8 spr_coll_buf[0x180]; // Buffer for sprite-sprite collisions and priorities +static uint8 fore_mask_buf[0x180/8]; // Foreground mask for sprite-graphics collisions and priorities + +static bool display_state; // true: Display state, false: Idle state +static bool border_on; // Flag: Upper/lower border on +static bool border_40_col; // Flag: 40 column border +static bool frame_skipped; // Flag: Frame is being skipped +static uint8 bad_lines_enabled; // Flag: Bad Lines enabled for this frame +static bool lp_triggered; // Flag: Lightpen was triggered in this frame +#endif + + +/* + * Constructor: Initialize variables + */ + +static void init_text_color_table(uint8 *colors) +{ + for (int i = 0; i < 16; i++) + for (int j = 0; j < 16; j++) + for (int k = 0; k < 256; k++) { +#ifdef __POWERPC__ + TextColorTable[i][j][k].a.a = colors[k & 128 ? i : j]; + TextColorTable[i][j][k].a.b = colors[k & 64 ? i : j]; + TextColorTable[i][j][k].a.c = colors[k & 32 ? i : j]; + TextColorTable[i][j][k].a.d = colors[k & 16 ? i : j]; + TextColorTable[i][j][k].a.e = colors[k & 8 ? i : j]; + TextColorTable[i][j][k].a.f = colors[k & 4 ? i : j]; + TextColorTable[i][j][k].a.g = colors[k & 2 ? i : j]; + TextColorTable[i][j][k].a.h = colors[k & 1 ? i : j]; +#else + TextColorTable[i][j][k][0].a.a = colors[k & 128 ? i : j]; + TextColorTable[i][j][k][0].a.b = colors[k & 64 ? i : j]; + TextColorTable[i][j][k][0].a.c = colors[k & 32 ? i : j]; + TextColorTable[i][j][k][0].a.d = colors[k & 16 ? i : j]; + TextColorTable[i][j][k][1].a.a = colors[k & 8 ? i : j]; + TextColorTable[i][j][k][1].a.b = colors[k & 4 ? i : j]; + TextColorTable[i][j][k][1].a.c = colors[k & 2 ? i : j]; + TextColorTable[i][j][k][1].a.d = colors[k & 1 ? i : j]; +#endif + } +} + +MOS6569::MOS6569(C64 *c64, C64Display *disp, MOS6510 *CPU, uint8 *RAM, uint8 *Char, uint8 *Color) +#ifndef GLOBAL_VARS + : ram(RAM), char_rom(Char), color_ram(Color), the_c64(c64), the_display(disp), the_cpu(CPU) +#endif +{ + int i; + + // Set pointers +#ifdef GLOBAL_VARS + the_c64 = c64; + the_display = disp; + the_cpu = CPU; + ram = RAM; + char_rom = Char; + color_ram = Color; +#endif + matrix_base = RAM; + char_base = RAM; + bitmap_base = RAM; + + // Get bitmap info + chunky_line_start = disp->BitmapBase(); + xmod = disp->BitmapXMod(); + + // Initialize VIC registers + mx8 = 0; + ctrl1 = ctrl2 = 0; + lpx = lpy = 0; + me = mxe = mye = mdp = mmc = 0; + vbase = irq_flag = irq_mask = 0; + clx_spr = clx_bgr = 0; + cia_vabase = 0; + ec = b0c = b1c = b2c = b3c = mm0 = mm1 = 0; + for (i=0; i<8; i++) mx[i] = my[i] = sc[i] = 0; + + // Initialize other variables + raster_y = 0xffff; + rc = 7; + irq_raster = vc = vc_base = x_scroll = y_scroll = 0; + dy_start = ROW24_YSTART; + dy_stop = ROW24_YSTOP; + + display_idx = 0; + display_state = false; + border_on = false; + lp_triggered = false; + + sprite_on = 0; + for (i=0; i<8; i++) + mc[i] = 63; + + frame_skipped = false; + skip_counter = 1; + + // Clear foreground mask + memset(fore_mask_buf, 0, DISPLAY_X/8); + + // Preset colors to black + disp->InitColors(colors); + init_text_color_table(colors); + ec_color = b0c_color = b1c_color = b2c_color = b3c_color = mm0_color = mm1_color = colors[0]; + ec_color_long = (ec_color << 24) | (ec_color << 16) | (ec_color << 8) | ec_color; + for (i=0; i<8; i++) spr_color[i] = colors[0]; +} + + +/* + * Reinitialize the colors table for when the palette has changed + */ + +void MOS6569::ReInitColors(void) +{ + int i; + + // Build inverse color table. + uint8 xlate_colors[256]; + memset(xlate_colors, 0, sizeof(xlate_colors)); + for (i = 0; i < 16; i++) + xlate_colors[colors[i]] = i; + + // Get the new colors. + the_display->InitColors(colors); + init_text_color_table(colors); + + // Build color translation table. + for (i = 0; i < 256; i++) + xlate_colors[i] = colors[xlate_colors[i]]; + + // Translate all the old colors variables. + ec_color = colors[ec]; + ec_color_long = ec_color | (ec_color << 8) | (ec_color << 16) | (ec_color << 24); + b0c_color = colors[b0c]; + b1c_color = colors[b1c]; + b2c_color = colors[b2c]; + b3c_color = colors[b3c]; + mm0_color = colors[mm0]; + mm1_color = colors[mm1]; + for (i = 0; i < 8; i++) + spr_color[i] = colors[sc[i]]; + mc_color_lookup[0] = b0c_color | (b0c_color << 8); + mc_color_lookup[1] = b1c_color | (b1c_color << 8); + mc_color_lookup[2] = b2c_color | (b2c_color << 8); + + // Translate the chunky buffer. + uint8 *scanline = the_display->BitmapBase(); + for (int y = 0; y < DISPLAY_Y; y++) { + for (int x = 0; x < DISPLAY_X; x++) + scanline[x] = xlate_colors[scanline[x]]; + scanline += xmod; + } +} + +#ifdef GLOBAL_VARS +static void make_mc_table(void) +#else +void MOS6569::make_mc_table(void) +#endif +{ + mc_color_lookup[0] = b0c_color | (b0c_color << 8); + mc_color_lookup[1] = b1c_color | (b1c_color << 8); + mc_color_lookup[2] = b2c_color | (b2c_color << 8); +} + + +/* + * Convert video address to pointer + */ + +#ifdef GLOBAL_VARS +static inline uint8 *get_physical(uint16 adr) +#else +inline uint8 *MOS6569::get_physical(uint16 adr) +#endif +{ + int va = adr | cia_vabase; + if ((va & 0x7000) == 0x1000) + return char_rom + (va & 0x0fff); + else + return ram + va; +} + + +/* + * Get VIC state + */ + +void MOS6569::GetState(MOS6569State *vd) +{ + int i; + + vd->m0x = mx[0] & 0xff; vd->m0y = my[0]; + vd->m1x = mx[1] & 0xff; vd->m1y = my[1]; + vd->m2x = mx[2] & 0xff; vd->m2y = my[2]; + vd->m3x = mx[3] & 0xff; vd->m3y = my[3]; + vd->m4x = mx[4] & 0xff; vd->m4y = my[4]; + vd->m5x = mx[5] & 0xff; vd->m5y = my[5]; + vd->m6x = mx[6] & 0xff; vd->m6y = my[6]; + vd->m7x = mx[7] & 0xff; vd->m7y = my[7]; + vd->mx8 = mx8; + + vd->ctrl1 = (ctrl1 & 0x7f) | ((raster_y & 0x100) >> 1); + vd->raster = raster_y & 0xff; + vd->lpx = lpx; vd->lpy = lpy; + vd->ctrl2 = ctrl2; + vd->vbase = vbase; + vd->irq_flag = irq_flag; + vd->irq_mask = irq_mask; + + vd->me = me; vd->mxe = mxe; vd->mye = mye; vd->mdp = mdp; vd->mmc = mmc; + vd->mm = clx_spr; vd->md = clx_bgr; + + vd->ec = ec; + vd->b0c = b0c; vd->b1c = b1c; vd->b2c = b2c; vd->b3c = b3c; + vd->mm0 = mm0; vd->mm1 = mm1; + vd->m0c = sc[0]; vd->m1c = sc[1]; + vd->m2c = sc[2]; vd->m3c = sc[3]; + vd->m4c = sc[4]; vd->m5c = sc[5]; + vd->m6c = sc[6]; vd->m7c = sc[7]; + + vd->pad0 = 0; + vd->irq_raster = irq_raster; + vd->vc = vc; + vd->vc_base = vc_base; + vd->rc = rc; + vd->spr_dma = vd->spr_disp = sprite_on; + for (i=0; i<8; i++) + vd->mc[i] = vd->mc_base[i] = mc[i]; + vd->display_state = display_state; + vd->bad_line = raster_y >= FIRST_DMA_LINE && raster_y <= LAST_DMA_LINE && ((raster_y & 7) == y_scroll) && bad_lines_enabled; + vd->bad_line_enable = bad_lines_enabled; + vd->lp_triggered = lp_triggered; + vd->border_on = border_on; + + vd->bank_base = cia_vabase; + vd->matrix_base = ((vbase & 0xf0) << 6) | cia_vabase; + vd->char_base = ((vbase & 0x0e) << 10) | cia_vabase; + vd->bitmap_base = ((vbase & 0x08) << 10) | cia_vabase; + for (i=0; i<8; i++) + vd->sprite_base[i] = (matrix_base[0x3f8 + i] << 6) | cia_vabase; + + vd->cycle = 1; + vd->raster_x = 0; + vd->ml_index = 0; + vd->ref_cnt = 0xff; + vd->last_vic_byte = 0; + vd->ud_border_on = border_on; +} + + +/* + * Set VIC state (only works if in VBlank) + */ + +void MOS6569::SetState(MOS6569State *vd) +{ + int i, j; + + mx[0] = vd->m0x; my[0] = vd->m0y; + mx[1] = vd->m1x; my[1] = vd->m1y; + mx[2] = vd->m2x; my[2] = vd->m2y; + mx[3] = vd->m3x; my[3] = vd->m3y; + mx[4] = vd->m4x; my[4] = vd->m4y; + mx[5] = vd->m5x; my[5] = vd->m5y; + mx[6] = vd->m6x; my[6] = vd->m6y; + mx[7] = vd->m7x; my[7] = vd->m7y; + mx8 = vd->mx8; + for (i=0, j=1; i<8; i++, j<<=1) { + if (mx8 & j) + mx[i] |= 0x100; + else + mx[i] &= 0xff; + } + + ctrl1 = vd->ctrl1; + ctrl2 = vd->ctrl2; + x_scroll = ctrl2 & 7; + y_scroll = ctrl1 & 7; + if (ctrl1 & 8) { + dy_start = ROW25_YSTART; + dy_stop = ROW25_YSTOP; + } else { + dy_start = ROW24_YSTART; + dy_stop = ROW24_YSTOP; + } + border_40_col = ctrl2 & 8; + display_idx = ((ctrl1 & 0x60) | (ctrl2 & 0x10)) >> 4; + + raster_y = 0; + lpx = vd->lpx; lpy = vd->lpy; + + vbase = vd->vbase; + cia_vabase = vd->bank_base; + matrix_base = get_physical((vbase & 0xf0) << 6); + char_base = get_physical((vbase & 0x0e) << 10); + bitmap_base = get_physical((vbase & 0x08) << 10); + + irq_flag = vd->irq_flag; + irq_mask = vd->irq_mask; + + me = vd->me; mxe = vd->mxe; mye = vd->mye; mdp = vd->mdp; mmc = vd->mmc; + clx_spr = vd->mm; clx_bgr = vd->md; + + ec = vd->ec; + ec_color = colors[ec]; + ec_color_long = (ec_color << 24) | (ec_color << 16) | (ec_color << 8) | ec_color; + + b0c = vd->b0c; b1c = vd->b1c; b2c = vd->b2c; b3c = vd->b3c; + b0c_color = colors[b0c]; + b1c_color = colors[b1c]; + b2c_color = colors[b2c]; + b3c_color = colors[b3c]; + make_mc_table(); + + mm0 = vd->mm0; mm1 = vd->mm1; + mm0_color = colors[mm0]; + mm1_color = colors[mm1]; + + sc[0] = vd->m0c; sc[1] = vd->m1c; + sc[2] = vd->m2c; sc[3] = vd->m3c; + sc[4] = vd->m4c; sc[5] = vd->m5c; + sc[6] = vd->m6c; sc[7] = vd->m7c; + for (i=0; i<8; i++) + spr_color[i] = colors[sc[i]]; + + irq_raster = vd->irq_raster; + vc = vd->vc; + vc_base = vd->vc_base; + rc = vd->rc; + sprite_on = vd->spr_dma; + for (i=0; i<8; i++) + mc[i] = vd->mc[i]; + display_state = vd->display_state; + bad_lines_enabled = vd->bad_line_enable; + lp_triggered = vd->lp_triggered; + border_on = vd->border_on; +} + + +/* + * Trigger raster IRQ + */ + +#ifdef GLOBAL_VARS +static inline void raster_irq(void) +#else +inline void MOS6569::raster_irq(void) +#endif +{ + irq_flag |= 0x01; + if (irq_mask & 0x01) { + irq_flag |= 0x80; + the_cpu->TriggerVICIRQ(); + } +} + + +/* + * Read from VIC register + */ + +uint8 MOS6569::ReadRegister(uint16 adr) +{ + switch (adr) { + case 0x00: case 0x02: case 0x04: case 0x06: + case 0x08: case 0x0a: case 0x0c: case 0x0e: + return mx[adr >> 1]; + + case 0x01: case 0x03: case 0x05: case 0x07: + case 0x09: case 0x0b: case 0x0d: case 0x0f: + return my[adr >> 1]; + + case 0x10: // Sprite X position MSB + return mx8; + + case 0x11: // Control register 1 + return (ctrl1 & 0x7f) | ((raster_y & 0x100) >> 1); + + case 0x12: // Raster counter + return raster_y; + + case 0x13: // Light pen X + return lpx; + + case 0x14: // Light pen Y + return lpy; + + case 0x15: // Sprite enable + return me; + + case 0x16: // Control register 2 + return ctrl2 | 0xc0; + + case 0x17: // Sprite Y expansion + return mye; + + case 0x18: // Memory pointers + return vbase | 0x01; + + case 0x19: // IRQ flags + return irq_flag | 0x70; + + case 0x1a: // IRQ mask + return irq_mask | 0xf0; + + case 0x1b: // Sprite data priority + return mdp; + + case 0x1c: // Sprite multicolor + return mmc; + + case 0x1d: // Sprite X expansion + return mxe; + + case 0x1e:{ // Sprite-sprite collision + uint8 ret = clx_spr; + clx_spr = 0; // Read and clear + return ret; + } + + case 0x1f:{ // Sprite-background collision + uint8 ret = clx_bgr; + clx_bgr = 0; // Read and clear + return ret; + } + + case 0x20: return ec | 0xf0; + case 0x21: return b0c | 0xf0; + case 0x22: return b1c | 0xf0; + case 0x23: return b2c | 0xf0; + case 0x24: return b3c | 0xf0; + case 0x25: return mm0 | 0xf0; + case 0x26: return mm1 | 0xf0; + + case 0x27: case 0x28: case 0x29: case 0x2a: + case 0x2b: case 0x2c: case 0x2d: case 0x2e: + return sc[adr - 0x27] | 0xf0; + + default: + return 0xff; + } +} + + +/* + * Write to VIC register + */ + +void MOS6569::WriteRegister(uint16 adr, uint8 byte) +{ + switch (adr) { + case 0x00: case 0x02: case 0x04: case 0x06: + case 0x08: case 0x0a: case 0x0c: case 0x0e: + mx[adr >> 1] = (mx[adr >> 1] & 0xff00) | byte; + break; + + case 0x10:{ + int i, j; + mx8 = byte; + for (i=0, j=1; i<8; i++, j<<=1) { + if (mx8 & j) + mx[i] |= 0x100; + else + mx[i] &= 0xff; + } + break; + } + + case 0x01: case 0x03: case 0x05: case 0x07: + case 0x09: case 0x0b: case 0x0d: case 0x0f: + my[adr >> 1] = byte; + break; + + case 0x11:{ // Control register 1 + ctrl1 = byte; + y_scroll = byte & 7; + + uint16 new_irq_raster = (irq_raster & 0xff) | ((byte & 0x80) << 1); + if (irq_raster != new_irq_raster && raster_y == new_irq_raster) + raster_irq(); + irq_raster = new_irq_raster; + + if (byte & 8) { + dy_start = ROW25_YSTART; + dy_stop = ROW25_YSTOP; + } else { + dy_start = ROW24_YSTART; + dy_stop = ROW24_YSTOP; + } + + display_idx = ((ctrl1 & 0x60) | (ctrl2 & 0x10)) >> 4; + break; + } + + case 0x12:{ // Raster counter + uint16 new_irq_raster = (irq_raster & 0xff00) | byte; + if (irq_raster != new_irq_raster && raster_y == new_irq_raster) + raster_irq(); + irq_raster = new_irq_raster; + break; + } + + case 0x15: // Sprite enable + me = byte; + break; + + case 0x16: // Control register 2 + ctrl2 = byte; + x_scroll = byte & 7; + border_40_col = byte & 8; + display_idx = ((ctrl1 & 0x60) | (ctrl2 & 0x10)) >> 4; + break; + + case 0x17: // Sprite Y expansion + mye = byte; + break; + + case 0x18: // Memory pointers + vbase = byte; + matrix_base = get_physical((byte & 0xf0) << 6); + char_base = get_physical((byte & 0x0e) << 10); + bitmap_base = get_physical((byte & 0x08) << 10); + break; + + case 0x19: // IRQ flags + irq_flag = irq_flag & (~byte & 0x0f); + the_cpu->ClearVICIRQ(); // Clear interrupt (hack!) + if (irq_flag & irq_mask) // Set master bit if allowed interrupt still pending + irq_flag |= 0x80; + break; + + case 0x1a: // IRQ mask + irq_mask = byte & 0x0f; + if (irq_flag & irq_mask) { // Trigger interrupt if pending and now allowed + irq_flag |= 0x80; + the_cpu->TriggerVICIRQ(); + } else { + irq_flag &= 0x7f; + the_cpu->ClearVICIRQ(); + } + break; + + case 0x1b: // Sprite data priority + mdp = byte; + break; + + case 0x1c: // Sprite multicolor + mmc = byte; + break; + + case 0x1d: // Sprite X expansion + mxe = byte; + break; + + case 0x20: + ec_color = colors[ec = byte]; + ec_color_long = (ec_color << 24) | (ec_color << 16) | (ec_color << 8) | ec_color; + break; + + case 0x21: + if (b0c != byte) { + b0c_color = colors[b0c = byte & 0xF]; + make_mc_table(); + } + break; + + case 0x22: + if (b1c != byte) { + b1c_color = colors[b1c = byte & 0xF]; + make_mc_table(); + } + break; + + case 0x23: + if (b2c != byte) { + b2c_color = colors[b2c = byte & 0xF]; + make_mc_table(); + } + break; + + case 0x24: b3c_color = colors[b3c = byte & 0xF]; break; + case 0x25: mm0_color = colors[mm0 = byte]; break; + case 0x26: mm1_color = colors[mm1 = byte]; break; + + case 0x27: case 0x28: case 0x29: case 0x2a: + case 0x2b: case 0x2c: case 0x2d: case 0x2e: + spr_color[adr - 0x27] = colors[sc[adr - 0x27] = byte]; + break; + } +} + + +/* + * CIA VA14/15 has changed + */ + +void MOS6569::ChangedVA(uint16 new_va) +{ + cia_vabase = new_va << 14; + WriteRegister(0x18, vbase); // Force update of memory pointers +} + + +/* + * Trigger lightpen interrupt, latch lightpen coordinates + */ + +void MOS6569::TriggerLightpen(void) +{ + if (!lp_triggered) { // Lightpen triggers only once per frame + lp_triggered = true; + + lpx = 0; // Latch current coordinates + lpy = raster_y; + + irq_flag |= 0x08; // Trigger IRQ + if (irq_mask & 0x08) { + irq_flag |= 0x80; + the_cpu->TriggerVICIRQ(); + } + } +} + + +/* + * VIC vertical blank: Reset counters and redraw screen + */ + +#ifdef GLOBAL_VARS +static inline void vblank(void) +#else +inline void MOS6569::vblank(void) +#endif +{ + raster_y = vc_base = 0; + lp_triggered = false; + + if (!(frame_skipped = --skip_counter)) + skip_counter = ThePrefs.SkipFrames; + + the_c64->VBlank(!frame_skipped); + + // Get bitmap pointer for next frame. This must be done + // after calling the_c64->VBlank() because the preferences + // and screen configuration may have been changed there + chunky_line_start = the_display->BitmapBase(); + xmod = the_display->BitmapXMod(); +} + + +#ifdef __riscos__ +#include "el_Acorn.h" +#else + +#ifdef GLOBAL_VARS +static inline void el_std_text(uint8 *p, uint8 *q, uint8 *r) +#else +inline void MOS6569::el_std_text(uint8 *p, uint8 *q, uint8 *r) +#endif +{ + unsigned int b0cc = b0c; +#ifdef __POWERPC__ + double *dp = (double *)p - 1; +#else + uint32 *lp = (uint32 *)p; +#endif + uint8 *cp = color_line; + uint8 *mp = matrix_line; + + // Loop for 40 characters + for (int i=0; i<40; i++) { + uint8 color = cp[i]; + uint8 data = r[i] = q[mp[i] << 3]; + +#ifdef __POWERPC__ + *++dp = TextColorTable[color][b0cc][data].b; +#else + *lp++ = TextColorTable[color][b0cc][data][0].b; + *lp++ = TextColorTable[color][b0cc][data][1].b; +#endif + } +} + + +#ifdef GLOBAL_VARS +static inline void el_mc_text(uint8 *p, uint8 *q, uint8 *r) +#else +inline void MOS6569::el_mc_text(uint8 *p, uint8 *q, uint8 *r) +#endif +{ + uint16 *wp = (uint16 *)p; + uint8 *cp = color_line; + uint8 *mp = matrix_line; + uint16 *mclp = mc_color_lookup; + + // Loop for 40 characters + for (int i=0; i<40; i++) { + uint8 data = q[mp[i] << 3]; + + if (cp[i] & 8) { + uint8 color = colors[cp[i] & 7]; + r[i] = (data & 0xaa) | (data & 0xaa) >> 1; + mclp[3] = color | (color << 8); + *wp++ = mclp[(data >> 6) & 3]; + *wp++ = mclp[(data >> 4) & 3]; + *wp++ = mclp[(data >> 2) & 3]; + *wp++ = mclp[(data >> 0) & 3]; + + } else { // Standard mode in multicolor mode + uint8 color = cp[i]; + r[i] = data; +#ifdef __POWERPC__ + *(double *)wp = TextColorTable[color][b0c][data].b; + wp += 4; +#else + *(uint32 *)wp = TextColorTable[color][b0c][data][0].b; + wp += 2; + *(uint32 *)wp = TextColorTable[color][b0c][data][1].b; + wp += 2; +#endif + } + } +} + + +#ifdef GLOBAL_VARS +static inline void el_std_bitmap(uint8 *p, uint8 *q, uint8 *r) +#else +inline void MOS6569::el_std_bitmap(uint8 *p, uint8 *q, uint8 *r) +#endif +{ +#ifdef __POWERPC__ + double *dp = (double *)p-1; +#else + uint32 *lp = (uint32 *)p; +#endif + uint8 *mp = matrix_line; + + // Loop for 40 characters + for (int i=0; i<40; i++, q+=8) { + uint8 data = r[i] = *q; + uint8 color = mp[i] >> 4; + uint8 bcolor = mp[i] & 15; + +#ifdef __POWERPC__ + *++dp = TextColorTable[color][bcolor][data].b; +#else + *lp++ = TextColorTable[color][bcolor][data][0].b; + *lp++ = TextColorTable[color][bcolor][data][1].b; +#endif + } +} + + +#ifdef GLOBAL_VARS +static inline void el_mc_bitmap(uint8 *p, uint8 *q, uint8 *r) +#else +inline void MOS6569::el_mc_bitmap(uint8 *p, uint8 *q, uint8 *r) +#endif +{ + uint16 lookup[4]; + uint16 *wp = (uint16 *)p - 1; + uint8 *cp = color_line; + uint8 *mp = matrix_line; + +#ifdef __GNU_C__ + &lookup; /* Statement with no effect other than preventing GCC from + * putting the array in a register, which generates + * spectacularly bad code. */ +#endif + + lookup[0] = (b0c_color << 8) | b0c_color; + + // Loop for 40 characters + for (int i=0; i<40; i++, q+=8) { + uint8 color, acolor, bcolor; + + color = colors[mp[i] >> 4]; + lookup[1] = (color << 8) | color; + bcolor = colors[mp[i]]; + lookup[2] = (bcolor << 8) | bcolor; + acolor = colors[cp[i]]; + lookup[3] = (acolor << 8) | acolor; + + uint8 data = *q; + r[i] = (data & 0xaa) | (data & 0xaa) >> 1; + + *++wp = lookup[(data >> 6) & 3]; + *++wp = lookup[(data >> 4) & 3]; + *++wp = lookup[(data >> 2) & 3]; + *++wp = lookup[(data >> 0) & 3]; + } +} + + +#ifdef GLOBAL_VARS +static inline void el_ecm_text(uint8 *p, uint8 *q, uint8 *r) +#else +inline void MOS6569::el_ecm_text(uint8 *p, uint8 *q, uint8 *r) +#endif +{ +#ifdef __POWERPC__ + double *dp = (double *)p - 1; +#else + uint32 *lp = (uint32 *)p; +#endif + uint8 *cp = color_line; + uint8 *mp = matrix_line; + uint8 *bcp = &b0c; + + // Loop for 40 characters + for (int i=0; i<40; i++) { + uint8 data = r[i] = mp[i]; + uint8 color = cp[i]; + uint8 bcolor = bcp[(data >> 6) & 3]; + + data = q[(data & 0x3f) << 3]; +#ifdef __POWERPC__ + *++dp = TextColorTable[color][bcolor][data].b; +#else + *lp++ = TextColorTable[color][bcolor][data][0].b; + *lp++ = TextColorTable[color][bcolor][data][1].b; +#endif + } +} + + +#ifdef GLOBAL_VARS +static inline void el_std_idle(uint8 *p, uint8 *r) +#else +inline void MOS6569::el_std_idle(uint8 *p, uint8 *r) +#endif +{ +#ifdef __POWERPC__ + uint8 data = *get_physical(ctrl1 & 0x40 ? 0x39ff : 0x3fff); + double *dp = (double *)p - 1; + double conv = TextColorTable[0][b0c][data].b; + r--; + + for (int i=0; i<40; i++) { + *++dp = conv; + *++r = data; + } +#else + uint8 data = *get_physical(ctrl1 & 0x40 ? 0x39ff : 0x3fff); + uint32 *lp = (uint32 *)p; + uint32 conv0 = TextColorTable[0][b0c][data][0].b; + uint32 conv1 = TextColorTable[0][b0c][data][1].b; + + for (int i=0; i<40; i++) { + *lp++ = conv0; + *lp++ = conv1; + *r++ = data; + } +#endif +} + + +#ifdef GLOBAL_VARS +static inline void el_mc_idle(uint8 *p, uint8 *r) +#else +inline void MOS6569::el_mc_idle(uint8 *p, uint8 *r) +#endif +{ + uint8 data = *get_physical(0x3fff); + uint32 *lp = (uint32 *)p - 1; + r--; + + uint16 lookup[4]; + lookup[0] = (b0c_color << 8) | b0c_color; + lookup[1] = lookup[2] = lookup[3] = colors[0]; + + uint16 conv0 = (lookup[(data >> 6) & 3] << 16) | lookup[(data >> 4) & 3]; + uint16 conv1 = (lookup[(data >> 2) & 3] << 16) | lookup[(data >> 0) & 3]; + + for (int i=0; i<40; i++) { + *++lp = conv0; + *++lp = conv1; + *++r = data; + } +} + +#endif //__riscos__ + + +#ifdef GLOBAL_VARS +static inline void el_sprites(uint8 *chunky_ptr) +#else +inline void MOS6569::el_sprites(uint8 *chunky_ptr) +#endif +{ + int i; + int snum, sbit; // Sprite number/bit mask + int spr_coll=0, gfx_coll=0; + + // Draw each active sprite + for (snum=0, sbit=1; snum<8; snum++, sbit<<=1) + if ((sprite_on & sbit) && mx[snum] < DISPLAY_X-32) { + int spr_mask_pos; // Sprite bit position in fore_mask_buf + uint32 sdata, fore_mask; + + uint8 *p = chunky_ptr + mx[snum] + 8; + uint8 *q = spr_coll_buf + mx[snum] + 8; + + uint8 *sdatap = get_physical(matrix_base[0x3f8 + snum] << 6 | mc[snum]); + sdata = (*sdatap << 24) | (*(sdatap+1) << 16) | (*(sdatap+2) << 8); + + uint8 color = spr_color[snum]; + + spr_mask_pos = mx[snum] + 8 - x_scroll; + + uint8 *fmbp = fore_mask_buf + (spr_mask_pos / 8); + int sshift = spr_mask_pos & 7; + fore_mask = (((*(fmbp+0) << 24) | (*(fmbp+1) << 16) | (*(fmbp+2) << 8) + | (*(fmbp+3))) << sshift) | (*(fmbp+4) >> (8-sshift)); + + if (mxe & sbit) { // X-expanded + if (mx[snum] >= DISPLAY_X-56) + continue; + + uint32 sdata_l = 0, sdata_r = 0, fore_mask_r; + fore_mask_r = (((*(fmbp+4) << 24) | (*(fmbp+5) << 16) | (*(fmbp+6) << 8) + | (*(fmbp+7))) << sshift) | (*(fmbp+8) >> (8-sshift)); + + if (mmc & sbit) { // Multicolor mode + uint32 plane0_l, plane0_r, plane1_l, plane1_r; + + // Expand sprite data + sdata_l = MultiExpTable[sdata >> 24 & 0xff] << 16 | MultiExpTable[sdata >> 16 & 0xff]; + sdata_r = MultiExpTable[sdata >> 8 & 0xff] << 16; + + // Convert sprite chunky pixels to bitplanes + plane0_l = (sdata_l & 0x55555555) | (sdata_l & 0x55555555) << 1; + plane1_l = (sdata_l & 0xaaaaaaaa) | (sdata_l & 0xaaaaaaaa) >> 1; + plane0_r = (sdata_r & 0x55555555) | (sdata_r & 0x55555555) << 1; + plane1_r = (sdata_r & 0xaaaaaaaa) | (sdata_r & 0xaaaaaaaa) >> 1; + + // Collision with graphics? + if ((fore_mask & (plane0_l | plane1_l)) || (fore_mask_r & (plane0_r | plane1_r))) { + gfx_coll |= sbit; + if (mdp & sbit) { + plane0_l &= ~fore_mask; // Mask sprite if in background + plane1_l &= ~fore_mask; + plane0_r &= ~fore_mask_r; + plane1_r &= ~fore_mask_r; + } + } + + // Paint sprite + for (i=0; i<32; i++, plane0_l<<=1, plane1_l<<=1) { + uint8 col; + if (plane1_l & 0x80000000) { + if (plane0_l & 0x80000000) + col = mm1_color; + else + col = color; + } else { + if (plane0_l & 0x80000000) + col = mm0_color; + else + continue; + } + if (q[i]) + spr_coll |= q[i] | sbit; + else { + p[i] = col; + q[i] = sbit; + } + } + for (; i<48; i++, plane0_r<<=1, plane1_r<<=1) { + uint8 col; + if (plane1_r & 0x80000000) { + if (plane0_r & 0x80000000) + col = mm1_color; + else + col = color; + } else { + if (plane0_r & 0x80000000) + col = mm0_color; + else + continue; + } + if (q[i]) + spr_coll |= q[i] | sbit; + else { + p[i] = col; + q[i] = sbit; + } + } + + } else { // Standard mode + + // Expand sprite data + sdata_l = ExpTable[sdata >> 24 & 0xff] << 16 | ExpTable[sdata >> 16 & 0xff]; + sdata_r = ExpTable[sdata >> 8 & 0xff] << 16; + + // Collision with graphics? + if ((fore_mask & sdata_l) || (fore_mask_r & sdata_r)) { + gfx_coll |= sbit; + if (mdp & sbit) { + sdata_l &= ~fore_mask; // Mask sprite if in background + sdata_r &= ~fore_mask_r; + } + } + + // Paint sprite + for (i=0; i<32; i++, sdata_l<<=1) + if (sdata_l & 0x80000000) { + if (q[i]) // Collision with sprite? + spr_coll |= q[i] | sbit; + else { // Draw pixel if no collision + p[i] = color; + q[i] = sbit; + } + } + for (; i<48; i++, sdata_r<<=1) + if (sdata_r & 0x80000000) { + if (q[i]) // Collision with sprite? + spr_coll |= q[i] | sbit; + else { // Draw pixel if no collision + p[i] = color; + q[i] = sbit; + } + } + } + + } else // Unexpanded + + if (mmc & sbit) { // Multicolor mode + uint32 plane0, plane1; + + // Convert sprite chunky pixels to bitplanes + plane0 = (sdata & 0x55555555) | (sdata & 0x55555555) << 1; + plane1 = (sdata & 0xaaaaaaaa) | (sdata & 0xaaaaaaaa) >> 1; + + // Collision with graphics? + if (fore_mask & (plane0 | plane1)) { + gfx_coll |= sbit; + if (mdp & sbit) { + plane0 &= ~fore_mask; // Mask sprite if in background + plane1 &= ~fore_mask; + } + } + + // Paint sprite + for (i=0; i<24; i++, plane0<<=1, plane1<<=1) { + uint8 col; + if (plane1 & 0x80000000) { + if (plane0 & 0x80000000) + col = mm1_color; + else + col = color; + } else { + if (plane0 & 0x80000000) + col = mm0_color; + else + continue; + } + if (q[i]) + spr_coll |= q[i] | sbit; + else { + p[i] = col; + q[i] = sbit; + } + } + + } else { // Standard mode + + // Collision with graphics? + if (fore_mask & sdata) { + gfx_coll |= sbit; + if (mdp & sbit) + sdata &= ~fore_mask; // Mask sprite if in background + } + + // Paint sprite + for (i=0; i<24; i++, sdata<<=1) + if (sdata & 0x80000000) { + if (q[i]) { // Collision with sprite? + spr_coll |= q[i] | sbit; + } else { // Draw pixel if no collision + p[i] = color; + q[i] = sbit; + } + } + + } + } + + if (ThePrefs.SpriteCollisions) { + + // Check sprite-sprite collisions + if (clx_spr) + clx_spr |= spr_coll; + else { + clx_spr |= spr_coll; + irq_flag |= 0x04; + if (irq_mask & 0x04) { + irq_flag |= 0x80; + the_cpu->TriggerVICIRQ(); + } + } + + // Check sprite-background collisions + if (clx_bgr) + clx_bgr |= gfx_coll; + else { + clx_bgr |= gfx_coll; + irq_flag |= 0x02; + if (irq_mask & 0x02) { + irq_flag |= 0x80; + the_cpu->TriggerVICIRQ(); + } + } + } +} + + +#ifdef GLOBAL_VARS +static inline int el_update_mc(int raster) +#else +inline int MOS6569::el_update_mc(int raster) +#endif +{ + int i, j; + int cycles_used = 0; + uint8 spron = sprite_on; + uint8 spren = me; + uint8 sprye = mye; + uint8 raster8bit = raster; + uint16 *mcp = mc; + uint8 *myp = my; + + // Increment sprite data counters + for (i=0, j=1; i<8; i++, j<<=1, mcp++, myp++) { + + // Sprite enabled? + if (spren & j) + + // Yes, activate if Y position matches raster counter + if (*myp == (raster8bit & 0xff)) { + *mcp = 0; + spron |= j; + } else + goto spr_off; + else +spr_off: + // No, turn sprite off when data counter exceeds 60 + // and increment counter + if (*mcp != 63) { + if (sprye & j) { // Y expansion + if (!((*myp ^ raster8bit) & 1)) { + *mcp += 3; + cycles_used += 2; + if (*mcp == 63) + spron &= ~j; + } + } else { + *mcp += 3; + cycles_used += 2; + if (*mcp == 63) + spron &= ~j; + } + } + } + + sprite_on = spron; + return cycles_used; +} + + +#ifdef __POWERPC__ +static asm void fastcopy(register uchar *dst, register uchar *src); +static asm void fastcopy(register uchar *dst, register uchar *src) +{ + lfd fp0,0(src) + lfd fp1,8(src) + lfd fp2,16(src) + lfd fp3,24(src) + lfd fp4,32(src) + lfd fp5,40(src) + lfd fp6,48(src) + lfd fp7,56(src) + addi src,src,64 + stfd fp0,0(dst) + stfd fp1,8(dst) + stfd fp2,16(dst) + stfd fp3,24(dst) + stfd fp4,32(dst) + stfd fp5,40(dst) + stfd fp6,48(dst) + stfd fp7,56(dst) + addi dst,dst,64 + + lfd fp0,0(src) + lfd fp1,8(src) + lfd fp2,16(src) + lfd fp3,24(src) + lfd fp4,32(src) + lfd fp5,40(src) + lfd fp6,48(src) + lfd fp7,56(src) + addi src,src,64 + stfd fp0,0(dst) + stfd fp1,8(dst) + stfd fp2,16(dst) + stfd fp3,24(dst) + stfd fp4,32(dst) + stfd fp5,40(dst) + stfd fp6,48(dst) + stfd fp7,56(dst) + addi dst,dst,64 + + lfd fp0,0(src) + lfd fp1,8(src) + lfd fp2,16(src) + lfd fp3,24(src) + lfd fp4,32(src) + lfd fp5,40(src) + lfd fp6,48(src) + lfd fp7,56(src) + addi src,src,64 + stfd fp0,0(dst) + stfd fp1,8(dst) + stfd fp2,16(dst) + stfd fp3,24(dst) + stfd fp4,32(dst) + stfd fp5,40(dst) + stfd fp6,48(dst) + stfd fp7,56(dst) + addi dst,dst,64 + + lfd fp0,0(src) + lfd fp1,8(src) + lfd fp2,16(src) + lfd fp3,24(src) + lfd fp4,32(src) + lfd fp5,40(src) + lfd fp6,48(src) + lfd fp7,56(src) + addi src,src,64 + stfd fp0,0(dst) + stfd fp1,8(dst) + stfd fp2,16(dst) + stfd fp3,24(dst) + stfd fp4,32(dst) + stfd fp5,40(dst) + stfd fp6,48(dst) + stfd fp7,56(dst) + addi dst,dst,64 + + lfd fp0,0(src) + lfd fp1,8(src) + lfd fp2,16(src) + lfd fp3,24(src) + lfd fp4,32(src) + lfd fp5,40(src) + lfd fp6,48(src) + lfd fp7,56(src) + addi src,src,64 + stfd fp0,0(dst) + stfd fp1,8(dst) + stfd fp2,16(dst) + stfd fp3,24(dst) + stfd fp4,32(dst) + stfd fp5,40(dst) + stfd fp6,48(dst) + stfd fp7,56(dst) + addi dst,dst,64 + + lfd fp0,0(src) + lfd fp1,8(src) + lfd fp2,16(src) + lfd fp3,24(src) + lfd fp4,32(src) + lfd fp5,40(src) + lfd fp6,48(src) + lfd fp7,56(src) + addi src,src,64 + stfd fp0,0(dst) + stfd fp1,8(dst) + stfd fp2,16(dst) + stfd fp3,24(dst) + stfd fp4,32(dst) + stfd fp5,40(dst) + stfd fp6,48(dst) + stfd fp7,56(dst) + addi dst,dst,64 + blr +} +#endif + + +/* + * Emulate one raster line + */ + +int MOS6569::EmulateLine(void) +{ + int cycles_left = ThePrefs.NormalCycles; // Cycles left for CPU + bool is_bad_line = false; + + // Get raster counter into local variable for faster access and increment + unsigned int raster = raster_y+1; + + // End of screen reached? + if (raster != TOTAL_RASTERS) + raster_y = raster; + else { + vblank(); + raster = 0; + } + + // Trigger raster IRQ if IRQ line reached + if (raster == irq_raster) + raster_irq(); + + // In line $30, the DEN bit controls if Bad Lines can occur + if (raster == 0x30) + bad_lines_enabled = ctrl1 & 0x10; + + // Skip frame? Only calculate Bad Lines then + if (frame_skipped) { + if (raster >= FIRST_DMA_LINE && raster <= LAST_DMA_LINE && ((raster & 7) == y_scroll) && bad_lines_enabled) { + is_bad_line = true; + cycles_left = ThePrefs.BadLineCycles; + } + goto VIC_nop; + } + + // Within the visible range? + if (raster >= FIRST_DISP_LINE && raster <= LAST_DISP_LINE) { + + // Our output goes here +#ifdef __POWERPC__ + uint8 *chunky_ptr = (uint8 *)chunky_tmp; +#else + uint8 *chunky_ptr = chunky_line_start; +#endif + + // Set video counter + vc = vc_base; + + // Bad Line condition? + if (raster >= FIRST_DMA_LINE && raster <= LAST_DMA_LINE && ((raster & 7) == y_scroll) && bad_lines_enabled) { + + // Turn on display + display_state = is_bad_line = true; + cycles_left = ThePrefs.BadLineCycles; + rc = 0; + + // Read and latch 40 bytes from video matrix and color RAM + uint8 *mp = matrix_line - 1; + uint8 *cp = color_line - 1; + int vc1 = vc - 1; + uint8 *mbp = matrix_base + vc1; + uint8 *crp = color_ram + vc1; + for (int i=0; i<40; i++) { + *++mp = *++mbp; + *++cp = *++crp; + } + } + + // Handler upper/lower border + if (raster == dy_stop) + border_on = true; + if (raster == dy_start && (ctrl1 & 0x10)) // Don't turn off border if DEN bit cleared + border_on = false; + + if (!border_on) { + + // Display window contents + uint8 *p = chunky_ptr + COL40_XSTART; // Pointer in chunky display buffer + uint8 *r = fore_mask_buf + COL40_XSTART/8; // Pointer in foreground mask buffer +#ifdef ALIGNMENT_CHECK + uint8 *use_p = ((((int)p) & 3) == 0) ? p : text_chunky_buf; +#endif + + { + p--; + uint8 b0cc = b0c_color; + int limit = x_scroll; + for (int i=0; i0 + *++p = b0cc; + p++; + } + + if (display_state) { + switch (display_idx) { + + case 0: // Standard text +#ifndef CAN_ACCESS_UNALIGNED +#ifdef ALIGNMENT_CHECK + el_std_text(use_p, char_base + rc, r); + if (use_p != p) + memcpy(p, use_p, 8*40); +#else + if (x_scroll) { + el_std_text(text_chunky_buf, char_base + rc, r); + memcpy(p, text_chunky_buf, 8*40); + } else + el_std_text(p, char_base + rc, r); +#endif +#else + el_std_text(p, char_base + rc, r); +#endif + break; + + case 1: // Multicolor text +#ifndef CAN_ACCESS_UNALIGNED +#ifdef ALIGNMENT_CHECK + el_mc_text(use_p, char_base + rc, r); + if (use_p != p) + memcpy(p, use_p, 8*40); +#else + if (x_scroll) { + el_mc_text(text_chunky_buf, char_base + rc, r); + memcpy(p, text_chunky_buf, 8*40); + } else + el_mc_text(p, char_base + rc, r); +#endif +#else + el_mc_text(p, char_base + rc, r); +#endif + break; + + case 2: // Standard bitmap +#ifndef CAN_ACCESS_UNALIGNED +#ifdef ALIGNMENT_CHECK + el_std_bitmap(use_p, bitmap_base + (vc << 3) + rc, r); + if (use_p != p) + memcpy(p, use_p, 8*40); +#else + if (x_scroll) { + el_std_bitmap(text_chunky_buf, bitmap_base + (vc << 3) + rc, r); + memcpy(p, text_chunky_buf, 8*40); + } else + el_std_bitmap(p, bitmap_base + (vc << 3) + rc, r); +#endif +#else + el_std_bitmap(p, bitmap_base + (vc << 3) + rc, r); +#endif + break; + + case 3: // Multicolor bitmap +#ifndef CAN_ACCESS_UNALIGNED +#ifdef ALIGNMENT_CHECK + el_mc_bitmap(use_p, bitmap_base + (vc << 3) + rc, r); + if (use_p != p) + memcpy(p, use_p, 8*40); +#else + if (x_scroll) { + el_mc_bitmap(text_chunky_buf, bitmap_base + (vc << 3) + rc, r); + memcpy(p, text_chunky_buf, 8*40); + } else + el_mc_bitmap(p, bitmap_base + (vc << 3) + rc, r); +#endif +#else + el_mc_bitmap(p, bitmap_base + (vc << 3) + rc, r); +#endif + break; + + case 4: // ECM text +#ifndef CAN_ACCESS_UNALIGNED +#ifdef ALIGNMENT_CHECK + el_ecm_text(use_p, char_base + rc, r); + if (use_p != p) + memcpy(p, use_p, 8*40); +#else + if (x_scroll) { + el_ecm_text(text_chunky_buf, char_base + rc, r); + memcpy(p, text_chunky_buf, 8*40); + } else + el_ecm_text(p, char_base + rc, r); +#endif +#else + el_ecm_text(p, char_base + rc, r); +#endif + break; + + default: // Invalid mode (all black) + memset(p, colors[0], 320); + memset(r, 0, 40); + break; + } + vc += 40; + + } else { // Idle state graphics + switch (display_idx) { + + case 0: // Standard text + case 1: // Multicolor text + case 4: // ECM text +#ifndef CAN_ACCESS_UNALIGNED +#ifdef ALIGNMENT_CHECK + el_std_idle(use_p, r); + if (use_p != p) {memcpy(p, use_p, 8*40);} +#else + if (x_scroll) { + el_std_idle(text_chunky_buf, r); + memcpy(p, text_chunky_buf, 8*40); + } else + el_std_idle(p, r); +#endif +#else + el_std_idle(p, r); +#endif + break; + + case 3: // Multicolor bitmap +#ifndef CAN_ACCESS_UNALIGNED +#ifdef ALIGNMENT_CHECK + el_mc_idle(use_p, r); + if (use_p != p) {memcpy(p, use_p, 8*40);} +#else + if (x_scroll) { + el_mc_idle(text_chunky_buf, r); + memcpy(p, text_chunky_buf, 8*40); + } else + el_mc_idle(p, r); +#endif +#else + el_mc_idle(p, r); +#endif + break; + + default: // Invalid mode (all black) + memset(p, colors[0], 320); + memset(r, 0, 40); + break; + } + } + + // Draw sprites + if (sprite_on && ThePrefs.SpritesOn) { + + // Clear sprite collision buffer + uint32 *lp = (uint32 *)spr_coll_buf - 1; + for (int i=0; i= FIRST_DMA_LINE-1 && raster <= LAST_DMA_LINE-1 && (((raster+1) & 7) == y_scroll) && bad_lines_enabled) + rc = 0; + } + +VIC_nop: + // Skip this if all sprites are off + if (me | sprite_on) + cycles_left -= el_update_mc(raster); + + return cycles_left; +} diff --git a/Src/VIC.h b/Src/VIC.h new file mode 100644 index 0000000..b16d102 --- /dev/null +++ b/Src/VIC.h @@ -0,0 +1,288 @@ +/* + * VIC.h - 6569R5 emulation (line based) + * + * Frodo (C) 1994-1997,2002-2005 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _VIC_H +#define _VIC_H + + +// Define this if you want global variables instead of member variables +#if defined(__i386) || defined(mc68000) || defined(__MC68K__) +#define GLOBAL_VARS +#endif + +// Define this if you have a processor that can do unaligned accesses quickly +#if defined(__i386) || defined(mc68000) || defined(__MC68K__) +#define CAN_ACCESS_UNALIGNED +#endif + + +// Total number of raster lines (PAL) +const unsigned TOTAL_RASTERS = 0x138; + +// Screen refresh frequency (PAL) +const unsigned SCREEN_FREQ = 50; + + +class MOS6510; +class C64Display; +class C64; +struct MOS6569State; + + +class MOS6569 { +public: + MOS6569(C64 *c64, C64Display *disp, MOS6510 *CPU, uint8 *RAM, uint8 *Char, uint8 *Color); + + uint8 ReadRegister(uint16 adr); + void WriteRegister(uint16 adr, uint8 byte); +#ifdef FRODO_SC + bool EmulateCycle(void); +#else + int EmulateLine(void); +#endif + void ChangedVA(uint16 new_va); // CIA VA14/15 has changed + void TriggerLightpen(void); // Trigger lightpen interrupt + void ReInitColors(void); + void GetState(MOS6569State *vd); + void SetState(MOS6569State *vd); + +#ifdef FRODO_SC + uint8 LastVICByte; +#endif + +private: +#ifndef GLOBAL_VARS + void vblank(void); + void raster_irq(void); + + uint16 mx[8]; // VIC registers + uint8 my[8]; + uint8 mx8; + uint8 ctrl1, ctrl2; + uint8 lpx, lpy; + uint8 me, mxe, mye, mdp, mmc; + uint8 vbase; + uint8 irq_flag, irq_mask; + uint8 clx_spr, clx_bgr; + uint8 ec, b0c, b1c, b2c, b3c, mm0, mm1; + uint8 sc[8]; + + uint8 *ram, *char_rom, *color_ram; // Pointers to RAM and ROM + C64 *the_c64; // Pointer to C64 + C64Display *the_display; // Pointer to C64Display + MOS6510 *the_cpu; // Pointer to 6510 + + uint8 colors[256]; // Indices of the 16 C64 colors (16 times mirrored to avoid "& 0x0f") + + uint8 ec_color, b0c_color, b1c_color, + b2c_color, b3c_color; // Indices for exterior/background colors + uint8 mm0_color, mm1_color; // Indices for MOB multicolors + uint8 spr_color[8]; // Indices for MOB colors + + uint32 ec_color_long; // ec_color expanded to 32 bits + + uint8 matrix_line[40]; // Buffer for video line, read in Bad Lines + uint8 color_line[40]; // Buffer for color line, read in Bad Lines + +#ifdef __POWERPC__ + double chunky_tmp[0x180/8]; // Temporary line buffer for speedup +#endif + uint8 *chunky_line_start; // Pointer to start of current line in bitmap buffer + int xmod; // Number of bytes per row + + uint16 raster_y; // Current raster line + uint16 irq_raster; // Interrupt raster line + uint16 dy_start; // Comparison values for border logic + uint16 dy_stop; + uint16 rc; // Row counter + uint16 vc; // Video counter + uint16 vc_base; // Video counter base + uint16 x_scroll; // X scroll value + uint16 y_scroll; // Y scroll value + uint16 cia_vabase; // CIA VA14/15 video base + + uint16 mc[8]; // Sprite data counters + + int display_idx; // Index of current display mode + int skip_counter; // Counter for frame-skipping + + long pad0; // Keep buffers long-aligned + uint8 spr_coll_buf[0x180]; // Buffer for sprite-sprite collisions and priorities + uint8 fore_mask_buf[0x180/8]; // Foreground mask for sprite-graphics collisions and priorities +#ifndef CAN_ACCESS_UNALIGNED + uint8 text_chunky_buf[40*8]; // Line graphics buffer +#endif + + bool display_state; // true: Display state, false: Idle state + bool border_on; // Flag: Upper/lower border on (Frodo SC: Main border flipflop) + bool frame_skipped; // Flag: Frame is being skipped + uint8 bad_lines_enabled; // Flag: Bad Lines enabled for this frame + bool lp_triggered; // Flag: Lightpen was triggered in this frame + +#ifdef FRODO_SC + uint8 read_byte(uint16 adr); + void matrix_access(void); + void graphics_access(void); + void draw_graphics(void); + void draw_sprites(void); + void draw_background(void); + + int cycle; // Current cycle in line (1..63) + + uint8 *chunky_ptr; // Pointer in chunky bitmap buffer (this is where out output goes) + uint8 *fore_mask_ptr; // Pointer in fore_mask_buf + + uint16 matrix_base; // Video matrix base + uint16 char_base; // Character generator base + uint16 bitmap_base; // Bitmap base + + bool is_bad_line; // Flag: Current line is bad line + bool draw_this_line; // Flag: This line is drawn on the screen + bool ud_border_on; // Flag: Upper/lower border on + bool vblanking; // Flag: VBlank in next cycle + + bool border_on_sample[5]; // Samples of border state at different cycles (1, 17, 18, 56, 57) + uint8 border_color_sample[0x180/8]; // Samples of border color at each "displayed" cycle + + uint8 ref_cnt; // Refresh counter + uint8 spr_exp_y; // 8 sprite y expansion flipflops + uint8 spr_dma_on; // 8 flags: Sprite DMA active + uint8 spr_disp_on; // 8 flags: Sprite display active + uint8 spr_draw; // 8 flags: Draw sprite in this line + uint16 spr_ptr[8]; // Sprite data pointers + uint16 mc_base[8]; // Sprite data counter bases + + uint16 raster_x; // Current raster x position + + int ml_index; // Index in matrix/color_line[] + uint8 gfx_data, char_data, color_data, last_char_data; + uint8 spr_data[8][4]; // Sprite data read + uint8 spr_draw_data[8][4]; // Sprite data for drawing + + uint32 first_ba_cycle; // Cycle when BA first went low +#else + uint8 *get_physical(uint16 adr); + void make_mc_table(void); + void el_std_text(uint8 *p, uint8 *q, uint8 *r); + void el_mc_text(uint8 *p, uint8 *q, uint8 *r); + void el_std_bitmap(uint8 *p, uint8 *q, uint8 *r); + void el_mc_bitmap(uint8 *p, uint8 *q, uint8 *r); + void el_ecm_text(uint8 *p, uint8 *q, uint8 *r); + void el_std_idle(uint8 *p, uint8 *r); + void el_mc_idle(uint8 *p, uint8 *r); + void el_sprites(uint8 *chunky_ptr); + int el_update_mc(int raster); + + uint16 mc_color_lookup[4]; + + bool border_40_col; // Flag: 40 column border + uint8 sprite_on; // 8 flags: Sprite display/DMA active + + uint8 *matrix_base; // Video matrix base + uint8 *char_base; // Character generator base + uint8 *bitmap_base; // Bitmap base +#endif +#endif +}; + + +// VIC state +struct MOS6569State { + uint8 m0x; // Sprite coordinates + uint8 m0y; + uint8 m1x; + uint8 m1y; + uint8 m2x; + uint8 m2y; + uint8 m3x; + uint8 m3y; + uint8 m4x; + uint8 m4y; + uint8 m5x; + uint8 m5y; + uint8 m6x; + uint8 m6y; + uint8 m7x; + uint8 m7y; + uint8 mx8; + + uint8 ctrl1; // Control registers + uint8 raster; + uint8 lpx; + uint8 lpy; + uint8 me; + uint8 ctrl2; + uint8 mye; + uint8 vbase; + uint8 irq_flag; + uint8 irq_mask; + uint8 mdp; + uint8 mmc; + uint8 mxe; + uint8 mm; + uint8 md; + + uint8 ec; // Color registers + uint8 b0c; + uint8 b1c; + uint8 b2c; + uint8 b3c; + uint8 mm0; + uint8 mm1; + uint8 m0c; + uint8 m1c; + uint8 m2c; + uint8 m3c; + uint8 m4c; + uint8 m5c; + uint8 m6c; + uint8 m7c; + // Additional registers + uint8 pad0; + uint16 irq_raster; // IRQ raster line + uint16 vc; // Video counter + uint16 vc_base; // Video counter base + uint8 rc; // Row counter + uint8 spr_dma; // 8 Flags: Sprite DMA active + uint8 spr_disp; // 8 Flags: Sprite display active + uint8 mc[8]; // Sprite data counters + uint8 mc_base[8]; // Sprite data counter bases + bool display_state; // true: Display state, false: Idle state + bool bad_line; // Flag: Bad Line state + bool bad_line_enable; // Flag: Bad Lines enabled for this frame + bool lp_triggered; // Flag: Lightpen was triggered in this frame + bool border_on; // Flag: Upper/lower border on (Frodo SC: Main border flipflop) + + uint16 bank_base; // VIC bank base address + uint16 matrix_base; // Video matrix base + uint16 char_base; // Character generator base + uint16 bitmap_base; // Bitmap base + uint16 sprite_base[8]; // Sprite bases + + // Frodo SC: + int cycle; // Current cycle in line (1..63) + uint16 raster_x; // Current raster x position + int ml_index; // Index in matrix/color_line[] + uint8 ref_cnt; // Refresh counter + uint8 last_vic_byte; // Last byte read by VIC + bool ud_border_on; // Flag: Upper/lower border on +}; + +#endif diff --git a/Src/VIC_SC.cpp b/Src/VIC_SC.cpp new file mode 100644 index 0000000..0a48d46 --- /dev/null +++ b/Src/VIC_SC.cpp @@ -0,0 +1,1999 @@ +/* + * VIC_SC.cpp - 6569R5 emulation (cycle based) + * + * Frodo (C) 1994-1997,2002-2005 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * Incompatibilities: + * ------------------ + * + * - Color of $ff bytes read when BA is low and AEC is high + * is not correct + * - Changes to border/background color are visible 7 pixels + * too late + * - Sprite data access doesn't respect BA + * - Sprite collisions are only detected within the visible + * screen area (excluding borders) + * - Sprites are only drawn if they completely fit within the + * left/right limits of the chunky bitmap + */ + +#include "sysdeps.h" + +#include "VIC.h" +#include "C64.h" +#include "CPUC64.h" +#include "Display.h" +#include "Prefs.h" + + +// First and last displayed line +const int FIRST_DISP_LINE = 0x10; +const int LAST_DISP_LINE = 0x11f; + +// First and last possible line for Bad Lines +const int FIRST_DMA_LINE = 0x30; +const int LAST_DMA_LINE = 0xf7; + +// Display window coordinates +const int ROW25_YSTART = 0x33; +const int ROW25_YSTOP = 0xfb; +const int ROW24_YSTART = 0x37; +const int ROW24_YSTOP = 0xf7; + +#if defined(SMALL_DISPLAY) +/* This does not work yet, the sprite code doesn't know about it. */ +const int COL40_XSTART = 0x14; +const int COL40_XSTOP = 0x154; +const int COL38_XSTART = 0x1B; +const int COL38_XSTOP = 0x14B; +#else +const int COL40_XSTART = 0x20; +const int COL40_XSTOP = 0x160; +const int COL38_XSTART = 0x27; +const int COL38_XSTOP = 0x157; +#endif + + +// Tables for sprite X expansion +uint16 ExpTable[256] = { + 0x0000, 0x0003, 0x000C, 0x000F, 0x0030, 0x0033, 0x003C, 0x003F, + 0x00C0, 0x00C3, 0x00CC, 0x00CF, 0x00F0, 0x00F3, 0x00FC, 0x00FF, + 0x0300, 0x0303, 0x030C, 0x030F, 0x0330, 0x0333, 0x033C, 0x033F, + 0x03C0, 0x03C3, 0x03CC, 0x03CF, 0x03F0, 0x03F3, 0x03FC, 0x03FF, + 0x0C00, 0x0C03, 0x0C0C, 0x0C0F, 0x0C30, 0x0C33, 0x0C3C, 0x0C3F, + 0x0CC0, 0x0CC3, 0x0CCC, 0x0CCF, 0x0CF0, 0x0CF3, 0x0CFC, 0x0CFF, + 0x0F00, 0x0F03, 0x0F0C, 0x0F0F, 0x0F30, 0x0F33, 0x0F3C, 0x0F3F, + 0x0FC0, 0x0FC3, 0x0FCC, 0x0FCF, 0x0FF0, 0x0FF3, 0x0FFC, 0x0FFF, + 0x3000, 0x3003, 0x300C, 0x300F, 0x3030, 0x3033, 0x303C, 0x303F, + 0x30C0, 0x30C3, 0x30CC, 0x30CF, 0x30F0, 0x30F3, 0x30FC, 0x30FF, + 0x3300, 0x3303, 0x330C, 0x330F, 0x3330, 0x3333, 0x333C, 0x333F, + 0x33C0, 0x33C3, 0x33CC, 0x33CF, 0x33F0, 0x33F3, 0x33FC, 0x33FF, + 0x3C00, 0x3C03, 0x3C0C, 0x3C0F, 0x3C30, 0x3C33, 0x3C3C, 0x3C3F, + 0x3CC0, 0x3CC3, 0x3CCC, 0x3CCF, 0x3CF0, 0x3CF3, 0x3CFC, 0x3CFF, + 0x3F00, 0x3F03, 0x3F0C, 0x3F0F, 0x3F30, 0x3F33, 0x3F3C, 0x3F3F, + 0x3FC0, 0x3FC3, 0x3FCC, 0x3FCF, 0x3FF0, 0x3FF3, 0x3FFC, 0x3FFF, + 0xC000, 0xC003, 0xC00C, 0xC00F, 0xC030, 0xC033, 0xC03C, 0xC03F, + 0xC0C0, 0xC0C3, 0xC0CC, 0xC0CF, 0xC0F0, 0xC0F3, 0xC0FC, 0xC0FF, + 0xC300, 0xC303, 0xC30C, 0xC30F, 0xC330, 0xC333, 0xC33C, 0xC33F, + 0xC3C0, 0xC3C3, 0xC3CC, 0xC3CF, 0xC3F0, 0xC3F3, 0xC3FC, 0xC3FF, + 0xCC00, 0xCC03, 0xCC0C, 0xCC0F, 0xCC30, 0xCC33, 0xCC3C, 0xCC3F, + 0xCCC0, 0xCCC3, 0xCCCC, 0xCCCF, 0xCCF0, 0xCCF3, 0xCCFC, 0xCCFF, + 0xCF00, 0xCF03, 0xCF0C, 0xCF0F, 0xCF30, 0xCF33, 0xCF3C, 0xCF3F, + 0xCFC0, 0xCFC3, 0xCFCC, 0xCFCF, 0xCFF0, 0xCFF3, 0xCFFC, 0xCFFF, + 0xF000, 0xF003, 0xF00C, 0xF00F, 0xF030, 0xF033, 0xF03C, 0xF03F, + 0xF0C0, 0xF0C3, 0xF0CC, 0xF0CF, 0xF0F0, 0xF0F3, 0xF0FC, 0xF0FF, + 0xF300, 0xF303, 0xF30C, 0xF30F, 0xF330, 0xF333, 0xF33C, 0xF33F, + 0xF3C0, 0xF3C3, 0xF3CC, 0xF3CF, 0xF3F0, 0xF3F3, 0xF3FC, 0xF3FF, + 0xFC00, 0xFC03, 0xFC0C, 0xFC0F, 0xFC30, 0xFC33, 0xFC3C, 0xFC3F, + 0xFCC0, 0xFCC3, 0xFCCC, 0xFCCF, 0xFCF0, 0xFCF3, 0xFCFC, 0xFCFF, + 0xFF00, 0xFF03, 0xFF0C, 0xFF0F, 0xFF30, 0xFF33, 0xFF3C, 0xFF3F, + 0xFFC0, 0xFFC3, 0xFFCC, 0xFFCF, 0xFFF0, 0xFFF3, 0xFFFC, 0xFFFF +}; + +uint16 MultiExpTable[256] = { + 0x0000, 0x0005, 0x000A, 0x000F, 0x0050, 0x0055, 0x005A, 0x005F, + 0x00A0, 0x00A5, 0x00AA, 0x00AF, 0x00F0, 0x00F5, 0x00FA, 0x00FF, + 0x0500, 0x0505, 0x050A, 0x050F, 0x0550, 0x0555, 0x055A, 0x055F, + 0x05A0, 0x05A5, 0x05AA, 0x05AF, 0x05F0, 0x05F5, 0x05FA, 0x05FF, + 0x0A00, 0x0A05, 0x0A0A, 0x0A0F, 0x0A50, 0x0A55, 0x0A5A, 0x0A5F, + 0x0AA0, 0x0AA5, 0x0AAA, 0x0AAF, 0x0AF0, 0x0AF5, 0x0AFA, 0x0AFF, + 0x0F00, 0x0F05, 0x0F0A, 0x0F0F, 0x0F50, 0x0F55, 0x0F5A, 0x0F5F, + 0x0FA0, 0x0FA5, 0x0FAA, 0x0FAF, 0x0FF0, 0x0FF5, 0x0FFA, 0x0FFF, + 0x5000, 0x5005, 0x500A, 0x500F, 0x5050, 0x5055, 0x505A, 0x505F, + 0x50A0, 0x50A5, 0x50AA, 0x50AF, 0x50F0, 0x50F5, 0x50FA, 0x50FF, + 0x5500, 0x5505, 0x550A, 0x550F, 0x5550, 0x5555, 0x555A, 0x555F, + 0x55A0, 0x55A5, 0x55AA, 0x55AF, 0x55F0, 0x55F5, 0x55FA, 0x55FF, + 0x5A00, 0x5A05, 0x5A0A, 0x5A0F, 0x5A50, 0x5A55, 0x5A5A, 0x5A5F, + 0x5AA0, 0x5AA5, 0x5AAA, 0x5AAF, 0x5AF0, 0x5AF5, 0x5AFA, 0x5AFF, + 0x5F00, 0x5F05, 0x5F0A, 0x5F0F, 0x5F50, 0x5F55, 0x5F5A, 0x5F5F, + 0x5FA0, 0x5FA5, 0x5FAA, 0x5FAF, 0x5FF0, 0x5FF5, 0x5FFA, 0x5FFF, + 0xA000, 0xA005, 0xA00A, 0xA00F, 0xA050, 0xA055, 0xA05A, 0xA05F, + 0xA0A0, 0xA0A5, 0xA0AA, 0xA0AF, 0xA0F0, 0xA0F5, 0xA0FA, 0xA0FF, + 0xA500, 0xA505, 0xA50A, 0xA50F, 0xA550, 0xA555, 0xA55A, 0xA55F, + 0xA5A0, 0xA5A5, 0xA5AA, 0xA5AF, 0xA5F0, 0xA5F5, 0xA5FA, 0xA5FF, + 0xAA00, 0xAA05, 0xAA0A, 0xAA0F, 0xAA50, 0xAA55, 0xAA5A, 0xAA5F, + 0xAAA0, 0xAAA5, 0xAAAA, 0xAAAF, 0xAAF0, 0xAAF5, 0xAAFA, 0xAAFF, + 0xAF00, 0xAF05, 0xAF0A, 0xAF0F, 0xAF50, 0xAF55, 0xAF5A, 0xAF5F, + 0xAFA0, 0xAFA5, 0xAFAA, 0xAFAF, 0xAFF0, 0xAFF5, 0xAFFA, 0xAFFF, + 0xF000, 0xF005, 0xF00A, 0xF00F, 0xF050, 0xF055, 0xF05A, 0xF05F, + 0xF0A0, 0xF0A5, 0xF0AA, 0xF0AF, 0xF0F0, 0xF0F5, 0xF0FA, 0xF0FF, + 0xF500, 0xF505, 0xF50A, 0xF50F, 0xF550, 0xF555, 0xF55A, 0xF55F, + 0xF5A0, 0xF5A5, 0xF5AA, 0xF5AF, 0xF5F0, 0xF5F5, 0xF5FA, 0xF5FF, + 0xFA00, 0xFA05, 0xFA0A, 0xFA0F, 0xFA50, 0xFA55, 0xFA5A, 0xFA5F, + 0xFAA0, 0xFAA5, 0xFAAA, 0xFAAF, 0xFAF0, 0xFAF5, 0xFAFA, 0xFAFF, + 0xFF00, 0xFF05, 0xFF0A, 0xFF0F, 0xFF50, 0xFF55, 0xFF5A, 0xFF5F, + 0xFFA0, 0xFFA5, 0xFFAA, 0xFFAF, 0xFFF0, 0xFFF5, 0xFFFA, 0xFFFF +}; + +#ifdef GLOBAL_VARS +static uint16 mx[8]; // VIC registers +static uint8 my[8]; +static uint8 mx8; +static uint8 ctrl1, ctrl2; +static uint8 lpx, lpy; +static uint8 me, mxe, mye, mdp, mmc; +static uint8 vbase; +static uint8 irq_flag, irq_mask; +static uint8 clx_spr, clx_bgr; +static uint8 ec, b0c, b1c, b2c, b3c, mm0, mm1; +static uint8 sc[8]; + +static uint8 *ram, *char_rom, *color_ram; // Pointers to RAM and ROM +static C64 *the_c64; // Pointer to C64 +static C64Display *the_display; // Pointer to C64Display +static MOS6510 *the_cpu; // Pointer to 6510 +static MOS6569 *the_vic; // Pointer to self + +static uint8 colors[256]; // Indices of the 16 C64 colors (16 times mirrored to avoid "& 0x0f") + +static uint8 ec_color, b0c_color, b1c_color, b2c_color, b3c_color; // Indices for exterior/background colors +static uint8 mm0_color, mm1_color; // Indices for MOB multicolors +static uint8 spr_color[8]; // Indices for MOB colors + +static uint8 matrix_line[40]; // Buffer for video line, read in Bad Lines +static uint8 color_line[40]; // Buffer for color line, read in Bad Lines + +#ifdef __POWERPC__ +static double chunky_tmp[DISPLAY_X/8]; // Temporary line buffer for GameKit speedup +#endif +static uint8 *chunky_ptr; // Pointer in chunky bitmap buffer +static uint8 *chunky_line_start; // Pointer to start of current line in bitmap buffer +static uint8 *fore_mask_ptr; // Pointer in fore_mask_buf +static int xmod; // Number of bytes per row + +static uint16 raster_x; // Current raster x position +static uint16 raster_y; // Current raster line +static uint16 irq_raster; // Interrupt raster line +static uint16 dy_start; // Comparison values for border logic +static uint16 dy_stop; +static uint16 rc; // Row counter +static uint16 vc; // Video counter +static uint16 vc_base; // Video counter base +static uint16 x_scroll; // X scroll value +static uint16 y_scroll; // Y scroll value +static uint16 cia_vabase; // CIA VA14/15 video base + +static int cycle; // Current cycle in line (1..63) + +static int display_idx; // Index of current display mode +static int ml_index; // Index in matrix/color_line[] +static int skip_counter; // Counter for frame-skipping + +static uint16 mc[8]; // Sprite data counters +static uint16 mc_base[8]; // Sprite data counter bases + +static uint8 spr_coll_buf[0x180]; // Buffer for sprite-sprite collisions and priorities +static uint8 fore_mask_buf[0x180/8]; // Foreground mask for sprite-graphics collisions and priorities + +static bool display_state; // true: Display state, false: Idle state +static bool border_on; // Flag: Upper/lower border on +static bool frame_skipped; // Flag: Frame is being skipped +static bool bad_lines_enabled; // Flag: Bad Lines enabled for this frame +static bool lp_triggered; // Flag: Lightpen was triggered in this frame +static bool is_bad_line; // Flag: Current line is Bad Line +static bool draw_this_line; // Flag: This line is drawn on the screen +static bool ud_border_on; // Flag: Upper/lower border on +static bool vblanking; // Flag: VBlank in next cycle + +static bool border_on_sample[5]; // Samples of border state at different cycles (1, 17, 18, 56, 57) +static uint8 border_color_sample[DISPLAY_X/8]; // Samples of border color at each "displayed" cycle + +static uint16 matrix_base; // Video matrix base +static uint16 char_base; // Character generator base +static uint16 bitmap_base; // Bitmap base + +static uint8 ref_cnt; // Refresh counter +static uint8 spr_exp_y; // 8 sprite y expansion flipflops +static uint8 spr_dma_on; // 8 flags: Sprite DMA active +static uint8 spr_disp_on; // 8 flags: Sprite display active +static uint8 spr_draw; // 8 flags: Draw sprite in this line +static uint16 spr_ptr[8]; // Sprite data pointers + +static uint8 gfx_data, char_data, color_data, last_char_data; +static uint8 spr_data[8][4]; // Sprite data read +static uint8 spr_draw_data[8][4]; // Sprite data for drawing + +static uint32 first_ba_cycle; // Cycle when BA first went low +#endif + + +/* + * Constructor: Initialize variables + */ + +MOS6569::MOS6569(C64 *c64, C64Display *disp, MOS6510 *CPU, uint8 *RAM, uint8 *Char, uint8 *Color) +#ifndef GLOBAL_VARS + : ram(RAM), char_rom(Char), color_ram(Color), the_c64(c64), the_display(disp), the_cpu(CPU) +#endif +{ + int i; + + // Set pointers +#ifdef GLOBAL_VARS + the_vic = this; + the_c64 = c64; + the_display = disp; + the_cpu = CPU; + ram = RAM; + char_rom = Char; + color_ram = Color; +#endif + matrix_base = 0; + char_base = 0; + bitmap_base = 0; + + // Get bitmap info + chunky_ptr = chunky_line_start = disp->BitmapBase(); + xmod = disp->BitmapXMod(); + + // Initialize VIC registers + mx8 = 0; + ctrl1 = ctrl2 = 0; + lpx = lpy = 0; + me = mxe = mye = mdp = mmc = 0; + vbase = irq_flag = irq_mask = 0; + clx_spr = clx_bgr = 0; + cia_vabase = 0; + ec = b0c = b1c = b2c = b3c = mm0 = mm1 = 0; + for (i=0; i<8; i++) mx[i] = my[i] = sc[i] = 0; + + // Initialize other variables + raster_y = TOTAL_RASTERS - 1; + rc = 7; + irq_raster = vc = vc_base = x_scroll = y_scroll = 0; + dy_start = ROW24_YSTART; + dy_stop = ROW24_YSTOP; + ml_index = 0; + + cycle = 1; + display_idx = 0; + display_state = false; + border_on = ud_border_on = vblanking = false; + lp_triggered = draw_this_line = false; + + spr_dma_on = spr_disp_on = 0; + for (i=0; i<8; i++) { + mc[i] = 63; + spr_ptr[i] = 0; + } + + frame_skipped = false; + skip_counter = 1; + + memset(spr_coll_buf, 0, 0x180); + memset(fore_mask_buf, 0, 0x180/8); + + // Preset colors to black + disp->InitColors(colors); + ec_color = b0c_color = b1c_color = b2c_color = b3c_color = mm0_color = mm1_color = colors[0]; + for (i=0; i<8; i++) spr_color[i] = colors[0]; +} + + +/* + * Reinitialize the colors table for when the palette has changed + */ + +void MOS6569::ReInitColors(void) +{ + int i; + + // Build inverse color table. + uint8 xlate_colors[256]; + memset(xlate_colors, 0, sizeof(xlate_colors)); + for (i=0; i<16; i++) + xlate_colors[colors[i]] = i; + + // Get the new colors. + the_display->InitColors(colors); + + // Build color translation table. + for (i=0; i<256; i++) + xlate_colors[i] = colors[xlate_colors[i]]; + + // Translate all the old colors variables. + ec_color = colors[ec]; + b0c_color = colors[b0c]; + b1c_color = colors[b1c]; + b2c_color = colors[b2c]; + b3c_color = colors[b3c]; + mm0_color = colors[mm0]; + mm1_color = colors[mm1]; + for (i=0; i<8; i++) + spr_color[i] = colors[sc[i]]; + + // Translate the border color sample buffer. + for (unsigned x = 0; x < sizeof(border_color_sample); x++) + border_color_sample[x] = xlate_colors[border_color_sample[x]]; + + // Translate the chunky buffer. + uint8 *scanline = the_display->BitmapBase(); + for (int y=0; ym0x = mx[0] & 0xff; vd->m0y = my[0]; + vd->m1x = mx[1] & 0xff; vd->m1y = my[1]; + vd->m2x = mx[2] & 0xff; vd->m2y = my[2]; + vd->m3x = mx[3] & 0xff; vd->m3y = my[3]; + vd->m4x = mx[4] & 0xff; vd->m4y = my[4]; + vd->m5x = mx[5] & 0xff; vd->m5y = my[5]; + vd->m6x = mx[6] & 0xff; vd->m6y = my[6]; + vd->m7x = mx[7] & 0xff; vd->m7y = my[7]; + vd->mx8 = mx8; + + vd->ctrl1 = (ctrl1 & 0x7f) | ((raster_y & 0x100) >> 1); + vd->raster = raster_y & 0xff; + vd->lpx = lpx; vd->lpy = lpy; + vd->ctrl2 = ctrl2; + vd->vbase = vbase; + vd->irq_flag = irq_flag; + vd->irq_mask = irq_mask; + + vd->me = me; vd->mxe = mxe; vd->mye = mye; vd->mdp = mdp; vd->mmc = mmc; + vd->mm = clx_spr; vd->md = clx_bgr; + + vd->ec = ec; + vd->b0c = b0c; vd->b1c = b1c; vd->b2c = b2c; vd->b3c = b3c; + vd->mm0 = mm0; vd->mm1 = mm1; + vd->m0c = sc[0]; + vd->m1c = sc[1]; + vd->m2c = sc[2]; + vd->m3c = sc[3]; + vd->m4c = sc[4]; + vd->m5c = sc[5]; + vd->m6c = sc[6]; + vd->m7c = sc[7]; + + vd->pad0 = 0; + vd->irq_raster = irq_raster; + vd->vc = vc; + vd->vc_base = vc_base; + vd->rc = rc; + vd->spr_dma = spr_dma_on; + vd->spr_disp = spr_disp_on; + for (i=0; i<8; i++) { + vd->mc[i] = mc[i]; + vd->mc_base[i] = mc_base[i]; + } + vd->display_state = display_state; + vd->bad_line = raster_y >= FIRST_DMA_LINE && raster_y <= LAST_DMA_LINE && ((raster_y & 7) == y_scroll) && bad_lines_enabled; + vd->bad_line_enable = bad_lines_enabled; + vd->lp_triggered = lp_triggered; + vd->border_on = border_on; + + vd->bank_base = cia_vabase; + vd->matrix_base = ((vbase & 0xf0) << 6) | cia_vabase; + vd->char_base = ((vbase & 0x0e) << 10) | cia_vabase; + vd->bitmap_base = ((vbase & 0x08) << 10) | cia_vabase; + for (i=0; i<8; i++) + vd->sprite_base[i] = spr_ptr[i] | cia_vabase; + + vd->cycle = cycle; + vd->raster_x = raster_x; + vd->ml_index = ml_index; + vd->ref_cnt = ref_cnt; + vd->last_vic_byte = LastVICByte; + vd->ud_border_on = ud_border_on; +} + + +/* + * Set VIC state (only works if in VBlank) + */ + +void MOS6569::SetState(MOS6569State *vd) +{ + int i, j; + + mx[0] = vd->m0x; my[0] = vd->m0y; + mx[1] = vd->m1x; my[1] = vd->m1y; + mx[2] = vd->m2x; my[2] = vd->m2y; + mx[3] = vd->m3x; my[3] = vd->m3y; + mx[4] = vd->m4x; my[4] = vd->m4y; + mx[5] = vd->m5x; my[5] = vd->m5y; + mx[6] = vd->m6x; my[6] = vd->m6y; + mx[7] = vd->m7x; my[7] = vd->m7y; + mx8 = vd->mx8; + for (i=0, j=1; i<8; i++, j<<=1) { + if (mx8 & j) + mx[i] |= 0x100; + else + mx[i] &= 0xff; + } + + ctrl1 = vd->ctrl1; + ctrl2 = vd->ctrl2; + x_scroll = ctrl2 & 7; + y_scroll = ctrl1 & 7; + if (ctrl1 & 8) { + dy_start = ROW25_YSTART; + dy_stop = ROW25_YSTOP; + } else { + dy_start = ROW24_YSTART; + dy_stop = ROW24_YSTOP; + } + display_idx = ((ctrl1 & 0x60) | (ctrl2 & 0x10)) >> 4; + + raster_y = 0; + lpx = vd->lpx; lpy = vd->lpy; + + vbase = vd->vbase; + cia_vabase = vd->bank_base; + matrix_base = (vbase & 0xf0) << 6; + char_base = (vbase & 0x0e) << 10; + bitmap_base = (vbase & 0x08) << 10; + + irq_flag = vd->irq_flag; + irq_mask = vd->irq_mask; + + me = vd->me; mxe = vd->mxe; mye = vd->mye; mdp = vd->mdp; mmc = vd->mmc; + clx_spr = vd->mm; clx_bgr = vd->md; + + ec = vd->ec; + ec_color = colors[ec]; + + b0c = vd->b0c; b1c = vd->b1c; b2c = vd->b2c; b3c = vd->b3c; + b0c_color = colors[b0c]; + b1c_color = colors[b1c]; + b2c_color = colors[b2c]; + b3c_color = colors[b3c]; + + mm0 = vd->mm0; mm1 = vd->mm1; + mm0_color = colors[mm0]; + mm1_color = colors[mm1]; + + sc[0] = vd->m0c; sc[1] = vd->m1c; + sc[2] = vd->m2c; sc[3] = vd->m3c; + sc[4] = vd->m4c; sc[5] = vd->m5c; + sc[6] = vd->m6c; sc[7] = vd->m7c; + for (i=0; i<8; i++) + spr_color[i] = colors[sc[i]]; + + irq_raster = vd->irq_raster; + vc = vd->vc; + vc_base = vd->vc_base; + rc = vd->rc; + spr_dma_on = vd->spr_dma; + spr_disp_on = vd->spr_disp; + for (i=0; i<8; i++) { + mc[i] = vd->mc[i]; + mc_base[i] = vd->mc_base[i]; + spr_ptr[i] = vd->sprite_base[i] & 0x3fff; + } + display_state = vd->display_state; + bad_lines_enabled = vd->bad_line_enable; + lp_triggered = vd->lp_triggered; + border_on = vd->border_on; + + cycle = vd->cycle; + raster_x = vd->raster_x; + ml_index = vd->ml_index; + ref_cnt = vd->ref_cnt; + LastVICByte = vd->last_vic_byte; + ud_border_on = vd->ud_border_on; +} + + +/* + * Trigger raster IRQ + */ + +#ifdef GLOBAL_VARS +static inline void raster_irq(void) +#else +inline void MOS6569::raster_irq(void) +#endif +{ + irq_flag |= 0x01; + if (irq_mask & 0x01) { + irq_flag |= 0x80; + the_cpu->TriggerVICIRQ(); + } +} + + +/* + * Read from VIC register + */ + +uint8 MOS6569::ReadRegister(uint16 adr) +{ + switch (adr) { + case 0x00: case 0x02: case 0x04: case 0x06: + case 0x08: case 0x0a: case 0x0c: case 0x0e: + return mx[adr >> 1]; + + case 0x01: case 0x03: case 0x05: case 0x07: + case 0x09: case 0x0b: case 0x0d: case 0x0f: + return my[adr >> 1]; + + case 0x10: // Sprite X position MSB + return mx8; + + case 0x11: // Control register 1 + return (ctrl1 & 0x7f) | ((raster_y & 0x100) >> 1); + + case 0x12: // Raster counter + return raster_y; + + case 0x13: // Light pen X + return lpx; + + case 0x14: // Light pen Y + return lpy; + + case 0x15: // Sprite enable + return me; + + case 0x16: // Control register 2 + return ctrl2 | 0xc0; + + case 0x17: // Sprite Y expansion + return mye; + + case 0x18: // Memory pointers + return vbase | 0x01; + + case 0x19: // IRQ flags + return irq_flag | 0x70; + + case 0x1a: // IRQ mask + return irq_mask | 0xf0; + + case 0x1b: // Sprite data priority + return mdp; + + case 0x1c: // Sprite multicolor + return mmc; + + case 0x1d: // Sprite X expansion + return mxe; + + case 0x1e:{ // Sprite-sprite collision + uint8 ret = clx_spr; + clx_spr = 0; // Read and clear + return ret; + } + + case 0x1f:{ // Sprite-background collision + uint8 ret = clx_bgr; + clx_bgr = 0; // Read and clear + return ret; + } + + case 0x20: return ec | 0xf0; + case 0x21: return b0c | 0xf0; + case 0x22: return b1c | 0xf0; + case 0x23: return b2c | 0xf0; + case 0x24: return b3c | 0xf0; + case 0x25: return mm0 | 0xf0; + case 0x26: return mm1 | 0xf0; + + case 0x27: case 0x28: case 0x29: case 0x2a: + case 0x2b: case 0x2c: case 0x2d: case 0x2e: + return sc[adr - 0x27] | 0xf0; + + default: + return 0xff; + } +} + + +/* + * Write to VIC register + */ + +void MOS6569::WriteRegister(uint16 adr, uint8 byte) +{ + switch (adr) { + case 0x00: case 0x02: case 0x04: case 0x06: + case 0x08: case 0x0a: case 0x0c: case 0x0e: + mx[adr >> 1] = (mx[adr >> 1] & 0xff00) | byte; + break; + + case 0x10:{ + int i, j; + mx8 = byte; + for (i=0, j=1; i<8; i++, j<<=1) { + if (mx8 & j) + mx[i] |= 0x100; + else + mx[i] &= 0xff; + } + break; + } + + case 0x01: case 0x03: case 0x05: case 0x07: + case 0x09: case 0x0b: case 0x0d: case 0x0f: + my[adr >> 1] = byte; + break; + + case 0x11:{ // Control register 1 + ctrl1 = byte; + y_scroll = byte & 7; + + uint16 new_irq_raster = (irq_raster & 0xff) | ((byte & 0x80) << 1); + if (irq_raster != new_irq_raster && raster_y == new_irq_raster) + raster_irq(); + irq_raster = new_irq_raster; + + if (byte & 8) { + dy_start = ROW25_YSTART; + dy_stop = ROW25_YSTOP; + } else { + dy_start = ROW24_YSTART; + dy_stop = ROW24_YSTOP; + } + + // In line $30, the DEN bit controls if Bad Lines can occur + if (raster_y == 0x30 && byte & 0x10) + bad_lines_enabled = true; + + // Bad Line condition? + is_bad_line = (raster_y >= FIRST_DMA_LINE && raster_y <= LAST_DMA_LINE && ((raster_y & 7) == y_scroll) && bad_lines_enabled); + + display_idx = ((ctrl1 & 0x60) | (ctrl2 & 0x10)) >> 4; + break; + } + + case 0x12:{ // Raster counter + uint16 new_irq_raster = (irq_raster & 0xff00) | byte; + if (irq_raster != new_irq_raster && raster_y == new_irq_raster) + raster_irq(); + irq_raster = new_irq_raster; + break; + } + + case 0x15: // Sprite enable + me = byte; + break; + + case 0x16: // Control register 2 + ctrl2 = byte; + x_scroll = byte & 7; + display_idx = ((ctrl1 & 0x60) | (ctrl2 & 0x10)) >> 4; + break; + + case 0x17: // Sprite Y expansion + mye = byte; + spr_exp_y |= ~byte; + break; + + case 0x18: // Memory pointers + vbase = byte; + matrix_base = (byte & 0xf0) << 6; + char_base = (byte & 0x0e) << 10; + bitmap_base = (byte & 0x08) << 10; + break; + + case 0x19: // IRQ flags + irq_flag = irq_flag & (~byte & 0x0f); + if (irq_flag & irq_mask) // Set master bit if allowed interrupt still pending + irq_flag |= 0x80; + else + the_cpu->ClearVICIRQ(); // Else clear interrupt + break; + + case 0x1a: // IRQ mask + irq_mask = byte & 0x0f; + if (irq_flag & irq_mask) { // Trigger interrupt if pending and now allowed + irq_flag |= 0x80; + the_cpu->TriggerVICIRQ(); + } else { + irq_flag &= 0x7f; + the_cpu->ClearVICIRQ(); + } + break; + + case 0x1b: // Sprite data priority + mdp = byte; + break; + + case 0x1c: // Sprite multicolor + mmc = byte; + break; + + case 0x1d: // Sprite X expansion + mxe = byte; + break; + + case 0x20: ec_color = colors[ec = byte]; break; + case 0x21: b0c_color = colors[b0c = byte]; break; + case 0x22: b1c_color = colors[b1c = byte]; break; + case 0x23: b2c_color = colors[b2c = byte]; break; + case 0x24: b3c_color = colors[b3c = byte]; break; + case 0x25: mm0_color = colors[mm0 = byte]; break; + case 0x26: mm1_color = colors[mm1 = byte]; break; + + case 0x27: case 0x28: case 0x29: case 0x2a: + case 0x2b: case 0x2c: case 0x2d: case 0x2e: + spr_color[adr - 0x27] = colors[sc[adr - 0x27] = byte]; + break; + } +} + + +/* + * CIA VA14/15 has changed + */ + +void MOS6569::ChangedVA(uint16 new_va) +{ + cia_vabase = new_va << 14; + WriteRegister(0x18, vbase); // Force update of memory pointers +} + + +/* + * Trigger lightpen interrupt, latch lightpen coordinates + */ + +void MOS6569::TriggerLightpen(void) +{ + if (!lp_triggered) { // Lightpen triggers only once per frame + lp_triggered = true; + + lpx = raster_x >> 1; // Latch current coordinates + lpy = raster_y; + + irq_flag |= 0x08; // Trigger IRQ + if (irq_mask & 0x08) { + irq_flag |= 0x80; + the_cpu->TriggerVICIRQ(); + } + } +} + + +/* + * Read a byte from the VIC's address space + */ + +#ifdef GLOBAL_VARS +static inline uint8 read_byte(uint16 adr) +#else +inline uint8 MOS6569::read_byte(uint16 adr) +#endif +{ + uint16 va = adr | cia_vabase; + if ((va & 0x7000) == 0x1000) +#ifdef GLOBAL_VARS + return the_vic->LastVICByte = char_rom[va & 0x0fff]; +#else + return LastVICByte = char_rom[va & 0x0fff]; +#endif + else +#ifdef GLOBAL_VARS + return the_vic->LastVICByte = ram[va]; +#else + return LastVICByte = ram[va]; +#endif +} + + +/* + * Quick memset of 8 bytes + */ + +inline void memset8(uint8 *p, uint8 c) +{ + p[0] = p[1] = p[2] = p[3] = p[4] = p[5] = p[6] = p[7] = c; +} + + +/* + * Video matrix access + */ + +#ifdef __i386 +inline +#endif +#ifdef GLOBAL_VARS +static void matrix_access(void) +#else +void MOS6569::matrix_access(void) +#endif +{ + if (the_cpu->BALow) { + if (the_c64->CycleCounter-first_ba_cycle < 3) + matrix_line[ml_index] = color_line[ml_index] = 0xff; + else { + uint16 adr = (vc & 0x03ff) | matrix_base; + matrix_line[ml_index] = read_byte(adr); + color_line[ml_index] = color_ram[adr & 0x03ff]; + } + } +} + + +/* + * Graphics data access + */ + +#ifdef __i386 +inline +#endif +#ifdef GLOBAL_VARS +static void graphics_access(void) +#else +void MOS6569::graphics_access(void) +#endif +{ + if (display_state) { + + uint16 adr; + if (ctrl1 & 0x20) // Bitmap + adr = ((vc & 0x03ff) << 3) | bitmap_base | rc; + else // Text + adr = (matrix_line[ml_index] << 3) | char_base | rc; + if (ctrl1 & 0x40) // ECM + adr &= 0xf9ff; + gfx_data = read_byte(adr); + char_data = matrix_line[ml_index]; + color_data = color_line[ml_index]; + ml_index++; + vc++; + + } else { + + // Display is off + gfx_data = read_byte(ctrl1 & 0x40 ? 0x39ff : 0x3fff); + char_data = color_data = 0; + } +} + + +/* + * Background display (8 pixels) + */ + +#ifdef GLOBAL_VARS +static void draw_background(void) +#else +void MOS6569::draw_background(void) +#endif +{ + uint8 *p = chunky_ptr; + uint8 c; + + if (!draw_this_line) + return; + + switch (display_idx) { + case 0: // Standard text + case 1: // Multicolor text + case 3: // Multicolor bitmap + c = b0c_color; + break; + case 2: // Standard bitmap + c = colors[last_char_data]; + break; + case 4: // ECM text + if (last_char_data & 0x80) + if (last_char_data & 0x40) + c = b3c_color; + else + c = b2c_color; + else + if (last_char_data & 0x40) + c = b1c_color; + else + c = b0c_color; + break; + default: + c = colors[0]; + break; + } + memset8(p, c); +} + + +/* + * Graphics display (8 pixels) + */ + +#ifdef __i386 +inline +#endif +#ifdef GLOBAL_VARS +static void draw_graphics(void) +#else +void MOS6569::draw_graphics(void) +#endif +{ + uint8 *p = chunky_ptr + x_scroll; + uint8 c[4], data; + + if (!draw_this_line) + return; + if (ud_border_on) { + draw_background(); + return; + } + + switch (display_idx) { + + case 0: // Standard text + c[0] = b0c_color; + c[1] = colors[color_data]; + goto draw_std; + + case 1: // Multicolor text + if (color_data & 8) { + c[0] = b0c_color; + c[1] = b1c_color; + c[2] = b2c_color; + c[3] = colors[color_data & 7]; + goto draw_multi; + } else { + c[0] = b0c_color; + c[1] = colors[color_data]; + goto draw_std; + } + + case 2: // Standard bitmap + c[0] = colors[char_data]; + c[1] = colors[char_data >> 4]; + goto draw_std; + + case 3: // Multicolor bitmap + c[0]= b0c_color; + c[1] = colors[char_data >> 4]; + c[2] = colors[char_data]; + c[3] = colors[color_data]; + goto draw_multi; + + case 4: // ECM text + if (char_data & 0x80) + if (char_data & 0x40) + c[0] = b3c_color; + else + c[0] = b2c_color; + else + if (char_data & 0x40) + c[0] = b1c_color; + else + c[0] = b0c_color; + c[1] = colors[color_data]; + goto draw_std; + + case 5: // Invalid multicolor text + memset8(p, colors[0]); + if (color_data & 8) { + fore_mask_ptr[0] |= ((gfx_data & 0xaa) | (gfx_data & 0xaa) >> 1) >> x_scroll; + fore_mask_ptr[1] |= ((gfx_data & 0xaa) | (gfx_data & 0xaa) >> 1) << (8-x_scroll); + } else { + fore_mask_ptr[0] |= gfx_data >> x_scroll; + fore_mask_ptr[1] |= gfx_data << (7-x_scroll); + } + return; + + case 6: // Invalid standard bitmap + memset8(p, colors[0]); + fore_mask_ptr[0] |= gfx_data >> x_scroll; + fore_mask_ptr[1] |= gfx_data << (7-x_scroll); + return; + + case 7: // Invalid multicolor bitmap + memset8(p, colors[0]); + fore_mask_ptr[0] |= ((gfx_data & 0xaa) | (gfx_data & 0xaa) >> 1) >> x_scroll; + fore_mask_ptr[1] |= ((gfx_data & 0xaa) | (gfx_data & 0xaa) >> 1) << (8-x_scroll); + return; + + default: // Can't happen + return; + } + +draw_std: + + fore_mask_ptr[0] |= gfx_data >> x_scroll; + fore_mask_ptr[1] |= gfx_data << (7-x_scroll); + + data = gfx_data; + p[7] = c[data & 1]; data >>= 1; + p[6] = c[data & 1]; data >>= 1; + p[5] = c[data & 1]; data >>= 1; + p[4] = c[data & 1]; data >>= 1; + p[3] = c[data & 1]; data >>= 1; + p[2] = c[data & 1]; data >>= 1; + p[1] = c[data & 1]; data >>= 1; + p[0] = c[data]; + return; + +draw_multi: + + fore_mask_ptr[0] |= ((gfx_data & 0xaa) | (gfx_data & 0xaa) >> 1) >> x_scroll; + fore_mask_ptr[1] |= ((gfx_data & 0xaa) | (gfx_data & 0xaa) >> 1) << (8-x_scroll); + + data = gfx_data; + p[7] = p[6] = c[data & 3]; data >>= 2; + p[5] = p[4] = c[data & 3]; data >>= 2; + p[3] = p[2] = c[data & 3]; data >>= 2; + p[1] = p[0] = c[data]; + return; +} + + +/* + * Sprite display + */ + +#ifdef GLOBAL_VARS +inline static void draw_sprites(void) +#else +inline void MOS6569::draw_sprites(void) +#endif +{ + int i; + int snum, sbit; // Sprite number/bit mask + int spr_coll=0, gfx_coll=0; + + // Clear sprite collision buffer + { + uint32 *lp = (uint32 *)spr_coll_buf - 1; + for (i=0; i> (8-sshift)); + + if (mxe & sbit) { // X-expanded + if (mx[snum] > DISPLAY_X-56) + continue; + + uint32 sdata_l = 0, sdata_r = 0, fore_mask_r; + fore_mask_r = (((*(fmbp+4) << 24) | (*(fmbp+5) << 16) | (*(fmbp+6) << 8) + | (*(fmbp+7))) << sshift) | (*(fmbp+8) >> (8-sshift)); + + if (mmc & sbit) { // Multicolor mode + uint32 plane0_l, plane0_r, plane1_l, plane1_r; + + // Expand sprite data + sdata_l = MultiExpTable[sdata >> 24 & 0xff] << 16 | MultiExpTable[sdata >> 16 & 0xff]; + sdata_r = MultiExpTable[sdata >> 8 & 0xff] << 16; + + // Convert sprite chunky pixels to bitplanes + plane0_l = (sdata_l & 0x55555555) | (sdata_l & 0x55555555) << 1; + plane1_l = (sdata_l & 0xaaaaaaaa) | (sdata_l & 0xaaaaaaaa) >> 1; + plane0_r = (sdata_r & 0x55555555) | (sdata_r & 0x55555555) << 1; + plane1_r = (sdata_r & 0xaaaaaaaa) | (sdata_r & 0xaaaaaaaa) >> 1; + + // Collision with graphics? + if ((fore_mask & (plane0_l | plane1_l)) || (fore_mask_r & (plane0_r | plane1_r))) { + gfx_coll |= sbit; + if (mdp & sbit) { + plane0_l &= ~fore_mask; // Mask sprite if in background + plane1_l &= ~fore_mask; + plane0_r &= ~fore_mask_r; + plane1_r &= ~fore_mask_r; + } + } + + // Paint sprite + for (i=0; i<32; i++, plane0_l<<=1, plane1_l<<=1) { + uint8 col; + if (plane1_l & 0x80000000) { + if (plane0_l & 0x80000000) + col = mm1_color; + else + col = color; + } else { + if (plane0_l & 0x80000000) + col = mm0_color; + else + continue; + } + if (q[i]) + spr_coll |= q[i] | sbit; + else { + p[i] = col; + q[i] = sbit; + } + } + for (; i<48; i++, plane0_r<<=1, plane1_r<<=1) { + uint8 col; + if (plane1_r & 0x80000000) { + if (plane0_r & 0x80000000) + col = mm1_color; + else + col = color; + } else { + if (plane0_r & 0x80000000) + col = mm0_color; + else + continue; + } + if (q[i]) + spr_coll |= q[i] | sbit; + else { + p[i] = col; + q[i] = sbit; + } + } + + } else { // Standard mode + + // Expand sprite data + sdata_l = ExpTable[sdata >> 24 & 0xff] << 16 | ExpTable[sdata >> 16 & 0xff]; + sdata_r = ExpTable[sdata >> 8 & 0xff] << 16; + + // Collision with graphics? + if ((fore_mask & sdata_l) || (fore_mask_r & sdata_r)) { + gfx_coll |= sbit; + if (mdp & sbit) { + sdata_l &= ~fore_mask; // Mask sprite if in background + sdata_r &= ~fore_mask_r; + } + } + + // Paint sprite + for (i=0; i<32; i++, sdata_l<<=1) + if (sdata_l & 0x80000000) { + if (q[i]) // Collision with sprite? + spr_coll |= q[i] | sbit; + else { // Draw pixel if no collision + p[i] = color; + q[i] = sbit; + } + } + for (; i<48; i++, sdata_r<<=1) + if (sdata_r & 0x80000000) { + if (q[i]) // Collision with sprite? + spr_coll |= q[i] | sbit; + else { // Draw pixel if no collision + p[i] = color; + q[i] = sbit; + } + } + } + + } else { // Unexpanded + + if (mmc & sbit) { // Multicolor mode + uint32 plane0, plane1; + + // Convert sprite chunky pixels to bitplanes + plane0 = (sdata & 0x55555555) | (sdata & 0x55555555) << 1; + plane1 = (sdata & 0xaaaaaaaa) | (sdata & 0xaaaaaaaa) >> 1; + + // Collision with graphics? + if (fore_mask & (plane0 | plane1)) { + gfx_coll |= sbit; + if (mdp & sbit) { + plane0 &= ~fore_mask; // Mask sprite if in background + plane1 &= ~fore_mask; + } + } + + // Paint sprite + for (i=0; i<24; i++, plane0<<=1, plane1<<=1) { + uint8 col; + if (plane1 & 0x80000000) { + if (plane0 & 0x80000000) + col = mm1_color; + else + col = color; + } else { + if (plane0 & 0x80000000) + col = mm0_color; + else + continue; + } + if (q[i]) + spr_coll |= q[i] | sbit; + else { + p[i] = col; + q[i] = sbit; + } + } + + } else { // Standard mode + + // Collision with graphics? + if (fore_mask & sdata) { + gfx_coll |= sbit; + if (mdp & sbit) + sdata &= ~fore_mask; // Mask sprite if in background + } + + // Paint sprite + for (i=0; i<24; i++, sdata<<=1) + if (sdata & 0x80000000) { + if (q[i]) { // Collision with sprite? + spr_coll |= q[i] | sbit; + } else { // Draw pixel if no collision + p[i] = color; + q[i] = sbit; + } + } + } + } + } + } + + if (ThePrefs.SpriteCollisions) { + + // Check sprite-sprite collisions + if (clx_spr) + clx_spr |= spr_coll; + else { + clx_spr |= spr_coll; + irq_flag |= 0x04; + if (irq_mask & 0x04) { + irq_flag |= 0x80; + the_cpu->TriggerVICIRQ(); + } + } + + // Check sprite-background collisions + if (clx_bgr) + clx_bgr |= gfx_coll; + else { + clx_bgr |= gfx_coll; + irq_flag |= 0x02; + if (irq_mask & 0x02) { + irq_flag |= 0x80; + the_cpu->TriggerVICIRQ(); + } + } + } +} + + +#ifdef __POWERPC__ +static asm void fastcopy(register uchar *dst, register uchar *src); +static asm void fastcopy(register uchar *dst, register uchar *src) +{ + lfd fp0,0(src) + lfd fp1,8(src) + lfd fp2,16(src) + lfd fp3,24(src) + lfd fp4,32(src) + lfd fp5,40(src) + lfd fp6,48(src) + lfd fp7,56(src) + addi src,src,64 + stfd fp0,0(dst) + stfd fp1,8(dst) + stfd fp2,16(dst) + stfd fp3,24(dst) + stfd fp4,32(dst) + stfd fp5,40(dst) + stfd fp6,48(dst) + stfd fp7,56(dst) + addi dst,dst,64 + + lfd fp0,0(src) + lfd fp1,8(src) + lfd fp2,16(src) + lfd fp3,24(src) + lfd fp4,32(src) + lfd fp5,40(src) + lfd fp6,48(src) + lfd fp7,56(src) + addi src,src,64 + stfd fp0,0(dst) + stfd fp1,8(dst) + stfd fp2,16(dst) + stfd fp3,24(dst) + stfd fp4,32(dst) + stfd fp5,40(dst) + stfd fp6,48(dst) + stfd fp7,56(dst) + addi dst,dst,64 + + lfd fp0,0(src) + lfd fp1,8(src) + lfd fp2,16(src) + lfd fp3,24(src) + lfd fp4,32(src) + lfd fp5,40(src) + lfd fp6,48(src) + lfd fp7,56(src) + addi src,src,64 + stfd fp0,0(dst) + stfd fp1,8(dst) + stfd fp2,16(dst) + stfd fp3,24(dst) + stfd fp4,32(dst) + stfd fp5,40(dst) + stfd fp6,48(dst) + stfd fp7,56(dst) + addi dst,dst,64 + + lfd fp0,0(src) + lfd fp1,8(src) + lfd fp2,16(src) + lfd fp3,24(src) + lfd fp4,32(src) + lfd fp5,40(src) + lfd fp6,48(src) + lfd fp7,56(src) + addi src,src,64 + stfd fp0,0(dst) + stfd fp1,8(dst) + stfd fp2,16(dst) + stfd fp3,24(dst) + stfd fp4,32(dst) + stfd fp5,40(dst) + stfd fp6,48(dst) + stfd fp7,56(dst) + addi dst,dst,64 + + lfd fp0,0(src) + lfd fp1,8(src) + lfd fp2,16(src) + lfd fp3,24(src) + lfd fp4,32(src) + lfd fp5,40(src) + lfd fp6,48(src) + lfd fp7,56(src) + addi src,src,64 + stfd fp0,0(dst) + stfd fp1,8(dst) + stfd fp2,16(dst) + stfd fp3,24(dst) + stfd fp4,32(dst) + stfd fp5,40(dst) + stfd fp6,48(dst) + stfd fp7,56(dst) + addi dst,dst,64 + + lfd fp0,0(src) + lfd fp1,8(src) + lfd fp2,16(src) + lfd fp3,24(src) + lfd fp4,32(src) + lfd fp5,40(src) + lfd fp6,48(src) + lfd fp7,56(src) + addi src,src,64 + stfd fp0,0(dst) + stfd fp1,8(dst) + stfd fp2,16(dst) + stfd fp3,24(dst) + stfd fp4,32(dst) + stfd fp5,40(dst) + stfd fp6,48(dst) + stfd fp7,56(dst) + addi dst,dst,64 + blr +} +#endif + + +/* + * Emulate one clock cycle, returns true if new raster line has started + */ + +// Set BA low +#define SetBALow \ + if (!the_cpu->BALow) { \ + first_ba_cycle = the_c64->CycleCounter; \ + the_cpu->BALow = true; \ + } + +// Turn on display if Bad Line +#define DisplayIfBadLine \ + if (is_bad_line) \ + display_state = true; + +// Turn on display and matrix access if Bad Line +#define FetchIfBadLine \ + if (is_bad_line) { \ + display_state = true; \ + SetBALow; \ + } + +// Turn on display and matrix access and reset RC if Bad Line +#define RCIfBadLine \ + if (is_bad_line) { \ + display_state = true; \ + rc = 0; \ + SetBALow; \ + } + +// Idle access +#define IdleAccess \ + read_byte(0x3fff) + +// Refresh access +#define RefreshAccess \ + read_byte(0x3f00 | ref_cnt--) + +// Turn on sprite DMA if necessary +#define CheckSpriteDMA \ + mask = 1; \ + for (i=0; i<8; i++, mask<<=1) \ + if ((me & mask) && (raster_y & 0xff) == my[i]) { \ + spr_dma_on |= mask; \ + mc_base[i] = 0; \ + if (mye & mask) \ + spr_exp_y &= ~mask; \ + } + +// Fetch sprite data pointer +#define SprPtrAccess(num) \ + spr_ptr[num] = read_byte(matrix_base | 0x03f8 | num) << 6; + +// Fetch sprite data, increment data counter +#define SprDataAccess(num, bytenum) \ + if (spr_dma_on & (1 << num)) { \ + spr_data[num][bytenum] = read_byte(mc[num] & 0x3f | spr_ptr[num]); \ + mc[num]++; \ + } else if (bytenum == 1) \ + IdleAccess; + +// Sample border color and increment chunky_ptr and fore_mask_ptr +#define SampleBorder \ + if (draw_this_line) { \ + if (border_on) \ + border_color_sample[cycle-13] = ec_color; \ + chunky_ptr += 8; \ + fore_mask_ptr++; \ + } + + +bool MOS6569::EmulateCycle(void) +{ + uint8 mask; + int i; + + switch (cycle) { + + // Fetch sprite pointer 3, increment raster counter, trigger raster IRQ, + // test for Bad Line, reset BA if sprites 3 and 4 off, read data of sprite 3 + case 1: + if (raster_y == TOTAL_RASTERS-1) + + // Trigger VBlank in cycle 2 + vblanking = true; + + else { + + // Increment raster counter + raster_y++; + + // Trigger raster IRQ if IRQ line reached + if (raster_y == irq_raster) + raster_irq(); + + // In line $30, the DEN bit controls if Bad Lines can occur + if (raster_y == 0x30) + bad_lines_enabled = ctrl1 & 0x10; + + // Bad Line condition? + is_bad_line = (raster_y >= FIRST_DMA_LINE && raster_y <= LAST_DMA_LINE && ((raster_y & 7) == y_scroll) && bad_lines_enabled); + + // Don't draw all lines, hide some at the top and bottom + draw_this_line = (raster_y >= FIRST_DISP_LINE && raster_y <= LAST_DISP_LINE && !frame_skipped); + } + + // First sample of border state + border_on_sample[0] = border_on; + + SprPtrAccess(3); + SprDataAccess(3, 0); + DisplayIfBadLine; + if (!(spr_dma_on & 0x18)) + the_cpu->BALow = false; + break; + + // Set BA for sprite 5, read data of sprite 3 + case 2: + if (vblanking) { + + // Vertical blank, reset counters + raster_y = vc_base = 0; + ref_cnt = 0xff; + lp_triggered = vblanking = false; + + if (!(frame_skipped = --skip_counter)) + skip_counter = ThePrefs.SkipFrames; + + the_c64->VBlank(!frame_skipped); + + // Get bitmap pointer for next frame. This must be done + // after calling the_c64->VBlank() because the preferences + // and screen configuration may have been changed there + chunky_line_start = the_display->BitmapBase(); + xmod = the_display->BitmapXMod(); + + // Trigger raster IRQ if IRQ in line 0 + if (irq_raster == 0) + raster_irq(); + + } + + // Our output goes here +#ifdef __POWERPC__ + chunky_ptr = (uint8 *)chunky_tmp; +#else + chunky_ptr = chunky_line_start; +#endif + + // Clear foreground mask + memset(fore_mask_buf, 0, DISPLAY_X/8); + fore_mask_ptr = fore_mask_buf; + + SprDataAccess(3,1); + SprDataAccess(3,2); + DisplayIfBadLine; + if (spr_dma_on & 0x20) + SetBALow; + break; + + // Fetch sprite pointer 4, reset BA is sprite 4 and 5 off + case 3: + SprPtrAccess(4); + SprDataAccess(4, 0); + DisplayIfBadLine; + if (!(spr_dma_on & 0x30)) + the_cpu->BALow = false; + break; + + // Set BA for sprite 6, read data of sprite 4 + case 4: + SprDataAccess(4, 1); + SprDataAccess(4, 2); + DisplayIfBadLine; + if (spr_dma_on & 0x40) + SetBALow; + break; + + // Fetch sprite pointer 5, reset BA if sprite 5 and 6 off + case 5: + SprPtrAccess(5); + SprDataAccess(5, 0); + DisplayIfBadLine; + if (!(spr_dma_on & 0x60)) + the_cpu->BALow = false; + break; + + // Set BA for sprite 7, read data of sprite 5 + case 6: + SprDataAccess(5, 1); + SprDataAccess(5, 2); + DisplayIfBadLine; + if (spr_dma_on & 0x80) + SetBALow; + break; + + // Fetch sprite pointer 6, reset BA if sprite 6 and 7 off + case 7: + SprPtrAccess(6); + SprDataAccess(6, 0); + DisplayIfBadLine; + if (!(spr_dma_on & 0xc0)) + the_cpu->BALow = false; + break; + + // Read data of sprite 6 + case 8: + SprDataAccess(6, 1); + SprDataAccess(6, 2); + DisplayIfBadLine; + break; + + // Fetch sprite pointer 7, reset BA if sprite 7 off + case 9: + SprPtrAccess(7); + SprDataAccess(7, 0); + DisplayIfBadLine; + if (!(spr_dma_on & 0x80)) + the_cpu->BALow = false; + break; + + // Read data of sprite 7 + case 10: + SprDataAccess(7, 1); + SprDataAccess(7, 2); + DisplayIfBadLine; + break; + + // Refresh, reset BA + case 11: + RefreshAccess; + DisplayIfBadLine; + the_cpu->BALow = false; + break; + + // Refresh, turn on matrix access if Bad Line + case 12: + RefreshAccess; + FetchIfBadLine; + break; + + // Refresh, turn on matrix access if Bad Line, reset raster_x, graphics display starts here + case 13: + draw_background(); + SampleBorder; + RefreshAccess; + FetchIfBadLine; + raster_x = 0xfffc; + break; + + // Refresh, VCBASE->VCCOUNT, turn on matrix access and reset RC if Bad Line + case 14: + draw_background(); + SampleBorder; + RefreshAccess; + RCIfBadLine; + vc = vc_base; + break; + + // Refresh and matrix access, increment mc_base by 2 if y expansion flipflop is set + case 15: + draw_background(); + SampleBorder; + RefreshAccess; + FetchIfBadLine; + + for (i=0; i<8; i++) + if (spr_exp_y & (1 << i)) + mc_base[i] += 2; + + ml_index = 0; + matrix_access(); + break; + + // Graphics and matrix access, increment mc_base by 1 if y expansion flipflop is set + // and check if sprite DMA can be turned off + case 16: + draw_background(); + SampleBorder; + graphics_access(); + FetchIfBadLine; + + mask = 1; + for (i=0; i<8; i++, mask<<=1) { + if (spr_exp_y & mask) + mc_base[i]++; + if ((mc_base[i] & 0x3f) == 0x3f) + spr_dma_on &= ~mask; + } + + matrix_access(); + break; + + // Graphics and matrix access, turn off border in 40 column mode, display window starts here + case 17: + if (ctrl2 & 8) { + if (raster_y == dy_stop) + ud_border_on = true; + else { + if (ctrl1 & 0x10) { + if (raster_y == dy_start) + border_on = ud_border_on = false; + else + if (!ud_border_on) + border_on = false; + } else + if (!ud_border_on) + border_on = false; + } + } + + // Second sample of border state + border_on_sample[1] = border_on; + + draw_background(); + draw_graphics(); + SampleBorder; + graphics_access(); + FetchIfBadLine; + matrix_access(); + break; + + // Turn off border in 38 column mode + case 18: + if (!(ctrl2 & 8)) { + if (raster_y == dy_stop) + ud_border_on = true; + else { + if (ctrl1 & 0x10) { + if (raster_y == dy_start) + border_on = ud_border_on = false; + else + if (!ud_border_on) + border_on = false; + } else + if (!ud_border_on) + border_on = false; + } + } + + // Third sample of border state + border_on_sample[2] = border_on; + + // Falls through + + // Graphics and matrix access + case 19: case 20: case 21: case 22: case 23: case 24: + case 25: case 26: case 27: case 28: case 29: case 30: + case 31: case 32: case 33: case 34: case 35: case 36: + case 37: case 38: case 39: case 40: case 41: case 42: + case 43: case 44: case 45: case 46: case 47: case 48: + case 49: case 50: case 51: case 52: case 53: case 54: // Gnagna... + draw_graphics(); + SampleBorder; + graphics_access(); + FetchIfBadLine; + matrix_access(); + last_char_data = char_data; + break; + + // Last graphics access, turn off matrix access, turn on sprite DMA if Y coordinate is + // right and sprite is enabled, handle sprite y expansion, set BA for sprite 0 + case 55: + draw_graphics(); + SampleBorder; + graphics_access(); + DisplayIfBadLine; + + // Invert y expansion flipflop if bit in MYE is set + mask = 1; + for (i=0; i<8; i++, mask<<=1) + if (mye & mask) + spr_exp_y ^= mask; + CheckSpriteDMA; + + if (spr_dma_on & 0x01) { // Don't remove these braces! + SetBALow; + } else + the_cpu->BALow = false; + break; + + // Turn on border in 38 column mode, turn on sprite DMA if Y coordinate is right and + // sprite is enabled, set BA for sprite 0, display window ends here + case 56: + if (!(ctrl2 & 8)) + border_on = true; + + // Fourth sample of border state + border_on_sample[3] = border_on; + + draw_graphics(); + SampleBorder; + IdleAccess; + DisplayIfBadLine; + CheckSpriteDMA; + + if (spr_dma_on & 0x01) + SetBALow; + break; + + // Turn on border in 40 column mode, set BA for sprite 1, paint sprites + case 57: + if (ctrl2 & 8) + border_on = true; + + // Fifth sample of border state + border_on_sample[4] = border_on; + + // Sample spr_disp_on and spr_data for sprite drawing + if ((spr_draw = spr_disp_on) != 0) + memcpy(spr_draw_data, spr_data, 8*4); + + // Turn off sprite display if DMA is off + mask = 1; + for (i=0; i<8; i++, mask<<=1) + if ((spr_disp_on & mask) && !(spr_dma_on & mask)) + spr_disp_on &= ~mask; + + draw_background(); + SampleBorder; + IdleAccess; + DisplayIfBadLine; + if (spr_dma_on & 0x02) + SetBALow; + break; + + // Fetch sprite pointer 0, mc_base->mc, turn on sprite display if necessary, + // turn off display if RC=7, read data of sprite 0 + case 58: + draw_background(); + SampleBorder; + + mask = 1; + for (i=0; i<8; i++, mask<<=1) { + mc[i] = mc_base[i]; + if ((spr_dma_on & mask) && (raster_y & 0xff) == my[i]) + spr_disp_on |= mask; + } + SprPtrAccess(0); + SprDataAccess(0, 0); + + if (rc == 7) { + vc_base = vc; + display_state = false; + } + if (is_bad_line || display_state) { + display_state = true; + rc = (rc + 1) & 7; + } + break; + + // Set BA for sprite 2, read data of sprite 0 + case 59: + draw_background(); + SampleBorder; + SprDataAccess(0, 1); + SprDataAccess(0, 2); + DisplayIfBadLine; + if (spr_dma_on & 0x04) + SetBALow; + break; + + // Fetch sprite pointer 1, reset BA if sprite 1 and 2 off, graphics display ends here + case 60: + draw_background(); + SampleBorder; + + if (draw_this_line) { + + // Draw sprites + if (spr_draw && ThePrefs.SpritesOn) + draw_sprites(); + + // Draw border +#ifdef __POWERPC__ + if (border_on_sample[0]) + for (i=0; i<4; i++) + memset8((uint8 *)chunky_tmp+i*8, border_color_sample[i]); + if (border_on_sample[1]) + memset8((uint8 *)chunky_tmp+4*8, border_color_sample[4]); + if (border_on_sample[2]) + for (i=5; i<43; i++) + memset8((uint8 *)chunky_tmp+i*8, border_color_sample[i]); + if (border_on_sample[3]) + memset8((uint8 *)chunky_tmp+43*8, border_color_sample[43]); + if (border_on_sample[4]) + for (i=44; iBALow = false; + break; + + // Set BA for sprite 3, read data of sprite 1 + case 61: + SprDataAccess(1, 1); + SprDataAccess(1, 2); + DisplayIfBadLine; + if (spr_dma_on & 0x08) + SetBALow; + break; + + // Read sprite pointer 2, reset BA if sprite 2 and 3 off, read data of sprite 2 + case 62: + SprPtrAccess(2); + SprDataAccess(2, 0); + DisplayIfBadLine; + if (!(spr_dma_on & 0x0c)) + the_cpu->BALow = false; + break; + + // Set BA for sprite 4, read data of sprite 2 + case 63: + SprDataAccess(2, 1); + SprDataAccess(2, 2); + DisplayIfBadLine; + + if (raster_y == dy_stop) + ud_border_on = true; + else + if (ctrl1 & 0x10 && raster_y == dy_start) + ud_border_on = false; + + if (spr_dma_on & 0x10) + SetBALow; + + // Last cycle + raster_x += 8; + cycle = 1; + return true; + } + + // Next cycle + raster_x += 8; + cycle++; + return false; +} diff --git a/Src/Version.h b/Src/Version.h new file mode 100644 index 0000000..5c8ef4f --- /dev/null +++ b/Src/Version.h @@ -0,0 +1,29 @@ +/* + * Version.h - Version information + * + * Frodo (C) 1994-1997,2002-2005 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _VERSION_H +#define _VERSION_H + +// Version/revision +const int FRODO_VERSION = 4; +const int FRODO_REVISION = 2; +const char VERSION_STRING[] = "Frodo V4.2"; + +#endif diff --git a/Src/autogen.sh b/Src/autogen.sh new file mode 100755 index 0000000..560fe72 --- /dev/null +++ b/Src/autogen.sh @@ -0,0 +1,57 @@ +#! /bin/sh +# Run this to generate all the initial makefiles, etc. +# This was lifted from the Gimp, and adapted slightly by +# Christian Bauer. + +DIE=0 + +PROG=Frodo + +# Check how echo works in this /bin/sh +case `echo -n` in +-n) _echo_n= _echo_c='\c';; +*) _echo_n=-n _echo_c=;; +esac + +(autoconf --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "You must have autoconf installed to compile $PROG." + echo "Download the appropriate package for your distribution," + echo "or get the source tarball at ftp://ftp.gnu.org/pub/gnu/" + DIE=1 +} + +(aclocal --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: Missing aclocal. The version of automake" + echo "installed doesn't appear recent enough." + echo "Get ftp://ftp.gnu.org/pub/gnu/automake-1.4.tar.gz" + echo "(or a newer version if it is available)" + DIE=1 +} + +if test "$DIE" -eq 1; then + exit 1 +fi + +aclocalinclude="$ACLOCAL_FLAGS"; \ +(echo $_echo_n " + Running aclocal: $_echo_c"; \ + aclocal $aclocalinclude; \ + echo "done.") && \ +(echo $_echo_n " + Running autoheader: $_echo_c"; \ + autoheader; \ + echo "done.") && \ +(echo $_echo_n " + Running autoconf: $_echo_c"; \ + autoconf; \ + echo "done.") + +rm -f config.cache + +if [ x"$NO_CONFIGURE" = "x" ]; then + echo " + Running 'configure $@':" + if [ -z "$*" ]; then + echo " ** If you wish to pass arguments to ./configure, please" + echo " ** specify them on the command line." + fi + ./configure "$@" +fi diff --git a/Src/char_to_kc.c b/Src/char_to_kc.c new file mode 100644 index 0000000..70f5fac --- /dev/null +++ b/Src/char_to_kc.c @@ -0,0 +1,101 @@ +/********************************************************************* + * + * Copyright (C) 2008, Simon Kagstrom + * + * Filename: char_to_kc.c + * Author: Simon Kagstrom + * Description: Convert chars to keycodes + * + * $Id:$ + * + ********************************************************************/ +#include + +#define MATRIX(a,b) (((a) << 3) | (b)) + +#define SHIFT (1<<7) + +static uint8_t char_to_kc[] = +{ + /* Some shifted stuff */ + ['\"'] = MATRIX(7, 3) | SHIFT, + + /* CUD */ + /* F5 */ + /* F3 */ + /* F1 */ + /* F7 */ + ['\n'] = MATRIX(0, 1), + [8] = MATRIX(0, 0), + + ['E'] = MATRIX(1, 6), + ['S'] = MATRIX(1, 5), + ['Z'] = MATRIX(1, 4), + ['4'] = MATRIX(1, 3), + ['A'] = MATRIX(1, 2), + ['W'] = MATRIX(1, 1), + ['3'] = MATRIX(1, 0), + + ['X'] = MATRIX(2, 7), + ['T'] = MATRIX(2, 6), + ['F'] = MATRIX(2, 5), + ['C'] = MATRIX(2, 4), + ['6'] = MATRIX(2, 3), + ['D'] = MATRIX(2, 2), + ['R'] = MATRIX(2, 1), + ['5'] = MATRIX(2, 0), + + ['V'] = MATRIX(3, 7), + ['U'] = MATRIX(3, 6), + ['H'] = MATRIX(3, 5), + ['B'] = MATRIX(3, 4), + ['8'] = MATRIX(3, 3), + ['G'] = MATRIX(3, 2), + ['Y'] = MATRIX(3, 1), + ['7'] = MATRIX(3, 0), + + ['N'] = MATRIX(4, 7), + ['O'] = MATRIX(4, 6), + ['K'] = MATRIX(4, 5), + ['M'] = MATRIX(4, 4), + ['0'] = MATRIX(4, 3), + ['J'] = MATRIX(4, 2), + ['I'] = MATRIX(4, 1), + ['9'] = MATRIX(4, 0), + + [','] = MATRIX(5, 7), + ['@'] = MATRIX(5, 6), + [':'] = MATRIX(5, 5), + ['.'] = MATRIX(5, 4), + ['-'] = MATRIX(5, 3), + ['L'] = MATRIX(5, 2), + ['P'] = MATRIX(5, 1), + ['+'] = MATRIX(5, 0), + + ['/'] = MATRIX(6, 7), + ['^'] = MATRIX(6, 6), + ['='] = MATRIX(6, 5), + /* SHR */ + /* HOM */ + [';'] = MATRIX(6, 2), + ['*'] = MATRIX(6, 1), + /* ?? */ + + /* R/S */ + ['Q'] = MATRIX(7, 6), + /* C= */ + [' '] = MATRIX(7, 4), + ['2'] = MATRIX(7, 3), + /* CTL */ + /* <- */ + ['1'] = MATRIX(7, 0), +}; + +int get_kc_from_char(char c_in, int *shifted) +{ + char c = char_to_kc[(int)c_in]; + int out = c & (~SHIFT); + + *shifted = c & SHIFT; + return out; +} diff --git a/Src/config.guess b/Src/config.guess new file mode 100755 index 0000000..45bee13 --- /dev/null +++ b/Src/config.guess @@ -0,0 +1,1465 @@ +#! /bin/sh +# Attempt to guess a canonical system name. +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, +# 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + +timestamp='2005-04-22' + +# This file 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 2 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, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Originally written by Per Bothner . +# Please send patches to . Submit a context +# diff and a properly formatted ChangeLog entry. +# +# This script attempts to guess a canonical system name similar to +# config.sub. If it succeeds, it prints the system name on stdout, and +# exits with 0. Otherwise, it exits with 1. +# +# The plan is that this can be called by configure scripts if you +# don't specify an explicit build system type. + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] + +Output the configuration name of the system \`$me' is run on. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.guess ($timestamp) + +Originally written by Per Bothner. +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 +Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit 0 ;; + --version | -v ) + echo "$version" ; exit 0 ;; + --help | --h* | -h ) + echo "$usage"; exit 0 ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" >&2 + exit 1 ;; + * ) + break ;; + esac +done + +if test $# != 0; then + echo "$me: too many arguments$help" >&2 + exit 1 +fi + +trap 'exit 1' 1 2 15 + +# CC_FOR_BUILD -- compiler used by this script. Note that the use of a +# compiler to aid in system detection is discouraged as it requires +# temporary files to be created and, as you can see below, it is a +# headache to deal with in a portable fashion. + +# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still +# use `HOST_CC' if defined, but it is deprecated. + +# Portable tmp directory creation inspired by the Autoconf team. + +set_cc_for_build=' +trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; +trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; +: ${TMPDIR=/tmp} ; + { tmp=`(umask 077 && mktemp -d -q "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || + { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || + { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || + { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; +dummy=$tmp/dummy ; +tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; +case $CC_FOR_BUILD,$HOST_CC,$CC in + ,,) echo "int x;" > $dummy.c ; + for c in cc gcc c89 c99 ; do + if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then + CC_FOR_BUILD="$c"; break ; + fi ; + done ; + if test x"$CC_FOR_BUILD" = x ; then + CC_FOR_BUILD=no_compiler_found ; + fi + ;; + ,,*) CC_FOR_BUILD=$CC ;; + ,*,*) CC_FOR_BUILD=$HOST_CC ;; +esac ;' + +# This is needed to find uname on a Pyramid OSx when run in the BSD universe. +# (ghazi@noc.rutgers.edu 1994-08-24) +if (test -f /.attbin/uname) >/dev/null 2>&1 ; then + PATH=$PATH:/.attbin ; export PATH +fi + +UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown +UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown +UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown +UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown + +# Note: order is significant - the case branches are not exclusive. + +case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in + *:NetBSD:*:*) + # NetBSD (nbsd) targets should (where applicable) match one or + # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*, + # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently + # switched to ELF, *-*-netbsd* would select the old + # object file format. This provides both forward + # compatibility and a consistent mechanism for selecting the + # object file format. + # + # Note: NetBSD doesn't particularly care about the vendor + # portion of the name. We always set it to "unknown". + sysctl="sysctl -n hw.machine_arch" + UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \ + /usr/sbin/$sysctl 2>/dev/null || echo unknown)` + case "${UNAME_MACHINE_ARCH}" in + armeb) machine=armeb-unknown ;; + arm*) machine=arm-unknown ;; + sh3el) machine=shl-unknown ;; + sh3eb) machine=sh-unknown ;; + *) machine=${UNAME_MACHINE_ARCH}-unknown ;; + esac + # The Operating System including object format, if it has switched + # to ELF recently, or will in the future. + case "${UNAME_MACHINE_ARCH}" in + arm*|i386|m68k|ns32k|sh3*|sparc|vax) + eval $set_cc_for_build + if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep __ELF__ >/dev/null + then + # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). + # Return netbsd for either. FIX? + os=netbsd + else + os=netbsdelf + fi + ;; + *) + os=netbsd + ;; + esac + # The OS release + # Debian GNU/NetBSD machines have a different userland, and + # thus, need a distinct triplet. However, they do not need + # kernel version information, so it can be replaced with a + # suitable tag, in the style of linux-gnu. + case "${UNAME_VERSION}" in + Debian*) + release='-gnu' + ;; + *) + release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` + ;; + esac + # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: + # contains redundant information, the shorter form: + # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. + echo "${machine}-${os}${release}" + exit 0 ;; + amd64:OpenBSD:*:*) + echo x86_64-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + amiga:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + cats:OpenBSD:*:*) + echo arm-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + hp300:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + luna88k:OpenBSD:*:*) + echo m88k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + mac68k:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + macppc:OpenBSD:*:*) + echo powerpc-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + mvme68k:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + mvme88k:OpenBSD:*:*) + echo m88k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + mvmeppc:OpenBSD:*:*) + echo powerpc-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + sgi:OpenBSD:*:*) + echo mips64-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + sun3:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + *:OpenBSD:*:*) + echo ${UNAME_MACHINE}-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + *:ekkoBSD:*:*) + echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} + exit 0 ;; + macppc:MirBSD:*:*) + echo powerppc-unknown-mirbsd${UNAME_RELEASE} + exit 0 ;; + *:MirBSD:*:*) + echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE} + exit 0 ;; + alpha:OSF1:*:*) + case $UNAME_RELEASE in + *4.0) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` + ;; + *5.*) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` + ;; + esac + # According to Compaq, /usr/sbin/psrinfo has been available on + # OSF/1 and Tru64 systems produced since 1995. I hope that + # covers most systems running today. This code pipes the CPU + # types through head -n 1, so we only detect the type of CPU 0. + ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` + case "$ALPHA_CPU_TYPE" in + "EV4 (21064)") + UNAME_MACHINE="alpha" ;; + "EV4.5 (21064)") + UNAME_MACHINE="alpha" ;; + "LCA4 (21066/21068)") + UNAME_MACHINE="alpha" ;; + "EV5 (21164)") + UNAME_MACHINE="alphaev5" ;; + "EV5.6 (21164A)") + UNAME_MACHINE="alphaev56" ;; + "EV5.6 (21164PC)") + UNAME_MACHINE="alphapca56" ;; + "EV5.7 (21164PC)") + UNAME_MACHINE="alphapca57" ;; + "EV6 (21264)") + UNAME_MACHINE="alphaev6" ;; + "EV6.7 (21264A)") + UNAME_MACHINE="alphaev67" ;; + "EV6.8CB (21264C)") + UNAME_MACHINE="alphaev68" ;; + "EV6.8AL (21264B)") + UNAME_MACHINE="alphaev68" ;; + "EV6.8CX (21264D)") + UNAME_MACHINE="alphaev68" ;; + "EV6.9A (21264/EV69A)") + UNAME_MACHINE="alphaev69" ;; + "EV7 (21364)") + UNAME_MACHINE="alphaev7" ;; + "EV7.9 (21364A)") + UNAME_MACHINE="alphaev79" ;; + esac + # A Pn.n version is a patched version. + # A Vn.n version is a released version. + # A Tn.n version is a released field test version. + # A Xn.n version is an unreleased experimental baselevel. + # 1.2 uses "1.2" for uname -r. + echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + exit 0 ;; + Alpha\ *:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # Should we change UNAME_MACHINE based on the output of uname instead + # of the specific Alpha model? + echo alpha-pc-interix + exit 0 ;; + 21064:Windows_NT:50:3) + echo alpha-dec-winnt3.5 + exit 0 ;; + Amiga*:UNIX_System_V:4.0:*) + echo m68k-unknown-sysv4 + exit 0;; + *:[Aa]miga[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-amigaos + exit 0 ;; + *:[Mm]orph[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-morphos + exit 0 ;; + *:OS/390:*:*) + echo i370-ibm-openedition + exit 0 ;; + *:z/VM:*:*) + echo s390-ibm-zvmoe + exit 0 ;; + *:OS400:*:*) + echo powerpc-ibm-os400 + exit 0 ;; + arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) + echo arm-acorn-riscix${UNAME_RELEASE} + exit 0;; + SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) + echo hppa1.1-hitachi-hiuxmpp + exit 0;; + Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) + # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. + if test "`(/bin/universe) 2>/dev/null`" = att ; then + echo pyramid-pyramid-sysv3 + else + echo pyramid-pyramid-bsd + fi + exit 0 ;; + NILE*:*:*:dcosx) + echo pyramid-pyramid-svr4 + exit 0 ;; + DRS?6000:unix:4.0:6*) + echo sparc-icl-nx6 + exit 0 ;; + DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) + case `/usr/bin/uname -p` in + sparc) echo sparc-icl-nx7 && exit 0 ;; + esac ;; + sun4H:SunOS:5.*:*) + echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) + echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + i86pc:SunOS:5.*:*) + echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + sun4*:SunOS:6*:*) + # According to config.sub, this is the proper way to canonicalize + # SunOS6. Hard to guess exactly what SunOS6 will be like, but + # it's likely to be more like Solaris than SunOS4. + echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + sun4*:SunOS:*:*) + case "`/usr/bin/arch -k`" in + Series*|S4*) + UNAME_RELEASE=`uname -v` + ;; + esac + # Japanese Language versions have a version number like `4.1.3-JL'. + echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` + exit 0 ;; + sun3*:SunOS:*:*) + echo m68k-sun-sunos${UNAME_RELEASE} + exit 0 ;; + sun*:*:4.2BSD:*) + UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` + test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 + case "`/bin/arch`" in + sun3) + echo m68k-sun-sunos${UNAME_RELEASE} + ;; + sun4) + echo sparc-sun-sunos${UNAME_RELEASE} + ;; + esac + exit 0 ;; + aushp:SunOS:*:*) + echo sparc-auspex-sunos${UNAME_RELEASE} + exit 0 ;; + # The situation for MiNT is a little confusing. The machine name + # can be virtually everything (everything which is not + # "atarist" or "atariste" at least should have a processor + # > m68000). The system name ranges from "MiNT" over "FreeMiNT" + # to the lowercase version "mint" (or "freemint"). Finally + # the system name "TOS" denotes a system which is actually not + # MiNT. But MiNT is downward compatible to TOS, so this should + # be no problem. + atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit 0 ;; + atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit 0 ;; + *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit 0 ;; + milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) + echo m68k-milan-mint${UNAME_RELEASE} + exit 0 ;; + hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) + echo m68k-hades-mint${UNAME_RELEASE} + exit 0 ;; + *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) + echo m68k-unknown-mint${UNAME_RELEASE} + exit 0 ;; + m68k:machten:*:*) + echo m68k-apple-machten${UNAME_RELEASE} + exit 0 ;; + powerpc:machten:*:*) + echo powerpc-apple-machten${UNAME_RELEASE} + exit 0 ;; + RISC*:Mach:*:*) + echo mips-dec-mach_bsd4.3 + exit 0 ;; + RISC*:ULTRIX:*:*) + echo mips-dec-ultrix${UNAME_RELEASE} + exit 0 ;; + VAX*:ULTRIX*:*:*) + echo vax-dec-ultrix${UNAME_RELEASE} + exit 0 ;; + 2020:CLIX:*:* | 2430:CLIX:*:*) + echo clipper-intergraph-clix${UNAME_RELEASE} + exit 0 ;; + mips:*:*:UMIPS | mips:*:*:RISCos) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c +#ifdef __cplusplus +#include /* for printf() prototype */ + int main (int argc, char *argv[]) { +#else + int main (argc, argv) int argc; char *argv[]; { +#endif + #if defined (host_mips) && defined (MIPSEB) + #if defined (SYSTYPE_SYSV) + printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_SVR4) + printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) + printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); + #endif + #endif + exit (-1); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c \ + && $dummy `echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` \ + && exit 0 + echo mips-mips-riscos${UNAME_RELEASE} + exit 0 ;; + Motorola:PowerMAX_OS:*:*) + echo powerpc-motorola-powermax + exit 0 ;; + Motorola:*:4.3:PL8-*) + echo powerpc-harris-powermax + exit 0 ;; + Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) + echo powerpc-harris-powermax + exit 0 ;; + Night_Hawk:Power_UNIX:*:*) + echo powerpc-harris-powerunix + exit 0 ;; + m88k:CX/UX:7*:*) + echo m88k-harris-cxux7 + exit 0 ;; + m88k:*:4*:R4*) + echo m88k-motorola-sysv4 + exit 0 ;; + m88k:*:3*:R3*) + echo m88k-motorola-sysv3 + exit 0 ;; + AViiON:dgux:*:*) + # DG/UX returns AViiON for all architectures + UNAME_PROCESSOR=`/usr/bin/uname -p` + if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] + then + if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ + [ ${TARGET_BINARY_INTERFACE}x = x ] + then + echo m88k-dg-dgux${UNAME_RELEASE} + else + echo m88k-dg-dguxbcs${UNAME_RELEASE} + fi + else + echo i586-dg-dgux${UNAME_RELEASE} + fi + exit 0 ;; + M88*:DolphinOS:*:*) # DolphinOS (SVR3) + echo m88k-dolphin-sysv3 + exit 0 ;; + M88*:*:R3*:*) + # Delta 88k system running SVR3 + echo m88k-motorola-sysv3 + exit 0 ;; + XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) + echo m88k-tektronix-sysv3 + exit 0 ;; + Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) + echo m68k-tektronix-bsd + exit 0 ;; + *:IRIX*:*:*) + echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` + exit 0 ;; + ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. + echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id + exit 0 ;; # Note that: echo "'`uname -s`'" gives 'AIX ' + i*86:AIX:*:*) + echo i386-ibm-aix + exit 0 ;; + ia64:AIX:*:*) + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} + exit 0 ;; + *:AIX:2:3) + if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + + main() + { + if (!__power_pc()) + exit(1); + puts("powerpc-ibm-aix3.2.5"); + exit(0); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c && $dummy && exit 0 + echo rs6000-ibm-aix3.2.5 + elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then + echo rs6000-ibm-aix3.2.4 + else + echo rs6000-ibm-aix3.2 + fi + exit 0 ;; + *:AIX:*:[45]) + IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` + if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then + IBM_ARCH=rs6000 + else + IBM_ARCH=powerpc + fi + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${IBM_ARCH}-ibm-aix${IBM_REV} + exit 0 ;; + *:AIX:*:*) + echo rs6000-ibm-aix + exit 0 ;; + ibmrt:4.4BSD:*|romp-ibm:BSD:*) + echo romp-ibm-bsd4.4 + exit 0 ;; + ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and + echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to + exit 0 ;; # report: romp-ibm BSD 4.3 + *:BOSX:*:*) + echo rs6000-bull-bosx + exit 0 ;; + DPX/2?00:B.O.S.:*:*) + echo m68k-bull-sysv3 + exit 0 ;; + 9000/[34]??:4.3bsd:1.*:*) + echo m68k-hp-bsd + exit 0 ;; + hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) + echo m68k-hp-bsd4.4 + exit 0 ;; + 9000/[34678]??:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + case "${UNAME_MACHINE}" in + 9000/31? ) HP_ARCH=m68000 ;; + 9000/[34]?? ) HP_ARCH=m68k ;; + 9000/[678][0-9][0-9]) + if [ -x /usr/bin/getconf ]; then + sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` + sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` + case "${sc_cpu_version}" in + 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 + 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 + 532) # CPU_PA_RISC2_0 + case "${sc_kernel_bits}" in + 32) HP_ARCH="hppa2.0n" ;; + 64) HP_ARCH="hppa2.0w" ;; + '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20 + esac ;; + esac + fi + if [ "${HP_ARCH}" = "" ]; then + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + + #define _HPUX_SOURCE + #include + #include + + int main () + { + #if defined(_SC_KERNEL_BITS) + long bits = sysconf(_SC_KERNEL_BITS); + #endif + long cpu = sysconf (_SC_CPU_VERSION); + + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1"); break; + case CPU_PA_RISC2_0: + #if defined(_SC_KERNEL_BITS) + switch (bits) + { + case 64: puts ("hppa2.0w"); break; + case 32: puts ("hppa2.0n"); break; + default: puts ("hppa2.0"); break; + } break; + #else /* !defined(_SC_KERNEL_BITS) */ + puts ("hppa2.0"); break; + #endif + default: puts ("hppa1.0"); break; + } + exit (0); + } +EOF + (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` + test -z "$HP_ARCH" && HP_ARCH=hppa + fi ;; + esac + if [ ${HP_ARCH} = "hppa2.0w" ] + then + # avoid double evaluation of $set_cc_for_build + test -n "$CC_FOR_BUILD" || eval $set_cc_for_build + if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E -) | grep __LP64__ >/dev/null + then + HP_ARCH="hppa2.0w" + else + HP_ARCH="hppa64" + fi + fi + echo ${HP_ARCH}-hp-hpux${HPUX_REV} + exit 0 ;; + ia64:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + echo ia64-hp-hpux${HPUX_REV} + exit 0 ;; + 3050*:HI-UX:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + int + main () + { + long cpu = sysconf (_SC_CPU_VERSION); + /* The order matters, because CPU_IS_HP_MC68K erroneously returns + true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct + results, however. */ + if (CPU_IS_PA_RISC (cpu)) + { + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; + case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; + default: puts ("hppa-hitachi-hiuxwe2"); break; + } + } + else if (CPU_IS_HP_MC68K (cpu)) + puts ("m68k-hitachi-hiuxwe2"); + else puts ("unknown-hitachi-hiuxwe2"); + exit (0); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c && $dummy && exit 0 + echo unknown-hitachi-hiuxwe2 + exit 0 ;; + 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) + echo hppa1.1-hp-bsd + exit 0 ;; + 9000/8??:4.3bsd:*:*) + echo hppa1.0-hp-bsd + exit 0 ;; + *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) + echo hppa1.0-hp-mpeix + exit 0 ;; + hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) + echo hppa1.1-hp-osf + exit 0 ;; + hp8??:OSF1:*:*) + echo hppa1.0-hp-osf + exit 0 ;; + i*86:OSF1:*:*) + if [ -x /usr/sbin/sysversion ] ; then + echo ${UNAME_MACHINE}-unknown-osf1mk + else + echo ${UNAME_MACHINE}-unknown-osf1 + fi + exit 0 ;; + parisc*:Lites*:*:*) + echo hppa1.1-hp-lites + exit 0 ;; + C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) + echo c1-convex-bsd + exit 0 ;; + C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit 0 ;; + C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) + echo c34-convex-bsd + exit 0 ;; + C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) + echo c38-convex-bsd + exit 0 ;; + C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) + echo c4-convex-bsd + exit 0 ;; + CRAY*Y-MP:*:*:*) + echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*[A-Z]90:*:*:*) + echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ + | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ + -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ + -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*TS:*:*:*) + echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*T3E:*:*:*) + echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*SV1:*:*:*) + echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + *:UNICOS/mp:*:*) + echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) + FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` + echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit 0 ;; + 5000:UNIX_System_V:4.*:*) + FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'` + echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit 0 ;; + i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) + echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} + exit 0 ;; + sparc*:BSD/OS:*:*) + echo sparc-unknown-bsdi${UNAME_RELEASE} + exit 0 ;; + *:BSD/OS:*:*) + echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} + exit 0 ;; + *:FreeBSD:*:*) + echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` + exit 0 ;; + i*:CYGWIN*:*) + echo ${UNAME_MACHINE}-pc-cygwin + exit 0 ;; + i*:MINGW*:*) + echo ${UNAME_MACHINE}-pc-mingw32 + exit 0 ;; + i*:PW*:*) + echo ${UNAME_MACHINE}-pc-pw32 + exit 0 ;; + x86:Interix*:[34]*) + echo i586-pc-interix${UNAME_RELEASE}|sed -e 's/\..*//' + exit 0 ;; + [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) + echo i${UNAME_MACHINE}-pc-mks + exit 0 ;; + i*:Windows_NT*:* | Pentium*:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we + # UNAME_MACHINE based on the output of uname instead of i386? + echo i586-pc-interix + exit 0 ;; + i*:UWIN*:*) + echo ${UNAME_MACHINE}-pc-uwin + exit 0 ;; + amd64:CYGWIN*:*:*) + echo x86_64-unknown-cygwin + exit 0 ;; + p*:CYGWIN*:*) + echo powerpcle-unknown-cygwin + exit 0 ;; + prep*:SunOS:5.*:*) + echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + *:GNU:*:*) + # the GNU system + echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` + exit 0 ;; + *:GNU/*:*:*) + # other systems with GNU libc and userland + echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu + exit 0 ;; + i*86:Minix:*:*) + echo ${UNAME_MACHINE}-pc-minix + exit 0 ;; + arm*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + cris:Linux:*:*) + echo cris-axis-linux-gnu + exit 0 ;; + crisv32:Linux:*:*) + echo crisv32-axis-linux-gnu + exit 0 ;; + frv:Linux:*:*) + echo frv-unknown-linux-gnu + exit 0 ;; + ia64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + m32r*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + m68*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + mips:Linux:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #undef CPU + #undef mips + #undef mipsel + #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) + CPU=mipsel + #else + #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) + CPU=mips + #else + CPU= + #endif + #endif +EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=` + test x"${CPU}" != x && echo "${CPU}-unknown-linux-gnu" && exit 0 + ;; + mips64:Linux:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #undef CPU + #undef mips64 + #undef mips64el + #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) + CPU=mips64el + #else + #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) + CPU=mips64 + #else + CPU= + #endif + #endif +EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=` + test x"${CPU}" != x && echo "${CPU}-unknown-linux-gnu" && exit 0 + ;; + ppc:Linux:*:*) + echo powerpc-unknown-linux-gnu + exit 0 ;; + ppc64:Linux:*:*) + echo powerpc64-unknown-linux-gnu + exit 0 ;; + alpha:Linux:*:*) + case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in + EV5) UNAME_MACHINE=alphaev5 ;; + EV56) UNAME_MACHINE=alphaev56 ;; + PCA56) UNAME_MACHINE=alphapca56 ;; + PCA57) UNAME_MACHINE=alphapca56 ;; + EV6) UNAME_MACHINE=alphaev6 ;; + EV67) UNAME_MACHINE=alphaev67 ;; + EV68*) UNAME_MACHINE=alphaev68 ;; + esac + objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null + if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi + echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} + exit 0 ;; + parisc:Linux:*:* | hppa:Linux:*:*) + # Look for CPU level + case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in + PA7*) echo hppa1.1-unknown-linux-gnu ;; + PA8*) echo hppa2.0-unknown-linux-gnu ;; + *) echo hppa-unknown-linux-gnu ;; + esac + exit 0 ;; + parisc64:Linux:*:* | hppa64:Linux:*:*) + echo hppa64-unknown-linux-gnu + exit 0 ;; + s390:Linux:*:* | s390x:Linux:*:*) + echo ${UNAME_MACHINE}-ibm-linux + exit 0 ;; + sh64*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + sh*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + sparc:Linux:*:* | sparc64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + x86_64:Linux:*:*) + echo x86_64-unknown-linux-gnu + exit 0 ;; + i*86:Linux:*:*) + # The BFD linker knows what the default object file format is, so + # first see if it will tell us. cd to the root directory to prevent + # problems with other programs or directories called `ld' in the path. + # Set LC_ALL=C to ensure ld outputs messages in English. + ld_supported_targets=`cd /; LC_ALL=C ld --help 2>&1 \ + | sed -ne '/supported targets:/!d + s/[ ][ ]*/ /g + s/.*supported targets: *// + s/ .*// + p'` + case "$ld_supported_targets" in + elf32-i386) + TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu" + ;; + a.out-i386-linux) + echo "${UNAME_MACHINE}-pc-linux-gnuaout" + exit 0 ;; + coff-i386) + echo "${UNAME_MACHINE}-pc-linux-gnucoff" + exit 0 ;; + "") + # Either a pre-BFD a.out linker (linux-gnuoldld) or + # one that does not give us useful --help. + echo "${UNAME_MACHINE}-pc-linux-gnuoldld" + exit 0 ;; + esac + # Determine whether the default compiler is a.out or elf + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + #ifdef __ELF__ + # ifdef __GLIBC__ + # if __GLIBC__ >= 2 + LIBC=gnu + # else + LIBC=gnulibc1 + # endif + # else + LIBC=gnulibc1 + # endif + #else + #ifdef __INTEL_COMPILER + LIBC=gnu + #else + LIBC=gnuaout + #endif + #endif + #ifdef __dietlibc__ + LIBC=dietlibc + #endif +EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^LIBC=` + test x"${LIBC}" != x && echo "${UNAME_MACHINE}-pc-linux-${LIBC}" && exit 0 + test x"${TENTATIVE}" != x && echo "${TENTATIVE}" && exit 0 + ;; + i*86:DYNIX/ptx:4*:*) + # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. + # earlier versions are messed up and put the nodename in both + # sysname and nodename. + echo i386-sequent-sysv4 + exit 0 ;; + i*86:UNIX_SV:4.2MP:2.*) + # Unixware is an offshoot of SVR4, but it has its own version + # number series starting with 2... + # I am not positive that other SVR4 systems won't match this, + # I just have to hope. -- rms. + # Use sysv4.2uw... so that sysv4* matches it. + echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} + exit 0 ;; + i*86:OS/2:*:*) + # If we were able to find `uname', then EMX Unix compatibility + # is probably installed. + echo ${UNAME_MACHINE}-pc-os2-emx + exit 0 ;; + i*86:XTS-300:*:STOP) + echo ${UNAME_MACHINE}-unknown-stop + exit 0 ;; + i*86:atheos:*:*) + echo ${UNAME_MACHINE}-unknown-atheos + exit 0 ;; + i*86:syllable:*:*) + echo ${UNAME_MACHINE}-pc-syllable + exit 0 ;; + i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*) + echo i386-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + i*86:*DOS:*:*) + echo ${UNAME_MACHINE}-pc-msdosdjgpp + exit 0 ;; + i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) + UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` + if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then + echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} + else + echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} + fi + exit 0 ;; + i*86:*:5:[78]*) + case `/bin/uname -X | grep "^Machine"` in + *486*) UNAME_MACHINE=i486 ;; + *Pentium) UNAME_MACHINE=i586 ;; + *Pent*|*Celeron) UNAME_MACHINE=i686 ;; + esac + echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} + exit 0 ;; + i*86:*:3.2:*) + if test -f /usr/options/cb.name; then + UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then + UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` + (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 + (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ + && UNAME_MACHINE=i586 + (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ + && UNAME_MACHINE=i686 + (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ + && UNAME_MACHINE=i686 + echo ${UNAME_MACHINE}-pc-sco$UNAME_REL + else + echo ${UNAME_MACHINE}-pc-sysv32 + fi + exit 0 ;; + pc:*:*:*) + # Left here for compatibility: + # uname -m prints for DJGPP always 'pc', but it prints nothing about + # the processor, so we play safe by assuming i386. + echo i386-pc-msdosdjgpp + exit 0 ;; + Intel:Mach:3*:*) + echo i386-pc-mach3 + exit 0 ;; + paragon:*:*:*) + echo i860-intel-osf1 + exit 0 ;; + i860:*:4.*:*) # i860-SVR4 + if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then + echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 + else # Add other i860-SVR4 vendors below as they are discovered. + echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 + fi + exit 0 ;; + mini*:CTIX:SYS*5:*) + # "miniframe" + echo m68010-convergent-sysv + exit 0 ;; + mc68k:UNIX:SYSTEM5:3.51m) + echo m68k-convergent-sysv + exit 0 ;; + M680?0:D-NIX:5.3:*) + echo m68k-diab-dnix + exit 0 ;; + M68*:*:R3V[5678]*:*) + test -r /sysV68 && echo 'm68k-motorola-sysv' && exit 0 ;; + 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) + OS_REL='' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && echo i486-ncr-sysv4.3${OS_REL} && exit 0 + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && echo i586-ncr-sysv4.3${OS_REL} && exit 0 ;; + 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && echo i486-ncr-sysv4 && exit 0 ;; + m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) + echo m68k-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + mc68030:UNIX_System_V:4.*:*) + echo m68k-atari-sysv4 + exit 0 ;; + TSUNAMI:LynxOS:2.*:*) + echo sparc-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + rs6000:LynxOS:2.*:*) + echo rs6000-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*) + echo powerpc-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + SM[BE]S:UNIX_SV:*:*) + echo mips-dde-sysv${UNAME_RELEASE} + exit 0 ;; + RM*:ReliantUNIX-*:*:*) + echo mips-sni-sysv4 + exit 0 ;; + RM*:SINIX-*:*:*) + echo mips-sni-sysv4 + exit 0 ;; + *:SINIX-*:*:*) + if uname -p 2>/dev/null >/dev/null ; then + UNAME_MACHINE=`(uname -p) 2>/dev/null` + echo ${UNAME_MACHINE}-sni-sysv4 + else + echo ns32k-sni-sysv + fi + exit 0 ;; + PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort + # says + echo i586-unisys-sysv4 + exit 0 ;; + *:UNIX_System_V:4*:FTX*) + # From Gerald Hewes . + # How about differentiating between stratus architectures? -djm + echo hppa1.1-stratus-sysv4 + exit 0 ;; + *:*:*:FTX*) + # From seanf@swdc.stratus.com. + echo i860-stratus-sysv4 + exit 0 ;; + i*86:VOS:*:*) + # From Paul.Green@stratus.com. + echo ${UNAME_MACHINE}-stratus-vos + exit 0 ;; + *:VOS:*:*) + # From Paul.Green@stratus.com. + echo hppa1.1-stratus-vos + exit 0 ;; + mc68*:A/UX:*:*) + echo m68k-apple-aux${UNAME_RELEASE} + exit 0 ;; + news*:NEWS-OS:6*:*) + echo mips-sony-newsos6 + exit 0 ;; + R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) + if [ -d /usr/nec ]; then + echo mips-nec-sysv${UNAME_RELEASE} + else + echo mips-unknown-sysv${UNAME_RELEASE} + fi + exit 0 ;; + BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. + echo powerpc-be-beos + exit 0 ;; + BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. + echo powerpc-apple-beos + exit 0 ;; + BePC:BeOS:*:*) # BeOS running on Intel PC compatible. + echo i586-pc-beos + exit 0 ;; + SX-4:SUPER-UX:*:*) + echo sx4-nec-superux${UNAME_RELEASE} + exit 0 ;; + SX-5:SUPER-UX:*:*) + echo sx5-nec-superux${UNAME_RELEASE} + exit 0 ;; + SX-6:SUPER-UX:*:*) + echo sx6-nec-superux${UNAME_RELEASE} + exit 0 ;; + Power*:Rhapsody:*:*) + echo powerpc-apple-rhapsody${UNAME_RELEASE} + exit 0 ;; + *:Rhapsody:*:*) + echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} + exit 0 ;; + *:Darwin:*:*) + UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown + case $UNAME_PROCESSOR in + *86) UNAME_PROCESSOR=i686 ;; + unknown) UNAME_PROCESSOR=powerpc ;; + esac + echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} + exit 0 ;; + *:procnto*:*:* | *:QNX:[0123456789]*:*) + UNAME_PROCESSOR=`uname -p` + if test "$UNAME_PROCESSOR" = "x86"; then + UNAME_PROCESSOR=i386 + UNAME_MACHINE=pc + fi + echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} + exit 0 ;; + *:QNX:*:4*) + echo i386-pc-qnx + exit 0 ;; + NSE-?:NONSTOP_KERNEL:*:*) + echo nse-tandem-nsk${UNAME_RELEASE} + exit 0 ;; + NSR-?:NONSTOP_KERNEL:*:*) + echo nsr-tandem-nsk${UNAME_RELEASE} + exit 0 ;; + *:NonStop-UX:*:*) + echo mips-compaq-nonstopux + exit 0 ;; + BS2000:POSIX*:*:*) + echo bs2000-siemens-sysv + exit 0 ;; + DS/*:UNIX_System_V:*:*) + echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} + exit 0 ;; + *:Plan9:*:*) + # "uname -m" is not consistent, so use $cputype instead. 386 + # is converted to i386 for consistency with other x86 + # operating systems. + if test "$cputype" = "386"; then + UNAME_MACHINE=i386 + else + UNAME_MACHINE="$cputype" + fi + echo ${UNAME_MACHINE}-unknown-plan9 + exit 0 ;; + *:TOPS-10:*:*) + echo pdp10-unknown-tops10 + exit 0 ;; + *:TENEX:*:*) + echo pdp10-unknown-tenex + exit 0 ;; + KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) + echo pdp10-dec-tops20 + exit 0 ;; + XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) + echo pdp10-xkl-tops20 + exit 0 ;; + *:TOPS-20:*:*) + echo pdp10-unknown-tops20 + exit 0 ;; + *:ITS:*:*) + echo pdp10-unknown-its + exit 0 ;; + SEI:*:*:SEIUX) + echo mips-sei-seiux${UNAME_RELEASE} + exit 0 ;; + *:DragonFly:*:*) + echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` + exit 0 ;; + *:*VMS:*:*) + UNAME_MACHINE=`(uname -p) 2>/dev/null` + case "${UNAME_MACHINE}" in + A*) echo alpha-dec-vms && exit 0 ;; + I*) echo ia64-dec-vms && exit 0 ;; + V*) echo vax-dec-vms && exit 0 ;; + esac ;; + *:XENIX:*:SysV) + echo i386-pc-xenix + exit 0 ;; +esac + +#echo '(No uname command or uname output not recognized.)' 1>&2 +#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2 + +eval $set_cc_for_build +cat >$dummy.c < +# include +#endif +main () +{ +#if defined (sony) +#if defined (MIPSEB) + /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, + I don't know.... */ + printf ("mips-sony-bsd\n"); exit (0); +#else +#include + printf ("m68k-sony-newsos%s\n", +#ifdef NEWSOS4 + "4" +#else + "" +#endif + ); exit (0); +#endif +#endif + +#if defined (__arm) && defined (__acorn) && defined (__unix) + printf ("arm-acorn-riscix"); exit (0); +#endif + +#if defined (hp300) && !defined (hpux) + printf ("m68k-hp-bsd\n"); exit (0); +#endif + +#if defined (NeXT) +#if !defined (__ARCHITECTURE__) +#define __ARCHITECTURE__ "m68k" +#endif + int version; + version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; + if (version < 4) + printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); + else + printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); + exit (0); +#endif + +#if defined (MULTIMAX) || defined (n16) +#if defined (UMAXV) + printf ("ns32k-encore-sysv\n"); exit (0); +#else +#if defined (CMU) + printf ("ns32k-encore-mach\n"); exit (0); +#else + printf ("ns32k-encore-bsd\n"); exit (0); +#endif +#endif +#endif + +#if defined (__386BSD__) + printf ("i386-pc-bsd\n"); exit (0); +#endif + +#if defined (sequent) +#if defined (i386) + printf ("i386-sequent-dynix\n"); exit (0); +#endif +#if defined (ns32000) + printf ("ns32k-sequent-dynix\n"); exit (0); +#endif +#endif + +#if defined (_SEQUENT_) + struct utsname un; + + uname(&un); + + if (strncmp(un.version, "V2", 2) == 0) { + printf ("i386-sequent-ptx2\n"); exit (0); + } + if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ + printf ("i386-sequent-ptx1\n"); exit (0); + } + printf ("i386-sequent-ptx\n"); exit (0); + +#endif + +#if defined (vax) +# if !defined (ultrix) +# include +# if defined (BSD) +# if BSD == 43 + printf ("vax-dec-bsd4.3\n"); exit (0); +# else +# if BSD == 199006 + printf ("vax-dec-bsd4.3reno\n"); exit (0); +# else + printf ("vax-dec-bsd\n"); exit (0); +# endif +# endif +# else + printf ("vax-dec-bsd\n"); exit (0); +# endif +# else + printf ("vax-dec-ultrix\n"); exit (0); +# endif +#endif + +#if defined (alliant) && defined (i860) + printf ("i860-alliant-bsd\n"); exit (0); +#endif + + exit (1); +} +EOF + +$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && $dummy && exit 0 + +# Apollos put the system type in the environment. + +test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit 0; } + +# Convex versions that predate uname can use getsysinfo(1) + +if [ -x /usr/convex/getsysinfo ] +then + case `getsysinfo -f cpu_type` in + c1*) + echo c1-convex-bsd + exit 0 ;; + c2*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit 0 ;; + c34*) + echo c34-convex-bsd + exit 0 ;; + c38*) + echo c38-convex-bsd + exit 0 ;; + c4*) + echo c4-convex-bsd + exit 0 ;; + esac +fi + +cat >&2 < in order to provide the needed +information to handle your system. + +config.guess timestamp = $timestamp + +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null` + +hostinfo = `(hostinfo) 2>/dev/null` +/bin/universe = `(/bin/universe) 2>/dev/null` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` +/bin/arch = `(/bin/arch) 2>/dev/null` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` + +UNAME_MACHINE = ${UNAME_MACHINE} +UNAME_RELEASE = ${UNAME_RELEASE} +UNAME_SYSTEM = ${UNAME_SYSTEM} +UNAME_VERSION = ${UNAME_VERSION} +EOF + +exit 1 + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/Src/config.sub b/Src/config.sub new file mode 100755 index 0000000..87a1ee4 --- /dev/null +++ b/Src/config.sub @@ -0,0 +1,1569 @@ +#! /bin/sh +# Configuration validation subroutine script. +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, +# 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + +timestamp='2005-04-22' + +# This file is (in principle) common to ALL GNU software. +# The presence of a machine in this file suggests that SOME GNU software +# can handle that machine. It does not imply ALL GNU software can. +# +# This file 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 2 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, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Please send patches to . Submit a context +# diff and a properly formatted ChangeLog entry. +# +# Configuration subroutine to validate and canonicalize a configuration type. +# Supply the specified configuration type as an argument. +# If it is invalid, we print an error message on stderr and exit with code 1. +# Otherwise, we print the canonical config type on stdout and succeed. + +# This file is supposed to be the same for all GNU packages +# and recognize all the CPU types, system types and aliases +# that are meaningful with *any* GNU software. +# Each package is responsible for reporting which valid configurations +# it does not support. The user should be able to distinguish +# a failure to support a valid configuration from a meaningless +# configuration. + +# The goal of this file is to map all the various variations of a given +# machine specification into a single specification in the form: +# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM +# or in some cases, the newer four-part form: +# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM +# It is wrong to echo any other type of specification. + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] CPU-MFR-OPSYS + $0 [OPTION] ALIAS + +Canonicalize a configuration name. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.sub ($timestamp) + +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 +Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit 0 ;; + --version | -v ) + echo "$version" ; exit 0 ;; + --help | --h* | -h ) + echo "$usage"; exit 0 ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" + exit 1 ;; + + *local*) + # First pass through any local machine types. + echo $1 + exit 0;; + + * ) + break ;; + esac +done + +case $# in + 0) echo "$me: missing argument$help" >&2 + exit 1;; + 1) ;; + *) echo "$me: too many arguments$help" >&2 + exit 1;; +esac + +# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). +# Here we must recognize all the valid KERNEL-OS combinations. +maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` +case $maybe_os in + nto-qnx* | linux-gnu* | linux-dietlibc | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | \ + kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* | storm-chaos* | os2-emx* | rtmk-nova*) + os=-$maybe_os + basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` + ;; + *) + basic_machine=`echo $1 | sed 's/-[^-]*$//'` + if [ $basic_machine != $1 ] + then os=`echo $1 | sed 's/.*-/-/'` + else os=; fi + ;; +esac + +### Let's recognize common machines as not being operating systems so +### that things like config.sub decstation-3100 work. We also +### recognize some manufacturers as not being operating systems, so we +### can provide default operating systems below. +case $os in + -sun*os*) + # Prevent following clause from handling this invalid input. + ;; + -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ + -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ + -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ + -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ + -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ + -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ + -apple | -axis | -knuth | -cray) + os= + basic_machine=$1 + ;; + -sim | -cisco | -oki | -wec | -winbond) + os= + basic_machine=$1 + ;; + -scout) + ;; + -wrs) + os=-vxworks + basic_machine=$1 + ;; + -chorusos*) + os=-chorusos + basic_machine=$1 + ;; + -chorusrdb) + os=-chorusrdb + basic_machine=$1 + ;; + -hiux*) + os=-hiuxwe2 + ;; + -sco5) + os=-sco3.2v5 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco4) + os=-sco3.2v4 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2.[4-9]*) + os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2v[4-9]*) + # Don't forget version if it is 3.2v4 or newer. + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco*) + os=-sco3.2v2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -udk*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -isc) + os=-isc2.2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -clix*) + basic_machine=clipper-intergraph + ;; + -isc*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -lynx*) + os=-lynxos + ;; + -ptx*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` + ;; + -windowsnt*) + os=`echo $os | sed -e 's/windowsnt/winnt/'` + ;; + -psos*) + os=-psos + ;; + -mint | -mint[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; +esac + +# Decode aliases for certain CPU-COMPANY combinations. +case $basic_machine in + # Recognize the basic CPU types without company name. + # Some are omitted here because they have special meanings below. + 1750a | 580 \ + | a29k \ + | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ + | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ + | am33_2.0 \ + | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr \ + | bfin \ + | c4x | clipper \ + | d10v | d30v | dlx | dsp16xx \ + | fr30 | frv \ + | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ + | i370 | i860 | i960 | ia64 \ + | ip2k | iq2000 \ + | m32r | m32rle | m68000 | m68k | m88k | maxq | mcore \ + | mips | mipsbe | mipseb | mipsel | mipsle \ + | mips16 \ + | mips64 | mips64el \ + | mips64vr | mips64vrel \ + | mips64orion | mips64orionel \ + | mips64vr4100 | mips64vr4100el \ + | mips64vr4300 | mips64vr4300el \ + | mips64vr5000 | mips64vr5000el \ + | mipsisa32 | mipsisa32el \ + | mipsisa32r2 | mipsisa32r2el \ + | mipsisa64 | mipsisa64el \ + | mipsisa64r2 | mipsisa64r2el \ + | mipsisa64sb1 | mipsisa64sb1el \ + | mipsisa64sr71k | mipsisa64sr71kel \ + | mipstx39 | mipstx39el \ + | mn10200 | mn10300 \ + | msp430 \ + | ns16k | ns32k \ + | openrisc | or32 \ + | pdp10 | pdp11 | pj | pjl \ + | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \ + | pyramid \ + | sh | sh[1234] | sh[23]e | sh[34]eb | shbe | shle | sh[1234]le | sh3ele \ + | sh64 | sh64le \ + | sparc | sparc64 | sparc64b | sparc86x | sparclet | sparclite \ + | sparcv8 | sparcv9 | sparcv9b \ + | strongarm \ + | tahoe | thumb | tic4x | tic80 | tron \ + | v850 | v850e \ + | we32k \ + | x86 | xscale | xscalee[bl] | xstormy16 | xtensa \ + | z8k) + basic_machine=$basic_machine-unknown + ;; + m6811 | m68hc11 | m6812 | m68hc12) + # Motorola 68HC11/12. + basic_machine=$basic_machine-unknown + os=-none + ;; + m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) + ;; + + # We use `pc' rather than `unknown' + # because (1) that's what they normally are, and + # (2) the word "unknown" tends to confuse beginning users. + i*86 | x86_64) + basic_machine=$basic_machine-pc + ;; + # Object if more than one company name word. + *-*-*) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; + # Recognize the basic CPU types with company name. + 580-* \ + | a29k-* \ + | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ + | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ + | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \ + | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ + | avr-* \ + | bfin-* | bs2000-* \ + | c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \ + | clipper-* | craynv-* | cydra-* \ + | d10v-* | d30v-* | dlx-* \ + | elxsi-* \ + | f30[01]-* | f700-* | fr30-* | frv-* | fx80-* \ + | h8300-* | h8500-* \ + | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ + | i*86-* | i860-* | i960-* | ia64-* \ + | ip2k-* | iq2000-* \ + | m32r-* | m32rle-* \ + | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ + | m88110-* | m88k-* | maxq-* | mcore-* \ + | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ + | mips16-* \ + | mips64-* | mips64el-* \ + | mips64vr-* | mips64vrel-* \ + | mips64orion-* | mips64orionel-* \ + | mips64vr4100-* | mips64vr4100el-* \ + | mips64vr4300-* | mips64vr4300el-* \ + | mips64vr5000-* | mips64vr5000el-* \ + | mipsisa32-* | mipsisa32el-* \ + | mipsisa32r2-* | mipsisa32r2el-* \ + | mipsisa64-* | mipsisa64el-* \ + | mipsisa64r2-* | mipsisa64r2el-* \ + | mipsisa64sb1-* | mipsisa64sb1el-* \ + | mipsisa64sr71k-* | mipsisa64sr71kel-* \ + | mipstx39-* | mipstx39el-* \ + | mmix-* \ + | msp430-* \ + | none-* | np1-* | ns16k-* | ns32k-* \ + | orion-* \ + | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ + | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \ + | pyramid-* \ + | romp-* | rs6000-* \ + | sh-* | sh[1234]-* | sh[23]e-* | sh[34]eb-* | shbe-* \ + | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ + | sparc-* | sparc64-* | sparc64b-* | sparc86x-* | sparclet-* \ + | sparclite-* \ + | sparcv8-* | sparcv9-* | sparcv9b-* | strongarm-* | sv1-* | sx?-* \ + | tahoe-* | thumb-* \ + | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ + | tron-* \ + | v850-* | v850e-* | vax-* \ + | we32k-* \ + | x86-* | x86_64-* | xps100-* | xscale-* | xscalee[bl]-* \ + | xstormy16-* | xtensa-* \ + | ymp-* \ + | z8k-*) + ;; + # Recognize the various machine names and aliases which stand + # for a CPU type and a company and sometimes even an OS. + 386bsd) + basic_machine=i386-unknown + os=-bsd + ;; + 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) + basic_machine=m68000-att + ;; + 3b*) + basic_machine=we32k-att + ;; + a29khif) + basic_machine=a29k-amd + os=-udi + ;; + abacus) + basic_machine=abacus-unknown + ;; + adobe68k) + basic_machine=m68010-adobe + os=-scout + ;; + alliant | fx80) + basic_machine=fx80-alliant + ;; + altos | altos3068) + basic_machine=m68k-altos + ;; + am29k) + basic_machine=a29k-none + os=-bsd + ;; + amd64) + basic_machine=x86_64-pc + ;; + amd64-*) + basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + amdahl) + basic_machine=580-amdahl + os=-sysv + ;; + amiga | amiga-*) + basic_machine=m68k-unknown + ;; + amigaos | amigados) + basic_machine=m68k-unknown + os=-amigaos + ;; + amigaunix | amix) + basic_machine=m68k-unknown + os=-sysv4 + ;; + apollo68) + basic_machine=m68k-apollo + os=-sysv + ;; + apollo68bsd) + basic_machine=m68k-apollo + os=-bsd + ;; + aux) + basic_machine=m68k-apple + os=-aux + ;; + balance) + basic_machine=ns32k-sequent + os=-dynix + ;; + c90) + basic_machine=c90-cray + os=-unicos + ;; + convex-c1) + basic_machine=c1-convex + os=-bsd + ;; + convex-c2) + basic_machine=c2-convex + os=-bsd + ;; + convex-c32) + basic_machine=c32-convex + os=-bsd + ;; + convex-c34) + basic_machine=c34-convex + os=-bsd + ;; + convex-c38) + basic_machine=c38-convex + os=-bsd + ;; + cray | j90) + basic_machine=j90-cray + os=-unicos + ;; + craynv) + basic_machine=craynv-cray + os=-unicosmp + ;; + cr16c) + basic_machine=cr16c-unknown + os=-elf + ;; + crds | unos) + basic_machine=m68k-crds + ;; + crisv32 | crisv32-* | etraxfs*) + basic_machine=crisv32-axis + ;; + cris | cris-* | etrax*) + basic_machine=cris-axis + ;; + crx) + basic_machine=crx-unknown + os=-elf + ;; + da30 | da30-*) + basic_machine=m68k-da30 + ;; + decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) + basic_machine=mips-dec + ;; + decsystem10* | dec10*) + basic_machine=pdp10-dec + os=-tops10 + ;; + decsystem20* | dec20*) + basic_machine=pdp10-dec + os=-tops20 + ;; + delta | 3300 | motorola-3300 | motorola-delta \ + | 3300-motorola | delta-motorola) + basic_machine=m68k-motorola + ;; + delta88) + basic_machine=m88k-motorola + os=-sysv3 + ;; + djgpp) + basic_machine=i586-pc + os=-msdosdjgpp + ;; + dpx20 | dpx20-*) + basic_machine=rs6000-bull + os=-bosx + ;; + dpx2* | dpx2*-bull) + basic_machine=m68k-bull + os=-sysv3 + ;; + ebmon29k) + basic_machine=a29k-amd + os=-ebmon + ;; + elxsi) + basic_machine=elxsi-elxsi + os=-bsd + ;; + encore | umax | mmax) + basic_machine=ns32k-encore + ;; + es1800 | OSE68k | ose68k | ose | OSE) + basic_machine=m68k-ericsson + os=-ose + ;; + fx2800) + basic_machine=i860-alliant + ;; + genix) + basic_machine=ns32k-ns + ;; + gmicro) + basic_machine=tron-gmicro + os=-sysv + ;; + go32) + basic_machine=i386-pc + os=-go32 + ;; + h3050r* | hiux*) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + h8300hms) + basic_machine=h8300-hitachi + os=-hms + ;; + h8300xray) + basic_machine=h8300-hitachi + os=-xray + ;; + h8500hms) + basic_machine=h8500-hitachi + os=-hms + ;; + harris) + basic_machine=m88k-harris + os=-sysv3 + ;; + hp300-*) + basic_machine=m68k-hp + ;; + hp300bsd) + basic_machine=m68k-hp + os=-bsd + ;; + hp300hpux) + basic_machine=m68k-hp + os=-hpux + ;; + hp3k9[0-9][0-9] | hp9[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k2[0-9][0-9] | hp9k31[0-9]) + basic_machine=m68000-hp + ;; + hp9k3[2-9][0-9]) + basic_machine=m68k-hp + ;; + hp9k6[0-9][0-9] | hp6[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k7[0-79][0-9] | hp7[0-79][0-9]) + basic_machine=hppa1.1-hp + ;; + hp9k78[0-9] | hp78[0-9]) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][13679] | hp8[0-9][13679]) + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][0-9] | hp8[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hppa-next) + os=-nextstep3 + ;; + hppaosf) + basic_machine=hppa1.1-hp + os=-osf + ;; + hppro) + basic_machine=hppa1.1-hp + os=-proelf + ;; + i370-ibm* | ibm*) + basic_machine=i370-ibm + ;; +# I'm not sure what "Sysv32" means. Should this be sysv3.2? + i*86v32) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv32 + ;; + i*86v4*) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv4 + ;; + i*86v) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv + ;; + i*86sol2) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-solaris2 + ;; + i386mach) + basic_machine=i386-mach + os=-mach + ;; + i386-vsta | vsta) + basic_machine=i386-unknown + os=-vsta + ;; + iris | iris4d) + basic_machine=mips-sgi + case $os in + -irix*) + ;; + *) + os=-irix4 + ;; + esac + ;; + isi68 | isi) + basic_machine=m68k-isi + os=-sysv + ;; + m88k-omron*) + basic_machine=m88k-omron + ;; + magnum | m3230) + basic_machine=mips-mips + os=-sysv + ;; + merlin) + basic_machine=ns32k-utek + os=-sysv + ;; + mingw32) + basic_machine=i386-pc + os=-mingw32 + ;; + miniframe) + basic_machine=m68000-convergent + ;; + *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; + mips3*-*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` + ;; + mips3*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown + ;; + monitor) + basic_machine=m68k-rom68k + os=-coff + ;; + morphos) + basic_machine=powerpc-unknown + os=-morphos + ;; + msdos) + basic_machine=i386-pc + os=-msdos + ;; + mvs) + basic_machine=i370-ibm + os=-mvs + ;; + ncr3000) + basic_machine=i486-ncr + os=-sysv4 + ;; + netbsd386) + basic_machine=i386-unknown + os=-netbsd + ;; + netwinder) + basic_machine=armv4l-rebel + os=-linux + ;; + news | news700 | news800 | news900) + basic_machine=m68k-sony + os=-newsos + ;; + news1000) + basic_machine=m68030-sony + os=-newsos + ;; + news-3600 | risc-news) + basic_machine=mips-sony + os=-newsos + ;; + necv70) + basic_machine=v70-nec + os=-sysv + ;; + next | m*-next ) + basic_machine=m68k-next + case $os in + -nextstep* ) + ;; + -ns2*) + os=-nextstep2 + ;; + *) + os=-nextstep3 + ;; + esac + ;; + nh3000) + basic_machine=m68k-harris + os=-cxux + ;; + nh[45]000) + basic_machine=m88k-harris + os=-cxux + ;; + nindy960) + basic_machine=i960-intel + os=-nindy + ;; + mon960) + basic_machine=i960-intel + os=-mon960 + ;; + nonstopux) + basic_machine=mips-compaq + os=-nonstopux + ;; + np1) + basic_machine=np1-gould + ;; + nsr-tandem) + basic_machine=nsr-tandem + ;; + op50n-* | op60c-*) + basic_machine=hppa1.1-oki + os=-proelf + ;; + or32 | or32-*) + basic_machine=or32-unknown + os=-coff + ;; + os400) + basic_machine=powerpc-ibm + os=-os400 + ;; + OSE68000 | ose68000) + basic_machine=m68000-ericsson + os=-ose + ;; + os68k) + basic_machine=m68k-none + os=-os68k + ;; + pa-hitachi) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + paragon) + basic_machine=i860-intel + os=-osf + ;; + pbd) + basic_machine=sparc-tti + ;; + pbb) + basic_machine=m68k-tti + ;; + pc532 | pc532-*) + basic_machine=ns32k-pc532 + ;; + pentium | p5 | k5 | k6 | nexgen | viac3) + basic_machine=i586-pc + ;; + pentiumpro | p6 | 6x86 | athlon | athlon_*) + basic_machine=i686-pc + ;; + pentiumii | pentium2 | pentiumiii | pentium3) + basic_machine=i686-pc + ;; + pentium4) + basic_machine=i786-pc + ;; + pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) + basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumpro-* | p6-* | 6x86-* | athlon-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentium4-*) + basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pn) + basic_machine=pn-gould + ;; + power) basic_machine=power-ibm + ;; + ppc) basic_machine=powerpc-unknown + ;; + ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppcle | powerpclittle | ppc-le | powerpc-little) + basic_machine=powerpcle-unknown + ;; + ppcle-* | powerpclittle-*) + basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64) basic_machine=powerpc64-unknown + ;; + ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64le | powerpc64little | ppc64-le | powerpc64-little) + basic_machine=powerpc64le-unknown + ;; + ppc64le-* | powerpc64little-*) + basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ps2) + basic_machine=i386-ibm + ;; + pw32) + basic_machine=i586-unknown + os=-pw32 + ;; + rom68k) + basic_machine=m68k-rom68k + os=-coff + ;; + rm[46]00) + basic_machine=mips-siemens + ;; + rtpc | rtpc-*) + basic_machine=romp-ibm + ;; + s390 | s390-*) + basic_machine=s390-ibm + ;; + s390x | s390x-*) + basic_machine=s390x-ibm + ;; + sa29200) + basic_machine=a29k-amd + os=-udi + ;; + sb1) + basic_machine=mipsisa64sb1-unknown + ;; + sb1el) + basic_machine=mipsisa64sb1el-unknown + ;; + sei) + basic_machine=mips-sei + os=-seiux + ;; + sequent) + basic_machine=i386-sequent + ;; + sh) + basic_machine=sh-hitachi + os=-hms + ;; + sh64) + basic_machine=sh64-unknown + ;; + sparclite-wrs | simso-wrs) + basic_machine=sparclite-wrs + os=-vxworks + ;; + sps7) + basic_machine=m68k-bull + os=-sysv2 + ;; + spur) + basic_machine=spur-unknown + ;; + st2000) + basic_machine=m68k-tandem + ;; + stratus) + basic_machine=i860-stratus + os=-sysv4 + ;; + sun2) + basic_machine=m68000-sun + ;; + sun2os3) + basic_machine=m68000-sun + os=-sunos3 + ;; + sun2os4) + basic_machine=m68000-sun + os=-sunos4 + ;; + sun3os3) + basic_machine=m68k-sun + os=-sunos3 + ;; + sun3os4) + basic_machine=m68k-sun + os=-sunos4 + ;; + sun4os3) + basic_machine=sparc-sun + os=-sunos3 + ;; + sun4os4) + basic_machine=sparc-sun + os=-sunos4 + ;; + sun4sol2) + basic_machine=sparc-sun + os=-solaris2 + ;; + sun3 | sun3-*) + basic_machine=m68k-sun + ;; + sun4) + basic_machine=sparc-sun + ;; + sun386 | sun386i | roadrunner) + basic_machine=i386-sun + ;; + sv1) + basic_machine=sv1-cray + os=-unicos + ;; + symmetry) + basic_machine=i386-sequent + os=-dynix + ;; + t3e) + basic_machine=alphaev5-cray + os=-unicos + ;; + t90) + basic_machine=t90-cray + os=-unicos + ;; + tic54x | c54x*) + basic_machine=tic54x-unknown + os=-coff + ;; + tic55x | c55x*) + basic_machine=tic55x-unknown + os=-coff + ;; + tic6x | c6x*) + basic_machine=tic6x-unknown + os=-coff + ;; + tx39) + basic_machine=mipstx39-unknown + ;; + tx39el) + basic_machine=mipstx39el-unknown + ;; + toad1) + basic_machine=pdp10-xkl + os=-tops20 + ;; + tower | tower-32) + basic_machine=m68k-ncr + ;; + tpf) + basic_machine=s390x-ibm + os=-tpf + ;; + udi29k) + basic_machine=a29k-amd + os=-udi + ;; + ultra3) + basic_machine=a29k-nyu + os=-sym1 + ;; + v810 | necv810) + basic_machine=v810-nec + os=-none + ;; + vaxv) + basic_machine=vax-dec + os=-sysv + ;; + vms) + basic_machine=vax-dec + os=-vms + ;; + vpp*|vx|vx-*) + basic_machine=f301-fujitsu + ;; + vxworks960) + basic_machine=i960-wrs + os=-vxworks + ;; + vxworks68) + basic_machine=m68k-wrs + os=-vxworks + ;; + vxworks29k) + basic_machine=a29k-wrs + os=-vxworks + ;; + w65*) + basic_machine=w65-wdc + os=-none + ;; + w89k-*) + basic_machine=hppa1.1-winbond + os=-proelf + ;; + xbox) + basic_machine=i686-pc + os=-mingw32 + ;; + xps | xps100) + basic_machine=xps100-honeywell + ;; + ymp) + basic_machine=ymp-cray + os=-unicos + ;; + z8k-*-coff) + basic_machine=z8k-unknown + os=-sim + ;; + none) + basic_machine=none-none + os=-none + ;; + +# Here we handle the default manufacturer of certain CPU types. It is in +# some cases the only manufacturer, in others, it is the most popular. + w89k) + basic_machine=hppa1.1-winbond + ;; + op50n) + basic_machine=hppa1.1-oki + ;; + op60c) + basic_machine=hppa1.1-oki + ;; + romp) + basic_machine=romp-ibm + ;; + mmix) + basic_machine=mmix-knuth + ;; + rs6000) + basic_machine=rs6000-ibm + ;; + vax) + basic_machine=vax-dec + ;; + pdp10) + # there are many clones, so DEC is not a safe bet + basic_machine=pdp10-unknown + ;; + pdp11) + basic_machine=pdp11-dec + ;; + we32k) + basic_machine=we32k-att + ;; + sh3 | sh4 | sh[34]eb | sh[1234]le | sh[23]ele) + basic_machine=sh-unknown + ;; + sh64) + basic_machine=sh64-unknown + ;; + sparc | sparcv8 | sparcv9 | sparcv9b) + basic_machine=sparc-sun + ;; + cydra) + basic_machine=cydra-cydrome + ;; + orion) + basic_machine=orion-highlevel + ;; + orion105) + basic_machine=clipper-highlevel + ;; + mac | mpw | mac-mpw) + basic_machine=m68k-apple + ;; + pmac | pmac-mpw) + basic_machine=powerpc-apple + ;; + *-unknown) + # Make sure to match an already-canonicalized machine name. + ;; + *) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; +esac + +# Here we canonicalize certain aliases for manufacturers. +case $basic_machine in + *-digital*) + basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` + ;; + *-commodore*) + basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` + ;; + *) + ;; +esac + +# Decode manufacturer-specific aliases for certain operating systems. + +if [ x"$os" != x"" ] +then +case $os in + # First match some system type aliases + # that might get confused with valid system types. + # -solaris* is a basic system type, with this one exception. + -solaris1 | -solaris1.*) + os=`echo $os | sed -e 's|solaris1|sunos4|'` + ;; + -solaris) + os=-solaris2 + ;; + -svr4*) + os=-sysv4 + ;; + -unixware*) + os=-sysv4.2uw + ;; + -gnu/linux*) + os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` + ;; + # First accept the basic system types. + # The portable systems comes first. + # Each alternative MUST END IN A *, to match a version number. + # -sysv* is not here because it comes later, after sysvr4. + -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ + | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\ + | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \ + | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ + | -aos* \ + | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ + | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ + | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* | -openbsd* \ + | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ + | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ + | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ + | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ + | -chorusos* | -chorusrdb* \ + | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ + | -mingw32* | -linux-gnu* | -linux-uclibc* | -uxpv* | -beos* | -mpeix* | -udk* \ + | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ + | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ + | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ + | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ + | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ + | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly*) + # Remember, each alternative MUST END IN *, to match a version number. + ;; + -qnx*) + case $basic_machine in + x86-* | i*86-*) + ;; + *) + os=-nto$os + ;; + esac + ;; + -nto-qnx*) + ;; + -nto*) + os=`echo $os | sed -e 's|nto|nto-qnx|'` + ;; + -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ + | -windows* | -osx | -abug | -netware* | -os9* | -beos* \ + | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) + ;; + -mac*) + os=`echo $os | sed -e 's|mac|macos|'` + ;; + -linux-dietlibc) + os=-linux-dietlibc + ;; + -linux*) + os=`echo $os | sed -e 's|linux|linux-gnu|'` + ;; + -sunos5*) + os=`echo $os | sed -e 's|sunos5|solaris2|'` + ;; + -sunos6*) + os=`echo $os | sed -e 's|sunos6|solaris3|'` + ;; + -opened*) + os=-openedition + ;; + -os400*) + os=-os400 + ;; + -wince*) + os=-wince + ;; + -osfrose*) + os=-osfrose + ;; + -osf*) + os=-osf + ;; + -utek*) + os=-bsd + ;; + -dynix*) + os=-bsd + ;; + -acis*) + os=-aos + ;; + -atheos*) + os=-atheos + ;; + -syllable*) + os=-syllable + ;; + -386bsd) + os=-bsd + ;; + -ctix* | -uts*) + os=-sysv + ;; + -nova*) + os=-rtmk-nova + ;; + -ns2 ) + os=-nextstep2 + ;; + -nsk*) + os=-nsk + ;; + # Preserve the version number of sinix5. + -sinix5.*) + os=`echo $os | sed -e 's|sinix|sysv|'` + ;; + -sinix*) + os=-sysv4 + ;; + -tpf*) + os=-tpf + ;; + -triton*) + os=-sysv3 + ;; + -oss*) + os=-sysv3 + ;; + -svr4) + os=-sysv4 + ;; + -svr3) + os=-sysv3 + ;; + -sysvr4) + os=-sysv4 + ;; + # This must come after -sysvr4. + -sysv*) + ;; + -ose*) + os=-ose + ;; + -es1800*) + os=-ose + ;; + -xenix) + os=-xenix + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + os=-mint + ;; + -aros*) + os=-aros + ;; + -kaos*) + os=-kaos + ;; + -zvmoe) + os=-zvmoe + ;; + -none) + ;; + *) + # Get rid of the `-' at the beginning of $os. + os=`echo $os | sed 's/[^-]*-//'` + echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 + exit 1 + ;; +esac +else + +# Here we handle the default operating systems that come with various machines. +# The value should be what the vendor currently ships out the door with their +# machine or put another way, the most popular os provided with the machine. + +# Note that if you're going to try to match "-MANUFACTURER" here (say, +# "-sun"), then you have to tell the case statement up towards the top +# that MANUFACTURER isn't an operating system. Otherwise, code above +# will signal an error saying that MANUFACTURER isn't an operating +# system, and we'll never get to this point. + +case $basic_machine in + *-acorn) + os=-riscix1.2 + ;; + arm*-rebel) + os=-linux + ;; + arm*-semi) + os=-aout + ;; + c4x-* | tic4x-*) + os=-coff + ;; + # This must come before the *-dec entry. + pdp10-*) + os=-tops20 + ;; + pdp11-*) + os=-none + ;; + *-dec | vax-*) + os=-ultrix4.2 + ;; + m68*-apollo) + os=-domain + ;; + i386-sun) + os=-sunos4.0.2 + ;; + m68000-sun) + os=-sunos3 + # This also exists in the configure program, but was not the + # default. + # os=-sunos4 + ;; + m68*-cisco) + os=-aout + ;; + mips*-cisco) + os=-elf + ;; + mips*-*) + os=-elf + ;; + or32-*) + os=-coff + ;; + *-tti) # must be before sparc entry or we get the wrong os. + os=-sysv3 + ;; + sparc-* | *-sun) + os=-sunos4.1.1 + ;; + *-be) + os=-beos + ;; + *-ibm) + os=-aix + ;; + *-knuth) + os=-mmixware + ;; + *-wec) + os=-proelf + ;; + *-winbond) + os=-proelf + ;; + *-oki) + os=-proelf + ;; + *-hp) + os=-hpux + ;; + *-hitachi) + os=-hiux + ;; + i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) + os=-sysv + ;; + *-cbm) + os=-amigaos + ;; + *-dg) + os=-dgux + ;; + *-dolphin) + os=-sysv3 + ;; + m68k-ccur) + os=-rtu + ;; + m88k-omron*) + os=-luna + ;; + *-next ) + os=-nextstep + ;; + *-sequent) + os=-ptx + ;; + *-crds) + os=-unos + ;; + *-ns) + os=-genix + ;; + i370-*) + os=-mvs + ;; + *-next) + os=-nextstep3 + ;; + *-gould) + os=-sysv + ;; + *-highlevel) + os=-bsd + ;; + *-encore) + os=-bsd + ;; + *-sgi) + os=-irix + ;; + *-siemens) + os=-sysv4 + ;; + *-masscomp) + os=-rtu + ;; + f30[01]-fujitsu | f700-fujitsu) + os=-uxpv + ;; + *-rom68k) + os=-coff + ;; + *-*bug) + os=-coff + ;; + *-apple) + os=-macos + ;; + *-atari*) + os=-mint + ;; + *) + os=-none + ;; +esac +fi + +# Here we handle the case where we know the os, and the CPU type, but not the +# manufacturer. We pick the logical manufacturer. +vendor=unknown +case $basic_machine in + *-unknown) + case $os in + -riscix*) + vendor=acorn + ;; + -sunos*) + vendor=sun + ;; + -aix*) + vendor=ibm + ;; + -beos*) + vendor=be + ;; + -hpux*) + vendor=hp + ;; + -mpeix*) + vendor=hp + ;; + -hiux*) + vendor=hitachi + ;; + -unos*) + vendor=crds + ;; + -dgux*) + vendor=dg + ;; + -luna*) + vendor=omron + ;; + -genix*) + vendor=ns + ;; + -mvs* | -opened*) + vendor=ibm + ;; + -os400*) + vendor=ibm + ;; + -ptx*) + vendor=sequent + ;; + -tpf*) + vendor=ibm + ;; + -vxsim* | -vxworks* | -windiss*) + vendor=wrs + ;; + -aux*) + vendor=apple + ;; + -hms*) + vendor=hitachi + ;; + -mpw* | -macos*) + vendor=apple + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + vendor=atari + ;; + -vos*) + vendor=stratus + ;; + esac + basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` + ;; +esac + +echo $basic_machine$os +exit 0 + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/Src/configarm b/Src/configarm new file mode 100755 index 0000000..b8b59ee --- /dev/null +++ b/Src/configarm @@ -0,0 +1,2 @@ +./configure --host=i386-linux --x-includes=/opt/Qtopia/sharp/include/ --x-libraries=/opt/Qtopia/sharp/lib/ --with-sdl-prefix=/opt/Qtopia/sharp/ --with-embedded-extension=qtopia + diff --git a/Src/configure.ac b/Src/configure.ac new file mode 100644 index 0000000..2c6d965 --- /dev/null +++ b/Src/configure.ac @@ -0,0 +1,126 @@ +dnl Process this file with autoconf to produce a configure script. + +AC_INIT(Frodo, 4.2, [Christian.Bauer@uni-mainz.de], Frodo) +AC_CONFIG_SRCDIR(main.cpp) +AC_PREREQ(2.52) +AC_CONFIG_HEADER(sysconfig.h) + +dnl Checks for programs. +AC_PROG_CC +AC_PROG_CXX +AC_PROG_CPP +AC_PROG_MAKE_SET +AC_PROG_INSTALL + +dnl -fomit-frame-pointer makes things faster +if [[ x"$GXX" = "xyes" ]]; then + CFLAGS="$CFLAGS -fomit-frame-pointer -fno-exceptions" +fi + +AC_AIX +AC_ISC_POSIX + +dnl Checks for libraries. +AC_CHECK_LIB(vga, vga_setmode, HAVE_SVGA_LIB=yes, HAVE_SVGA_LIB=no) +AM_PATH_SDL(1.2.0, HAVE_SDL=yes, HAVE_SDL=no) + +EMBEDDED_EXTENSION=none +AC_MSG_CHECKING([if Frodo should be compiled with extensions for an embedded device]) +AC_ARG_WITH([embedded-extension], [AS_HELP_STRING([--with-embedded-extension=qtopia|maemo],[Specify which extension for Frodo on an embedded device should be used.])]) +if [[ x"$with_embedded_extension" == "xqtopia" ]]; then + EMBEDDED_EXTENSION=$with_embedded_extension + AC_DEFINE(QTOPIA, 1, [Extension for Qtopia is enabled]) +elif [[ x"$with_embedded_extension" == "xmaemo" ]]; then + EMBEDDED_EXTENSION=$with_embedded_extension + AC_DEFINE(MAEMO, 1, [Extension for Maemo is enabled]) +fi +AC_MSG_RESULT([$EMBEDDED_EXTENSION]) + +if [[ x"$EMBEDDED_EXTENSION" == "xmaemo" ]]; then + PKG_CHECK_MODULES(OSSO, [libosso >= 0.9.19], AC_DEFINE(HAVE_LIBOSSO, [], [Weather libosso is present on the system]), [AC_MSG_NOTICE([libosso not present.])]) +fi + +if [[ x"$EMBEDDED_EXTENSION" == "xnone" ]]; then + HAVE_GLADE=no + PKG_CHECK_MODULES(GLADE, libglade-2.0 libgnomeui-2.0 >= 1.110.0, HAVE_GLADE=yes) + if [[ $HAVE_GLADE = yes ]]; then + AC_DEFINE(HAVE_GLADE, 1, [Glade support is enabled]) + LDFLAGS="$LDFLAGS -Wl,-E" + fi +else + GUIOBJS="sdlgui.o file.o dlgFileSelect.o dlgMain.o dlgDrives.o dlgOptions.o dlgVideoSound.o dlgAdvanced.o dlgInput.o" + GUISRCS="sdlgui.cpp file.cpp dlgFileSelect.cpp dlgMain.cpp dlgDrives.cpp dlgOptions.cpp dlgVidoSound.cpp dlgAdvanced.cpp dlgInput.cpp" +fi + +AC_PATH_XTRA + +AC_HEADER_DIRENT +AC_HEADER_STDC +AC_CHECK_HEADERS(unistd.h fcntl.h sys/time.h sys/types.h utime.h string.h strings.h values.h ncurses.h) +AC_CHECK_HEADERS(sys/vfs.h sys/mount.h sys/select.h sys/param.h sys/statfs.h sys/statvfs.h sys/stat.h) + +dnl Checks for types. +AC_CHECK_SIZEOF(char) +AC_CHECK_SIZEOF(short) +AC_CHECK_SIZEOF(int) +AC_CHECK_SIZEOF(long) +AC_CHECK_SIZEOF(long long) +AC_CHECK_SIZEOF(void *) + +dnl Checks for typedefs, structures, and compiler characteristics. +AC_TYPE_MODE_T +AC_TYPE_OFF_T +AC_TYPE_PID_T +AC_STRUCT_ST_BLOCKS +AC_HEADER_TIME +AC_STRUCT_TM + +dnl Checks for library functions. +AC_PROG_GCC_TRADITIONAL +AC_FUNC_MEMCMP +AC_TYPE_SIGNAL +AC_FUNC_UTIME_NULL +AC_CHECK_FUNCS(gettimeofday sigaction mkdir rmdir select strerror strstr statfs usleep) + +KBD_LANG=0 +AC_ARG_ENABLE(kbd-lang-de,[ --enable-kbd-lang-de Use german keyboard layout],[KBD_LANG=1],[]) +AC_ARG_ENABLE(kbd-lang-us,[ --enable-kbd-lang-us Use american keyboard layout],[KBD_LANG=0],[]) + +AC_MSG_CHECKING(which target to use) +if [[ x"$no_x" = "xyes" ]]; then + if [[ x"$HAVE_SVGA_LIB" = "xyes" ]]; then + AC_MSG_RESULT([SVGAlib]) + TARGET=svgalib + CFLAGS="$CFLAGS -D__svgalib__" + LIBS="$LIBS -lvga" + else + AC_MSG_RESULT([Ummm...]) + AC_MSG_ERROR([Neither X nor SVGAlib found, don't know what target to use.]) + fi +elif [[ x"$HAVE_SDL" = "xyes" ]]; then + AC_MSG_RESULT([SDL]) + TARGET=sdl + CFLAGS="$CFLAGS $SDL_CFLAGS -DHAVE_SDL" + LIBS="$LIBS $SDL_LIBS" +else + AC_MSG_RESULT([X Window System]) + TARGET=x11 + CFLAGS="$CFLAGS $X_CFLAGS -I$ac_cv_x_include" + LIBS="$LIBS $X_LIBS $X_PRE_LIBS -lXext -lX11 $X_EXTRA_LIBS" +fi + +AC_SUBST(TARGET) +AC_SUBST(SET_MAKE) +AC_SUBST(top_srcdir) +AC_SUBST(HPUX_REV) +AC_SUBST(KBD_LANG) +AC_SUBST(GUIOBJS) +AC_SUBST(GUISRCS) + +dnl Generate Makefile. +AC_CONFIG_FILES([Makefile]) +AC_OUTPUT + +dnl Print summary. +echo +echo "Configuration done. Now type \"make\"." diff --git a/Src/debian/changelog b/Src/debian/changelog new file mode 100644 index 0000000..2a16ae7 --- /dev/null +++ b/Src/debian/changelog @@ -0,0 +1,17 @@ +frodo (4.2.0-7) unstable; urgency=low + + * Initial Release. + + -- Bernd Lachner Wed, 23 Jan 2008 11:15:58 +0100 + + * Added Maemo Icon. + + -- Bernd Lachner Wed, 23 Jan 2008 14:30:00 +0100 + + * Added Maemo desktop file. + + -- Bernd Lachner Wed, 23 Jan 2008 14:34:00 +0100 + + * Added Maemo icons. + + -- Bernd Lachner Wed, 23 Jan 2008 14:35:00 +0100 diff --git a/Src/debian/compat b/Src/debian/compat new file mode 100644 index 0000000..7ed6ff8 --- /dev/null +++ b/Src/debian/compat @@ -0,0 +1 @@ +5 diff --git a/Src/debian/control b/Src/debian/control new file mode 100644 index 0000000..a256fea --- /dev/null +++ b/Src/debian/control @@ -0,0 +1,40 @@ +Source: frodo +Section: user/other +Priority: extra +Maintainer: Bernd Lachner +Build-Depends: debhelper (>= 5), autotools-dev +Standards-Version: 3.7.2 + +Package: frodo +Architecture: any +Depends: ${shlibs:Depends}, ${misc:Depends} +Description: Frodo C64 Emulator + A C64 Emulator. +XB-Maemo-Icon-26: + iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAMAAACelLz8AAADAFBMVEUAACkA + ABAAABgAACEAAAAAADEAADkAAFIAAFoAAGMAAGsAAHsAAIQAAIwAAJQAAJwA + AKUAAK0AALUAAL0AAMYAAM4AAN4AAP8ICL0ICMYICOcICP8QAAAQEPcQEP8Y + GBgYGP8hIf8pAAApKSkpKf8xMf85AAA5Of9CAABCQkJCQv9KAABKSkpKSv9S + UlJSUv9aAABaWlpaWv9jAABjY2NjY/9ra2tra/9zAABzc/97AAB7e/+EAACE + hP+MjIyMjP+UlP+cnP+lAAClpf+tAAC9AAC9vb3GAADGxsbOAADW1tbe3t7n + 5+fvAAD39/f/AAD/EBD/GBj/KSn/MTH/QkL///////////////////////// + //////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////2 + g8mqAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACvAAAArw + AUKsNJgAAAAHdElNRQfYARcNEzJj9/E1AAABLklEQVQoz7WSX0+DMBTF2cZE + MEPGwrJFpcyBZAjB8WfYKCIzDlIUs8zv/1ks7SAiPvjieWt+t/ec216G+Sed + CQLPc7+R6VRR5rLI9TtE9aIwDF1D7BdlC2hODEPXMk1zwduodcVJYtdQZ+PJ + VNPbaJEkUJdOAZE6n8jyeDRgCTLSB088AWBpY8VO1XfEImJqptAaYrJGWC8J + hPr50Laf3jCyUk/DICfZLhRZ4qo6crKe3Suw2tHUyK4ccSHToDpZsakcA9qC + NqzR9t73b1E9OIlRo/3n4bDf3hxv0fBHr4/3LMteHy/z7yPTwqqh799ds2Xr + oWRJGNCEOCOtLFQrhlEUhc6sF+wQFU1S5s2n9NrPW7HmK38iplzTBQCgg5g8 + WC2J+6bobkdZEPO/LdkXNRI8wk8H6PoAAAAASUVORK5CYII= \ No newline at end of file diff --git a/Src/debian/copyright b/Src/debian/copyright new file mode 100644 index 0000000..4061de6 --- /dev/null +++ b/Src/debian/copyright @@ -0,0 +1,22 @@ +This package was debianized by Bernd Lachner on +Wed, 23 Jan 2008 11:15:58 +0100. + +It was downloaded from + +Upstream Author: + +Copyright: + +License: + + + + +The Debian packaging is (C) 2008, Bernd Lachner and +is licensed under the GPL, see `/usr/share/common-licenses/GPL'. + + +# Please also look if there are files or directories which have a +# different copyright/license attached and list them here. + diff --git a/Src/debian/postinst b/Src/debian/postinst new file mode 100644 index 0000000..67a8e73 --- /dev/null +++ b/Src/debian/postinst @@ -0,0 +1,20 @@ +#!/bin/sh +## ---------------------------------------------------------------------- +## debian/postinst : postinstallation script for frodo +## ---------------------------------------------------------------------- + +## ---------------------------------------------------------------------- +set -e + +chmod +x /usr/bin/mirage +echo "updated permiss" + +gtk-update-icon-cache -f /usr/share/icons/hicolor + + +oldversion="$2" +if [ -z "$oldversion" ]; then + maemo-select-menu-location frodo.desktop +fi + +exit 0 diff --git a/Src/debian/rules b/Src/debian/rules new file mode 100755 index 0000000..5a80a64 --- /dev/null +++ b/Src/debian/rules @@ -0,0 +1,107 @@ +#!/usr/bin/make -f +# -*- makefile -*- +# Sample debian/rules that uses debhelper. +# This file was originally written by Joey Hess and Craig Small. +# As a special exception, when this file is copied by dh-make into a +# dh-make output file, you may use that output file without restriction. +# This special exception was added by Craig Small in version 0.37 of dh-make. + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 + + +# These are used for cross-compiling and for saving the configure script +# from having to guess our platform (since we know it already) +DEB_HOST_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE) +DEB_BUILD_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE) + + +CFLAGS = -Wall -g + +ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) + CFLAGS += -O0 +else + CFLAGS += -O2 +endif + +config.status: configure + dh_testdir + # Add here commands to configure the package. + ./configure --host=$(DEB_HOST_GNU_TYPE) --build=$(DEB_BUILD_GNU_TYPE) --with-embedded-extension=maemo --prefix=/usr --mandir=\$${prefix}/share/man --infodir=\$${prefix}/share/info CFLAGS="$(CFLAGS)" LDFLAGS="-Wl,-z,defs" + + +build: build-stamp + +build-stamp: config.status + dh_testdir + + # Add here commands to compile the package. + $(MAKE) + #docbook-to-man debian/frodo.sgml > frodo.1 + + touch $@ + +clean: + dh_testdir + dh_testroot + rm -f build-stamp + + # Add here commands to clean up after the build process. + -$(MAKE) distclean +ifneq "$(wildcard /usr/share/misc/config.sub)" "" + cp -f /usr/share/misc/config.sub config.sub +endif +ifneq "$(wildcard /usr/share/misc/config.guess)" "" + cp -f /usr/share/misc/config.guess config.guess +endif + + + dh_clean + +install: build + dh_testdir + dh_testroot + dh_clean -k + dh_installdirs + + # Add here commands to install the package into debian/frodo. + $(MAKE) prefix=$(CURDIR)/debian/frodo/usr install + + +# Build architecture-independent files here. +binary-indep: build install +# We have nothing to do by default. + +# Build architecture-dependent files here. +binary-arch: build install + dh_testdir + dh_testroot + dh_installchangelogs + dh_installdocs + dh_installexamples +# dh_install +# dh_installmenu +# dh_installdebconf +# dh_installlogrotate +# dh_installemacsen +# dh_installpam +# dh_installmime +# dh_python +# dh_installinit +# dh_installcron +# dh_installinfo + dh_installman + dh_link + dh_strip + dh_compress + dh_fixperms +# dh_perl +# dh_makeshlibs + dh_installdeb + dh_shlibdeps + dh_gencontrol + dh_md5sums + dh_builddeb + +binary: binary-indep binary-arch +.PHONY: build clean binary-indep binary-arch binary install diff --git a/Src/debug.h b/Src/debug.h new file mode 100644 index 0000000..267efb1 --- /dev/null +++ b/Src/debug.h @@ -0,0 +1,33 @@ +/* + * debug.h - Debugging utilities + * + * Frodo (C) 1994-1997,2002-2005 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef DEBUG_H +#define DEBUG_H + +#include +#define bug printf + +#if DEBUG +#define D(x) (x); +#else +#define D(x) ; +#endif + +#endif diff --git a/Src/dlgAdvanced.cpp b/Src/dlgAdvanced.cpp new file mode 100644 index 0000000..ed3ae0c --- /dev/null +++ b/Src/dlgAdvanced.cpp @@ -0,0 +1,107 @@ +/* + * dlgAdvanced.cpp - SDL GUI dialog for C64 advanced options + * + * (C) 2006 Bernd Lachner + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "sysdeps.h" +#include "sdlgui.h" + +#include "Prefs.h" + +enum ADVANCEDDLG { + box_main, + box_timing, + text_timing, + text_line_cpu, + LINE_CPU, + LINE_UP_CPU, + LINE_DOWN_CPU, + text_bad_line_cpu, + BAD_LINE_CPU, + BAD_LINE_UP_CPU, + BAD_LINE_DOWN_CPU, + text_line_cia, + LINE_CIA, + LINE_UP_CIA, + LINE_DOWN_CIA, + text_line_1541, + LINE_1541, + LINE_UP_1541, + LINE_DOWN_1541, + box_advancedoptions, + text_advancedoptions, + CLEAR_CIA_ICR, + OK, + CANCEL +}; + +static char Cycles[4][4]; + +/* The keyboard dialog: */ +/* Spalte, Zeile, Länge, Höhe*/ +static SGOBJ advanceddlg[] = +{ + { SGBOX, SG_BACKGROUND, 0, 0,0, 35,20, NULL }, + { SGBOX, 0, 0, 1,2, 33,9, NULL }, + { SGTEXT, 0, 0, 2, 1, 16, 1, " Timing Control"}, + + { SGTEXT, 0, 0, 2, 3, 16, 1, "Cycles / Line (CPU):"}, + { SGEDITFIELD, 0, 0, 26, 3, sizeof(Cycles[0])-1, 1, Cycles[0]}, + { SGBUTTON, SG_SELECTABLE | SG_TOUCHEXIT, 0, 30, 3, 1, 1, "\x01"}, + /* Arrow up */ + { SGBUTTON, SG_SELECTABLE | SG_TOUCHEXIT, 0, 32, 3, 1, 1, "\x02"}, + /* Arrow down */ + + { SGTEXT, 0, 0, 2, 5, 16, 1, "Cycles / bad line (CPU):"}, + { SGEDITFIELD, 0, 0, 26, 5, sizeof(Cycles[1])-1, 1, Cycles[1]}, + { SGBUTTON, SG_SELECTABLE | SG_TOUCHEXIT, 0, 30, 5, 1, 1, "\x01"}, + /* Arrow up */ + { SGBUTTON, SG_SELECTABLE | SG_TOUCHEXIT, 0, 32, 5, 1, 1, "\x02"}, + /* Arrow down */ + + { SGTEXT, 0, 0, 2, 7, 16, 1, "Cycles / Line (CIA):"}, + { SGEDITFIELD, 0, 0, 26, 7, sizeof(Cycles[2])-1, 1, Cycles[2]}, + { SGBUTTON, SG_SELECTABLE | SG_TOUCHEXIT, 0, 30, 7, 1, 1, "\x01"}, + /* Arrow up */ + { SGBUTTON, SG_SELECTABLE | SG_TOUCHEXIT, 0, 32, 7, 1, 1, "\x02"}, + /* Arrow down */ + + { SGTEXT, 0, 0, 2, 9, 16, 1, "Cycles / Line (1541):"}, + { SGEDITFIELD, 0, 0, 26, 9, sizeof(Cycles[3])-1, 1, Cycles[3]}, + { SGBUTTON, SG_SELECTABLE | SG_TOUCHEXIT, 0, 30, 9, 1, 1, "\x01"}, + /* Arrow up */ + { SGBUTTON, SG_SELECTABLE | SG_TOUCHEXIT, 0, 32, 9, 1, 1, "\x02"}, + /* Arrow down */ + + { SGBOX, 0, 0, 1,13, 33,3, NULL }, + { SGTEXT, 0, 0, 2, 12, 22, 1, " Advanced Options"}, + { SGCHECKBOX, SG_SELECTABLE, 0, 2, 14, 30, 1, "Clear CIA ICR on Write Access"}, + + { SGBUTTON, SG_SELECTABLE|SG_EXIT|SG_DEFAULT, 0, 1, 18, 6, 1, "OK"}, + { SGBUTTON, SG_SELECTABLE|SG_EXIT, 0, 9, 18, 6, 1, "Cancel"}, + + { -1, 0, 0, 0,0, 0,0, NULL } +}; + +void Dialog_Advanced(Prefs &prefs) +{ + switch (SDLGui_DoDialog(advanceddlg)) + { + } +} + diff --git a/Src/dlgDrives.cpp b/Src/dlgDrives.cpp new file mode 100644 index 0000000..3366698 --- /dev/null +++ b/Src/dlgDrives.cpp @@ -0,0 +1,138 @@ +/* + * dlgDrives.cpp - SDL GUI dialog for C64 drive settings + * + * (C) 2006 Bernd Lachner + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "sysdeps.h" +#include "sdlgui.h" +#include "file.h" + +#include "Prefs.h" + +#include "dlgFileSelect.h" + + +enum DRIVESDLG { + box_main, + box_drives, + text_drives, + text_drive8, + PATH8TXT, + PATH8, + text_drive9, + PATH9TXT, + PATH9, + text_drive10, + PATH10TXT, + PATH10, + text_drive11, + PATH11TXT, + PATH11, + box_options, + text_options, + ENABLE_FULL_1541, + MAP_FILE_NAMES, + OK, + CANCEL +}; + +static char DrivePath[4][18]; + +/* The keyboard dialog: */ +/* Spalte, Zeile, Länge, Höhe*/ +static SGOBJ drivesdlg[] = +{ + { SGBOX, SG_BACKGROUND, 0, 0,0, 35,20, NULL }, + { SGBOX, 0, 0, 1,2, 33,9, NULL }, + { SGTEXT, 0, 0, 2, 1, 13, 1, " Drive Paths"}, + + { SGTEXT, 0, 0, 2, 3, 8, 1, "Drive 8:"}, + { SGEDITFIELD, 0, 0, 12, 3, sizeof(DrivePath[0])-1, 1, DrivePath[0]}, + { SGBUTTON, SG_SELECTABLE | SG_TOUCHEXIT, 0, 30, 3, 4, 1, "Path"}, + + { SGTEXT, 0, 0, 2, 5, 16, 1, "Drive 9:"}, + { SGEDITFIELD, 0, 0, 12, 5, sizeof(DrivePath[1])-1, 1, DrivePath[1]}, + { SGBUTTON, SG_SELECTABLE | SG_TOUCHEXIT, 0, 30, 5, 4, 1, "Path"}, + + { SGTEXT, 0, 0, 2, 7, 16, 1, "Drive 10:"}, + { SGEDITFIELD, 0, 0, 12, 7, sizeof(DrivePath[2])-1, 1, DrivePath[2]}, + { SGBUTTON, SG_SELECTABLE | SG_TOUCHEXIT, 0, 30, 7, 4, 1, "Path"}, + + { SGTEXT, 0, 0, 2, 9, 16, 1, "Drive 11:"}, + { SGEDITFIELD, 0, 0, 12, 9, sizeof(DrivePath[3])-1, 1, DrivePath[3]}, + { SGBUTTON, SG_SELECTABLE | SG_TOUCHEXIT, 0, 30, 9, 4, 1, "Path"}, + + { SGBOX, 0, 0, 1,13, 33,3, NULL }, + { SGTEXT, 0, 0, 2, 12, 8, 1, " Options"}, + { SGCHECKBOX, SG_SELECTABLE, 0, 2, 14, 30, 1, "Enable Full 1541 Emulation"}, + { SGCHECKBOX, SG_SELECTABLE, 0, 2, 15, 30, 1, "Map '/' to '\\` in File Names"}, + + {SGBUTTON, SG_SELECTABLE|SG_EXIT|SG_DEFAULT, 0, 1, 18, 6, 1, "OK"}, + {SGBUTTON, SG_SELECTABLE|SG_EXIT, 0, 9, 18, 6, 1, "Cancel"}, + + { -1, 0, 0, 0,0, 0,0, NULL } +}; + +void Dialog_Drives(Prefs &prefs) +{ + // Set values from prefs + // TODO Check length + char path[4][MAX_FILENAME_LENGTH]; + for (int i = 0; i < 4; i++) + { + int widget = PATH8 + i*3; + char shrinkedpath[MAX_FILENAME_LENGTH]; + File_ShrinkName(shrinkedpath, prefs.DrivePath[i], drivesdlg[widget-1].w); + strcpy(drivesdlg[widget-1].txt, shrinkedpath); + strcpy(path[i], prefs.DrivePath[i]); + } + drivesdlg[ENABLE_FULL_1541].state |= prefs.Emul1541Proc == true ? SG_SELECTED : 0; + drivesdlg[MAP_FILE_NAMES].state |= prefs.MapSlash == true ? SG_SELECTED : 0; + while (1) + { + int widget = SDLGui_DoDialog(drivesdlg); + switch (widget) + { + case PATH8: + case PATH9: + case PATH10: + case PATH11: + { + if (SDLGui_FileSelect(path[(widget-PATH8)/3], false)) + { + char shrinkedpath[MAX_FILENAME_LENGTH]; + File_ShrinkName(shrinkedpath, path[(widget-PATH8)/3], drivesdlg[widget-1].w); + strcpy(drivesdlg[widget-1].txt, shrinkedpath); + } + } + break; + case OK: + for (int i = 0; i < 4; i++) + { + strcpy(prefs.DrivePath[i], path[i]); + fprintf (stderr, "Path(%s)\n", prefs.DrivePath[i]); + } + prefs.Emul1541Proc = drivesdlg[ENABLE_FULL_1541].state &= SG_SELECTED ? true : false; + prefs.MapSlash = drivesdlg[MAP_FILE_NAMES].state &= SG_SELECTED ? true : false; + return; + case CANCEL: + return; + } + } +} + diff --git a/Src/dlgFileSelect.cpp b/Src/dlgFileSelect.cpp new file mode 100644 index 0000000..e6613db --- /dev/null +++ b/Src/dlgFileSelect.cpp @@ -0,0 +1,414 @@ +/* + * dlgFileselect.cpp - dialog for selecting files (fileselector box) + * + * This file is taken from the ARAnyM project which builds a new and powerful + * TOS/FreeMiNT compatible virtual machine running on almost any hardware. + * + * Originally taken from the hatari project, thanks Thothy! + * + * Copyright (c) 2003-2005 ARAnyM dev team (see AUTHORS) + * + * Frodo 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 2 of the License, or + * (at your option) any later version. + * + * Frodo 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 Frodo; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include "sdlgui.h" +#include "file.h" + +#define SGFSDLG_FILENAME 5 +#define SGFSDLG_UPDIR 6 +#define SGFSDLG_ROOTDIR 7 +#define SGFSDLG_FIRSTENTRY 10 +#define SGFSDLG_LASTENTRY 24 +#define SGFSDLG_UP 25 +#define SGFSDLG_DOWN 26 +#define SGFSDLG_OKAY 27 +#define SGFSDLG_CANCEL 28 + +#define ENTRY_COUNT (SGFSDLG_LASTENTRY - SGFSDLG_FIRSTENTRY + 1) +#define ENTRY_LENGTH 30 + + +static char dlgpath[39], dlgfname[33]; /* File and path name in the dialog */ +static char dlgfilenames[ENTRY_COUNT][ENTRY_LENGTH + 1]; + +/* The dialog data: */ +static SGOBJ fsdlg[] = { + {SGBOX, SG_BACKGROUND, 0, 0, 0, 35, 25, NULL}, + {SGTEXT, 0, 0, 10, 1, 13, 1, "Choose a file"}, + {SGTEXT, 0, 0, 1, 2, 7, 1, "Folder:"}, + {SGTEXT, 0, 0, 1, 3, 33, 1, dlgpath}, + {SGTEXT, 0, 0, 1, 4, 6, 1, "File:"}, + {SGTEXT, 0, 0, 7, 4, 26, 1, dlgfname}, + {SGBUTTON, SG_SELECTABLE | SG_EXIT, 0, 26, 1, 4, 1, ".."}, + {SGBUTTON, SG_SELECTABLE | SG_EXIT, 0, 31, 1, 3, 1, "/"}, + {SGBOX, 0, 0, 1, 6, 33, 15, NULL}, + {SGBOX, 0, 0, 33, 6, 1, 15, NULL}, + {SGTEXT, 0, 0, 2, 6, ENTRY_LENGTH, 1, dlgfilenames[0]}, + {SGTEXT, 0, 0, 2, 7, ENTRY_LENGTH, 1, dlgfilenames[1]}, + {SGTEXT, 0, 0, 2, 8, ENTRY_LENGTH, 1, dlgfilenames[2]}, + {SGTEXT, 0, 0, 2, 9, ENTRY_LENGTH, 1, dlgfilenames[3]}, + {SGTEXT, 0, 0, 2, 10, ENTRY_LENGTH, 1, dlgfilenames[4]}, + {SGTEXT, 0, 0, 2, 11, ENTRY_LENGTH, 1, dlgfilenames[5]}, + {SGTEXT, 0, 0, 2, 12, ENTRY_LENGTH, 1, dlgfilenames[6]}, + {SGTEXT, 0, 0, 2, 13, ENTRY_LENGTH, 1, dlgfilenames[7]}, + {SGTEXT, 0, 0, 2, 14, ENTRY_LENGTH, 1, dlgfilenames[8]}, + {SGTEXT, 0, 0, 2, 15, ENTRY_LENGTH, 1, dlgfilenames[9]}, + {SGTEXT, 0, 0, 2, 16, ENTRY_LENGTH, 1, dlgfilenames[10]}, + {SGTEXT, 0, 0, 2, 17, ENTRY_LENGTH, 1, dlgfilenames[11]}, + {SGTEXT, 0, 0, 2, 18, ENTRY_LENGTH, 1, dlgfilenames[12]}, + {SGTEXT, 0, 0, 2, 19, ENTRY_LENGTH, 1, dlgfilenames[13]}, + {SGTEXT, 0, 0, 2, 20, ENTRY_LENGTH, 1, dlgfilenames[14]}, + {SGBUTTON, SG_SELECTABLE | SG_TOUCHEXIT, 0, 33, 6, 1, 1, "\x01"}, + /* Arrow up */ + {SGBUTTON, SG_SELECTABLE | SG_TOUCHEXIT, 0, 33, 20, 1, 1, "\x02"}, + /* Arrow down */ + {SGBUTTON, SG_SELECTABLE | SG_EXIT | SG_DEFAULT, 0, 7, 23, 8, 1, + "OK"}, + {SGBUTTON, SG_SELECTABLE | SG_EXIT, 0, 21, 23, 8, 1, "Cancel"}, + {-1, 0, 0, 0, 0, 0, 0, NULL} +}; + +struct listentry { + char *filename; + bool directory; + struct listentry *next; +}; + +/* Create a sorted list from the directory listing of path */ +struct listentry *create_list(char *path) +{ + struct listentry *list = NULL; + struct listentry *newentry; + DIR *dd; + struct dirent *direntry; + char tempstr[MAX_FILENAME_LENGTH]; + struct stat filestat; + + if ((dd = opendir(path)) == NULL) + return NULL; + + while ((direntry = readdir(dd)) != NULL) { + // skip "." name + if (strcmp(direntry->d_name, ".") == 0) + continue; + + /* Allocate enough memory to store a new list entry and + its filemane */ + newentry = + (struct listentry *) malloc(sizeof(struct listentry) + + strlen(direntry->d_name) + 1); + + /* Store filename */ + newentry->filename = (char *) (newentry + 1); + strcpy(newentry->filename, direntry->d_name); + + /* Is this entry a directory ? */ + strcpy(tempstr, path); + strcat(tempstr, newentry->filename); + if (stat(tempstr, &filestat) == 0 && S_ISDIR(filestat.st_mode)) + newentry->directory = true; + else + newentry->directory = false; + + /* Search for right place to insert new entry */ + struct listentry **prev = &list; + struct listentry *next = list; + while (next != NULL) { + /* directory first, then files */ + if ((newentry->directory == true) + && (next->directory == false)) + break; + + /* Sort by name */ + if ((newentry->directory == next->directory) + && (strcmp(newentry->filename, next->filename) < 0)) + break; + + prev = &(next->next); + next = next->next; + } + + /* Insert new entry */ + newentry->next = next; + *prev = newentry; + } + + if (closedir(dd) != 0) + bug("Error on closedir."); + + return list; +} + +/* Free memory allocated for each member of list */ +void free_list(struct listentry *list) +{ + while (list != NULL) { + struct listentry *temp = list; + list = list->next; + free(temp); + } +} + +/*-----------------------------------------------------------------------*/ +/* + Show and process a file selection dialog. + Returns TRUE if user selected "OK", FALSE if "cancel". + bAllowNew: TRUE if the user is allowed to insert new file names. +*/ +int SDLGui_FileSelect(char *path_and_name, bool bAllowNew) +{ + int i; + int ypos = 0; + struct listentry *list = NULL; + /* The actual file and path names */ + char path[MAX_FILENAME_LENGTH], fname[128]; + /* Do we have to reload the directory file list? */ + bool reloaddir = true; + /* Do we have to update the file names in the dialog? */ + bool refreshentries = true; + int retbut; + int oldcursorstate; + /* The actual selection, -1 if none selected */ + int selection = -1; + bool eol = true; + + if (bAllowNew) + fsdlg[SGFSDLG_FILENAME].type = SGEDITFIELD; + else + fsdlg[SGFSDLG_FILENAME].type = SGTEXT; + + /* Prepare the path and filename variables */ + File_splitpath(path_and_name, path, fname, NULL); + if (strlen(path) == 0) { + getcwd(path, sizeof(path)); + File_AddSlashToEndFileName(path); + } + File_ShrinkName(dlgpath, path, 38); + File_ShrinkName(dlgfname, fname, 32); + + /* Save old mouse cursor state and enable cursor anyway */ + screenlock(); + oldcursorstate = SDL_ShowCursor(SDL_QUERY); + if (oldcursorstate == SDL_DISABLE) + SDL_ShowCursor(SDL_ENABLE); + screenunlock(); + + do { + if (reloaddir) { + if (strlen(path) >= MAX_FILENAME_LENGTH) { + fprintf(stderr, + "SDLGui_FileSelect: Path name too long!\n"); + return false; + } + + free_list(list); + + /* Load directory entries: */ + list = create_list(path); + if (list == NULL) { + fprintf(stderr, "SDLGui_FileSelect: Path not found.\n"); + /* reset path and reload entries */ + strcpy(path, "/"); + strcpy(dlgpath, path); + list = create_list(path); + if (list == NULL) + /* we're really lost if even root is + unreadable */ + return false; + } + reloaddir = false; + refreshentries = true; + } + + if (refreshentries) { + struct listentry *temp = list; + for (i = 0; i < ypos; i++) + temp = temp->next; + + /* Copy entries to dialog: */ + for (i = 0; i < ENTRY_COUNT; i++) { + if (temp != NULL) { + char tempstr[MAX_FILENAME_LENGTH]; + /* Prepare entries: */ + strcpy(tempstr, " "); + strcat(tempstr, temp->filename); + File_ShrinkName(dlgfilenames[i], tempstr, + ENTRY_LENGTH); + /* Mark folders: */ + if (temp->directory) + dlgfilenames[i][0] = SGFOLDER; + fsdlg[SGFSDLG_FIRSTENTRY + i].flags = + (SG_SELECTABLE | SG_EXIT | SG_RADIO); + temp = temp->next; + } + else { + /* Clear entry */ + dlgfilenames[i][0] = 0; + fsdlg[SGFSDLG_FIRSTENTRY + i].flags = 0; + } + fsdlg[SGFSDLG_FIRSTENTRY + i].state = 0; + } + + if (temp == NULL) + eol = true; + else + eol = false; + + refreshentries = false; + } + + /* Show dialog: */ + retbut = SDLGui_DoDialog(fsdlg); + + /* Has the user clicked on a file or folder? */ + if ((retbut >= SGFSDLG_FIRSTENTRY) + && (retbut <= SGFSDLG_LASTENTRY)) { + char tempstr[MAX_FILENAME_LENGTH]; + struct stat filestat; + struct listentry *temp = list; + + strcpy(tempstr, path); + for (int i = 0; i < ((retbut - SGFSDLG_FIRSTENTRY) + ypos); + i++) + temp = temp->next; + strcat(tempstr, temp->filename); + if (stat(tempstr, &filestat) == 0 && S_ISDIR(filestat.st_mode)) { + /* Set the new directory */ + strcpy(path, tempstr); + if (strlen(path) >= 3) { + if ((path[strlen(path) - 2] == '/') + && (path[strlen(path) - 1] == '.')) + /* Strip a single dot at the + end of the path name */ + path[strlen(path) - 2] = 0; + if ((path[strlen(path) - 3] == '/') + && (path[strlen(path) - 2] == '.') + && (path[strlen(path) - 1] == '.')) { + /* Handle the ".." folder */ + char *ptr; + if (strlen(path) == 3) + path[1] = 0; + else { + path[strlen(path) - 3] = 0; + ptr = strrchr(path, '/'); + if (ptr) + *(ptr + 1) = 0; + } + } + } + File_AddSlashToEndFileName(path); + reloaddir = true; + /* Copy the path name to the dialog */ + File_ShrinkName(dlgpath, path, 38); + selection = -1; /* Remove old selection */ + // fname[0] = 0; + dlgfname[0] = 0; + ypos = 0; + } + else { + /* Select a file */ + selection = retbut - SGFSDLG_FIRSTENTRY + ypos; + strcpy(fname, temp->filename); + File_ShrinkName(dlgfname, fname, 32); + } + } + else { + /* Has the user clicked on another button? */ + switch (retbut) { + case SGFSDLG_UPDIR: + /* Change path to parent directory */ + if (strlen(path) > 2) { + char *ptr; + File_CleanFileName(path); + ptr = strrchr(path, '/'); + if (ptr) + *(ptr + 1) = 0; + File_AddSlashToEndFileName(path); + reloaddir = true; + /* Copy the path name to the dialog */ + File_ShrinkName(dlgpath, path, 38); + /* Remove old selection */ + selection = -1; + fname[0] = 0; + dlgfname[0] = 0; + ypos = 0; + } + break; + case SGFSDLG_ROOTDIR: + /* Change to root directory */ + strcpy(path, "/"); + reloaddir = true; + strcpy(dlgpath, path); + /* Remove old selection */ + selection = -1; + fname[0] = 0; + dlgfname[0] = 0; + ypos = 0; + break; + case SGFSDLG_UP: + /* Scroll up */ + if (ypos > 0) { + --ypos; + refreshentries = true; + } + SDL_Delay(20); + break; + case SGFSDLG_DOWN: + /* Scroll down */ + if (eol == false) { + ++ypos; + refreshentries = true; + } + SDL_Delay(20); + break; + } /* switch */ + } /* other button code */ + + } + while ((retbut != SGFSDLG_OKAY) && (retbut != SGFSDLG_CANCEL) + && (retbut != -1)); + + screenlock(); + if (oldcursorstate == SDL_DISABLE) + SDL_ShowCursor(SDL_DISABLE); + screenunlock(); + + { + /* if user edited filename, use new one */ + char dlgfname2[33]; + File_ShrinkName(dlgfname2, fname, 32); + if (strcmp(dlgfname, dlgfname2) != 0) + strcpy(fname, dlgfname); + } + + File_makepath(path_and_name, path, fname, NULL); + + free_list(list); + + return (retbut == SGFSDLG_OKAY); +} + +/* +vim:ts=4:sw=4: +*/ diff --git a/Src/dlgFileSelect.h b/Src/dlgFileSelect.h new file mode 100644 index 0000000..96fdfa1 --- /dev/null +++ b/Src/dlgFileSelect.h @@ -0,0 +1,24 @@ +/* + * dlgFileSelect.h - SDL GUI dialog for file selection + * + * (C) 2006 Bernd Lachner + * + * Originally taken from the hatari project, + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +int SDLGui_FileSelect(char *path_and_name, bool bAllowNew); diff --git a/Src/dlgInput.cpp b/Src/dlgInput.cpp new file mode 100644 index 0000000..17da4cc --- /dev/null +++ b/Src/dlgInput.cpp @@ -0,0 +1,74 @@ +/* + * dlgInput.cpp - SDL GUI dialog for C64 input options + * + * (C) 2006 Bernd Lachner + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "sysdeps.h" +#include "sdlgui.h" + +#include "Prefs.h" + +enum INPUTDLG { + box_main, + box_timing, + text_timing, + text_line_cpu, + LINE_CPU, + LINE_UP_CPU, + LINE_DOWN_CPU, + text_bad_line_cpu, + BAD_LINE_CPU, + BAD_LINE_UP_CPU, + BAD_LINE_DOWN_CPU, + text_line_cia, + LINE_CIA, + LINE_UP_CIA, + LINE_DOWN_CIA, + text_line_1541, + LINE_1541, + LINE_UP_1541, + LINE_DOWN_1541, + box_advancedoptions, + text_advancedoptions, + CLEAR_CIA_ICR, + OK, + CANCEL +}; + +/* The keyboard dialog: */ +/* Spalte, Zeile, Länge, Höhe*/ +static SGOBJ inputdlg[] = +{ + { SGBOX, SG_BACKGROUND, 0, 0,0, 35,20, NULL }, + { SGBOX, 0, 0, 1,2, 33,9, NULL }, + { SGTEXT, 0, 0, 2, 1, 11, 1, " Joysticks"}, + { SGCHECKBOX, SG_SELECTABLE, 0, 2, 3, 30, 1, "Swap Joysticks"}, + + {SGBUTTON, SG_SELECTABLE|SG_EXIT|SG_DEFAULT, 0, 1, 18, 6, 1, "OK"}, + {SGBUTTON, SG_SELECTABLE|SG_EXIT, 0, 9, 18, 6, 1, "Cancel"}, + + { -1, 0, 0, 0,0, 0,0, NULL } +}; + +void Dialog_Input(Prefs &prefs) +{ + switch (SDLGui_DoDialog(inputdlg)) + { + } +} + diff --git a/Src/dlgMain.cpp b/Src/dlgMain.cpp new file mode 100644 index 0000000..e8305a4 --- /dev/null +++ b/Src/dlgMain.cpp @@ -0,0 +1,113 @@ +/* + * dlgMain.cpp - Main SDL GUI dialog + * + * (C) 2006 Bernd Lachner + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "dlgMain.h" +#include "sdlgui.h" + +extern void Dialog_Drives(Prefs &prefs); +extern void Dialog_Options(Prefs &prefs); +extern void Dialog_VideoSound(Prefs &prefs); +extern void Dialog_Advanced(Prefs &prefs); +extern void Dialog_Input(Prefs &prefs); + +enum MAINDLG { + box_main, + box_settings, + text_settings, + DRIVES, + VIDEOSOUND, + INPUT, + OPTIONS, + ADVANCED, + APPLY, + CANCEL, + SAVE, + box_quickaccess, + text_quickaccess, + RESET, + QUIT, + ABOUT +}; + +/* The keyboard dialog: */ +/* Spalte, Zeile, Länge, Höhe*/ +static SGOBJ maindlg[] = +{ + { SGBOX, SG_BACKGROUND, 0, 0,0, 35,20, NULL }, + { SGBOX, 0, 0, 1,2, 33,10, NULL }, + { SGTEXT, 0, 0, 2, 1, 16, 1, " Frodo Settings "}, + + { SGBUTTON, SG_SELECTABLE|SG_EXIT, 0, 2, 3, 14,1, "Drives" }, + { SGBUTTON, SG_SELECTABLE|SG_EXIT, 0, 2, 5, 14,1, "Video/Sound" }, + { SGBUTTON, SG_SELECTABLE|SG_EXIT, 0, 2, 7, 14,1, "Input" }, + { SGBUTTON, SG_SELECTABLE|SG_EXIT, 0, 18,3, 14,1, "Options" }, + { SGBUTTON, SG_SELECTABLE|SG_EXIT, 0, 18,5, 14,1, "Advanced" }, + + { SGBUTTON, SG_SELECTABLE|SG_EXIT|SG_DEFAULT, 0, 2,10,8,1, "Apply" }, + { SGBUTTON, SG_SELECTABLE|SG_EXIT, 0, 13,10, 8,1, "Cancel" }, + { SGBUTTON, SG_SELECTABLE|SG_EXIT, 0, 24,10, 8,1, "Save" }, + + { SGBOX, 0, 0, 1,15, 33,3, NULL }, + { SGTEXT, 0, 0, 2, 14, 22, 1, " Quick-Access-Buttons "}, + + { SGBUTTON, SG_SELECTABLE|SG_EXIT, 0, 2,16,8,1, "Reset" }, + { SGBUTTON, SG_SELECTABLE|SG_EXIT, 0, 13,16, 8,1, "Quit" }, + { SGBUTTON, SG_SELECTABLE|SG_EXIT, 0, 24,16, 8,1, "About" }, + + { -1, 0, 0, 0,0, 0,0, NULL } +}; + +int Dialog_Main(Prefs &prefs) +{ + // apply + while (1) + { + switch (SDLGui_DoDialog(maindlg)) + { + case DRIVES: + Dialog_Drives(prefs); + break; + case OPTIONS: + Dialog_Options(prefs); + break; + case VIDEOSOUND: + Dialog_VideoSound(prefs); + break; + case ADVANCED: + Dialog_Advanced(prefs); + break; + case INPUT: + Dialog_Input(prefs); + break; + case APPLY: + return DO_USEPREFS; + case SAVE: + return DO_SAVEPREFS; + case RESET: + return DO_RESET; + case QUIT: + return DO_QUIT; + case CANCEL: + return DO_NOTHING; + } + } + return DO_NOTHING; +} + diff --git a/Src/dlgMain.h b/Src/dlgMain.h new file mode 100644 index 0000000..6768264 --- /dev/null +++ b/Src/dlgMain.h @@ -0,0 +1,29 @@ +/* + * dlgMain.h - Main SDL GUI dialog + * + * (C) 2006 Bernd Lachner + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +const int DO_NOTHING = 1; +const int DO_QUIT = 2; +const int DO_RESET = 3; +const int DO_USEPREFS = 4; +const int DO_SAVEPREFS = 5; + +class Prefs; + +int Dialog_Main(Prefs &prefs); diff --git a/Src/dlgOptions.cpp b/Src/dlgOptions.cpp new file mode 100644 index 0000000..7b654f5 --- /dev/null +++ b/Src/dlgOptions.cpp @@ -0,0 +1,116 @@ +/* + * dlgOptions.cpp - SDL GUI dialog for C64 emulator options + * + * (C) 2006 Bernd Lachner + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "sysdeps.h" +#include "sdlgui.h" + +#include "Prefs.h" + +enum OPTIONSDDLG { + box_main, + box_speedcontrol, + text_speedcontrol, + text_draweverynthframe, + DRAW_EVERYNTH_FRAME, + DRAW_EVERYNTH_FRAME_UP, + DRAW_EVERYNTH_FRAME_DOWN, + LIMIT_SPEED_TO100PERCENT, + FAST_RESET, + box_memoryexpansion, + text_memoryexpansion, + text_reu_size, + RADIO_REU_NONE, + RADIO_REU_128K, + RADIO_REU_256K, + RADIO_REU_512K, + OK, + CANCEL +}; + +/* The keyboard dialog: */ +/* Spalte, Zeile, Länge, Höhe*/ +static SGOBJ optionsdlg[] = +{ + { SGBOX, SG_BACKGROUND, 0, 0,0, 35,20, NULL }, + { SGBOX, 0, 0, 1,2, 33,9, NULL }, + { SGTEXT, 0, 0, 2, 1, 15, 1, " Speed Control"}, + + { SGTEXT, 0, 0, 2, 3, 16, 1, "Draw every n-th frame:"}, + { SGEDITFIELD, 0, 0, 26, 3, 3, 1, " "}, + {SGBUTTON, SG_SELECTABLE | SG_TOUCHEXIT, 0, 30, 3, 1, 1, "\x01"}, + /* Arrow up */ + {SGBUTTON, SG_SELECTABLE | SG_TOUCHEXIT, 0, 32, 3, 1, 1, "\x02"}, + /* Arrow down */ + { SGCHECKBOX, SG_SELECTABLE, 0, 2, 5, 30, 1, "Limit Speed to 100%"}, + { SGCHECKBOX, SG_SELECTABLE, 0, 2, 7, 30, 1, "Fast Reset"}, + + { SGBOX, 0, 0, 1,13, 33,3, NULL }, + { SGTEXT, 0, 0, 2, 12, 18, 1, " Memory Expansion"}, + { SGTEXT, 0, 0, 2, 14, 30, 1, "REU Size:"}, + { SGCHECKBOX, SG_SELECTABLE|SG_RADIO, 0, 2, 15, 4, 1, "None" }, + { SGCHECKBOX, SG_SELECTABLE|SG_RADIO, 0, 10, 15, 20,1, "128K" }, + { SGCHECKBOX, SG_SELECTABLE|SG_RADIO, 0, 18, 15, 20,1, "256K" }, + { SGCHECKBOX, SG_SELECTABLE|SG_RADIO, 0, 26, 15, 20,1, "512K" }, + + {SGBUTTON, SG_SELECTABLE|SG_EXIT|SG_DEFAULT, 0, 1, 18, 6, 1, "OK"}, + {SGBUTTON, SG_SELECTABLE|SG_EXIT, 0, 9, 18, 6, 1, "Cancel"}, + + { -1, 0, 0, 0,0, 0,0, NULL } +}; + +void Dialog_Options(Prefs &prefs) +{ + // Set values from prefs +// TODO optionsdlg[DRAW_EVERYNTH_FRAME].txt = prefs.SkipFrames; + optionsdlg[LIMIT_SPEED_TO100PERCENT].state |= prefs.LimitSpeed == true ? SG_SELECTED : 0; + optionsdlg[FAST_RESET].state |= prefs.FastReset == true ? SG_SELECTED : 0; + switch (prefs.REUSize) + { + case REU_NONE: + optionsdlg[RADIO_REU_NONE].state |= SG_SELECTED; + break; + case REU_128K: + optionsdlg[RADIO_REU_128K].state |= SG_SELECTED; + break; + case REU_256K: + optionsdlg[RADIO_REU_256K].state |= SG_SELECTED; + break; + case REU_512K: + optionsdlg[RADIO_REU_512K].state |= SG_SELECTED; + break; + } + switch (SDLGui_DoDialog(optionsdlg)) + { + case OK: + // Set values to prefs + prefs.LimitSpeed = optionsdlg[LIMIT_SPEED_TO100PERCENT].state &= SG_SELECTED ? true : false; + prefs.FastReset = optionsdlg[FAST_RESET].state &= SG_SELECTED ? true : false; + for (int i = RADIO_REU_NONE; i <= RADIO_REU_512K; i++) + { + if (optionsdlg[i].state &= SG_SELECTED) + { + prefs.REUSize = i - RADIO_REU_NONE; + break; + } + } + break; + } +} + diff --git a/Src/dlgVideoSound.cpp b/Src/dlgVideoSound.cpp new file mode 100644 index 0000000..b8c0154 --- /dev/null +++ b/Src/dlgVideoSound.cpp @@ -0,0 +1,82 @@ +/* + * dlgVideoSound.cpp - SDL GUI dialog for C64 video and sound options + * + * (C) 2006 Bernd Lachner + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "sysdeps.h" +#include "sdlgui.h" + +#include "Prefs.h" + +enum VIDEOSOUNDDDLG { + box_main, + box_timing, + text_timing, + text_line_cpu, + LINE_CPU, + LINE_UP_CPU, + LINE_DOWN_CPU, + text_bad_line_cpu, + BAD_LINE_CPU, + BAD_LINE_UP_CPU, + BAD_LINE_DOWN_CPU, + text_line_cia, + LINE_CIA, + LINE_UP_CIA, + LINE_DOWN_CIA, + text_line_1541, + LINE_1541, + LINE_UP_1541, + LINE_DOWN_1541, + box_advancedoptions, + text_advancedoptions, + CLEAR_CIA_ICR, + OK, + CANCEL +}; + +/* The keyboard dialog: */ +/* Spalte, Zeile, Länge, Höhe*/ +static SGOBJ videosounddlg[] = +{ + { SGBOX, SG_BACKGROUND, 0, 0,0, 35,20, NULL }, + { SGBOX, 0, 0, 1,2, 33, 7, NULL }, + { SGTEXT, 0, 0, 2, 1, 15, 1, " Video Options"}, + { SGCHECKBOX, SG_SELECTABLE, 0, 2, 3, 30, 1, "Display Sprites"}, + { SGCHECKBOX, SG_SELECTABLE, 0, 2, 5, 30, 1, "Detect Sprite Collisions"}, + + { SGBOX, 0, 0, 1, 11, 33, 5, NULL }, + { SGTEXT, 0, 0, 2, 10, 15, 1, " Sound Options"}, + { SGTEXT, 0, 0, 2, 12, 10, 1, "Sound Emu:"}, + { SGCHECKBOX, SG_SELECTABLE|SG_RADIO, 0, 13, 12, 12, 1, "Off" }, + { SGCHECKBOX, SG_SELECTABLE|SG_RADIO, 0, 20, 12, 20,1, "Software" }, + { SGCHECKBOX, SG_SELECTABLE, 0, 2, 14, 30, 1, "Enable SID Filters"}, + + {SGBUTTON, SG_SELECTABLE|SG_EXIT|SG_DEFAULT, 0, 1, 18, 6, 1, "OK"}, + {SGBUTTON, SG_SELECTABLE|SG_EXIT, 0, 9, 18, 6, 1, "Cancel"}, + + { -1, 0, 0, 0,0, 0,0, NULL } +}; + +void Dialog_VideoSound(Prefs &prefs) +{ + switch (SDLGui_DoDialog(videosounddlg)) + { + } +} + diff --git a/Src/el_Acorn.h b/Src/el_Acorn.h new file mode 100644 index 0000000..7c9d834 --- /dev/null +++ b/Src/el_Acorn.h @@ -0,0 +1,188 @@ +/* + * el_Acorn.h - Somewhat faster (on Acorn) versions of the el_-functions + * of VIC.cpp + * + * Frodo (C) 1994-1997,2002-2005 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifdef GLOBAL_VARS +static inline void el_std_text(uint8 *p, uint8 *q, uint8 *r) +#else +inline void MOS6569::el_std_text(uint8 *p, uint8 *q, uint8 *r) +#endif +{ + register uint32 *lp = (uint32 *)p, *t; + uint8 *cp = color_line; + uint8 *mp = matrix_line; + uint32 *tab = (uint32*)&TextColorTable[0][b0c][0][0]; + + // Loop for 40 characters + for (int i=0; i<40; i++) { + uint8 data = r[i] = q[mp[i] << 3]; + + t = tab + ((cp[i] & 15) << 13) + (data << 1); + *lp++ = *t++; *lp++ = *t++; + } +} + + +#ifdef GLOBAL_VARS +static inline void el_mc_text(uint8 *p, uint8 *q, uint8 *r) +#else +inline void MOS6569::el_mc_text(uint8 *p, uint8 *q, uint8 *r) +#endif +{ + register uint32 *wp = (uint32 *)p, *t; + uint32 *tab = (uint32*)&TextColorTable[0][b0c][0][0]; + uint8 *cp = color_line; + uint8 *mp = matrix_line; + uint8 *lookup = (uint8*)mc_color_lookup; + + // Loop for 40 characters + for (int i=0; i<40; i++) { + register uint32 color = cp[i]; + uint8 data = q[mp[i] << 3]; + + if (color & 8) { + r[i] = (data & 0xaa) | (data & 0xaa) >> 1; + lookup[6] = colors[color & 7]; + color = lookup[(data & 0xc0) >> 5] | (lookup[(data & 0x30) >> 3] << 16); + *wp++ = color | (color << 8); + color = lookup[(data & 0x0c) >> 1] | (lookup[(data & 0x03) << 1] << 16); + *wp++ = color | (color << 8); + + } else { // Standard mode in multicolor mode + r[i] = data; + color = cp[i]; t = tab + (color << 13) + (data << 1); + *wp++ = *t++; *wp++ = *t++; + } + } +} + + +#ifdef GLOBAL_VARS +static inline void el_std_bitmap(uint8 *p, uint8 *q, uint8 *r) +#else +inline void MOS6569::el_std_bitmap(uint8 *p, uint8 *q, uint8 *r) +#endif +{ + register uint32 *lp = (uint32 *)p, *t; + uint8 *mp = matrix_line; + + // Loop for 40 characters + for (int i=0; i<40; i++, q+=8) { + uint8 data = r[i] = *q; + uint8 h = mp[i]; + + t = (uint32*)&TextColorTable[h >> 4][h & 15][data][0]; + *lp++ = *t++; *lp++ = *t++; + } +} + + +#ifdef GLOBAL_VARS +static inline void el_mc_bitmap(uint8 *p, uint8 *q, uint8 *r) +#else +inline void MOS6569::el_mc_bitmap(uint8 *p, uint8 *q, uint8 *r) +#endif +{ + uint8 lookup[4]; + register uint32 *wp = (uint32 *)p; + uint8 *cp = color_line; + uint8 *mp = matrix_line; + + lookup[0] = b0c_color; + + // Loop for 40 characters + for (int i=0; i<40; i++, q+=8) { + uint8 data = *q; + register uint32 h = mp[i]; + + lookup[1] = colors[h >> 4]; + lookup[2] = colors[h]; + lookup[3] = colors[cp[i]]; + + r[i] = (data & 0xaa) | (data & 0xaa) >> 1; + + h = lookup[data >> 6] | (lookup[(data >> 4) & 3] << 16); *wp++ = h | (h << 8); + h = lookup[(data >> 2) & 3] | (lookup[data & 3] << 16); *wp++ = h | (h << 8); + } +} + + +#ifdef GLOBAL_VARS +static inline void el_ecm_text(uint8 *p, uint8 *q, uint8 *r) +#else +inline void MOS6569::el_ecm_text(uint8 *p, uint8 *q, uint8 *r) +#endif +{ + register uint32 *lp = (uint32 *)p, *t; + uint8 *cp = color_line; + uint8 *mp = matrix_line; + uint8 *bcp = &b0c; + + // Loop for 40 characters + for (int i=0; i<40; i++) { + uint8 data = r[i] = mp[i]; + + t = (uint32*)&TextColorTable[cp[i]][bcp[(data >> 6) & 3]][q[(data & 0x3f) << 3]][0]; + *lp++ = *t++; *lp++ = *t++; + } +} + + +#ifdef GLOBAL_VARS +static inline void el_std_idle(uint8 *p, uint8 *r) +#else +inline void MOS6569::el_std_idle(uint8 *p, uint8 *r) +#endif +{ + uint8 data = *get_physical(ctrl1 & 0x40 ? 0x39ff : 0x3fff); + uint32 *lp = (uint32 *)p; + uint32 conv0 = TextColorTable[0][b0c][data][0].b; + uint32 conv1 = TextColorTable[0][b0c][data][1].b; + + for (int i=0; i<40; i++) { + *lp++ = conv0; + *lp++ = conv1; + *r++ = data; + } +} + + +#ifdef GLOBAL_VARS +static inline void el_mc_idle(uint8 *p, uint8 *r) +#else +inline void MOS6569::el_mc_idle(uint8 *p, uint8 *r) +#endif +{ + uint8 data = *get_physical(0x3fff); + register uint8 c0 = b0c_color, c1 = colors[0]; + register uint32 *lp = (uint32 *)p; + register uint32 conv0, conv1; + + conv0 = (((data & 0xc0) == 0) ? c0 : c1) | (((data & 0x30) == 0) ? c0<<16 : c1<<16); + conv1 = (((data & 0x0c) == 0) ? c0 : c1) | (((data & 0x03) == 0) ? c0<<16 : c1<<16); + conv0 |= (conv0 << 8); conv1 |= (conv1 << 8); + + for (int i=0; i<40; i++) { + *lp++ = conv0; + *lp++ = conv1; + *r++ = data; + } +} diff --git a/Src/file.cpp b/Src/file.cpp new file mode 100644 index 0000000..7f14505 --- /dev/null +++ b/Src/file.cpp @@ -0,0 +1,541 @@ +/* + * file.cpp + * + * This file is taken from the ARAnyM project which builds a new and powerful + * TOS/FreeMiNT compatible virtual machine running on almost any hardware. + * + * It 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 2 of the License, or + * (at your option) any later version. + * + * You should have received a copy of the GNU General Public License + * along with Frodo; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "sysdeps.h" +#include "file.h" + +#define DEBUG 0 +#include "debug.h" + +# include +# include +# include + +#ifdef OS_solaris + #define DIRHANDLE dirp->d_fd +#elif defined(OS_mingw) + #define DIRHANDLE dirp->dd_handle +#else + #define DIRHANDLE dirp->fd +#endif + +#if defined(__BEOS__) || defined(OS_solaris) || defined(OS_mingw) +/* The scandir() and alphasort() functions aren't available on BeOS, */ +/* so let's declare them here... */ +#ifndef OS_mingw +#include +#endif + +#undef DIRSIZ + +#define DIRSIZ(dp) \ + ((sizeof(struct dirent) - sizeof(dp)->d_name) + \ + (((dp)->d_reclen + 1 + 3) &~ 3)) + + +/*-----------------------------------------------------------------------*/ +/* + Alphabetic order comparison routine for those who want it. +*/ +int alphasort(const void *d1, const void *d2) +{ + return(strcmp((*(struct dirent **)d1)->d_name, (*(struct dirent **)d2)->d_name)); +} + +/*-----------------------------------------------------------------------*/ +/* + Scan a directory for all its entries +*/ +int scandir(const char *dirname, struct dirent ***namelist, int(*select) (const struct dirent *), int (*dcomp) (const void *, const void *)) +{ + register struct dirent *d, *p, **names; + register size_t nitems; + struct stat stb; + unsigned long arraysz; + DIR *dirp; + + if ((dirp = opendir(dirname)) == NULL) + return(-1); + + if (fstat(DIRHANDLE, &stb) < 0) + return(-1); + + /* + * estimate the array size by taking the size of the directory file + * and dividing it by a multiple of the minimum size entry. + */ + arraysz = (stb.st_size / 24); + + names = (struct dirent **)malloc(arraysz * sizeof(struct dirent *)); + if (names == NULL) + return(-1); + + nitems = 0; + + while ((d = readdir(dirp)) != NULL) { + + if (select != NULL && !(*select)(d)) + continue; /* just selected names */ + + /* + * Make a minimum size copy of the data + */ + + p = (struct dirent *)malloc(DIRSIZ(d)); + if (p == NULL) + return(-1); + + p->d_ino = d->d_ino; + p->d_reclen = d->d_reclen; + /*p->d_namlen = d->d_namlen;*/ + bcopy(d->d_name, p->d_name, p->d_reclen + 1); + + /* + * Check to make sure the array has space left and + * realloc the maximum size. + */ + + if (++nitems >= arraysz) { + + if (fstat(DIRHANDLE, &stb) < 0) + return(-1); /* just might have grown */ + + arraysz = stb.st_size / 12; + + names = (struct dirent **)realloc((char *)names, arraysz * sizeof(struct dirent *)); + if (names == NULL) + return(-1); + } + + names[nitems-1] = p; + } + + closedir(dirp); + + if (nitems && dcomp != NULL) + qsort(names, nitems, sizeof(struct dirent *), dcomp); + + *namelist = names; + + return(nitems); +} + + +#endif /* __BEOS__ */ + + + +/*-----------------------------------------------------------------------*/ +/* + Remove any '/'s from end of filenames, but keeps / intact +*/ +void File_CleanFileName(char *pszFileName) +{ + int len; + + len = strlen(pszFileName); + + /* Security length check: */ + if( len>MAX_FILENAME_LENGTH ) + { + pszFileName[MAX_FILENAME_LENGTH-1] = 0; + len = MAX_FILENAME_LENGTH; + } + + /* Remove end slash from filename! But / remains! Doh! */ + if( len>2 && pszFileName[len-1]=='/' ) + pszFileName[len-1] = 0; +} + + +/*-----------------------------------------------------------------------*/ +/* + Add '/' to end of filename +*/ +void File_AddSlashToEndFileName(char *pszFileName) +{ + /* Check dir/filenames */ + if (strlen(pszFileName)!=0) { + if (pszFileName[strlen(pszFileName)-1]!='/') + strcat(pszFileName,"/"); /* Must use end slash */ + } +} + + +/*-----------------------------------------------------------------------*/ +/* + Does filename extension match? If so, return true +*/ +bool File_DoesFileExtensionMatch(char *pszFileName, char *pszExtension) +{ + if ( strlen(pszFileName) < strlen(pszExtension) ) + return(false); + /* Is matching extension? */ + if ( !strcasecmp(&pszFileName[strlen(pszFileName)-strlen(pszExtension)], pszExtension) ) + return(true); + + /* No */ + return(false); +} + + +/*-----------------------------------------------------------------------*/ +/* + Check if filename is from root + + Return true if filename is '/', else give false +*/ +bool File_IsRootFileName(char *pszFileName) +{ + if (pszFileName[0]=='\0') /* If NULL string return! */ + return(false); + + if (pszFileName[0]=='/') + return(true); + + return(false); +} + + +/*-----------------------------------------------------------------------*/ +/* + Return string, to remove 'C:' part of filename +*/ +char *File_RemoveFileNameDrive(char *pszFileName) +{ + if ( (pszFileName[0]!='\0') && (pszFileName[1]==':') ) + return(&pszFileName[2]); + else + return(pszFileName); +} + + +/*-----------------------------------------------------------------------*/ +/* + Check if filename end with a '/' + + Return true if filename ends with '/' +*/ +bool File_DoesFileNameEndWithSlash(char *pszFileName) +{ + if (pszFileName[0]=='\0') /* If NULL string return! */ + return(false); + + /* Does string end in a '/'? */ + if (pszFileName[strlen(pszFileName)-1]=='/') + return(true); + + return(false); +} + + +/*-----------------------------------------------------------------------*/ +/* + Remove any double '/'s from end of filenames. So just the one +*/ +void File_RemoveFileNameTrailingSlashes(char *pszFileName) +{ + int Length; + + /* Do have slash at end of filename? */ + Length = strlen(pszFileName); + if (Length>=3) { + if (pszFileName[Length-1]=='/') { /* Yes, have one previous? */ + if (pszFileName[Length-2]=='/') + pszFileName[Length-1] = '\0'; /* then remove it! */ + } + } +} + + +/*-----------------------------------------------------------------------*/ +/* + Does filename end with a .MSA extension? If so, return true +*/ +bool File_FileNameIsMSA(char *pszFileName) +{ + return(File_DoesFileExtensionMatch(pszFileName,".msa")); +} + + +/*-----------------------------------------------------------------------*/ +/* + Does filename end with a .ST extension? If so, return true +*/ +bool File_FileNameIsST(char *pszFileName) +{ + return(File_DoesFileExtensionMatch(pszFileName,".st")); +} + + +/*-----------------------------------------------------------------------*/ +/* + Read file from PC into memory, allocate memory for it if need to (pass Address as NULL) + Also may pass 'unsigned long' if want to find size of file read (may pass as NULL) +*/ +void *File_Read(const char *pszFileName, void *pAddress, long *pFileSize, char *ppszExts[]) +{ +// TODO DUNUSED(ppszExts); + FILE *DiscFile; + void *pFile=NULL; + long FileSize=0; + + /* Open our file */ + DiscFile = fopen(pszFileName, "rb"); + if (DiscFile!=NULL) { + /* Find size of TOS image - 192k or 256k */ + fseek(DiscFile, 0, SEEK_END); + FileSize = ftell(DiscFile); + fseek(DiscFile, 0, SEEK_SET); + /* Find pointer to where to load, allocate memory if pass NULL */ + if (pAddress) + pFile = pAddress; + else + pFile = malloc(FileSize); + /* Read in... */ + if (pFile) + fread((char *)pFile, 1, FileSize, DiscFile); + + fclose(DiscFile); + } + /* Store size of file we read in (or 0 if failed) */ + if (pFileSize) + *pFileSize = FileSize; + + return(pFile); /* Return to where read in/allocated */ +} + + +/*-----------------------------------------------------------------------*/ +/* + Save file to PC, return false if errors +*/ +bool File_Save(char *pszFileName, void *pAddress,unsigned long Size,bool bQueryOverwrite) +{ + FILE *DiscFile; + bool bRet=false; + + /* Check if need to ask user if to overwrite */ + if (bQueryOverwrite) { + /* If file exists, ask if OK to overwrite */ + if (!File_QueryOverwrite(pszFileName)) + return(false); + } + + /* Create our file */ + DiscFile = fopen(pszFileName, "wb"); + if (DiscFile!=NULL) { + /* Write data, set success flag */ + if ( fwrite(pAddress, 1, Size, DiscFile)==Size ) + bRet = true; + + fclose(DiscFile); + } + + return(bRet); +} + + +/*-----------------------------------------------------------------------*/ +/* + Return size of file, -1 if error +*/ +int File_Length(char *pszFileName) +{ + FILE *DiscFile; + int FileSize; + DiscFile = fopen(pszFileName, "rb"); + if (DiscFile!=NULL) { + fseek(DiscFile, 0, SEEK_END); + FileSize = ftell(DiscFile); + fseek(DiscFile, 0, SEEK_SET); + fclose(DiscFile); + return(FileSize); + } + + return(-1); +} + + +/*-----------------------------------------------------------------------*/ +/* + Return true if file exists +*/ +bool File_Exists(const char *pszFileName) +{ + FILE *DiscFile; + + /* Attempt to open file */ + DiscFile = fopen(pszFileName, "rb"); + if (DiscFile!=NULL) { + fclose(DiscFile); + return(true); + } + return(false); +} + + +/*-----------------------------------------------------------------------*/ +/* + Delete file, return true if OK +*/ +bool File_Delete(char *pszFileName) +{ + /* Delete the file (must be closed first) */ + return( remove(pszFileName) ); +} + + +/*-----------------------------------------------------------------------*/ +/* + Find if file exists, and if so ask user if OK to overwrite +*/ +bool File_QueryOverwrite(char *pszFileName) +{ + + char szString[MAX_FILENAME_LENGTH]; + + /* Try and find if file exists */ + if (File_Exists(pszFileName)) { + /* File does exist, are we OK to overwrite? */ + sprintf(szString,"File '%s' exists, overwrite?",pszFileName); +/* FIXME: */ +// if (MessageBox(hWnd,szString,PROG_NAME,MB_YESNO | MB_DEFBUTTON2 | MB_ICONSTOP)==IDNO) +// return(false); + } + + return(true); +} + + +/*-----------------------------------------------------------------------*/ +/* + Try filename with various extensions and check if file exists - if so return correct name +*/ +bool File_FindPossibleExtFileName(char *pszFileName, char *ppszExts[]) +{ + char szSrcDir[256], szSrcName[128], szSrcExt[32]; + char szTempFileName[MAX_FILENAME_LENGTH]; + int i=0; + + /* Split filename into parts */ + File_splitpath(pszFileName, szSrcDir, szSrcName, szSrcExt); + + /* Scan possible extensions */ + while(ppszExts[i]) { + /* Re-build with new file extension */ + File_makepath(szTempFileName, szSrcDir, szSrcName, ppszExts[i]); + /* Does this file exist? */ + if (File_Exists(szTempFileName)) { + /* Copy name for return */ + strcpy(pszFileName,szTempFileName); + return(true); + } + + /* Next one */ + i++; + } + + /* No, none of the files exist */ + return(false); +} + + +/*-----------------------------------------------------------------------*/ +/* + Split a complete filename into path, filename and extension. + If pExt is NULL, don't split the extension from the file name! +*/ +void File_splitpath(char *pSrcFileName, char *pDir, char *pName, char *pExt) +{ + char *ptr1, *ptr2; + + /* Build pathname: */ + ptr1 = strrchr(pSrcFileName, '/'); + if( ptr1 ) + { + strcpy(pDir, pSrcFileName); + strcpy(pName, ptr1+1); + pDir[ptr1-pSrcFileName+1] = 0; + } + else + { + strcpy(pDir, ""); + strcpy(pName, pSrcFileName); + } + + /* Build the raw filename: */ + if( pExt!=NULL ) + { + ptr2 = strrchr(pName+1, '.'); + if( ptr2 ) + { + pName[ptr2-pName] = 0; + /* Copy the file extension: */ + strcpy(pExt, ptr2+1); + } + else + pExt[0] = 0; + } +} + + +/*-----------------------------------------------------------------------*/ +/* + Build a complete filename from path, filename and extension. + pExt can also be NULL. +*/ +void File_makepath(char *pDestFileName, char *pDir, char *pName, char *pExt) +{ + strcpy(pDestFileName, pDir); + if( strlen(pDestFileName)==0 ) + strcpy(pDestFileName, ""); + if( pDestFileName[strlen(pDestFileName)-1]!='/' ) + strcat(pDestFileName, "/"); + + strcat(pDestFileName, pName); + + if( pExt!=NULL ) + { + if( strlen(pExt)>0 && pExt[0]!='.' ) + strcat(pDestFileName, "."); + strcat(pDestFileName, pExt); + } +} + + +/*-----------------------------------------------------------------------*/ +/* + Shrink a file name to a certain length and insert some dots if we cut + something away (usefull for showing file names in a dialog). +*/ +void File_ShrinkName(char *pDestFileName, char *pSrcFileName, int maxlen) +{ + int srclen = strlen(pSrcFileName); + if( srclen +extern int alphasort(const void *d1, const void *d2); +extern int scandir(const char *dirname, struct dirent ***namelist, int(*select) (const struct dirent *), int (*dcomp) (const void *, const void *)); +#endif /* __BEOS__ */ + +extern void File_CleanFileName(char *pszFileName); +extern void File_AddSlashToEndFileName(char *pszFileName); +extern bool File_DoesFileExtensionMatch(char *pszFileName, char *pszExtension); +extern bool File_IsRootFileName(char *pszFileName); +extern char *File_RemoveFileNameDrive(char *pszFileName); +extern bool File_DoesFileNameEndWithSlash(char *pszFileName); +extern void File_RemoveFileNameTrailingSlashes(char *pszFileName); +extern bool File_FileNameIsMSA(char *pszFileName); +extern bool File_FileNameIsST(char *pszFileName); +extern void *File_Read(char *pszFileName, void *pAddress, long *pFileSize, char *ppszExts[]); +extern bool File_Save(char *pszFileName, void *pAddress,long Size,bool bQueryOverwrite); +extern int File_Length(char *pszFileName); +extern bool File_Exists(const char *pszFileName); +extern bool File_Delete(char *pszFileName); +extern bool File_QueryOverwrite(char *pszFileName); +extern bool File_FindPossibleExtFileName(char *pszFileName,char *ppszExts[]); +extern void File_splitpath(char *pSrcFileName, char *pDir, char *pName, char *Ext); +extern void File_makepath(char *pDestFileName, char *pDir, char *pName, char *pExt); +extern void File_ShrinkName(char *pDestFileName, char *pSrcFileName, int maxlen); + +#endif /* HATARI_FILE_H */ diff --git a/Src/font8.h b/Src/font8.h new file mode 100644 index 0000000..cf499d1 --- /dev/null +++ b/Src/font8.h @@ -0,0 +1,175 @@ +// convert font8.bmp xbm:font8.h +#define font8_width 128 +#define font8_height 128 +static unsigned char font8_bits[] = { + 0x00, 0x18, 0x3C, 0x18, 0x18, 0x3C, 0xFF, 0xE7, 0x80, 0x7E, 0x18, 0x18, + 0x18, 0x18, 0xFF, 0xFF, 0x00, 0x3C, 0x24, 0x38, 0x1C, 0x99, 0xFF, 0xC3, + 0xC0, 0xC3, 0x3C, 0x38, 0x24, 0x24, 0x81, 0x81, 0x00, 0x66, 0x24, 0x6F, + 0xF6, 0xC3, 0x7F, 0x99, 0x60, 0xCB, 0x3C, 0x68, 0x42, 0x5A, 0x81, 0xC1, + 0x00, 0xC3, 0xE7, 0xC1, 0x83, 0xE7, 0x3F, 0x3C, 0x31, 0xCB, 0x3C, 0x08, + 0x81, 0xBD, 0x81, 0xA1, 0x00, 0xE7, 0xC3, 0xC1, 0x83, 0xC3, 0x9F, 0x99, + 0x1B, 0xDB, 0x7E, 0x08, 0x81, 0xBD, 0x81, 0x95, 0x00, 0x24, 0x66, 0x6F, + 0xF6, 0x99, 0xCF, 0xC3, 0x0E, 0xC3, 0x08, 0x0E, 0x42, 0x5A, 0x81, 0x89, + 0x00, 0x24, 0x3C, 0x38, 0x1C, 0x3C, 0xE7, 0xE7, 0x04, 0xC3, 0x1C, 0x0F, + 0x24, 0x24, 0xFF, 0xFF, 0x00, 0x3C, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x7E, 0x08, 0x06, 0x18, 0x18, 0x00, 0x00, 0x3E, 0x60, 0x3E, 0x3E, + 0x63, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x63, 0x60, 0x60, 0x60, 0x63, 0x03, 0x03, 0x60, 0x63, 0x63, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x63, 0x60, 0x60, 0x60, 0x63, 0x03, 0x03, 0x60, + 0x63, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x3E, + 0x3E, 0x3E, 0x3E, 0x00, 0x3E, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x63, 0x60, 0x03, 0x60, 0x60, 0x60, 0x63, 0x60, 0x63, 0x60, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x63, 0x60, 0x03, 0x60, 0x60, 0x60, 0x63, 0x60, + 0x63, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x60, 0x3E, 0x3E, + 0x60, 0x3E, 0x3E, 0x60, 0x3E, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x66, 0x00, 0x18, 0x00, 0x1C, 0x18, + 0x70, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x18, 0x66, 0x36, + 0x7C, 0x66, 0x36, 0x18, 0x38, 0x1C, 0x66, 0x18, 0x00, 0x00, 0x00, 0x60, + 0x00, 0x18, 0x66, 0x7F, 0x06, 0x36, 0x1C, 0x18, 0x18, 0x18, 0x3C, 0x18, + 0x00, 0x00, 0x00, 0x30, 0x00, 0x18, 0x00, 0x36, 0x3C, 0x18, 0x0E, 0x00, + 0x18, 0x18, 0xFF, 0x7E, 0x00, 0x7E, 0x00, 0x18, 0x00, 0x18, 0x00, 0x36, + 0x60, 0x0C, 0x7B, 0x00, 0x18, 0x18, 0x3C, 0x18, 0x00, 0x00, 0x00, 0x0C, + 0x00, 0x00, 0x00, 0x7F, 0x3E, 0x66, 0x33, 0x00, 0x38, 0x1C, 0x66, 0x18, + 0x0C, 0x00, 0x18, 0x06, 0x00, 0x18, 0x00, 0x36, 0x18, 0x62, 0x6E, 0x00, + 0x70, 0x0E, 0x00, 0x00, 0x0C, 0x00, 0x18, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x3C, 0x18, 0x3C, 0x7E, 0x30, 0x7E, 0x3C, 0x7E, 0x3C, 0x3C, 0x00, 0x00, + 0x60, 0x00, 0x06, 0x3C, 0x66, 0x1C, 0x66, 0x30, 0x38, 0x06, 0x06, 0x60, + 0x66, 0x66, 0x18, 0x18, 0x30, 0x00, 0x0C, 0x66, 0x76, 0x18, 0x60, 0x18, + 0x3C, 0x3E, 0x06, 0x30, 0x66, 0x66, 0x18, 0x18, 0x18, 0x7E, 0x18, 0x60, + 0x6E, 0x18, 0x30, 0x30, 0x36, 0x60, 0x3E, 0x18, 0x3C, 0x7C, 0x00, 0x00, + 0x0C, 0x00, 0x30, 0x30, 0x66, 0x18, 0x18, 0x60, 0x7E, 0x60, 0x66, 0x0C, + 0x66, 0x60, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x66, 0x18, 0x0C, 0x66, + 0x30, 0x66, 0x66, 0x0C, 0x66, 0x30, 0x18, 0x18, 0x30, 0x7E, 0x0C, 0x00, + 0x3C, 0x7E, 0x7E, 0x3C, 0x30, 0x3C, 0x3C, 0x0C, 0x3C, 0x1C, 0x00, 0x0C, + 0x60, 0x00, 0x06, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x18, 0x3E, 0x3C, + 0x1E, 0x7E, 0x7E, 0x7C, 0x66, 0x3C, 0x60, 0x66, 0x06, 0x63, 0x66, 0x3C, + 0x66, 0x3C, 0x66, 0x66, 0x36, 0x06, 0x06, 0x06, 0x66, 0x18, 0x60, 0x36, + 0x06, 0x77, 0x6E, 0x66, 0x76, 0x66, 0x66, 0x06, 0x66, 0x06, 0x06, 0x06, + 0x66, 0x18, 0x60, 0x1E, 0x06, 0x7F, 0x7E, 0x66, 0x56, 0x66, 0x3E, 0x06, + 0x66, 0x3E, 0x3E, 0x76, 0x7E, 0x18, 0x60, 0x0E, 0x06, 0x6B, 0x7E, 0x66, + 0x76, 0x7E, 0x66, 0x06, 0x66, 0x06, 0x06, 0x66, 0x66, 0x18, 0x60, 0x1E, + 0x06, 0x63, 0x76, 0x66, 0x06, 0x66, 0x66, 0x66, 0x36, 0x06, 0x06, 0x66, + 0x66, 0x18, 0x66, 0x36, 0x06, 0x63, 0x66, 0x66, 0x7C, 0x66, 0x3E, 0x3C, + 0x1E, 0x7E, 0x06, 0x7C, 0x66, 0x3C, 0x3C, 0x66, 0x7E, 0x63, 0x66, 0x3C, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x3C, 0x3E, 0x3C, 0x7E, 0x66, 0x66, 0x63, + 0x66, 0x66, 0x7E, 0x78, 0x02, 0x1E, 0x08, 0x00, 0x66, 0x66, 0x66, 0x66, + 0x18, 0x66, 0x66, 0x63, 0x66, 0x66, 0x60, 0x18, 0x06, 0x18, 0x1C, 0x00, + 0x66, 0x66, 0x66, 0x06, 0x18, 0x66, 0x66, 0x63, 0x3C, 0x66, 0x30, 0x18, + 0x0C, 0x18, 0x36, 0x00, 0x3E, 0x66, 0x3E, 0x3C, 0x18, 0x66, 0x66, 0x6B, + 0x18, 0x3C, 0x18, 0x18, 0x18, 0x18, 0x63, 0x00, 0x06, 0x6E, 0x36, 0x60, + 0x18, 0x66, 0x66, 0x7F, 0x3C, 0x18, 0x0C, 0x18, 0x30, 0x18, 0x00, 0x00, + 0x06, 0x36, 0x66, 0x66, 0x18, 0x66, 0x3C, 0x77, 0x66, 0x18, 0x06, 0x18, + 0x60, 0x18, 0x00, 0x00, 0x06, 0x6C, 0x66, 0x3C, 0x18, 0x7C, 0x18, 0x63, + 0x66, 0x18, 0x7E, 0x78, 0x40, 0x1E, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x60, 0x00, 0x38, 0x00, 0x06, 0x18, 0x18, 0x06, + 0x1C, 0x00, 0x00, 0x00, 0x03, 0x00, 0x06, 0x00, 0x60, 0x00, 0x0C, 0x00, + 0x06, 0x00, 0x00, 0x06, 0x18, 0x00, 0x00, 0x00, 0x06, 0x3C, 0x3E, 0x3C, + 0x7C, 0x3C, 0x3E, 0x7C, 0x3E, 0x1C, 0x18, 0x66, 0x18, 0x37, 0x3E, 0x3C, + 0x0C, 0x60, 0x66, 0x06, 0x66, 0x66, 0x0C, 0x66, 0x66, 0x18, 0x18, 0x36, + 0x18, 0x7F, 0x66, 0x66, 0x00, 0x7C, 0x66, 0x06, 0x66, 0x7E, 0x0C, 0x66, + 0x66, 0x18, 0x18, 0x1E, 0x18, 0x6B, 0x66, 0x66, 0x00, 0x66, 0x66, 0x06, + 0x66, 0x06, 0x0C, 0x7C, 0x66, 0x18, 0x18, 0x36, 0x18, 0x63, 0x66, 0x66, + 0x00, 0x7C, 0x3E, 0x3C, 0x7C, 0x3C, 0x0C, 0x60, 0x66, 0x3C, 0x18, 0x66, + 0x3C, 0x63, 0x66, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, + 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x18, 0x0E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, + 0x18, 0x18, 0x06, 0x18, 0x3E, 0x7C, 0x3E, 0x7C, 0x7E, 0x66, 0x66, 0x63, + 0x66, 0x66, 0x7E, 0x18, 0x18, 0x18, 0x4F, 0x18, 0x66, 0x66, 0x66, 0x06, + 0x18, 0x66, 0x66, 0x63, 0x3C, 0x66, 0x30, 0x0C, 0x18, 0x30, 0x79, 0x2C, + 0x66, 0x66, 0x06, 0x3C, 0x18, 0x66, 0x66, 0x6B, 0x18, 0x66, 0x18, 0x18, + 0x18, 0x18, 0x30, 0x2C, 0x66, 0x66, 0x06, 0x60, 0x18, 0x66, 0x3C, 0x3E, + 0x3C, 0x7C, 0x0C, 0x18, 0x18, 0x18, 0x00, 0x46, 0x3E, 0x7C, 0x06, 0x3E, + 0x70, 0x7C, 0x18, 0x36, 0x66, 0x60, 0x7E, 0x70, 0x18, 0x0E, 0x00, 0x7E, + 0x06, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; diff --git a/Src/gcaudio.c b/Src/gcaudio.c new file mode 100644 index 0000000..5cd945e --- /dev/null +++ b/Src/gcaudio.c @@ -0,0 +1,148 @@ +/**************************************************************************** + * FCE Ultra 0.98.12 + * Nintendo Wii/Gamecube Port + * + * Tantric September 2008 + * eke-eke October 2008 + * + * gcaudio.c + * + * Audio driver + ****************************************************************************/ + +#include +#include + +#define SAMPLERATE 48000 + +#define MIXBUFSIZE_BYTES 16000 +#define MIXBUFSIZE_SHORT (MIXBUFSIZE_BYTES / 2) +#define MIXBUFSIZE_WORDS (MIXBUFSIZE_BYTES / 4) + +#define SOUNDBUFSIZE 2048 + +static u8 ConfigRequested = 0; +static u8 soundbuffer[2][SOUNDBUFSIZE] ATTRIBUTE_ALIGN(32); +static u8 mixbuffer[MIXBUFSIZE_BYTES]; +static int mixhead = 0; +static int mixtail = 0; +static int whichab = 0; +static int IsPlaying = 0; + +/**************************************************************************** + * MixerCollect + * + * Collects sound samples from mixbuffer and puts them into outbuffer + * Makes sure to align them to 32 bytes for AUDIO_InitDMA + ***************************************************************************/ +static int MixerCollect( u8 *outbuffer, int len ) +{ + u32 *dst = (u32 *)outbuffer; + u32 *src = (u32 *)mixbuffer; + int done = 0; + + // Always clear output buffer + memset(outbuffer, 0, len); + + while ( ( mixtail != mixhead ) && ( done < len ) ) + { + *dst++ = src[mixtail++]; + if (mixtail == MIXBUFSIZE_WORDS) mixtail = 0; + done += 4; + } + + // Realign to 32 bytes for DMA + mixtail -= ((done&0x1f) >> 2); + if (mixtail < 0) + mixtail += MIXBUFSIZE_WORDS; + done &= ~0x1f; + + return done; +} + +/**************************************************************************** + * AudioSwitchBuffers + * + * Manages which buffer is played next + ***************************************************************************/ +static void AudioSwitchBuffers() +{ + if ( !ConfigRequested ) + { + int len = MixerCollect( soundbuffer[whichab], SOUNDBUFSIZE ); + if (len == 0) + return; + + DCFlushRange(soundbuffer[whichab], len); + AUDIO_InitDMA((u32)soundbuffer[whichab], len); + AUDIO_StartDMA(); + whichab ^= 1; + IsPlaying = 1; + } + else IsPlaying = 0; +} + +/**************************************************************************** + * InitialiseAudio + * + * Initializes sound system on first load of emulator + ***************************************************************************/ +void InitialiseAudio() +{ + AUDIO_Init(NULL); // Start audio subsystem + AUDIO_SetDSPSampleRate(AI_SAMPLERATE_48KHZ); + AUDIO_RegisterDMACallback( AudioSwitchBuffers ); + memset(soundbuffer, 0, SOUNDBUFSIZE*2); + memset(mixbuffer, 0, MIXBUFSIZE_BYTES); +} + +/**************************************************************************** + * StopAudio + * + * Pause audio output when returning to menu + ***************************************************************************/ +void StopAudio() +{ + AUDIO_StopDMA(); + IsPlaying = 0; +} + +/**************************************************************************** + * ResetAudio + * + * Reset audio output when loading a new game + ***************************************************************************/ +void ResetAudio() +{ + memset(soundbuffer, 0, SOUNDBUFSIZE*2); + memset(mixbuffer, 0, MIXBUFSIZE_BYTES); + mixhead = mixtail = 0; +} + +/**************************************************************************** + * PlaySound + * + * Puts incoming mono samples into mixbuffer + * Splits mono samples into two channels (stereo) + ****************************************************************************/ +void PlaySound( int16_t *Buffer, int count ) +{ + u32 *dst = (u32 *)mixbuffer; + int i; + + for( i = 0; i < count; i++ ) + { + u16 sample; + + sample = (Buffer[i] & 0xffff); + dst[mixhead++] = sample | ( sample << 16); + if (mixhead == MIXBUFSIZE_WORDS) + mixhead = 0; + } + + // Restart Sound Processing if stopped + if (IsPlaying == 0) + { + AudioSwitchBuffers (); + } +} diff --git a/Src/gcaudio.h b/Src/gcaudio.h new file mode 100644 index 0000000..93fcf1f --- /dev/null +++ b/Src/gcaudio.h @@ -0,0 +1,27 @@ +/**************************************************************************** + * From the FCE Ultra 0.98.12 + * Nintendo Wii/Gamecube Port + * + * Tantric September 2008 + * eke-eke October 2008 + * Simon Kagstrom Jan 2009 + * + * gcaudio.h + * + * Audio driver + ****************************************************************************/ + +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +void InitialiseAudio(); +void StopAudio(); +void ResetAudio(); +void PlaySound( int16_t *Buffer, int samples ); + +#if defined(__cplusplus) +}; +#endif diff --git a/Src/glade/Frodo.glade b/Src/glade/Frodo.glade new file mode 100644 index 0000000..9210637 --- /dev/null +++ b/Src/glade/Frodo.glade @@ -0,0 +1,1860 @@ + + + + + + + + + True + Frodo + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_NONE + False + True + False + True + False + False + GDK_WINDOW_TYPE_HINT_NORMAL + GDK_GRAVITY_NORTH_WEST + True + False + True + + + + + + True + True + + + + True + GTK_SHADOW_NONE + + + + True + GTK_PACK_DIRECTION_LTR + GTK_PACK_DIRECTION_LTR + + + + True + GNOMEUIINFO_MENU_FILE_TREE + + + + + + + True + _Run + True + + + + + True + gtk-ok + 1 + 0.5 + 0.5 + 0 + 0 + + + + + + + + True + + + + + + True + GNOMEUIINFO_MENU_EXIT_ITEM + + + + + + + + + + + True + GNOMEUIINFO_MENU_HELP_TREE + + + + + + + True + GNOMEUIINFO_MENU_ABOUT_ITEM + + + + + + + + + + + + BONOBO_DOCK_TOP + 0 + 0 + 0 + BONOBO_DOCK_ITEM_BEH_EXCLUSIVE|BONOBO_DOCK_ITEM_BEH_NEVER_VERTICAL|BONOBO_DOCK_ITEM_BEH_LOCKED + + + + + + True + GTK_SHADOW_OUT + + + + 1 + True + GTK_ORIENTATION_HORIZONTAL + GTK_TOOLBAR_BOTH + True + True + + + + True + Run + gtk-ok + True + True + False + + + + False + True + + + + + + True + Quit + gtk-quit + True + True + False + + + + False + True + + + + + + + BONOBO_DOCK_TOP + 1 + 0 + 0 + BONOBO_DOCK_ITEM_BEH_EXCLUSIVE + + + + + + True + True + True + True + GTK_POS_TOP + False + False + + + + 6 + True + False + 6 + + + + True + 0 + 0.5 + GTK_SHADOW_ETCHED_IN + + + + True + True + Enable Full 1541 Emulation + True + GTK_RELIEF_NORMAL + True + False + False + True + + + + + + + True + 1541 Emulation: + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + label_item + + + + + 0 + False + True + + + + + + True + 0 + 0.5 + GTK_SHADOW_ETCHED_IN + + + + 4 + True + 4 + 2 + False + 4 + 4 + + + + True + Drive 8: + False + False + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + 1 + 0 + 1 + fill + + + + + + + True + Drive 9: + False + False + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + 1 + 1 + 2 + fill + + + + + + + True + Drive 10: + False + False + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + 1 + 2 + 3 + fill + + + + + + + True + Drive 11: + False + False + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + 1 + 3 + 4 + fill + + + + + + + True + 10 + False + True + False + GTK_FILE_CHOOSER_ACTION_OPEN + + + + True + True + True + True + 0 + + True + * + False + + + + + 1 + 2 + 0 + 1 + + + + + + + True + 10 + False + True + False + GTK_FILE_CHOOSER_ACTION_OPEN + + + + True + True + True + True + 0 + + True + * + False + + + + + 1 + 2 + 1 + 2 + + + + + + + True + 10 + False + True + False + GTK_FILE_CHOOSER_ACTION_OPEN + + + + True + True + True + True + 0 + + True + * + False + + + + + 1 + 2 + 2 + 3 + + + + + + + True + 10 + False + True + False + GTK_FILE_CHOOSER_ACTION_OPEN + + + + True + True + True + True + 0 + + True + * + False + + + + + 1 + 2 + 3 + 4 + + + + + + + + + True + Drive Paths: + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + label_item + + + + + 0 + False + True + + + + + + True + 0 + 0.5 + GTK_SHADOW_ETCHED_IN + + + + True + True + Map '/' to '\' in File Names + True + GTK_RELIEF_NORMAL + True + False + False + True + + + + + + True + Options: + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + label_item + + + + + 0 + False + True + + + + + False + True + + + + + + True + Drives + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + tab + + + + + + 6 + True + False + 6 + + + + True + 0 + 0.5 + GTK_SHADOW_ETCHED_IN + + + + True + False + 0 + + + + True + False + 4 + + + + True + Display Type: + False + False + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + True + True + 0 + + + + + + + True + Window + True + + + + + + True + Full Screen + True + + + + + + + 0 + False + False + + + + + 0 + True + True + + + + + + True + True + Display Sprites + True + GTK_RELIEF_NORMAL + True + False + False + True + + + 0 + False + False + + + + + + True + True + Detect Sprite Collisions + True + GTK_RELIEF_NORMAL + True + False + False + True + + + 0 + False + False + + + + + + + + True + Video Options: + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + label_item + + + + + 0 + False + True + + + + + + True + 0 + 0.5 + GTK_SHADOW_ETCHED_IN + + + + True + False + 0 + + + + True + False + 4 + + + + True + Sound Emulation: + False + False + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + True + True + 0 + + + + + + + True + Off + True + + + + + + + True + Software + True + + + + + + + True + Catweasel + True + + + + + + + + 0 + False + False + + + + + 0 + False + True + + + + + + True + True + Enable SID Filters + True + GTK_RELIEF_NORMAL + True + False + False + True + + + + 0 + False + False + + + + + + + + True + Sound Options: + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + label_item + + + + + 0 + False + True + + + + + False + True + + + + + + True + Video/Sound + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + tab + + + + + + 6 + True + False + 6 + + + + True + 0 + 0.5 + GTK_SHADOW_ETCHED_IN + + + + True + False + 0 + + + + 4 + True + 2 + 2 + False + 4 + 4 + + + + True + Joystick Port 1: + False + False + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + 1 + 0 + 1 + fill + + + + + + + True + Joystick Port 2: + False + False + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + 1 + 1 + 2 + fill + + + + + + + True + True + -1 + + + + + + + + 1 + 2 + 0 + 1 + fill + + + + + + + True + True + -1 + + + + + + + + 1 + 2 + 1 + 2 + fill + + + + + + 0 + True + True + + + + + + True + True + Swap Joysticks + True + GTK_RELIEF_NORMAL + True + False + False + True + + + 0 + False + False + + + + + + + + True + Joysticks: + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + label_item + + + + + 0 + False + True + + + + + False + True + + + + + + True + Input + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + tab + + + + + + 6 + True + False + 6 + + + + True + 0 + 0.5 + GTK_SHADOW_ETCHED_IN + + + + True + False + 0 + + + + True + False + 4 + + + + True + Draw Every n-th Frame: + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + True + True + 1 + 0 + False + GTK_UPDATE_ALWAYS + True + False + 1 1 100 1 10 10 + + + 0 + False + True + + + + + 0 + True + True + + + + + + True + True + Limit Speed to 100% + True + GTK_RELIEF_NORMAL + True + False + False + True + + + 0 + False + False + + + + + + True + True + Fast Reset + True + GTK_RELIEF_NORMAL + True + False + False + True + + + 0 + False + False + + + + + + + + True + Speed Control: + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + label_item + + + + + 0 + False + False + + + + + + True + 0 + 0.5 + GTK_SHADOW_ETCHED_IN + + + + 4 + True + False + 4 + + + + True + REU Size: + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + True + True + 0 + + + + + + + True + None + True + + + + + + True + 128K + True + + + + + + True + 256K + True + + + + + + True + 512K + True + + + + + + + 0 + False + False + + + + + + + + True + Memory Expansion: + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + label_item + + + + + 0 + False + False + + + + + False + True + + + + + + True + Options + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + tab + + + + + + 6 + True + False + 6 + + + + True + 0 + 0.5 + GTK_SHADOW_ETCHED_IN + + + + 4 + True + 4 + 2 + False + 4 + 4 + + + + True + Cycles Per Line (CPU): + False + False + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + 1 + 0 + 1 + fill + + + + + + + True + Cycles Per Bad Line (CPU): + False + False + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + 1 + 1 + 2 + fill + + + + + + + True + Cycles Per Line (CIA): + False + False + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + 1 + 2 + 3 + fill + + + + + + + True + Cycles Per Line (1541): + False + False + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + 1 + 3 + 4 + fill + + + + + + + True + True + 1 + 0 + False + GTK_UPDATE_ALWAYS + True + False + 1 1 1000 1 10 10 + + + 1 + 2 + 0 + 1 + fill + + + + + + + True + True + 1 + 0 + False + GTK_UPDATE_ALWAYS + True + False + 1 1 1000 1 10 10 + + + 1 + 2 + 1 + 2 + fill + + + + + + + True + True + 1 + 0 + False + GTK_UPDATE_ALWAYS + True + False + 1 1 1000 1 10 10 + + + 1 + 2 + 2 + 3 + fill + + + + + + + True + True + 1 + 0 + False + GTK_UPDATE_ALWAYS + True + False + 1 1 1000 1 10 10 + + + 1 + 2 + 3 + 4 + fill + + + + + + + + + True + Timing Control: + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + label_item + + + + + 0 + False + True + + + + + + True + 0 + 0.5 + GTK_SHADOW_ETCHED_IN + + + + True + True + Clear CIA ICR on Write Access + True + GTK_RELIEF_NORMAL + True + False + False + True + + + + + + True + Advanced Options: + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + label_item + + + + + 0 + False + True + + + + + False + True + + + + + + True + Advanced + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + tab + + + + + + + 0 + True + True + + + + + + True + False + True + + + 0 + True + True + + + + + + 5 + False + True + False + False + GDK_WINDOW_TYPE_HINT_DIALOG + GDK_GRAVITY_NORTH_WEST + True + Copyright © 1994-1997,2002-2004 Christian Bauer et al. + A free, portable Commodore 64 emulator + Main Program and Project Administration: +Christian Bauer + +Initial Unix Port: +Bernd Schmidt +Lutz Vieweg + +Tcl/Tk Interface: +Lutz Vieweg +Gerard Decatrel + +Frodo Logo Design: +Tinic Urou + +Additional Contributions by: +Marc Chabanas +Jake Hamby +Erik Lindberg +Wolfgang Lorenz +Tinic Urou + translator_credits + + + diff --git a/Src/glade/Frodo.gladep b/Src/glade/Frodo.gladep new file mode 100644 index 0000000..d48c33a --- /dev/null +++ b/Src/glade/Frodo.gladep @@ -0,0 +1,9 @@ + + + + + Frodo + Frodo + FALSE + FALSE + diff --git a/Src/install-sh b/Src/install-sh new file mode 100755 index 0000000..11870f1 --- /dev/null +++ b/Src/install-sh @@ -0,0 +1,251 @@ +#!/bin/sh +# +# install - install a program, script, or datafile +# This comes from X11R5 (mit/util/scripts/install.sh). +# +# Copyright 1991 by the Massachusetts Institute of Technology +# +# Permission to use, copy, modify, distribute, and sell this software and its +# documentation for any purpose is hereby granted without fee, provided that +# the above copyright notice appear in all copies and that both that +# copyright notice and this permission notice appear in supporting +# documentation, and that the name of M.I.T. not be used in advertising or +# publicity pertaining to distribution of the software without specific, +# written prior permission. M.I.T. makes no representations about the +# suitability of this software for any purpose. It is provided "as is" +# without express or implied warranty. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# `make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. It can only install one file at a time, a restriction +# shared with many OS's install programs. + + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit="${DOITPROG-}" + + +# put in absolute paths if you don't have them in your path; or use env. vars. + +mvprog="${MVPROG-mv}" +cpprog="${CPPROG-cp}" +chmodprog="${CHMODPROG-chmod}" +chownprog="${CHOWNPROG-chown}" +chgrpprog="${CHGRPPROG-chgrp}" +stripprog="${STRIPPROG-strip}" +rmprog="${RMPROG-rm}" +mkdirprog="${MKDIRPROG-mkdir}" + +transformbasename="" +transform_arg="" +instcmd="$mvprog" +chmodcmd="$chmodprog 0755" +chowncmd="" +chgrpcmd="" +stripcmd="" +rmcmd="$rmprog -f" +mvcmd="$mvprog" +src="" +dst="" +dir_arg="" + +while [ x"$1" != x ]; do + case $1 in + -c) instcmd="$cpprog" + shift + continue;; + + -d) dir_arg=true + shift + continue;; + + -m) chmodcmd="$chmodprog $2" + shift + shift + continue;; + + -o) chowncmd="$chownprog $2" + shift + shift + continue;; + + -g) chgrpcmd="$chgrpprog $2" + shift + shift + continue;; + + -s) stripcmd="$stripprog" + shift + continue;; + + -t=*) transformarg=`echo $1 | sed 's/-t=//'` + shift + continue;; + + -b=*) transformbasename=`echo $1 | sed 's/-b=//'` + shift + continue;; + + *) if [ x"$src" = x ] + then + src=$1 + else + # this colon is to work around a 386BSD /bin/sh bug + : + dst=$1 + fi + shift + continue;; + esac +done + +if [ x"$src" = x ] +then + echo "install: no input file specified" + exit 1 +else + : +fi + +if [ x"$dir_arg" != x ]; then + dst=$src + src="" + + if [ -d $dst ]; then + instcmd=: + chmodcmd="" + else + instcmd=$mkdirprog + fi +else + +# Waiting for this to be detected by the "$instcmd $src $dsttmp" command +# might cause directories to be created, which would be especially bad +# if $src (and thus $dsttmp) contains '*'. + + if [ -f "$src" ] || [ -d "$src" ] + then + : + else + echo "install: $src does not exist" + exit 1 + fi + + if [ x"$dst" = x ] + then + echo "install: no destination specified" + exit 1 + else + : + fi + +# If destination is a directory, append the input filename; if your system +# does not like double slashes in filenames, you may need to add some logic + + if [ -d $dst ] + then + dst="$dst"/`basename $src` + else + : + fi +fi + +## this sed command emulates the dirname command +dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` + +# Make sure that the destination directory exists. +# this part is taken from Noah Friedman's mkinstalldirs script + +# Skip lots of stat calls in the usual case. +if [ ! -d "$dstdir" ]; then +defaultIFS=' + ' +IFS="${IFS-${defaultIFS}}" + +oIFS="${IFS}" +# Some sh's can't handle IFS=/ for some reason. +IFS='%' +set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` +IFS="${oIFS}" + +pathcomp='' + +while [ $# -ne 0 ] ; do + pathcomp="${pathcomp}${1}" + shift + + if [ ! -d "${pathcomp}" ] ; + then + $mkdirprog "${pathcomp}" + else + : + fi + + pathcomp="${pathcomp}/" +done +fi + +if [ x"$dir_arg" != x ] +then + $doit $instcmd $dst && + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else : ; fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else : ; fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else : ; fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else : ; fi +else + +# If we're going to rename the final executable, determine the name now. + + if [ x"$transformarg" = x ] + then + dstfile=`basename $dst` + else + dstfile=`basename $dst $transformbasename | + sed $transformarg`$transformbasename + fi + +# don't allow the sed command to completely eliminate the filename + + if [ x"$dstfile" = x ] + then + dstfile=`basename $dst` + else + : + fi + +# Make a temp file name in the proper directory. + + dsttmp=$dstdir/#inst.$$# + +# Move or copy the file name to the temp name + + $doit $instcmd $src $dsttmp && + + trap "rm -f ${dsttmp}" 0 && + +# and set any options; do chmod last to preserve setuid bits + +# If any of these fail, we abort the whole thing. If we want to +# ignore errors from any of these, just make sure not to ignore +# errors from the above "$doit $instcmd $src $dsttmp" command. + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else :;fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else :;fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else :;fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else :;fi && + +# Now rename the file to the real destination. + + $doit $rmcmd -f $dstdir/$dstfile && + $doit $mvcmd $dsttmp $dstdir/$dstfile + +fi && + + +exit 0 diff --git a/Src/maemo/Frodo_26_26.png b/Src/maemo/Frodo_26_26.png new file mode 100644 index 0000000000000000000000000000000000000000..52019ce3d0f379850986bff95ed3d24fd2d168b1 GIT binary patch literal 1205 zcmeH_>r0bS9LK*)WY0`vlra$_r-6#MX&P!4HD_~{yR{e2(qw8{LRK>>gd$y%AcQf< z7t2dAD^b&l^`;?O4kh!_$}3GW*o+J{( zfgZpPIDiqr1rUa@GmL{_JPboFbAu;sOQ@%Z{BlKBI)92M^4k$#z^)|Hniz%bur7SG=guirmBfEw+98vqIr?qs z*5Mn4Oy5fJRCvhVZCd~C;U%Jv`Vt&+@caDL(reFH%WPitM%X>UmQiDG-6x&KZ5v$M zFI&4W@y$(J6cuePQTnD$#%CG(W*=P|P#7lF#UF=s&e@W>J?gtkUR_G#;MXe&`GWX2 zd{rth|4hMv^4;Z^A%?RpV}S()jX`@SG)2dzshfT-$>*rfjF4`L2M|RGP zza8udx*&EorRcjJ2YJm@?rR%gcd=!f14%WS2fsWYI_F$LW#h~8C%4(CS9!Z*SC>Ma z{jJSQbGh9`pPLI60b{15(Do=@WVOcs=17}pQMfZp>wf+4+sx>RE_dYhkd~89Z!}fa mr)Edj{AgJ?C)W#Cd!wgCiQ7~ARs4lb`j}9pD`g$h{L0@mvJc<@ literal 0 HcmV?d00001 diff --git a/Src/maemo/Frodo_40_40.png b/Src/maemo/Frodo_40_40.png new file mode 100644 index 0000000000000000000000000000000000000000..0c5535527a7c7daacfd3e159983ae2a64ead8ce4 GIT binary patch literal 1361 zcmeH_i%XMH6u^&K`uSK*)Y4wG5`3g1O;cG9T{FLLy0xYW=`3+kpgm?A)Gge28D$ooINH6aRInA zh!4Q0L1G$&tESN)Tw6+ma%fN#^DR__*0@wpy*z0Cu|_Ulsk)LVs0o`TzXeFR=W1xHE2FhkUUx8Dc-1_S2iJ!~`Bx zEnG?BjjN2@jhoMHh8_;w_J|Ak&?TP|#JiXry%_$!mg-|79P^ao#ic}#yjSKVm&JOY z5o{O{d)mbQss-+4C;0O4~$OOE+w+aj0ZQ42`O#bQzu2GA)>2}5h&PInQgvP+`XJke3_qJ-{`R|=EKs#M&vaVoILE; z7M#J#P4HMN-#VBaGJMlfP%yc#)sQvkq38*Dl~VL^M)6P^^0PgTYg~7pD^FLnRLr$r zn>9L--*|pcBChj)yyt7a7M-#<#AiGgP?O%CQQe#UsS~ACQeBr%jrJs`S?6*&BC2pz z>YFLXyBAyP%bf|wba$qoudBsDNof^VQI~vZJs&l%<(FZGv-)6KF(FYK0EcG)HD+*o9j*M*rFPn zKQ-B~)4i!Jmp4?@oW#xwkMuUAr#zDt78FbzZ}s7iNiX_WA7#yk86SnlD)ualvsB%; zPTX7JzGHFrHmyYFd46=&7c%O=h|Lr;VE=(LmrrouHPjKeRZS_YI$`csSU;*SO@UdZd@W|sX1@I6o` literal 0 HcmV?d00001 diff --git a/Src/maemo/Frodo_64_64.png b/Src/maemo/Frodo_64_64.png new file mode 100644 index 0000000000000000000000000000000000000000..7a3ccc8b8aa4da0b23ee5f6a06910ba265507c97 GIT binary patch literal 1495 zcmeH_k5Ai06u@6g8DF5?zN1A*0oM+2Fw206gtED`g$f-|+5&3Hz*@`<0XhaakUH6F z2Z&9l49XnOhqEHIN=OdgqmI5Uv zBAi_)L=pfA1v(0Flme+J5UyHCfpF~-3RFgcssXC25mwRb5wiz_Pu8^gR z^Ix0tSWe^(ia1T|gFgoC&Pxd7^WW#S26EeXzp^g+W!bacU~#nA_k8`r+{osjJFcr~ z#Z`N1-sy~=c(bY|pLJDmeWcIV6Fq!J?p`-WoeZBMr?)-~N?^{H-L5MaEF={KGr}{- z8SnUTl+c$MDZ2o-!mlMTrNWnfx)X9^VfNvvIE`BB?_i}BM3;^j*7mm-=UR$tt@K7E z$3b^#)*W> zd$Q46dieaBpI`6QJ0a3{&XvlaYYD0-8-7`-o6H^O5j%WVlXGN!cdLYgJv$?2iukhl zc+J@X|0Bw49S)Jc`r)HTURoAUHdYaOc$b82nLn=Ft~?eRC)TXgpSDdeANq*VY5z(? zyIZv}zFIMw*~&AYPZX@N6^!}$!TR^ieYT#MRQaEOR2HethEcEotYOIR9bd?NDqgyf zWsGc2KAD$rDfdckZG+|5>oa?q>XDr)i{=RW>)l;-r)?7x-`qaOb7rdzH-6pTJ~6qp zq)qHDF4mQ6b>dBt%rCZ({MOC0g`g^asjhD}C`tFTGrW~a~@#($3Basic, BASIC_ROM_SIZE, builtin_basic_rom); + load_rom("Kernal", KERNAL_ROM_FILE, TheC64->Kernal, KERNAL_ROM_SIZE, builtin_kernal_rom); + load_rom("Char", CHAR_ROM_FILE, TheC64->Char, CHAR_ROM_SIZE, builtin_char_rom); + load_rom("1541", DRIVE_ROM_FILE, TheC64->ROM1541, DRIVE_ROM_SIZE, builtin_drive_rom); +} + + +#ifdef __BEOS__ +#include "main_Be.h" +#endif + +#ifdef AMIGA +#include "main_Amiga.h" +#endif + +#ifdef __unix +#include "main_x.h" +#endif + +#ifdef __mac__ +#include "main_mac.h" +#endif + +#ifdef WIN32 +#include "main_WIN32.h" +#endif + +#ifdef __riscos__ +#include "main_Acorn.h" +#endif + +#ifdef GEKKO +#include "main_wii.h" +#endif + diff --git a/Src/main.h b/Src/main.h new file mode 100644 index 0000000..b9ca8af --- /dev/null +++ b/Src/main.h @@ -0,0 +1,223 @@ +/* + * main.h - Main program + * + * Frodo (C) 1994-1997,2002-2005 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _MAIN_H +#define _MAIN_H + + +class C64; + +// Global variables +extern char AppDirPath[1024]; // Path of application directory + + +/* + * BeOS specific stuff + */ + +#ifdef __BEOS__ +#include +#include + +// Global variables +extern bool FromShell; // true: Started from shell, SAM can be used +extern BEntry AppDirectory; // Application directory + + +// Message codes +const uint32 MSG_STARTUP = 'strt'; // Start emulation +const uint32 MSG_PREFS = 'pref'; // Show preferences editor +const uint32 MSG_PREFS_DONE = 'pdon'; // Preferences editor closed +const uint32 MSG_RESET = 'rset'; // Reset C64 +const uint32 MSG_NMI = 'nmi '; // Raise NMI +const uint32 MSG_SAM = 'sam '; // Invoke SAM +const uint32 MSG_NEXTDISK = 'ndsk'; // Insert next disk in drive 8 +const uint32 MSG_TOGGLE_1541 = '1541'; // Toggle processor-level 1541 emulation +const uint32 MSG_OPEN_SNAPSHOT = 'opss'; // Open snapshot file +const uint32 MSG_SAVE_SNAPSHOT = 'svss'; // Save snapshot file +const uint32 MSG_OPEN_SNAPSHOT_RETURNED = 'opsr'; // Open snapshot file panel returned +const uint32 MSG_SAVE_SNAPSHOT_RETURNED = 'svsr'; // Save snapshot file panel returned + + +// Application signature +const char APP_SIGNATURE[] = "application/x-vnd.cebix-Frodo"; + + +// Application class +class Frodo : public BApplication { +public: + Frodo(); + virtual void ArgvReceived(int32 argc, char **argv); + virtual void RefsReceived(BMessage *message); + virtual void ReadyToRun(void); + virtual void MessageReceived(BMessage *msg); + virtual bool QuitRequested(void); + virtual void AboutRequested(void); + +private: + void load_rom(const char *which, const char *path, uint8 *where, size_t size, const uint8 *builtin); + void load_rom_files(); + + char prefs_path[1024]; // Pathname of current preferences file + bool prefs_showing; // true: Preferences editor is on screen + + BMessenger this_messenger; + BFilePanel *open_panel; + BFilePanel *save_panel; +}; + +#endif + + +/* + * AmigaOS specific stuff + */ + +#ifdef AMIGA + +class Frodo { +public: + Frodo(); + void ArgvReceived(int argc, char **argv); + void ReadyToRun(void); + void RunPrefsEditor(void); + +private: + void load_rom(const char *which, const char *path, uint8 *where, size_t size, const uint8 *builtin); + void load_rom_files(); + + char prefs_path[256]; // Pathname of current preferences file +}; + +// Global variables +extern Frodo *be_app; // Pointer to Frodo object + +#endif + + +/* + * X specific stuff + */ + +#if defined(__unix) || defined(GEKKO) +class Prefs; + +class Frodo { +public: + Frodo(); + void ArgvReceived(int argc, char **argv); + void ReadyToRun(void); + static Prefs *reload_prefs(void); + static char* get_prefs_path() { return prefs_path; }; +private: + void load_rom(const char *which, const char *path, uint8 *where, size_t size, const uint8 *builtin); + void load_rom_files(); + + static char prefs_path[256]; // Pathname of current preferences file +}; + +#endif + + +/* + * Mac specific stuff + */ + +#ifdef __mac__ + +class Frodo { +public: + Frodo(); + + void Run(void); + +private: + void load_rom(const char *which, const char *path, uint8 *where, size_t size, const uint8 *builtin); + void load_rom_files(); +}; + +#endif + + +/* + * WIN32 specific stuff + */ + +#ifdef WIN32 + +class Frodo { +public: + Frodo(); + ~Frodo(); + void ArgvReceived(int argc, char **argv); + void ReadyToRun(); + void RunPrefsEditor(); + + char prefs_path[256]; // Pathname of current preferences file + +private: + void load_rom(const char *which, const char *path, uint8 *where, size_t size, const uint8 *builtin); + void load_rom_files(); +}; + +// Global variables +extern Frodo *TheApp; // Pointer to Frodo object +extern HINSTANCE hInstance; +extern int nCmdShow; +extern HWND hwnd; + +// Command line options. +extern BOOL full_screen; + +#endif + + +/* + * RiscOS specific stuff + */ + +#ifdef __riscos__ + +class Frodo +{ +public: + Frodo(); + ~Frodo(); + void ReadyToRun(void); + +private: + void load_rom(const char *which, const char *path, uint8 *where, size_t size, const uint8 *builtin); + void load_rom_files(); +}; + +#endif + +// Global C64 object +extern C64 *TheC64; + + +/* + * Functions + */ + +// Determine whether path name refers to a directory +extern bool IsDirectory(const char *path); + +#endif diff --git a/Src/main_Acorn.h b/Src/main_Acorn.h new file mode 100644 index 0000000..0e143c5 --- /dev/null +++ b/Src/main_Acorn.h @@ -0,0 +1,576 @@ +/* + * main_Acorn.h - Main program, RISC OS specific stuff + * + * Frodo (C) 1994-1997,2002-2005 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ROlib.h" +#include "AcornGUI.h" + + +// Shared, system-specific data +// The application +Frodo *the_app; + +// My task handle +unsigned int TaskHandle; + +int WimpMessages[] = { + Message_DataSave, Message_DataSaveAck, Message_DataLoad, Message_DataLoadAck, + Message_DataOpen, Message_PaletteChange, Message_ModeChange, Message_MenuWarning, 0 +}; + + + + +// RORes member-functions - very simple reading of screen res and eigen +RORes::RORes(void) +{ + resx = OS_ReadModeVariable(-1,11); resy = OS_ReadModeVariable(-1,12); + eigx = OS_ReadModeVariable(-1,4) ; eigy = OS_ReadModeVariable(-1,5); + resx = (resx+1)< 0 if error +int ROScreen::ReadMode(void) +{ + register int h=0; + + if ((resx = OS_ReadModeVariable(-1,11)) < 0) h++; + if ((resy = OS_ReadModeVariable(-1,12)) < 0) h++; + if ((eigx = OS_ReadModeVariable(-1,4)) < 0) h++; + if ((eigy = OS_ReadModeVariable(-1,5)) < 0) h++; + resx = (resx+1)< eigy) + { + ModeError.errnum = 0x0; + sprintf((char*)ModeError.errmess,"Can't handle screen modes with eigen_x > eigen_y!"); + Wimp_ReportError(&ModeError,1,TASKNAME); + } + return(h); +} + + + + +// Window member-functions - handle all things concerned with windows +// WDesc is a pointer to a complete window descriptor (incl. Handle at pos 0!) +Window::Window(const int *WDesc, const char *Title) +{ + register int h; + + wind = (RO_Window *)WDesc; + if (Title != NULL) + { + if ((wind->tflags & IFlg_Indir) != 0) + { + strcpy((char*)(wind->dat.ind.tit),Title); // indirected + } + else {strncpy((char *)&wind->dat,Title,12);} + } + if ((wind->Handle = Wimp_CreateWindow((int*)&wind->vminx)) == 0) + { + _kernel_oserror WindError; + + WindError.errnum = 0x0; strcpy((char*)WindError.errmess,"Can't create window!"); + Wimp_ReportError(&WindError,1,TASKNAME); + } + // Isopen is unreliable. Works only if the window is never opened/closed by other + // means than calling the member-functions. For 100% accuracy use OpenStatus(). + isopen = false; +} + + +Window::~Window(void) +{ + Wimp_DeleteWindow((int*)wind); +} + + +int Window::MyHandle(void) +{ + return(wind->Handle); +} + + +void Window::GetWorkArea(int *Dest) +{ + Dest[0] = wind->wminx; Dest[1] = wind->wminy; + Dest[2] = wind->wmaxx; Dest[3] = wind->wmaxy; +} + + +void Window::open(void) {isopen = true; Wimp_OpenWindow((int*)wind);} + + +void Window::open(int *Block) {isopen = true; Wimp_OpenWindow(Block);} + + +void Window::close(void) {isopen = false; Wimp_CloseWindow((int*)wind);} + + +void Window::forceredraw(int minx, int miny, int maxx, int maxy) +{ + Wimp_ForceRedraw(wind->Handle,minx,miny,maxx,maxy); +} + + +void Window::redraw(int *Block, uint8 *Bitmap, C64Display *Disp) +{ + int more; + + more = Wimp_RedrawWindow(Block); + while (more != 0) + { + RedrawAWindow(Block,Bitmap,Disp); + more = Wimp_GetRectangle(Block); + } +} + + +// Block contains the coordinates to update, the handle is entered by this +// memberfunction +void Window::update(int *Block, uint8 *Bitmap, C64Display *Disp) +{ + int more; + + Block[0] = wind->Handle; + more = Wimp_UpdateWindow(Block); + while (more != 0) + { + RedrawAWindow(Block,Bitmap,Disp); + more = Wimp_GetRectangle(Block); + } +} + + +// This updated the entire window +void Window::update(uint8 *Bitmap, C64Display *Disp) +{ + int more; + int Block[11]; + + Block[0] = wind->Handle; + GetWorkArea(Block+1); + more = Wimp_UpdateWindow(Block); + while (more != 0) + { + RedrawAWindow(Block,Bitmap,Disp); + more = Wimp_GetRectangle(Block); + } +} + + +void Window::extent(int minx, int miny, int maxx, int maxy) +{ + int extent[4]; + + extent[0] = minx; extent[1] = miny; extent[2] = maxx; extent[3] = maxy; + Wimp_SetExtent(wind->Handle,(int*)extent); + // update internal window info as well + wind->wminx = minx; wind->wminy = miny; + wind->wmaxx = maxx; wind->wmaxy = maxy; +} + + +void Window::getstate(void) {Wimp_GetWindowState((int*)wind);} + + +void Window::getstate(int *dest) +{ + dest[0] = wind->Handle; + Wimp_GetWindowState(dest); +} + + +// The actual redrawing: if the bitmap pointer is not NULL the bitmap is +// painted into the window. +void Window::RedrawAWindow(int *Block, uint8 *Bitmap, C64Display *Disp) +{ + if (Bitmap != NULL) + { + // Plot the bitmap into the window + graph_env ge; + unsigned int *ct; + + // Coordinates are TOP left of rectangle (not bottom, like in RO) + ge.x = Block[RedrawB_VMinX] - Block[RedrawB_ScrollX]; + ge.y = Block[RedrawB_VMaxY] - Block[RedrawB_ScrollY]; + ge.dimx = DISPLAY_X; ge.dimy = DISPLAY_Y; + ct = Disp->GetColourTable(); + + if (Disp->TheC64->TheWIMP->ReadEmuWindowSize() == 1) + { + PlotZoom1(&ge,&Block[RedrawB_CMinX],Bitmap,ct); + } + else + { + PlotZoom2(&ge,&Block[RedrawB_CMinX],Bitmap,ct); + } + } +} + + +// Returns a pointer to a window's icon (or NULL of invalid number) +RO_Icon *Window::GetIcon(unsigned int number) +{ + if (number > wind->icon_no) {return(NULL);} + return((RO_Icon*)(((int*)wind) + RO_WINDOW_WORDS + RO_ICON_WORDS*number)); +} + + +void Window::SetIconState(unsigned int number, unsigned int eor, unsigned int clear) +{ + int Block[4]; + + Block[0] = wind->Handle; Block[1] = number; Block[2] = eor; Block[3] = clear; + Wimp_SetIconState((int*)Block); +} + + +void Window::GetIconState(unsigned int number, int *Block) +{ + Block[0] = wind->Handle; Block[1] = number; + Wimp_GetIconState(Block); +} + + +// Returns true if this window has the input focus +bool Window::HaveInput(void) +{ + RO_Caret Store; + + Wimp_GetCaretPosition(&Store); + return(Store.WHandle == wind->Handle); +} + + +// Writes text into an indirected icon +void Window::WriteIconText(unsigned int number, const char *text) +{ + RO_Icon *ic; + + if ((ic = GetIcon(number)) != NULL) + { + // This only makes sense for indirected icons! + if ((ic->iflags & IFlg_Indir) != 0) + { + strncpy((char*)ic->dat.ind.tit,text,ic->dat.ind.len); + ForceIconRedraw(number); + } + } +} + + +// The same but update the window (i.e. immediate result) +void Window::WriteIconTextU(unsigned int number, const char *text) +{ + RO_Icon *ic; + + if ((ic = GetIcon(number)) != NULL) + { + if ((ic->iflags & IFlg_Indir) != 0) + { + strncpy((char*)ic->dat.ind.tit,text,ic->dat.ind.len); + UpdateIcon(number); + } + } +} + + +// Writes the value as a decimal string into the indirected icon +void Window::WriteIconNumber(unsigned int number, int value) +{ + RO_Icon *ic; + + if ((ic = GetIcon(number)) != NULL) + { + if ((ic->iflags & IFlg_Indir) != 0) + { + ConvertInteger4(value,(char*)ic->dat.ind.tit,ic->dat.ind.len); + ForceIconRedraw(number); + } + } +} + + +// The same but update the window +void Window::WriteIconNumberU(unsigned int number, int value) +{ + RO_Icon *ic; + + if ((ic = GetIcon(number)) != NULL) + { + if ((ic->iflags & IFlg_Indir) != 0) + { + ConvertInteger4(value,(char*)ic->dat.ind.tit,ic->dat.ind.len); + UpdateIcon(number); + } + } +} + + +// Returns a pointer to the text in the icon +char *Window::ReadIconText(unsigned int number) +{ + RO_Icon *ic; + + if ((ic = GetIcon(number)) != NULL) + { + // only indirected icons! + if ((ic->iflags & IFlg_Indir) != 0) {return((char*)ic->dat.ind.tit);} + } + return(NULL); +} + + +// Reads the number in an indirected icon. +int Window::ReadIconNumber(unsigned int number) +{ + RO_Icon *ic; + + if ((ic = GetIcon(number)) != NULL) + { + if ((ic->iflags & IFlg_Indir) != 0) + { + return(atoi((char*)ic->dat.ind.tit)); + } + } + return(-1); // rather arbitrary, but we only have positive numbers here... +} + + +void Window::WriteTitle(const char *title) +{ + // only indirected window titles, must contain text + if ((wind->tflags & (IFlg_Indir | IFlg_Text)) == (IFlg_Indir | IFlg_Text)) + { + strcpy((char*)wind->dat.ind.tit,title); + UpdateTitle(); + } +} + + +char *Window::ReadTitle(void) +{ + if ((wind->tflags & (IFlg_Indir | IFlg_Text)) == (IFlg_Indir | IFlg_Text)) + { + return((char*)wind->dat.ind.tit); + } + else {return(NULL);} +} + + +// Forces a redraw of the window title +void Window::UpdateTitle(void) +{ + getstate(); + // Force global redraw (in screen-coordinates) + Wimp_ForceRedraw(-1, wind->vminx, wind->vmaxy, wind->vmaxx, wind->vmaxy + TitleBarHeight); +} + + +RO_Window *Window::Descriptor(void) {return(wind);} + + +// Force a redraw on an icon (visible after the next Poll) +void Window::ForceIconRedraw(unsigned int number) +{ + if (number <= wind->icon_no) + { + register RO_Icon *ic; + + ic = GetIcon(number); + forceredraw(ic->minx, ic->miny, ic->maxx, ic->maxy); + } +} + + +// Update an icon (visible immediately -- works only on purely WIMP-drawn icons!) +void Window::UpdateIcon(unsigned int number) +{ + if (number <= wind->icon_no) + { + register RO_Icon *ic; + int Block[11]; // redraw block + int more; + + ic = GetIcon(number); + Block[RedrawB_Handle] = wind->Handle; + Block[RedrawB_VMinX] = ic->minx; Block[RedrawB_VMinY] = ic->miny; + Block[RedrawB_VMaxX] = ic->maxx; Block[RedrawB_VMaxY] = ic->maxy; + more = Wimp_UpdateWindow(Block); // standard redraw loop + while (more != 0) + { + more = Wimp_GetRectangle(Block); + } + } +} + + +// returns the current open-state of the window (true if open) +bool Window::OpenStatus(void) +{ + getstate(); + return(((wind->wflags & (1<<16)) == 0) ? false : true); +} + + +// Same as above, but reads the current state to Block +bool Window::OpenStatus(int *Block) +{ + getstate(Block); + return(((Block[8] & (1<<16)) == 0) ? false : true); +} + + + + + + + +// Icon member-functions - handle all things concerned with icons +Icon::Icon(int Priority, const RO_IconDesc *IDesc) +{ + memcpy((char*)&icon,(char*)IDesc,sizeof(RO_IconDesc)); + IHandle = Wimp_CreateIcon(Priority,IDesc); +} + + +Icon::~Icon(void) +{ + int blk[2]; + + blk[0] = icon.WindowHandle; blk[1] = IHandle; + Wimp_DeleteIcon((int*)blk); +} + + +void Icon::setstate(unsigned int eor, unsigned int clear) +{ + int blk[4]; + + blk[0] = icon.WindowHandle; blk[1] = IHandle; blk[2] = eor; blk[3] = clear; + Wimp_SetIconState((int*)blk); +} + + +void Icon::getstate(void) +{ + int blk[10]; + + blk[0] = icon.WindowHandle; blk[1] = IHandle; + Wimp_GetIconState((int*)blk); + memcpy((char*)&icon,(int*)&blk[2],sizeof(RO_Icon)); +} + + + + +// Frodo member functions +Frodo::Frodo(void) {TheC64 = NULL;} + + +Frodo::~Frodo(void) +{ + if (TheC64 != NULL) {delete TheC64;} +} + + +void Frodo::ReadyToRun(void) +{ + ThePrefs.Load(DEFAULT_PREFS); + TheC64 = new C64; + load_rom_files(); + TheC64->Run(); + delete TheC64; TheC64 = NULL; +} + + + + + +extern void (*__new_handler)(void); + +// Out of memory +void OutOfMemory(void) +{ + _kernel_oserror err; + + + err.errnum = 0; sprintf(err.errmess,"Out of memory error! Aborting."); + Wimp_ReportError(&err,1,TASKNAME); + delete the_app; + Wimp_CloseDown(TaskHandle,TASK_WORD); + exit(1); +} + + + + +// Frodo main +int main(int argc, char *argv[]) +{ +#ifdef __GNUG__ + // Switch off filename conversions in UnixLib: + //__uname_control = __UNAME_NO_PROCESS; +#endif + +#ifdef FRODO_SC + TaskHandle = Wimp_Initialise(310,TASK_WORD,"FrodoSC",(int*)WimpMessages); +#else +# ifdef FRODO_PC + TaskHandle = Wimp_Initialise(310,TASK_WORD,"FrodoPC",(int*)WimpMessages); +# else + TaskHandle = Wimp_Initialise(310,TASK_WORD,"Frodo",(int*)WimpMessages); +# endif +#endif + + // Install handler for failed new + __new_handler = OutOfMemory; + + the_app = new Frodo(); + the_app->ReadyToRun(); + // Clean up directory scrap file + DeleteFile(RO_TEMPFILE); + delete the_app; + Wimp_CloseDown(TaskHandle,TASK_WORD); + return(0); +} diff --git a/Src/main_Amiga.h b/Src/main_Amiga.h new file mode 100644 index 0000000..d4fa473 --- /dev/null +++ b/Src/main_Amiga.h @@ -0,0 +1,177 @@ +/* + * main_Amiga.h - Main program, AmigaOS specific stuff + * + * Frodo (C) 1994-1997,2002-2005 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include + + +// Global variables +Frodo *be_app; // Pointer to Frodo object + +// Library bases +extern ExecBase *SysBase; +struct GfxBase *GfxBase = NULL; +struct IntuitionBase *IntuitionBase = NULL; +struct Library *GadToolsBase = NULL; +struct Library *DiskfontBase = NULL; +struct Library *AslBase = NULL; + +// Prototypes +void error_exit(char *str); +void open_libs(void); +void close_libs(void); + + +/* + * Create application object and start it + */ + +int main(int argc, char **argv) +{ + if ((SysBase->AttnFlags & (AFF_68040 | AFF_68881)) != (AFF_68040 | AFF_68881)) + error_exit("68040/68881 or higher required.\n"); + open_libs(); + + ULONG secs, micros; + CurrentTime(&secs, µs); + srand(micros); + + be_app = new Frodo(); + be_app->ArgvReceived(argc, argv); + be_app->ReadyToRun(); + delete be_app; + + close_libs(); + return 0; +} + + +/* + * Low-level failure + */ + +void error_exit(char *str) +{ + printf(str); + close_libs(); + exit(20); +} + + +/* + * Open libraries + */ + +void open_libs(void) +{ + if (!(GfxBase = (struct GfxBase *)OpenLibrary("graphics.library", 39))) + error_exit("Couldn't open Gfx V39.\n"); + if (!(IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library", 39))) + error_exit("Couldn't open Intuition V39.\n"); + if (!(GadToolsBase = OpenLibrary("gadtools.library", 39))) + error_exit("Couldn't open GadTools V39.\n"); + if (!(DiskfontBase = OpenLibrary("diskfont.library", 39))) + error_exit("Couldn't open Diskfont V39.\n"); + if (!(AslBase = OpenLibrary("asl.library", 39))) + error_exit("Couldn't open ASL V39.\n"); +} + + +/* + * Close libraries + */ + +void close_libs(void) +{ + if (AslBase) + CloseLibrary(AslBase); + if (DiskfontBase) + CloseLibrary(DiskfontBase); + if (GadToolsBase) + CloseLibrary(GadToolsBase); + if (IntuitionBase) + CloseLibrary((struct Library *)IntuitionBase); + if (GfxBase) + CloseLibrary((struct Library *)GfxBase); +} + + +/* + * Constructor: Initialize member variables + */ + +Frodo::Frodo() +{ + TheC64 = NULL; + prefs_path[0] = 0; +} + + +/* + * Process command line arguments + */ + +void Frodo::ArgvReceived(int argc, char **argv) +{ + if (argc == 2) + strncpy(prefs_path, argv[1], 255); +} + + +/* + * Arguments processed, run emulation + */ + +void Frodo::ReadyToRun(void) +{ + getcwd(AppDirPath, 256); + + // Load preferences + if (!prefs_path[0]) + strcpy(prefs_path, "Frodo Prefs"); + ThePrefs.Load(prefs_path); + + // Show preferences editor + if (ThePrefs.ShowEditor(TRUE, prefs_path)) { + + // Create and start C64 + TheC64 = new C64; + load_rom_files(); + TheC64->Run(); + delete TheC64; + } +} + + +/* + * Run preferences editor + */ + +void Frodo::RunPrefsEditor(void) +{ + Prefs *prefs = new Prefs(ThePrefs); + if (prefs->ShowEditor(FALSE, prefs_path)) { + TheC64->NewPrefs(prefs); + ThePrefs = *prefs; + } + delete prefs; +} diff --git a/Src/main_Be.h b/Src/main_Be.h new file mode 100644 index 0000000..753499a --- /dev/null +++ b/Src/main_Be.h @@ -0,0 +1,401 @@ +/* + * main_Be.h - Main program, BeOS specific stuff + * + * Frodo (C) 1994-1997,2002-2005 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include + +#include "Version.h" + + +// Global variables +bool FromShell; +BEntry AppDirectory; +BBitmap *AboutBitmap; +const BRect AboutFrame = BRect(0, 0, 383, 99); + + +/* + * Create application object and start it + */ + +int main(int argc, char **argv) +{ + Frodo *the_app; + + srand(real_time_clock()); + FromShell = (argc != 0); // !! This doesn't work... + + the_app = new Frodo(); + if (the_app != NULL) { + the_app->Run(); + delete the_app; + } + return 0; +} + + +/* + * Constructor: Initialize member variables + */ + +Frodo::Frodo() : BApplication(APP_SIGNATURE), this_messenger(this) +{ + TheC64 = NULL; + AboutBitmap = NULL; + strcpy(prefs_path, "/boot/home/config/settings/Frodo_settings"); + prefs_showing = false; + + // Create file panels + open_panel = new BFilePanel(B_OPEN_PANEL, &this_messenger, NULL, 0, false, new BMessage(MSG_OPEN_SNAPSHOT_RETURNED)); + open_panel->Window()->SetTitle("Frodo: Load snapshot"); + save_panel = new BFilePanel(B_SAVE_PANEL, &this_messenger, NULL, 0, false, new BMessage(MSG_SAVE_SNAPSHOT_RETURNED)); + save_panel->Window()->SetTitle("Frodo: Save snapshot"); +} + + +/* + * Process command line arguments + */ + +void Frodo::ArgvReceived(int32 argc, char **argv) +{ + if (argc == 2) { + strncpy(prefs_path, argv[1], 1023); + prefs_path[1023] = 0; + } +} + + +/* + * Process Browser arguments + */ + +void Frodo::RefsReceived(BMessage *message) +{ + // Set preferences path unless prefs editor is open or C64 is running + if (!prefs_showing && !TheC64) { + entry_ref the_ref; + BEntry the_entry; + + if (message->FindRef("refs", &the_ref) == B_NO_ERROR) + if (the_entry.SetTo(&the_ref) == B_NO_ERROR) + if (the_entry.IsFile()) { + BPath the_path; + the_entry.GetPath(&the_path); + strncpy(prefs_path, the_path.Path(), 1023); + prefs_path[1023] = 0; + } + } +} + + +/* + * Arguments processed, prepare emulation and show preferences editor window + */ + +void Frodo::ReadyToRun(void) +{ + // Find application directory and cwd to it + app_info the_info; + GetAppInfo(&the_info); + BEntry the_file(&the_info.ref); + the_file.GetParent(&AppDirectory); + BPath the_path; + AppDirectory.GetPath(&the_path); + strncpy(AppDirPath, the_path.Path(), 1023); + AppDirPath[1023] = 0; + chdir(AppDirPath); + + // Set up "about" window bitmap + AboutBitmap = new BBitmap(AboutFrame, B_COLOR_8_BIT); + FILE *logofile = fopen("Frodo Logo", "rb"); + if (logofile != NULL) { + fread(AboutBitmap->Bits(), 384*100, 1, logofile); + fclose(logofile); + } + + // Load preferences + ThePrefs.Load(prefs_path); + + // Show preferences editor (sends MSG_STARTUP on close) + prefs_showing = true; + ThePrefs.ShowEditor(true, prefs_path); +} + + +/* + * Handle incoming messages + */ + +void Frodo::MessageReceived(BMessage *msg) +{ + switch (msg->what) { + case MSG_STARTUP: // Start the emulation + + // Preferences editor is not longer on screen + prefs_showing = false; + + // Create everything + TheC64 = new C64; + + // Load ROMs + load_rom_files(); + + // Run the 6510 + TheC64->Run(); + break; + + case MSG_PREFS: // Show preferences editor + if (TheC64 != NULL && !prefs_showing) { + TheC64->Pause(); + TheC64->TheDisplay->Pause(); + + Prefs *prefs = new Prefs(ThePrefs); + prefs_showing = true; + prefs->ShowEditor(false, prefs_path); // Sends MSG_PREFS_DONE on close + } + break; + + case MSG_PREFS_DONE: { // Preferences editor closed + Prefs *prefs; + msg->FindPointer("prefs", (void **)&prefs); + if (!msg->FindBool("canceled")) { + TheC64->NewPrefs(prefs); + ThePrefs = *prefs; + } + delete prefs; + prefs_showing = false; + + TheC64->TheDisplay->Resume(); + TheC64->Resume(); + break; + } + + case MSG_RESET: // Reset C64 + if (TheC64 != NULL) + TheC64->Reset(); + break; + + case MSG_NMI: // NMI + if (TheC64 != NULL) + TheC64->NMI(); + break; + + case MSG_SAM: // Invoke SAM + if (TheC64 != NULL && !prefs_showing && FromShell) { + TheC64->Pause(); + TheC64->TheDisplay->Pause(); + SAM(TheC64); + TheC64->TheDisplay->Resume(); + TheC64->Resume(); + } + break; + + case MSG_NEXTDISK: // Insert next disk in drive 8 + if (TheC64 != NULL && !prefs_showing && strlen(ThePrefs.DrivePath[0]) > 4) { + char str[256]; + strcpy(str, ThePrefs.DrivePath[0]); + char *p = str + strlen(str) - 5; + + // If path matches "*.?64", increment character before the '.' + if (p[1] == '.' && p[3] == '6' && p[4] == '4') { + p[0]++; + + // If no such file exists, set character before the '.' to '1', 'a' or 'A' + FILE *file; + if ((file = fopen(str, "rb")) == NULL) { + if (isdigit(p[0])) + p[0] = '1'; + else if (isupper(p[0])) + p[0] = 'A'; + else + p[0] = 'a'; + } else + fclose(file); + + // Set new prefs + TheC64->Pause(); + Prefs *prefs = new Prefs(ThePrefs); + strcpy(prefs->DrivePath[0], str); + TheC64->NewPrefs(prefs); + ThePrefs = *prefs; + delete prefs; + TheC64->Resume(); + } + } + break; + + case MSG_TOGGLE_1541: // Toggle processor-level 1541 emulation + if (TheC64 != NULL && !prefs_showing) { + TheC64->Pause(); + Prefs *prefs = new Prefs(ThePrefs); + prefs->Emul1541Proc = !prefs->Emul1541Proc; + TheC64->NewPrefs(prefs); + ThePrefs = *prefs; + delete prefs; + TheC64->Resume(); + } + break; + + case MSG_OPEN_SNAPSHOT: + if (TheC64 != NULL && !prefs_showing) + open_panel->Show(); + break; + + case MSG_SAVE_SNAPSHOT: + if (TheC64 != NULL && !prefs_showing) + save_panel->Show(); + break; + + case MSG_OPEN_SNAPSHOT_RETURNED: + if (TheC64 != NULL && !prefs_showing) { + entry_ref the_ref; + BEntry the_entry; + if (msg->FindRef("refs", &the_ref) == B_NO_ERROR) + if (the_entry.SetTo(&the_ref) == B_NO_ERROR) + if (the_entry.IsFile()) { + char path[1024]; + BPath the_path; + the_entry.GetPath(&the_path); + strncpy(path, the_path.Path(), 1023); + path[1023] = 0; + TheC64->Pause(); + TheC64->LoadSnapshot(path); + TheC64->Resume(); + } + } + break; + + case MSG_SAVE_SNAPSHOT_RETURNED: + if (TheC64 != NULL && !prefs_showing) { + entry_ref the_ref; + BEntry the_entry; + if (msg->FindRef("directory", &the_ref) == B_NO_ERROR) + if (the_entry.SetTo(&the_ref) == B_NO_ERROR) { + char path[1024]; + BPath the_path; + the_entry.GetPath(&the_path); + strncpy(path, the_path.Path(), 1023); + strncat(path, "/", 1023); + strncat(path, msg->FindString("name"), 1023); + path[1023] = 0; + TheC64->Pause(); + TheC64->SaveSnapshot(path); + TheC64->Resume(); + } + } + break; + + default: + BApplication::MessageReceived(msg); + } +} + + +/* + * Quit requested (either by menu or by closing the C64 display window) + */ + +bool Frodo::QuitRequested(void) +{ + // Stop emulation + if (TheC64) { + TheC64->Quit(); + delete TheC64; + } + + delete AboutBitmap; + delete open_panel; + delete save_panel; + + return BApplication::QuitRequested(); +} + + +/* + * Display "about" window + */ + +class AboutView : public BView { +public: + AboutView() : BView(AboutFrame, "", B_FOLLOW_NONE, B_WILL_DRAW) {} + + virtual void AttachedToWindow(void) + { + SetHighColor(0, 0, 0); + } + + virtual void Draw(BRect update) + { + DrawBitmap(AboutBitmap, update, update); + + SetFont(be_bold_font); + SetDrawingMode(B_OP_OVER); + MovePenTo(204, 20); + DrawString(VERSION_STRING); + + SetFont(be_plain_font); + MovePenTo(204, 40); + DrawString("by Christian Bauer"); + MovePenTo(204, 52); + DrawString(""); + MovePenTo(204, 75); + DrawString(B_UTF8_COPYRIGHT " Copyright 1994-1997,2002-2005"); + MovePenTo(204, 87); + DrawString("Freely distributable."); + } + + virtual void MouseDown(BPoint point) + { + Window()->PostMessage(B_QUIT_REQUESTED); + } +}; + +class AboutWindow : public BWindow { +public: + AboutWindow() : BWindow(AboutFrame, NULL, B_BORDERED_WINDOW, B_NOT_RESIZABLE | B_NOT_ZOOMABLE | B_WILL_ACCEPT_FIRST_CLICK) + { + Lock(); + MoveTo(100, 100); + AboutView *view = new AboutView; + AddChild(view); + view->MakeFocus(); + Unlock(); + Show(); + } +}; + +void Frodo::AboutRequested(void) +{ + new AboutWindow(); +} + + +/* + * Determine whether path name refers to a directory + */ + +bool IsDirectory(const char *path) +{ + struct stat st; + return stat(path.c_str(), &st) == 0 && S_ISDIR(st.st_mode); +} diff --git a/Src/main_WIN32.h b/Src/main_WIN32.h new file mode 100644 index 0000000..3dea9f2 --- /dev/null +++ b/Src/main_WIN32.h @@ -0,0 +1,126 @@ +/* + * main_WIN32.h - Main program, WIN32 specific stuff + * + * Frodo (C) 1994-1997,2002-2005 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +// The application. +Frodo *TheApp; + +// WinMain args. +HINSTANCE hInstance; +int nCmdShow; +HWND hwnd; + +int PASCAL WinMain(HINSTANCE hInstance_arg, HINSTANCE /* hPrevInstance */, LPSTR lpCmdLine, int nCmdShow_arg) +{ + hInstance = hInstance_arg; + nCmdShow = nCmdShow_arg; + TheApp = new Frodo(); + TheApp->ArgvReceived(__argc, __argv); + TheApp->ReadyToRun(); + delete TheApp; + DestroyWindow(hwnd); + return 0; +} + +/* + * Constructor: Initialize member variables + */ + +Frodo::Frodo() +{ + TheC64 = NULL; + prefs_path[0] = 0; +} + + +Frodo::~Frodo() +{ + delete TheC64; +} + + +/* + * Process command line arguments + */ + +void Frodo::ArgvReceived(int argc, char **argv) +{ + char *progname = argv[0]; + argc--, argv++; + if (argc >= 1 && argv[0][0] != '\0') { + + // XXX: This should be a function. + char cwd[256]; + GetCurrentDirectory(sizeof(cwd), cwd); + int cwd_len = strlen(cwd); + if (cwd_len > 0 && cwd[cwd_len - 1] != '\\') { + strcat(cwd, "\\"); + cwd_len++; + } + if (strnicmp(argv[0], cwd, cwd_len) == 0) + strncpy(prefs_path, argv[0] + cwd_len, 255); + else + strncpy(prefs_path, argv[0], 255); + int length = strlen(prefs_path); + if (length > 4 && strchr(prefs_path, '.') == NULL) + strcat(prefs_path, ".fpr"); + } +} + + +/* + * Arguments processed, run emulation + */ + +void Frodo::ReadyToRun(void) +{ + getcwd(AppDirPath, 256); + + // Load preferences + if (!prefs_path[0]) + strcpy(prefs_path, "Frodo.fpr"); + ThePrefs.Load(prefs_path); + + if (ThePrefs.PrefsAtStartup) { + if (!ThePrefs.ShowEditor(TRUE, prefs_path)) + return; + } + + // Create and start C64 + TheC64 = new C64; + load_rom_files(); + TheC64->Run(); +} + + +/* + * Run preferences editor + */ + +void Frodo::RunPrefsEditor(void) +{ + Prefs *prefs = new Prefs(ThePrefs); + if (prefs->ShowEditor(FALSE, prefs_path)) { + TheC64->NewPrefs(prefs); + ThePrefs = *prefs; + } + delete prefs; +} diff --git a/Src/main_wii.h b/Src/main_wii.h new file mode 100644 index 0000000..310dfe5 --- /dev/null +++ b/Src/main_wii.h @@ -0,0 +1,114 @@ +/* + * main_wii.i - Main program, Wii specific stuff + * + * Frodo (C) 1994-1997,2002 Christian Bauer + */ + +#include "Version.h" + +#include +#include +#include + +extern int init_graphics(void); + +/* + * Create application object and start it + */ + +extern "C" int main(int argc, char **argv) +{ + Frodo *the_app; + + timeval tv; + gettimeofday(&tv, NULL); + srand(tv.tv_usec); + + printf("%s by Christian Bauer\n", VERSION_STRING); + if (!init_graphics()) + { + fprintf(stderr, "Could not initialize graphics\n"); + return 0; + } + fflush(stdout); + fatInitDefault(); + + // Init SDL + if (SDL_Init(SDL_INIT_VIDEO) < 0) { + fprintf(stderr, "Couldn't initialize SDL (%s)\n", SDL_GetError()); + return 0; + } + if (TTF_Init() < 0) + { + fprintf(stderr, "Unable to init TTF: %s\n", TTF_GetError() ); + return 0; + } + + if (WPAD_Init() != WPAD_ERR_NONE) + { + fprintf(stderr, "Failed initializing controllers\n"); + return 0; + } + + the_app = new Frodo(); + the_app->ArgvReceived(argc, argv); + the_app->ReadyToRun(); + delete the_app; + + return 0; +} + + +/* + * Constructor: Initialize member variables + */ + +Frodo::Frodo() +{ + TheC64 = NULL; +} + + +/* + * Process command line arguments + */ + +void Frodo::ArgvReceived(int argc, char **argv) +{ +} + + +/* + * Arguments processed, run emulation + */ + +void Frodo::ReadyToRun(void) +{ + getcwd(AppDirPath, 256); + + ThePrefs.Load((char*)PREFS_PATH); + + // Create and start C64 + TheC64 = new C64; + load_rom_files(); + TheC64->Run(); + delete TheC64; +} + + +Prefs *Frodo::reload_prefs(void) +{ + static Prefs newprefs; + newprefs.Load((char*)PREFS_PATH); + return &newprefs; +} + +/* + * Determine whether path name refers to a directory + */ + +bool IsDirectory(const char *path) +{ + struct stat st; + return stat(path, &st) == 0 && S_ISDIR(st.st_mode); +} diff --git a/Src/main_x.h b/Src/main_x.h new file mode 100644 index 0000000..9ee8d3a --- /dev/null +++ b/Src/main_x.h @@ -0,0 +1,160 @@ +/* + * main_x.h - Main program, Unix specific stuff + * + * Frodo (C) 1994-1997,2002-2005 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Version.h" + +#ifdef HAVE_GLADE +#include +#endif + +// Qtopia Windowing System +#ifdef QTOPIA +extern "C" int main(int argc, char *argv[]); +#include +#endif +#if defined(HAVE_SDL) +#include +#endif + +extern int init_graphics(void); + + +// Global variables +char Frodo::prefs_path[256] = ""; + + +/* + * Create application object and start it + */ + +int main(int argc, char **argv) +{ +#ifdef HAVE_GLADE + gnome_program_init(PACKAGE_NAME, PACKAGE_VERSION, LIBGNOMEUI_MODULE, argc, argv, + GNOME_PARAM_APP_DATADIR, DATADIR, NULL); +#endif + + timeval tv; + gettimeofday(&tv, NULL); + srand(tv.tv_usec); + +#ifndef HAVE_GLADE + printf( + "%s Copyright (C) 1994-1997,2002-2005 Christian Bauer\n" + "This is free software with ABSOLUTELY NO WARRANTY.\n" + , VERSION_STRING + ); +#endif +#if defined(HAVE_SDL) + // Init SDL + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER) < 0) { + fprintf(stderr, "Couldn't initialize SDL (%s)\n", SDL_GetError()); + return 1; + } + if (TTF_Init() < 0) + { + fprintf(stderr, "Unable to init TTF: %s\n", TTF_GetError() ); + return 1; + } +#endif + if (!init_graphics()) + return 1; + fflush(stdout); + + Frodo *the_app = new Frodo(); + the_app->ArgvReceived(argc, argv); + the_app->ReadyToRun(); + delete the_app; + + return 0; +} + + +/* + * Constructor: Initialize member variables + */ + +Frodo::Frodo() +{ + TheC64 = NULL; +} + + +/* + * Process command line arguments + */ + +void Frodo::ArgvReceived(int argc, char **argv) +{ + if (argc == 2) + strncpy(prefs_path, argv[1], 255); +} + + +/* + * Arguments processed, run emulation + */ + +void Frodo::ReadyToRun(void) +{ + getcwd(AppDirPath, 256); + + // Load preferences + if (!prefs_path[0]) { + char *home = getenv("HOME"); + if (home != NULL && strlen(home) < 240) { + strncpy(prefs_path, home, 200); + strcat(prefs_path, "/"); + } + strcat(prefs_path, ".frodorc"); + } + ThePrefs.Load(prefs_path); + + // Show preferences editor +#ifdef HAVE_GLADE + if (!ThePrefs.ShowEditor(true, prefs_path)) + return; +#endif + + // Create and start C64 + TheC64 = new C64; + load_rom_files(); + TheC64->Run(); + delete TheC64; +} + + +Prefs *Frodo::reload_prefs(void) +{ + static Prefs newprefs; + newprefs.Load(prefs_path); + return &newprefs; +} + + +/* + * Determine whether path name refers to a directory + */ + +bool IsDirectory(const char *path) +{ + struct stat st; + return stat(path, &st) == 0 && S_ISDIR(st.st_mode); +} diff --git a/Src/menu.cpp b/Src/menu.cpp new file mode 100644 index 0000000..abc5041 --- /dev/null +++ b/Src/menu.cpp @@ -0,0 +1,469 @@ +/********************************************************************* + * + * Copyright (C) 2004,2008, Simon Kagstrom + * + * Filename: menu.c + * Author: Simon Kagstrom + * Description: Code for menus (originally for Mophun) + * + * $Id$ + * + ********************************************************************/ +#include +#include + +#if defined(GEKKO) +# include +#endif + +#include "menu.h" + +#define KEY_UP 1 +#define KEY_DOWN 2 +#define KEY_LEFT 4 +#define KEY_RIGHT 8 +#define KEY_SELECT 16 +#define KEY_ESCAPE 32 +#define KEY_PAGEDOWN 64 +#define KEY_PAGEUP 128 + +#define IS_SUBMENU(p_msg) ( (p_msg)[0] == '^' ) + +static submenu_t *find_submenu(menu_t *p_menu, int index) +{ + int i; + + for (i=0; in_submenus; i++) + { + if (p_menu->p_submenus[i].index == index) + return &p_menu->p_submenus[i]; + } + + return NULL; +} + +static void print_font(SDL_Surface *screen, TTF_Font *font, int r, int g, int b, + int x, int y, const char *msg) +{ + SDL_Surface *font_surf; + SDL_Rect dst = {x, y, 0, 0}; + SDL_Color color = {r, g, b}; + char buf[255]; + unsigned int i; + + memset(buf, 0, sizeof(buf)); + strncpy(buf, msg, 254); + + /* Fixup multi-menu option look */ + for (i = 0; i < strlen(buf) ; i++) + { + if (buf[i] == '^' || buf[i] == '|') + buf[i] = ' '; + } + + font_surf = TTF_RenderText_Blended(font, buf, + color); + if (!font_surf) + { + fprintf(stderr, "%s\n", TTF_GetError()); + exit(1); + } + + SDL_BlitSurface(font_surf, NULL, screen, &dst); + + SDL_FreeSurface(font_surf); +} + + +static void menu_draw(SDL_Surface *screen, menu_t *p_menu) +{ + int x_start = p_menu->x1 + (p_menu->x2 - p_menu->x1) / 2 - p_menu->text_w / 2; + int y_start = p_menu->y1 + (p_menu->y2 - p_menu->y1) / 2 - p_menu->text_h / 2; + int font_height = TTF_FontHeight(p_menu->p_font); + int line_height = (font_height + font_height / 4); + int entries_visible = p_menu->y2 / line_height; + int i; + + if ( p_menu->n_entries * line_height > p_menu->y2 ) + y_start = p_menu->y1; + if ( p_menu->cur_sel - p_menu->start_entry_visible > entries_visible ) + p_menu->start_entry_visible += p_menu->cur_sel - entries_visible; + else if ( p_menu->cur_sel < p_menu->start_entry_visible ) + p_menu->start_entry_visible = p_menu->cur_sel; + + for (i = p_menu->start_entry_visible; i < p_menu->n_entries; i++) + { + const char *msg = p_menu->pp_msgs[i]; + int y = (i - p_menu->start_entry_visible) * line_height; + + if (p_menu->cur_sel == i) /* Selected - color */ + print_font(screen, p_menu->p_font, 255,255,0, x_start, + y_start + y, msg); + else /* Otherwise white */ + print_font(screen, p_menu->p_font, 255,255,255, x_start, + y_start + y, msg); + if (IS_SUBMENU(msg)) + { + submenu_t *p_submenu = find_submenu(p_menu, i); + int n_pipe = 0; + int n; + + for (n=0; msg[n] != '\0'; n++) + { + /* Underline the selected entry */ + if (msg[n] == '|') + { + int16_t n_chars; + + for (n_chars = 1; msg[n+n_chars] && msg[n+n_chars] != '|'; n_chars++); + + n_pipe++; + if (p_submenu->sel == n_pipe-1) + { + SDL_Rect r; + int w; + int h; + + if (TTF_SizeText(p_menu->p_font, "X", &w, &h) < 0) + { + fprintf(stderr, "%s\n", TTF_GetError()); + exit(1); + } + + r = (SDL_Rect) { x_start + (n+1) * w-1, + y_start + (i+1 - p_menu->start_entry_visible) * ((h + h/4)-1), + (n_chars - 1) * w, + 2 }; + SDL_FillRect(screen, &r, SDL_MapRGB(screen->format, 0, 255, 0)); + break; + } + } + } + } + } +} + +static int get_next_seq_y(menu_t *p_menu, int v, int dy) +{ + if (v + dy < 0) + return p_menu->n_entries - 1; + if (v + dy > p_menu->n_entries - 1) + return 0; + return v + dy; +} + +static void select_next(menu_t *p_menu, int dx, int dy) +{ + int next; + + p_menu->cur_sel = get_next_seq_y(p_menu, p_menu->cur_sel, dy); + next = get_next_seq_y(p_menu, p_menu->cur_sel, dy + 1); + + if (p_menu->pp_msgs[p_menu->cur_sel][0] == ' ' || + IS_SUBMENU(p_menu->pp_msgs[p_menu->cur_sel]) ) + select_next(p_menu, dx, dy); + /* If the next is a submenu */ + if (dx != 0 && + IS_SUBMENU(p_menu->pp_msgs[next])) + { + submenu_t *p_submenu = find_submenu(p_menu, next); + + p_submenu->sel = (p_submenu->sel + dx) < 0 ? p_submenu->n_entries - 1 : + (p_submenu->sel + dx) % p_submenu->n_entries; + } +} + +static int is_submenu_title(menu_t *p_menu, int n) +{ + if (n+1 >= p_menu->n_entries) + return 0; + else + return IS_SUBMENU(p_menu->pp_msgs[n+1]); +} + + +void menu_init(menu_t *p_menu, TTF_Font *p_font, const char **pp_msgs, + int16_t x1, int16_t y1, int16_t x2, int16_t y2) +{ + int submenu; + int i; + int j; + + memset(p_menu, 0, sizeof(menu_t)); + + p_menu->pp_msgs = pp_msgs; + p_menu->p_font = p_font; + p_menu->x1 = x1; + p_menu->y1 = y1; + p_menu->x2 = x2; + p_menu->y2 = y2; + + p_menu->text_w = 0; + p_menu->n_submenus = 0; + + for (p_menu->n_entries = 0; p_menu->pp_msgs[p_menu->n_entries]; p_menu->n_entries++) + { + int text_w_font; + + /* Is this a submenu? */ + if (IS_SUBMENU(p_menu->pp_msgs[p_menu->n_entries])) + { + p_menu->n_submenus++; + continue; /* Length of submenus is unimportant */ + } + + if (TTF_SizeText(p_font, p_menu->pp_msgs[p_menu->n_entries], &text_w_font, NULL) != 0) + { + fprintf(stderr, "%s\n", TTF_GetError()); + exit(1); + } + if (text_w_font > p_menu->text_w) + p_menu->text_w = text_w_font; + } + if (p_menu->text_w > p_menu->x2 - p_menu->x1) + p_menu->text_w = p_menu->x2 - p_menu->x1; + + if ( !(p_menu->p_submenus = (submenu_t *)malloc(sizeof(submenu_t) * p_menu->n_submenus)) ) + { + perror("malloc failed!\n"); + exit(1); + } + + j=0; + submenu = 0; + for (; j < p_menu->n_entries; j++) + { + if (IS_SUBMENU(p_menu->pp_msgs[j])) + { + int n; + + p_menu->p_submenus[submenu].index = j; + p_menu->p_submenus[submenu].sel = 0; + p_menu->p_submenus[submenu].n_entries = 0; + for (n=0; p_menu->pp_msgs[j][n] != '\0'; n++) + { + if (p_menu->pp_msgs[j][n] == '|') + p_menu->p_submenus[submenu].n_entries++; + } + submenu++; + } + } + p_menu->text_h = p_menu->n_entries * (TTF_FontHeight(p_font) + TTF_FontHeight(p_font) / 4); +} + +void menu_fini(menu_t *p_menu) +{ + free(p_menu->p_submenus); +} + + +static uint32_t wait_key_press(void) +{ + SDL_Event ev; + uint32_t keys = 0; + + while (1) + { +#if defined(GEKKO) + Uint32 remote_keys, classic_keys; + WPADData *wpad, *wpad_other; + + WPAD_ScanPads(); + + wpad = WPAD_Data(WPAD_CHAN_0); + wpad_other = WPAD_Data(WPAD_CHAN_1); + remote_keys = wpad->btns_d | wpad_other->btns_d; + classic_keys = 0; + + /* Check classic controllers as well */ + if (wpad->exp.type == WPAD_EXP_CLASSIC || + wpad_other->exp.type == WPAD_EXP_CLASSIC) + { + static bool classic_keys_changed; + static Uint32 classic_last; + + classic_keys = wpad->exp.classic.btns | wpad_other->exp.classic.btns; + + classic_keys_changed = classic_keys != classic_last; + classic_last = classic_keys; + + /* No repeat, thank you */ + if (!classic_keys_changed) + classic_keys = 0; + } + + if ( (remote_keys & WPAD_BUTTON_DOWN) || (classic_keys & CLASSIC_CTRL_BUTTON_RIGHT) ) + keys |= KEY_RIGHT; + if ( (remote_keys & WPAD_BUTTON_UP) || (classic_keys & CLASSIC_CTRL_BUTTON_LEFT) ) + keys |= KEY_LEFT; + if ( (remote_keys & WPAD_BUTTON_LEFT) || (classic_keys & CLASSIC_CTRL_BUTTON_DOWN) ) + keys |= KEY_DOWN; + if ( (remote_keys & WPAD_BUTTON_RIGHT) || (classic_keys & CLASSIC_CTRL_BUTTON_UP) ) + keys |= KEY_UP; + if ( (remote_keys & WPAD_BUTTON_PLUS) || (classic_keys & CLASSIC_CTRL_BUTTON_PLUS) ) + keys |= KEY_PAGEUP; + if ( (remote_keys & WPAD_BUTTON_MINUS) || (classic_keys & CLASSIC_CTRL_BUTTON_MINUS) ) + keys |= KEY_PAGEDOWN; + if ( (remote_keys & (WPAD_BUTTON_A | WPAD_BUTTON_2) ) || + (classic_keys & (CLASSIC_CTRL_BUTTON_A | CLASSIC_CTRL_BUTTON_X)) ) + keys |= KEY_SELECT; + if ( (remote_keys & (WPAD_BUTTON_1 | WPAD_BUTTON_HOME) ) || + (classic_keys & (CLASSIC_CTRL_BUTTON_B | CLASSIC_CTRL_BUTTON_Y)) ) + keys |= KEY_ESCAPE; +#endif + if (SDL_PollEvent(&ev)) + { + switch(ev.type) + { + case SDL_KEYDOWN: + switch (ev.key.keysym.sym) + { + case SDLK_UP: + keys |= KEY_UP; + break; + case SDLK_DOWN: + keys |= KEY_DOWN; + break; + case SDLK_LEFT: + keys |= KEY_LEFT; + break; + case SDLK_RIGHT: + keys |= KEY_RIGHT; + break; + case SDLK_PAGEDOWN: + keys |= KEY_PAGEDOWN; + break; + case SDLK_PAGEUP: + keys |= KEY_PAGEUP; + break; + case SDLK_RETURN: + case SDLK_SPACE: + keys |= KEY_SELECT; + break; + case SDLK_ESCAPE: + keys |= KEY_ESCAPE; + break; + default: + break; + } + break; + case SDL_QUIT: + exit(0); + break; + default: + break; + } + break; + } + if (keys != 0) + return keys; + SDL_Delay(100); + } + + return keys; +} + + +int menu_select(SDL_Surface *screen, menu_t *p_menu, + int *p_submenus) +{ + int ret = -1; + + for (int i = 0; i < p_menu->n_submenus; i++) + p_menu->p_submenus[i].sel = p_submenus[i]; + + while(1) + { + uint32_t keys; + + SDL_FillRect(screen, 0, SDL_MapRGB(screen->format, 0x00, 0x80, 0x80)); + + menu_draw(screen, p_menu); + SDL_Flip(screen); + + keys = wait_key_press(); + + if (keys & KEY_UP) + select_next(p_menu, 0, -1); + else if (keys & KEY_DOWN) + select_next(p_menu, 0, 1); + else if (keys & KEY_PAGEUP) + select_next(p_menu, 0, -6); + else if (keys & KEY_PAGEDOWN) + select_next(p_menu, 0, 6); + else if (keys & KEY_LEFT) + select_next(p_menu, -1, 0); + else if (keys & KEY_RIGHT) + select_next(p_menu, 1, 0); + else if (keys & KEY_ESCAPE) + break; + else if (keys & KEY_SELECT) + { + ret = p_menu->cur_sel; + int i; + + for (i=0; in_submenus; i++) + p_submenus[i] = p_menu->p_submenus[i].sel; + break; + } + } + + SDL_FillRect(screen, 0, SDL_MapRGB(screen->format, 0, 0, 0)); + return ret; +} + +#if defined(TEST_MENU) +char *main_menu[] = { + "Insert disc", + "Reset C64", + "Joystick port", + "^|1|2", + " ", + "Quit", + NULL, +}; + +int main(int argc, char *argv[]) +{ + SDL_Surface *screen; + TTF_Font *font; + const SDL_VideoInfo *info; + int submenus[1] = {0}; + int selected; + menu_t menu; + + if (SDL_Init( SDL_INIT_EVERYTHING ) < 0) + { + fprintf(stderr, "Unable to init SDL: %s\n", SDL_GetError() ); + exit(1); + } + if (TTF_Init() < 0) + { + fprintf(stderr, "Unable to init TTF: %s\n", TTF_GetError() ); + exit(1); + } + + atexit(SDL_Quit); + atexit(TTF_Quit); + font = TTF_OpenFont("FreeMono.ttf", 20); + if (!font) + { + fprintf(stderr, "Unable to open font: %s\n", TTF_GetError() ); + exit(1); + } + + info = SDL_GetVideoInfo(); + /* Open a 640x480 display with the optimal color depth */ + screen = SDL_SetVideoMode(640, 480, info->vfmt->BitsPerPixel, + info->hw_available ? SDL_HWSURFACE : SDL_SWSURFACE); + menu_init(&menu, font, main_menu, 640 / 3, 480 / 3, 400, 400); + + selected = menu_select(screen, &menu, ~0, submenus); + printf("Selected: %d:%d\n", + selected, submenus[0]); + + menu_fini(&menu); + + return 0; +} +#endif diff --git a/Src/menu.h b/Src/menu.h new file mode 100644 index 0000000..1a335c6 --- /dev/null +++ b/Src/menu.h @@ -0,0 +1,59 @@ +/********************************************************************* + * + * Copyright (C) 2004, 2008, Simon Kagstrom + * + * Filename: menu.h + * Author: Simon Kagstrom + * Description: + * + * $Id$ + * + ********************************************************************/ +#ifndef __MENU_H__ +#define __MENU_H__ + +#include +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif /* __cplusplus */ + +typedef struct +{ + int n_entries; + int index; + int sel; +} submenu_t; + + +typedef struct +{ + const char **pp_msgs; + TTF_Font *p_font; + int x1,y1; + int x2,y2; + int text_w; + int text_h; + + int n_submenus; + submenu_t *p_submenus; + + int cur_sel; /* Main selection */ + int start_entry_visible; + int n_entries; +} menu_t; + +void menu_init(menu_t *p_menu, TTF_Font *p_font, const char **pp_msgs, + int16_t x1, int16_t y1, int16_t x2, int16_t y2); +void menu_fini(menu_t *p_menu); + +int menu_select(SDL_Surface *screen, menu_t *p_menu, + int *p_submenus); + +#if defined(__cplusplus) +}; +#endif /* __cplusplus */ + +#endif /* !__MENU_H__ */ diff --git a/Src/mkinstalldirs b/Src/mkinstalldirs new file mode 100755 index 0000000..8ab885e --- /dev/null +++ b/Src/mkinstalldirs @@ -0,0 +1,99 @@ +#! /bin/sh +# mkinstalldirs --- make directory hierarchy +# Author: Noah Friedman +# Created: 1993-05-16 +# Public domain + +errstatus=0 +dirmode="" + +usage="\ +Usage: mkinstalldirs [-h] [--help] [-m mode] dir ..." + +# process command line arguments +while test $# -gt 0 ; do + case "${1}" in + -h | --help | --h* ) # -h for help + echo "${usage}" 1>&2; exit 0 ;; + -m ) # -m PERM arg + shift + test $# -eq 0 && { echo "${usage}" 1>&2; exit 1; } + dirmode="${1}" + shift ;; + -- ) shift; break ;; # stop option processing + -* ) echo "${usage}" 1>&2; exit 1 ;; # unknown option + * ) break ;; # first non-opt arg + esac +done + +for file +do + if test -d "$file"; then + shift + else + break + fi +done + +case $# in +0) exit 0 ;; +esac + +case $dirmode in +'') + if mkdir -p -- . 2>/dev/null; then + echo "mkdir -p -- $*" + exec mkdir -p -- "$@" + fi ;; +*) + if mkdir -m "$dirmode" -p -- . 2>/dev/null; then + echo "mkdir -m $dirmode -p -- $*" + exec mkdir -m "$dirmode" -p -- "$@" + fi ;; +esac + +for file +do + set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'` + shift + + pathcomp= + for d + do + pathcomp="$pathcomp$d" + case "$pathcomp" in + -* ) pathcomp=./$pathcomp ;; + esac + + if test ! -d "$pathcomp"; then + echo "mkdir $pathcomp" + + mkdir "$pathcomp" || lasterr=$? + + if test ! -d "$pathcomp"; then + errstatus=$lasterr + else + if test ! -z "$dirmode"; then + echo "chmod $dirmode $pathcomp" + + lasterr="" + chmod "$dirmode" "$pathcomp" || lasterr=$? + + if test ! -z "$lasterr"; then + errstatus=$lasterr + fi + fi + fi + fi + + pathcomp="$pathcomp/" + done +done + +exit $errstatus + +# Local Variables: +# mode: shell-script +# sh-indentation: 3 +# End: +# mkinstalldirs ends here diff --git a/Src/ndir.c b/Src/ndir.c new file mode 100644 index 0000000..7d7cfc5 --- /dev/null +++ b/Src/ndir.c @@ -0,0 +1,222 @@ +/* msd_dir.c - portable directory routines + Copyright (C) 1990 by Thorsten Ohl, td12@ddagsi3.bitnet + + 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 1, 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Header: /home/cvs/cebix/Frodo4/Src/ndir.c,v 1.1 2003/07/01 17:09:43 cebix Exp $ + */ + +/* Everything non trivial in this code is from: @(#)msd_dir.c 1.4 + 87/11/06. A public domain implementation of BSD directory routines + for MS-DOS. Written by Michael Rendell ({uunet,utai}michael@garfield), + August 1987 */ + + +#include +#include +#include +#include +#include +#include + +#include + +#include + +static void free_dircontents (struct _dircontents *); + +/* find ALL files! */ +#define ATTRIBUTES (_A_RDONLY | _A_HIDDEN | _A_SYSTEM | _A_SUBDIR) + + + +DIR * +opendir (const char *name) +{ + struct _finddata_t find_buf; + DIR *dirp; + struct _dircontents *dp; + char name_buf[_MAX_PATH + 1]; + char *slash = ""; + long hFile; + + if (!name) + name = ""; + else if (*name) + { + const char *s; + int l = strlen (name); + + s = name + l - 1; + if ( !(l == 2 && *s == ':') && *s != '\\' && *s != '/') + slash = "/"; /* save to insert slash between path and "*.*" */ + } + + strcat (strcat (strcpy (name_buf, name), slash), "*.*"); + + dirp = (DIR *) malloc (sizeof (DIR)); + if (dirp == (DIR *)0) + return (DIR *)0; + + dirp->dd_loc = 0; + dirp->dd_contents = dirp->dd_cp = (struct _dircontents *) 0; + + if ((hFile = _findfirst (name_buf, &find_buf)) < 0) + { + free (dirp); + return (DIR *)0; + } + + do + { + dp = (struct _dircontents *) malloc (sizeof (struct _dircontents)); + if (dp == (struct _dircontents *)0) + { + free_dircontents (dirp->dd_contents); + return (DIR *)0; + } + + dp->_d_entry = malloc (strlen (find_buf.name) + 1); + if (dp->_d_entry == (char *)0) + { + free (dp); + free_dircontents (dirp->dd_contents); + return (DIR *)0; + } + + if (dirp->dd_contents) + dirp->dd_cp = dirp->dd_cp->_d_next = dp; + else + dirp->dd_contents = dirp->dd_cp = dp; + + strcpy (dp->_d_entry, find_buf.name); + + dp->_d_next = (struct _dircontents *)0; + + } while (!_findnext (hFile, &find_buf)); + + dirp->dd_cp = dirp->dd_contents; + + _findclose(hFile); + + return dirp; +} + + +void +closedir (DIR *dirp) +{ + free_dircontents (dirp->dd_contents); + free ((char *) dirp); +} + + +struct direct * +readdir (DIR *dirp) +{ + static struct direct dp; + + if (dirp->dd_cp == (struct _dircontents *)0) + return (struct direct *)0; + dp.d_namlen = dp.d_reclen = + strlen (strcpy (dp.d_name, dirp->dd_cp->_d_entry)); +#if 0 /* JB */ + strlwr (dp.d_name); /* JF */ +#endif + dp.d_ino = 0; + dirp->dd_cp = dirp->dd_cp->_d_next; + dirp->dd_loc++; + + return &dp; +} + + +void +seekdir (DIR *dirp, long off) +{ + long i = off; + struct _dircontents *dp; + + if (off < 0) + return; + for (dp = dirp->dd_contents; --i >= 0 && dp; dp = dp->_d_next) + ; + dirp->dd_loc = off - (i + 1); + dirp->dd_cp = dp; +} + + +long +telldir (DIR *dirp) +{ + return dirp->dd_loc; +} + + +/* Garbage collection */ + +static void +free_dircontents (struct _dircontents *dp) +{ + struct _dircontents *odp; + + while (dp) + { + if (dp->_d_entry) + free (dp->_d_entry); + dp = (odp = dp)->_d_next; + free (odp); + } +} + + +#ifdef TEST + +void main (int argc, char *argv[]); + +void +main (int argc, char *argv[]) +{ + static DIR *directory; + struct direct *entry = (struct direct *)0; + + char *name = ""; + + if (argc > 1) + name = argv[1]; + + directory = opendir (name); + + if (!directory) + { + fprintf (stderr, "can't open directory `%s'.\n", name); + exit (2); + } + + while (entry = readdir (directory)) + printf ("> %s\n", entry->d_name); + + printf ("done.\n"); +} + +#endif /* TEST */ + +/* + * Local Variables: + * mode:C + * ChangeLog:ChangeLog + * compile-command:make + * End: + */ diff --git a/Src/ndir.h b/Src/ndir.h new file mode 100644 index 0000000..3181fab --- /dev/null +++ b/Src/ndir.h @@ -0,0 +1,69 @@ +/* ndir.c - portable directory routines + Copyright (C) 1990 by Thorsten Ohl, td12@ddagsi3.bitnet + + 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 1, 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Header: /home/cvs/cebix/Frodo4/Src/ndir.h,v 1.1 2003/07/01 17:09:43 cebix Exp $ + */ + +/* Everything non trivial in this code is taken from: @(#)msd_dir.c 1.4 + 87/11/06. A public domain implementation of BSD directory routines + for MS-DOS. Written by Michael Rendell ({uunet,utai}michael@garfield), + August 1987 */ + +#include /* ino_t definition */ + +#define rewinddir(dirp) seekdir(dirp, 0L) + +/* 255 is said to be big enough for Windows NT. The more elegant + solution would be declaring d_name as one byte long and allocating + it to the actual size needed. */ +#define MAXNAMLEN 255 + +struct direct +{ + ino_t d_ino; /* a bit of a farce */ + int d_reclen; /* more farce */ + int d_namlen; /* length of d_name */ + char d_name[MAXNAMLEN + 1]; /* garentee null termination */ +}; + +struct _dircontents +{ + char *_d_entry; + struct _dircontents *_d_next; +}; + +typedef struct _dirdesc +{ + int dd_id; /* uniquely identify each open directory */ + long dd_loc; /* where we are in directory entry is this */ + struct _dircontents *dd_contents; /* pointer to contents of dir */ + struct _dircontents *dd_cp; /* pointer to current position */ +} DIR; + +extern void seekdir (DIR *, long); +extern long telldir (DIR *); +extern DIR *opendir (const char *); +extern void closedir (DIR *); +extern struct direct *readdir (DIR *); + +/* + * Local Variables: + * mode:C + * ChangeLog:ChangeLog + * compile-command:make + * End: + */ diff --git a/Src/resource.h b/Src/resource.h new file mode 100644 index 0000000..cc66374 --- /dev/null +++ b/Src/resource.h @@ -0,0 +1,92 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by Frodo.rc +// +#define FRODO_ICON 101 +#define IDR_MAIN_MENU 101 +#define IDD_PREFERENCES_STANDARD 102 +#define IDD_PREFERENCES_WIN32 103 +#define IDC_INVISIBLE 106 +#define IDC_NORMAL 1000 +#define IDC_SPRITES 1001 +#define IDC_SPRITECOLLISIONS 1002 +#define IDC_JOYSTICK1 1003 +#define IDC_JOYSTICK2 1004 +#define IDC_LIMITSPEED 1005 +#define IDC_SWAPJOYSTICKS 1006 +#define IDC_FASTRESET 1007 +#define IDC_CIAIRQHACK 1008 +#define IDC_1541EMULATION 1009 +#define IDC_MAPSLASH 1010 +#define IDC_SIDEMULATION 1011 +#define IDC_SIDFILTERS 1012 +#define IDC_BADLINES 1013 +#define IDC_NORMAL_SPIN 1014 +#define IDC_BADLINES_SPIN 1015 +#define IDC_CIA 1016 +#define IDC_CIA_SPIN 1017 +#define IDC_FLOPPY 1018 +#define IDC_FLOPPY_SPIN 1019 +#define IDC_DRAWEVERY 1020 +#define IDC_DRAWEVERY_SPIN 1021 +#define IDC_DEVICE8 1022 +#define IDC_DEVICE9 1023 +#define IDC_BROWSE8 1024 +#define IDC_BROWSE9 1025 +#define IDC_DEVICE10 1026 +#define IDC_BROWSE10 1027 +#define IDC_DEVICE11 1028 +#define IDC_BROWSE11 1029 +#define IDC_FULLSCREEN 1035 +#define IDC_AUTOPAUSE 1036 +#define IDC_SYSTEMMEMORY 1037 +#define IDC_EXCLUSIVESOUND 1038 +#define IDC_PREFSATSTARTUP 1039 +#define IDC_VIEWPORT 1040 +#define IDC_DISPLAYMODE 1041 +#define IDC_ALWAYSCOPY 1042 +#define IDC_HIDECURSOR 1043 +#define IDC_SYSTEMKEYS 1044 +#define IDC_LATENCYMIN 1045 +#define IDC_LATENCYMAX 1046 +#define IDC_LATENCYAVG 1047 +#define IDC_SCALINGNUMERATOR 1048 +#define IDC_LATENCYMIN_SPIN 1049 +#define IDC_LATENCYAVG_SPIN 1050 +#define IDC_REUSIZE 1050 +#define IDC_LATENCYMAX_SPIN 1051 +#define IDC_SCALINGDENOMINATOR 1052 +#define IDC_SCALINGNUMERATOR_SPIN 1053 +#define IDC_SCALINGDENOMINATOR_SPIN 1054 +#define IDC_DIRECTSOUND 1055 +#define IDC_SHOWLEDS 1056 +#define ID_FILE_EX 40001 +#define ID_TOOLS_RESETC64 40002 +#define ID_TOOLS_SAM 40003 +#define ID_TOOLS_INSERTNEXTDISK 40004 +#define ID_TOOLS_PREFERENCES 40005 +#define ID_HELP_ABOUT 40006 +#define ID_FILE_NEW 40007 +#define ID_FILE_OPEN 40009 +#define ID_FILE_SAVE 40011 +#define ID_FILE_SAVEAS 40012 +#define ID_HELP_CONTENTS 40013 +#define ID_HELP_KEYBOARD 40014 +#define ID_TOOLS_PAUSE 40015 +#define ID_HELP_SETTINGS 40016 +#define ID_TOOLS_FULLSCREEN 40017 +#define ID_TOOLS_RESETDIRECTDRAW 40018 +#define IDC_STATIC -1 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NO_MFC 1 +#define _APS_3D_CONTROLS 1 +#define _APS_NEXT_RESOURCE_VALUE 103 +#define _APS_NEXT_COMMAND_VALUE 40019 +#define _APS_NEXT_CONTROL_VALUE 1051 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/Src/sdlgui.cpp b/Src/sdlgui.cpp new file mode 100644 index 0000000..27d89b8 --- /dev/null +++ b/Src/sdlgui.cpp @@ -0,0 +1,1476 @@ +/* + * sdlgui.cpp + * + * This file is taken from the ARAnyM project which builds a new and powerful + * TOS/FreeMiNT compatible virtual machine running on almost any hardware. + * + * Copyright (c) 2001 Thomas Huth - taken from his hatari project + * Copyright (c) 2002-2005 Petr Stehlik of ARAnyM dev team (see AUTHORS) + * + * It 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 2 of the License, or + * (at your option) any later version. + * + * You should have received a copy of the GNU General Public License + * along with Frodo; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "sysdeps.h" +#include "sdlgui.h" + +#include + +#include "font8.h" + +static SDL_Surface *mainsurface = NULL; +#define sdlscrn mainsurface + +static SDL_Surface *fontgfx=NULL; +static int fontwidth, fontheight; /* Height and width of the actual font */ + +// Stores current dialog coordinates +static SDL_Rect DialogRect = {0, 0, 0, 0}; + +// Used by SDLGui_Get[First|Next]BackgroundRect() +static SDL_Rect BackgroundRect = {0, 0, 0, 0}; +static int BackgroundRectCounter; +enum +{ + SG_BCKGND_RECT_BEGIN, + SG_BCKGND_RECT_TOP, + SG_BCKGND_RECT_LEFT, + SG_BCKGND_RECT_RIGHT, + SG_BCKGND_RECT_BOTTOM, + SG_BCKGND_RECT_END +}; + +SDL_Color blackc[] = {{0, 0, 0, 0}}; +SDL_Color darkgreyc[] = {{128, 128, 128, 0}}; +SDL_Color greyc[] = {{192, 192, 192, 0}}; +SDL_Color whitec[] = {{255, 255, 255, 0}}; + +enum +{ + SG_FIRST_EDITFIELD, + SG_PREVIOUS_EDITFIELD, + SG_NEXT_EDITFIELD, + SG_LAST_EDITFIELD +}; + +/*-----------------------------------------------------------------------*/ +/* + Load an 1 plane XBM into a 8 planes SDL_Surface. +*/ +static SDL_Surface *SDLGui_LoadXBM(int w, int h, Uint8 *srcbits) +{ + SDL_Surface *bitmap; + Uint8 *dstbits; + int x, y, srcpitch; + + /* Allocate the bitmap */ + bitmap = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, 8, 0, 0, 0, 0); + if ( bitmap == NULL ) + { + // TODO panicbug("Couldn't allocate bitmap: %s", SDL_GetError()); + return(NULL); + } + + srcpitch = ((w + 7) / 8); + dstbits = (Uint8 *)bitmap->pixels; + int mask = 1; + + /* Copy the pixels */ + for (y = 0 ; y < h ; y++) + { + for (x = 0 ; x < w ; x++) + { + dstbits[x] = (srcbits[x / 8] & mask) ? 1 : 0; + mask <<= 1; + mask |= (mask >> 8); + mask &= 0xFF; + } + dstbits += bitmap->pitch; + srcbits += srcpitch; + } + + return(bitmap); +} + +/*-----------------------------------------------------------------------*/ +/* + Initialize the GUI. +*/ +bool SDLGui_Init(SDL_Surface *GUISurface) +{ + mainsurface = GUISurface; + /* Load the font graphics: */ + fontgfx = SDLGui_LoadXBM(font8_width, font8_height, font8_bits); + if (fontgfx == NULL) + { +// TODO panicbug("Could not create font data"); +// TODO panicbug("ARAnyM GUI will not be available"); + return false; + } + + /* Set font color 0 as transparent */ + SDL_SetColorKey(fontgfx, SDL_SRCCOLORKEY, 0); + + /* Get the font width and height: */ + fontwidth = fontgfx->w/16; + fontheight = fontgfx->h/16; + + return true; +} + + +/*-----------------------------------------------------------------------*/ +/* + Uninitialize the GUI. +*/ +int SDLGui_UnInit() +{ + if (fontgfx) + { + SDL_FreeSurface(fontgfx); + fontgfx = NULL; + } + + return 0; +} + + +/*-----------------------------------------------------------------------*/ +/* + Compute real coordinates for a given object. + Note: centers dialog on screen. +*/ +static void SDLGui_ObjCoord(SGOBJ *dlg, int objnum, SDL_Rect *rect) +{ + rect->x = dlg[objnum].x * fontwidth; + rect->y = dlg[objnum].y * fontheight; + rect->w = dlg[objnum].w * fontwidth; + rect->h = dlg[objnum].h * fontheight; + + rect->x += (sdlscrn->w - (dlg[0].w * fontwidth)) / 2; + rect->y += (sdlscrn->h - (dlg[0].h * fontheight)) / 2; +} + + +/*-----------------------------------------------------------------------*/ +/* + Compute real coordinates for a given object. + This one takes borders into account and give coordinates as seen by user +*/ +void SDLGui_ObjFullCoord(SGOBJ *dlg, int objnum, SDL_Rect *coord) +{ + SDLGui_ObjCoord(dlg, objnum, coord); + + switch (dlg[objnum].type) + { + case SGBOX: + case SGBUTTON: + { + // Take border into account + int border_size; + + if (dlg[objnum].flags & SG_SELECTABLE) + { + if (dlg[objnum].flags & SG_DEFAULT) + border_size = 4; + else + border_size = 3; + } + else + { + if (dlg[objnum].flags & SG_BACKGROUND) + border_size = 6; + else + border_size = 5; + } + + coord->x -= border_size; + coord->y -= border_size; + coord->w += (border_size * 2); + coord->h += (border_size * 2); + } + break; + case SGEDITFIELD: + // Allow one more pixel to the right for cursor + coord->w += 1; + // There is a line below + coord->h += 1; + break; + } +} + + +/*-----------------------------------------------------------------------*/ +/* + Refresh display at given coordinates. + Unlike SDL_UpdateRect() this function can eat coords that goes beyond screen + boundaries. + "rect" will be modified to represent the area actually refreshed. +*/ +void SDLGui_UpdateRect(SDL_Rect *rect) +{ + if (rect->x < 0) + { + rect->w += rect->x; + rect->x = 0; + } + if ((rect->x + rect->w) > sdlscrn->w) + rect->w = (sdlscrn->w - rect->x); + + if (rect->y < 0) + { + rect->h += rect->y; + rect->y = 0; + } + if ((rect->y + rect->h) > sdlscrn->h) + rect->h = (sdlscrn->h - rect->y); + + if ((rect->w > 0) && (rect->h > 0)) + SDL_UpdateRects(sdlscrn, 1, rect); + else + { + rect->x = 0; + rect->y = 0; + rect->w = 0; + rect->h = 0; + } +} + + +/*-----------------------------------------------------------------------*/ +/* + Maps an SDL_Color to the screen format. +*/ +Uint32 SDLGui_MapColor(SDL_Color *color) +{ + return SDL_MapRGB(sdlscrn->format, color->r, color->g, color->b); +} + + +/*-----------------------------------------------------------------------*/ +/* + Refresh display to reflect an object change. +*/ +void SDLGui_RefreshObj(SGOBJ *dlg, int objnum) +{ + SDL_Rect coord; + + SDLGui_ObjFullCoord(dlg, objnum, &coord); + + screenlock(); + SDLGui_UpdateRect(&coord); + screenunlock(); +} + + +/*-----------------------------------------------------------------------*/ +/* + Draw a text string. +*/ +void SDLGui_Text(int x, int y, const char *txt, SDL_Color *col) +{ + int i; + char c; + SDL_Rect sr, dr; + + SDL_SetColors(fontgfx, col, 1, 1); + + screenlock(); + for (i = 0 ; txt[i] != 0 ; i++) + { + c = txt[i]; + sr.x = fontwidth * (c % 16); + sr.y = fontheight * (c / 16); + sr.w = fontwidth; + sr.h = fontheight; + + dr.x = x + (fontwidth * i); + dr.y = y; + dr.w = fontwidth; + dr.h = fontheight; + + SDL_BlitSurface(fontgfx, &sr, sdlscrn, &dr); + } + screenunlock(); +} + + +/*-----------------------------------------------------------------------*/ +/* + Draw a dialog text object. +*/ +void SDLGui_DrawText(SGOBJ *tdlg, int objnum) +{ + SDL_Rect coord; + SDL_Color *textc, *backgroundc; + + if (tdlg[objnum].state & SG_SELECTED) + { + textc = whitec; + backgroundc = darkgreyc; + } + else if (tdlg[objnum].state & SG_DISABLED) + { + textc = darkgreyc; + backgroundc = greyc; + } + else + { + textc = blackc; + backgroundc = greyc; + } + + SDLGui_ObjCoord(tdlg, objnum, &coord); + SDL_FillRect(sdlscrn, &coord, SDLGui_MapColor(backgroundc)); + SDLGui_Text(coord.x, coord.y, tdlg[objnum].txt, textc); +} + + +/*-----------------------------------------------------------------------*/ +/* + Draw an edit field object. +*/ +void SDLGui_DrawEditField(SGOBJ *edlg, int objnum) +{ + SDL_Rect coord; + SDL_Color *textc; + + if (edlg[objnum].state & SG_DISABLED) + textc = darkgreyc; + else + textc = blackc; + + SDLGui_ObjCoord(edlg, objnum, &coord); + coord.w += 1; + SDL_FillRect(sdlscrn, &coord, SDLGui_MapColor(greyc)); + SDLGui_Text(coord.x, coord.y, edlg[objnum].txt, textc); + + // Draw a line below. + coord.y = coord.y + coord.h; + coord.h = 1; + SDL_FillRect(sdlscrn, &coord, SDLGui_MapColor(darkgreyc)); +} + + +/*-----------------------------------------------------------------------*/ +/* + Draw or erase cursor. +*/ +void SDLGui_DrawCursor(SGOBJ *dlg, cursor_state *cursor) +{ + if (cursor->object != -1) + { + SDL_Rect coord; + SDL_Color *cursorc; + + SDLGui_DrawEditField(dlg, cursor->object); + + if (cursor->blink_state) + cursorc = blackc; + else + cursorc = greyc; + + SDLGui_ObjCoord(dlg, cursor->object, &coord); + coord.x += (cursor->position * fontwidth); + coord.w = 1; + SDL_FillRect(sdlscrn, &coord, SDLGui_MapColor(cursorc)); + + SDLGui_RefreshObj(dlg, cursor->object); + } +} + + +/*-----------------------------------------------------------------------*/ +/* + Draw a 3D effect around a given rectangle. + Rectangle is updated to the full size of the new object. +*/ +void SDLGui_Draw3DAround(SDL_Rect *coord, SDL_Color *upleftc, SDL_Color *downrightc, SDL_Color *cornerc, int width) +{ + SDL_Rect rect; + int i; + Uint32 upleftcol = SDLGui_MapColor(upleftc); + Uint32 downrightcol = SDLGui_MapColor(downrightc); + Uint32 cornercol = SDLGui_MapColor(cornerc); + + screenlock(); + + for ( i = 1 ; i <= width ; i++) + { + rect.x = coord->x - i; + rect.y = coord->y - i; + rect.w = coord->w + (i * 2) - 1; + rect.h = 1; + SDL_FillRect(sdlscrn, &rect, upleftcol); + + rect.x = coord->x - i; + rect.y = coord->y - i; + rect.w = 1; + rect.h = coord->h + (i * 2) - 1; + SDL_FillRect(sdlscrn, &rect, upleftcol); + + rect.x = coord->x - i + 1; + rect.y = coord->y + coord->h - 1 + i; + rect.w = coord->w + (i * 2) - 1; + rect.h = 1; + SDL_FillRect(sdlscrn, &rect, downrightcol); + + rect.x = coord->x + coord->w - 1 + i; + rect.y = coord->y - i + 1; + rect.w = 1; + rect.h = coord->h + (i * 2) - 1; + SDL_FillRect(sdlscrn, &rect, downrightcol); + + rect.x = coord->x + coord->w + i - 1; + rect.y = coord->y - i; + rect.w = 1; + rect.h = 1; + SDL_FillRect(sdlscrn, &rect, cornercol); + + rect.x = coord->x - i; + rect.y = coord->y + coord->h + i - 1; + rect.w = 1; + rect.h = 1; + SDL_FillRect(sdlscrn, &rect, cornercol); + } + + screenunlock(); + + coord->x -= width; + coord->y -= width; + coord->w += (width * 2); + coord->h += (width * 2); +} + + +/*-----------------------------------------------------------------------*/ +/* + Draw a colored box around a given rectangle. + Rectangle is updated to the full size of the new object. +*/ +void SDLGui_DrawBoxAround(SDL_Rect *coord, SDL_Color *color, int width) +{ + SDL_Rect rect; + Uint32 col = SDLGui_MapColor(color); + + screenlock(); + + rect.x = coord->x - width; + rect.y = coord->y - width; + rect.w = coord->w + (width * 2); + rect.h = width; + SDL_FillRect(sdlscrn, &rect, col); + + rect.x = coord->x - width; + rect.y = coord->y - width; + rect.w = width; + rect.h = coord->h + (width * 2); + SDL_FillRect(sdlscrn, &rect, col); + + rect.x = coord->x + coord->w; + rect.y = coord->y - width; + rect.w = width; + rect.h = coord->h + (width * 2); + SDL_FillRect(sdlscrn, &rect, col); + + rect.x = coord->x - width; + rect.y = coord->y + coord->h; + rect.w = coord->w + (width * 2); + rect.h = width; + SDL_FillRect(sdlscrn, &rect, col); + + screenunlock(); + + coord->x -= width; + coord->y -= width; + coord->w += (width * 2); + coord->h += (width * 2); +} + + +/*-----------------------------------------------------------------------*/ +/* + Draw a 3D box with given attributes. +*/ +void SDLGui_Draw3DBox(SDL_Rect *coord, + SDL_Color *backgroundc, + SDL_Color *inboxc, + SDL_Color *upleftc, + SDL_Color *downrightc, + SDL_Color *outboxc, + int widthbackground, + int widthinbox, + int width3D1, + int width3D2, + int widthoutbox) +{ + SDL_Rect rect; + + screenlock(); + + // Draw background + rect.x = coord->x - widthbackground; + rect.y = coord->y - widthbackground; + rect.w = coord->w + (widthbackground * 2); + rect.h = coord->h + (widthbackground * 2); + SDL_FillRect(sdlscrn, &rect, SDLGui_MapColor(backgroundc)); + + screenunlock(); + + // Update coords + coord->x -= widthbackground; + coord->y -= widthbackground; + coord->w += (widthbackground * 2); + coord->h += (widthbackground * 2); + + if (widthinbox > 0) + SDLGui_DrawBoxAround(coord, inboxc, widthinbox); + + if (width3D1 > 0) + SDLGui_Draw3DAround(coord, upleftc, downrightc, backgroundc, width3D1); + + if (width3D2 > 0) + SDLGui_Draw3DAround(coord, downrightc, upleftc, backgroundc, width3D2); + + if (widthoutbox > 0) + SDLGui_DrawBoxAround(coord, outboxc, widthoutbox); +} + + +/*-----------------------------------------------------------------------*/ +/* + Draw a dialog box object. +*/ +void SDLGui_DrawBox(SGOBJ *bdlg, int objnum) +{ + SDL_Rect coord; + SDL_Color *my_blackc; + SDL_Color *upleftc, *downrightc; + + SDLGui_ObjCoord(bdlg, objnum, &coord); + + // Modify box drawing according to object state + if (bdlg[objnum].state & SG_DISABLED) + my_blackc = darkgreyc; + else + my_blackc = blackc; + + if (bdlg[objnum].state & SG_SELECTED) + { + upleftc = darkgreyc; + downrightc = whitec; + } + else + { + upleftc = whitec; + downrightc = darkgreyc; + } + + // Draw box according to object flags + switch (bdlg[objnum].flags & (SG_SELECTABLE | SG_DEFAULT | SG_BACKGROUND)) + { + case (SG_SELECTABLE | SG_DEFAULT | SG_BACKGROUND): + case (SG_SELECTABLE | SG_DEFAULT): + SDLGui_Draw3DBox(&coord, + greyc, NULL, upleftc, downrightc, my_blackc, + 1, 0, 1, 0, 2); + break; + case (SG_SELECTABLE | SG_BACKGROUND): + case SG_SELECTABLE: + SDLGui_Draw3DBox(&coord, + greyc, NULL, upleftc, downrightc, my_blackc, + 1, 0, 1, 0, 1); + break; + case (SG_DEFAULT | SG_BACKGROUND): + case SG_BACKGROUND: + SDLGui_Draw3DBox(&coord, + greyc, my_blackc, upleftc, downrightc, darkgreyc, + 0, 2, 3, 0, 1); + break; + case SG_DEFAULT: + case 0: + SDLGui_Draw3DBox(&coord, + greyc, NULL, upleftc, downrightc, NULL, + 3, 0, 1, 1, 0); + break; + } +} + + +/*-----------------------------------------------------------------------*/ +/* + Draw a normal button. +*/ +void SDLGui_DrawButton(SGOBJ *bdlg, int objnum) +{ + SDL_Rect coord; + int x, y; + SDL_Color *textc; + + SDLGui_ObjCoord(bdlg, objnum, &coord); + + x = coord.x + ((coord.w - (strlen(bdlg[objnum].txt) * fontwidth)) / 2); + y = coord.y + ((coord.h - fontheight) / 2); + + if (bdlg[objnum].state & SG_SELECTED) + { + x += 1; + y += 1; + } + + if (bdlg[objnum].state & SG_DISABLED) + textc = darkgreyc; + else + textc = blackc; + + SDLGui_DrawBox(bdlg, objnum); + SDLGui_Text(x, y, bdlg[objnum].txt, textc); +} + + +/*-----------------------------------------------------------------------*/ +/* + Draw a dialog check box object state. +*/ +void SDLGui_DrawCheckBoxState(SGOBJ *cdlg, int objnum) +{ + Uint32 grey = SDLGui_MapColor(greyc); + SDL_Rect coord; + char str[2]; + SDL_Color *textc; + + SDLGui_ObjCoord(cdlg, objnum, &coord); + + if (cdlg[objnum].flags & SG_RADIO) + { + if (cdlg[objnum].state & SG_SELECTED) + str[0]=SGCHECKBOX_RADIO_SELECTED; + else + str[0]=SGCHECKBOX_RADIO_NORMAL; + } + else + { + if (cdlg[objnum].state & SG_SELECTED) + str[0]=SGCHECKBOX_SELECTED; + else + str[0]=SGCHECKBOX_NORMAL; + } + + if (cdlg[objnum].state & SG_DISABLED) + textc = darkgreyc; + else + textc = blackc; + + str[1]='\0'; + + coord.w = fontwidth; + coord.h = fontheight; + + if (cdlg[objnum].flags & SG_BUTTON_RIGHT) + coord.x += ((strlen(cdlg[objnum].txt) + 1) * fontwidth); + + SDL_FillRect(sdlscrn, &coord, grey); + SDLGui_Text(coord.x, coord.y, str, textc); +} + + +/*-----------------------------------------------------------------------*/ +/* + Draw a dialog check box object. +*/ +void SDLGui_DrawCheckBox(SGOBJ *cdlg, int objnum) +{ + SDL_Rect coord; + SDL_Color *textc; + + SDLGui_ObjCoord(cdlg, objnum, &coord); + + if (!(cdlg[objnum].flags&SG_BUTTON_RIGHT)) + coord.x += (fontwidth * 2); + + if (cdlg[objnum].state & SG_DISABLED) + textc = darkgreyc; + else + textc = blackc; + + SDLGui_Text(coord.x, coord.y, cdlg[objnum].txt, textc); + SDLGui_DrawCheckBoxState(cdlg, objnum); +} + + +/*-----------------------------------------------------------------------*/ +/* + Draw a dialog popup button object. +*/ +void SDLGui_DrawPopupButton(SGOBJ *pdlg, int objnum) +{ + SDL_Rect coord; + const char *downstr = "\x02"; + SDL_Color *textc; + + if (pdlg[objnum].state & SG_DISABLED) + textc = darkgreyc; + else + textc = blackc; + + SDLGui_DrawBox(pdlg, objnum); + + SDLGui_ObjCoord(pdlg, objnum, &coord); + + SDLGui_Text(coord.x, coord.y, pdlg[objnum].txt, textc); + SDLGui_Text(coord.x+coord.w-fontwidth, coord.y, downstr, textc); +} + + +/*-----------------------------------------------------------------------*/ +/* + Draw an object. +*/ +void SDLGui_DrawObject(SGOBJ *dlg, int objnum) +{ + switch (dlg[objnum].type) + { + case SGBOX: + SDLGui_DrawBox(dlg, objnum); + break; + case SGTEXT: + SDLGui_DrawText(dlg, objnum); + break; + case SGEDITFIELD: + SDLGui_DrawEditField(dlg, objnum); + break; + case SGBUTTON: + SDLGui_DrawButton(dlg, objnum); + break; + case SGCHECKBOX: + SDLGui_DrawCheckBox(dlg, objnum); + break; + case SGPOPUP: + SDLGui_DrawPopupButton(dlg, objnum); + break; + } +} + + +/*-----------------------------------------------------------------------*/ +/* + Draw a whole dialog. +*/ +void SDLGui_DrawDialog(SGOBJ *dlg) +{ + int i; + + // Store dialog coordinates + SDLGui_ObjFullCoord(dlg, 0, &DialogRect); + + for (i = 0 ; dlg[i].type != -1 ; i++) + { + if (dlg[i].state & SG_HIDDEN) continue; + SDLGui_DrawObject(dlg, i); + } + SDLGui_RefreshObj(dlg, 0); +} + + +/*-----------------------------------------------------------------------*/ +/* + Search default object in a dialog. +*/ +int SDLGui_FindDefaultObj(SGOBJ *dlg) +{ + int i = 0; + + while (dlg[i].type != -1) + { + if (dlg[i].flags & SG_DEFAULT) + return i; + i++; + } + + return -1; +} + + +/*-----------------------------------------------------------------------*/ +/* + Search an object at given coordinates. +*/ +int SDLGui_FindObj(SGOBJ *dlg, int fx, int fy) +{ + SDL_Rect coord; + int end, i; + int ob = -1; + + // Search end object in dialog + i = 0; + while (dlg[i++].type != -1); + end = i; + + // Now check each object + for (i = end-1 ; i >= 0 ; i--) + { + SDLGui_ObjFullCoord(dlg, i, &coord); + + if(fx >= coord.x && + fy >= coord.y && + fx < (coord.x + coord.w) && + fy < (coord.y + coord.h)) + { + if (dlg[i].state & (SG_HIDDEN | SG_DISABLED)) continue; + ob = i; + break; + } + } + + return ob; +} + + +/*-----------------------------------------------------------------------*/ +/* + A radio object has been selected. Let's deselect any other in his group. +*/ +void SDLGui_SelectRadioObject(SGOBJ *dlg, int clicked_obj) +{ + int obj; + + // Find first radio object in this group + obj = clicked_obj; + while (dlg[--obj].flags & SG_RADIO); + + // Update state + while (dlg[++obj].flags & SG_RADIO) + { + // This code scan every object in the group. This allows to solve cases + // where multiple objects where selected in the group by clicking one. + if ((obj != clicked_obj) && (dlg[obj].state & SG_SELECTED)) + { + // Deselect this radio button + dlg[obj].state &= ~SG_SELECTED; + SDLGui_DrawObject(dlg, obj); + SDLGui_RefreshObj(dlg, obj); + } + } +} + + +/*-----------------------------------------------------------------------*/ +/* + Update clicked object state depending on given mouse coordinates. + Returns true if the mouse is over the object, false otherwise. +*/ +bool SDLGui_UpdateObjState(SGOBJ *dlg, int clicked_obj, int original_state, + int x, int y) +{ + int obj; + + obj = SDLGui_FindObj(dlg, x, y); + + // Special case : user clicked on an already selected radio object + // do not modify its state. + // We handle it here because it allows to exit if the object is SG_EXIT or + // SG_TOUCHEXIT without any additional test. + if ((dlg[clicked_obj].flags & SG_RADIO) && (original_state & SG_SELECTED)) + return (obj == clicked_obj); + + if (((obj != clicked_obj) && + (dlg[clicked_obj].state != original_state)) || + ((obj == clicked_obj) && + (dlg[clicked_obj].state == original_state))) + { + if (dlg[clicked_obj].flags & SG_SELECTABLE) + { + dlg[clicked_obj].state ^= SG_SELECTED; + SDLGui_DrawObject(dlg, clicked_obj); + SDLGui_RefreshObj(dlg, clicked_obj); + } + } + + return (obj == clicked_obj); +} + + +/*-----------------------------------------------------------------------*/ +/* + Search edit field in a dialog. +*/ +int SDLGui_FindEditField(SGOBJ *dlg, int objnum, int mode) +{ + int i, j; + + switch (mode) + { + case SG_FIRST_EDITFIELD: + i = 0; + while (dlg[i].type != -1) + { + if ((dlg[i].type == SGEDITFIELD) && + ((dlg[i].state & (SG_HIDDEN | SG_DISABLED)) == 0)) + return i; + i++; + } + break; + + case SG_PREVIOUS_EDITFIELD: + i = objnum - 1; + while (i >= 0) + { + if ((dlg[i].type == SGEDITFIELD) && + ((dlg[i].state & (SG_HIDDEN | SG_DISABLED)) == 0)) + return i; + i--; + } + break; + + case SG_NEXT_EDITFIELD: + i = objnum + 1; + while (dlg[i].type != -1) + { + if ((dlg[i].type == SGEDITFIELD) && + ((dlg[i].state & (SG_HIDDEN | SG_DISABLED)) == 0)) + return i; + i++; + } + break; + + case SG_LAST_EDITFIELD: + i = objnum + 1; + j = -1; + while (dlg[i].type != -1) + { + if ((dlg[i].type == SGEDITFIELD) && + ((dlg[i].state & (SG_HIDDEN | SG_DISABLED)) == 0)) + j = i; + i++; + } + if (j != -1) + return j; + break; + } + + return objnum; +} + + +/*-----------------------------------------------------------------------*/ +/* + Move cursor to another edit field. +*/ +void SDLGui_MoveCursor(SGOBJ *dlg, cursor_state *cursor, int mode) +{ + int new_object; + + new_object = SDLGui_FindEditField(dlg, cursor->object, mode); + + if (new_object != cursor->object) + { + /* Erase old cursor */ + cursor->blink_state = false; + SDLGui_DrawCursor(dlg, cursor); + + cursor->object = new_object; + cursor->position = strlen(dlg[new_object].txt); + } + else + { + /* We stay in the same field */ + /* Move cursor to begin or end of text depending on mode */ + switch (mode) + { + case SG_FIRST_EDITFIELD: + case SG_PREVIOUS_EDITFIELD: + cursor->position = 0; + break; + + case SG_NEXT_EDITFIELD: + case SG_LAST_EDITFIELD: + cursor->position = strlen(dlg[new_object].txt); + break; + } + } +} + + +/*-----------------------------------------------------------------------*/ +/* + Handle mouse clicks on edit fields. +*/ +void SDLGui_ClickEditField(SGOBJ *dlg, cursor_state *cursor, int clicked_obj, int x) +{ + SDL_Rect coord; + int i, j; + + /* Erase old cursor */ + cursor->blink_state = false; + SDLGui_DrawCursor(dlg, cursor); + + SDLGui_ObjFullCoord(dlg, clicked_obj, &coord); + i = (x - coord.x + (fontwidth / 2)) / fontwidth; + j = strlen(dlg[clicked_obj].txt); + + cursor->object = clicked_obj; + cursor->position = MIN(i, j); + cursor->blink_state = true; + cursor->blink_counter = 0; + SDLGui_DrawCursor(dlg, cursor); +} + + +/*-----------------------------------------------------------------------*/ +/* + Handle mouse clicks. +*/ +int SDLGui_MouseClick(SGOBJ *dlg, int fx, int fy, cursor_state *cursor) +{ + int clicked_obj; + int return_obj = -1; + int original_state = 0; + int x, y; + + clicked_obj = SDLGui_FindObj(dlg, fx, fy); + + if (clicked_obj >= 0) + { + original_state = dlg[clicked_obj].state; + SDLGui_UpdateObjState(dlg, clicked_obj, original_state, fx, fy); + + if (dlg[clicked_obj].flags & SG_TOUCHEXIT) + { + return_obj = clicked_obj; + clicked_obj = -1; + } + } + + while (clicked_obj >= 0) + { + SDL_Event evnt; + // SDL_PumpEvents() - not necessary, the main check_event thread calls it + if (SDL_PeepEvents(&evnt, 1, SDL_GETEVENT, SDL_EVENTMASK(SDL_USEREVENT))) + { + switch (evnt.user.code) + { + case SDL_USEREVENT: + // a signal that resolution has changed + // Restore clicked object original state + dlg[clicked_obj].state = original_state; + + // re-draw dialog + SDLGui_DrawDialog(dlg); + + // Exit from mouse click handling. + clicked_obj = -1; + break; + + case SDL_MOUSEBUTTONUP: + x = reinterpret_cast(evnt.user.data1); + y = reinterpret_cast(evnt.user.data2); + if (SDLGui_UpdateObjState(dlg, clicked_obj, original_state, x, y)) + { + // true if mouse button is released over clicked object. + // If applicable, the object has been selected by + // SDLGui_UpdateObjState(). Let's do additional handling here. + + // Exit if object is an SG_EXIT one. + if (dlg[clicked_obj].flags & SG_EXIT) + return_obj = clicked_obj; + + // If it's a SG_RADIO object, deselect other objects in his group. + if (dlg[clicked_obj].flags & SG_RADIO) + SDLGui_SelectRadioObject(dlg, clicked_obj); + + if (dlg[clicked_obj].type == SGEDITFIELD) + SDLGui_ClickEditField(dlg, cursor, clicked_obj, x); + } + + // Exit from mouse click handling. + clicked_obj = -1; + + break; + } + } + else + { + // No special event occured. + // Update object state according to mouse coordinates. + SDL_GetMouseState(&x, &y); + SDLGui_UpdateObjState(dlg, clicked_obj, original_state, x, y); + + // Wait a little to avoid eating CPU. + SDL_Delay(100); + } + } + + return return_obj; +} + + +/*-----------------------------------------------------------------------*/ +/* + Handle key press. +*/ +int SDLGui_KeyPress(SGOBJ *dlg, int keysym, int mod, cursor_state *cursor) +{ + int return_obj = -1; + int obj; + + if (cursor->object != -1) + { + switch(keysym) + { + case SDLK_RETURN: + case SDLK_KP_ENTER: + break; + + case SDLK_BACKSPACE: + if (cursor->position > 0) + { + memmove(&dlg[cursor->object].txt[cursor->position-1], + &dlg[cursor->object].txt[cursor->position], + strlen(&dlg[cursor->object].txt[cursor->position])+1); + cursor->position--; + } + break; + + case SDLK_DELETE: + if(cursor->position < (int)strlen(dlg[cursor->object].txt)) + { + memmove(&dlg[cursor->object].txt[cursor->position], + &dlg[cursor->object].txt[cursor->position+1], + strlen(&dlg[cursor->object].txt[cursor->position+1])+1); + } + break; + + case SDLK_LEFT: + if (cursor->position > 0) + cursor->position--; + break; + + case SDLK_RIGHT: + if (cursor->position < (int)strlen(dlg[cursor->object].txt)) + cursor->position++; + break; + + case SDLK_DOWN: + SDLGui_MoveCursor(dlg, cursor, SG_NEXT_EDITFIELD); + break; + + case SDLK_UP: + SDLGui_MoveCursor(dlg, cursor, SG_PREVIOUS_EDITFIELD); + break; + + case SDLK_TAB: + if (mod & KMOD_SHIFT) + SDLGui_MoveCursor(dlg, cursor, SG_PREVIOUS_EDITFIELD); + else + SDLGui_MoveCursor(dlg, cursor, SG_NEXT_EDITFIELD); + break; + + case SDLK_HOME: + if (mod & KMOD_CTRL) + SDLGui_MoveCursor(dlg, cursor, SG_FIRST_EDITFIELD); + else + cursor->position = 0; + break; + + case SDLK_END: + if (mod & KMOD_CTRL) + SDLGui_MoveCursor(dlg, cursor, SG_LAST_EDITFIELD); + else + cursor->position = strlen(dlg[cursor->object].txt); + break; + + default: + if ((keysym >= SDLK_KP0) && (keysym <= SDLK_KP9)) + { + // map numpad numbers to normal numbers + keysym -= (SDLK_KP0 - SDLK_0); + } + /* If it is a "good" key then insert it into the text field */ + if ((keysym >= SDLK_SPACE) && (keysym < SDLK_KP0)) + { + if (strlen(dlg[cursor->object].txt) < dlg[cursor->object].w) + { + memmove(&dlg[cursor->object].txt[cursor->position+1], + &dlg[cursor->object].txt[cursor->position], + strlen(&dlg[cursor->object].txt[cursor->position])+1); + if (mod & KMOD_SHIFT) + dlg[cursor->object].txt[cursor->position] = toupper(keysym); + else + dlg[cursor->object].txt[cursor->position] = keysym; + cursor->position += 1; + } + } + break; + } + } + + switch(keysym) + { + case SDLK_RETURN: + case SDLK_KP_ENTER: + obj = SDLGui_FindDefaultObj(dlg); + if (obj >= 0) + { + dlg[obj].state ^= SG_SELECTED; + SDLGui_DrawObject(dlg, obj); + SDLGui_RefreshObj(dlg, obj); + if (dlg[obj].flags & (SG_EXIT | SG_TOUCHEXIT)) + { + return_obj = obj; + SDL_Delay(300); + } + } + break; + } + + // Force cursor display. Should ease text input. + cursor->blink_state = true; + cursor->blink_counter = 0; + // Redraw current edit field... + SDLGui_DrawCursor(dlg, cursor); + + return return_obj; +} + + +/*-----------------------------------------------------------------------*/ +/* + Used to update screen while GUI is opened. Return a list of rectangles that + covers the screen without overlaping the current dialog. +*/ +SDL_Rect *SDLGui_GetFirstBackgroundRect(void) +{ + // Reset counter... + BackgroundRectCounter = SG_BCKGND_RECT_BEGIN; + // And returns first rectangle + return SDLGui_GetNextBackgroundRect(); +} + + +/*-----------------------------------------------------------------------*/ +/* + Returns next rectangle to be redrawn to update screen or NULL if we reached + the end of the list. + This code is "flying dialog" ready :) + It will need some updating if we implement popup buttons handled by sdlgui, + as the popup could be higher than the root box... + I used some recursivity here to simplify the code. +*/ +SDL_Rect *SDLGui_GetNextBackgroundRect(void) +{ + SDL_Rect *return_rect = NULL; + + switch (BackgroundRectCounter) + { + case SG_BCKGND_RECT_END: + // Nothing to do : return_rect is already initialized to NULL. + break; + + case SG_BCKGND_RECT_BEGIN: + if (DialogRect.w == 0) + { + // The dialog is not drawn yet... + // Let's redraw the full screen. + BackgroundRect.x = 0; + BackgroundRect.y = 0; + BackgroundRect.w = sdlscrn->w; + BackgroundRect.h = sdlscrn->h; + return_rect = &BackgroundRect; + // We reached the end of the list. + BackgroundRectCounter = SG_BCKGND_RECT_END; + } + else + { + BackgroundRectCounter = SG_BCKGND_RECT_TOP; + return_rect = SDLGui_GetNextBackgroundRect(); + } + break; + + case SG_BCKGND_RECT_TOP: + BackgroundRectCounter = SG_BCKGND_RECT_LEFT; + if (DialogRect.y > 0) + { + BackgroundRect.x = 0; + BackgroundRect.y = 0; + BackgroundRect.w = sdlscrn->w; + BackgroundRect.h = DialogRect.y; + return_rect = &BackgroundRect; + } + else + return_rect = SDLGui_GetNextBackgroundRect(); + break; + + case SG_BCKGND_RECT_LEFT: + BackgroundRectCounter = SG_BCKGND_RECT_RIGHT; + if (DialogRect.x > 0) + { + BackgroundRect.x = 0; + BackgroundRect.y = (DialogRect.y > 0) ? DialogRect.y : 0; + BackgroundRect.w = DialogRect.x; + BackgroundRect.h = + ((DialogRect.y + DialogRect.h) < (int)sdlscrn->h) ? + (DialogRect.h + DialogRect.y - BackgroundRect.y) : + (sdlscrn->h - DialogRect.y); + return_rect = &BackgroundRect; + } + else + return_rect = SDLGui_GetNextBackgroundRect(); + break; + + case SG_BCKGND_RECT_RIGHT: + BackgroundRectCounter = SG_BCKGND_RECT_BOTTOM; + if ((DialogRect.x + DialogRect.w) < (int)sdlscrn->w) + { + BackgroundRect.x = DialogRect.x + DialogRect.w; + BackgroundRect.y = (DialogRect.y > 0) ? DialogRect.y : 0; + BackgroundRect.w = sdlscrn->w - (DialogRect.x + DialogRect.w); + BackgroundRect.h = + ((DialogRect.y + DialogRect.h) < (int)sdlscrn->w) ? + (DialogRect.h + DialogRect.y - BackgroundRect.y) : + (sdlscrn->h - DialogRect.y); + return_rect = &BackgroundRect; + } + else + return_rect = SDLGui_GetNextBackgroundRect(); + break; + + case SG_BCKGND_RECT_BOTTOM: + BackgroundRectCounter = SG_BCKGND_RECT_END; + if ((DialogRect.y + DialogRect.h) < (int)sdlscrn->h) + { + // Bottom + BackgroundRect.x = 0; + BackgroundRect.y = DialogRect.y + DialogRect.h; + BackgroundRect.w = sdlscrn->w; + BackgroundRect.h = sdlscrn->h - (DialogRect.y + DialogRect.h); + return_rect = &BackgroundRect; + } + else + return_rect = SDLGui_GetNextBackgroundRect(); + break; + } + + return return_rect; +} + +SDL_Event getEvent(SGOBJ *dlg, cursor_state *cursor) +{ + int i = 0; + while(1) { + SDL_Event evnt; +// fprintf(stderr, "Debug Before Peep events\n"); + if (SDL_PeepEvents(&evnt, 1, SDL_GETEVENT, SDL_EVENTMASK(SDL_USEREVENT))) + { + fprintf(stderr, "Debug Peep events %d\n",i++); + SDL_Event e; + switch(evnt.user.code) + { + case SDL_KEYDOWN: + case SDL_KEYUP: + e.type = evnt.user.code; + e.key.keysym.sym = (SDLKey)reinterpret_cast(evnt.user.data1); + e.key.keysym.mod = (SDLMod)reinterpret_cast(evnt.user.data2); + return e; + + case SDL_MOUSEBUTTONDOWN: + case SDL_MOUSEBUTTONUP: + e.type = evnt.user.code; + if (evnt.user.code == SDL_MOUSEBUTTONDOWN) + fprintf(stderr, "Debug mouse down\n"); + else + fprintf(stderr, "Debug mouse down\n"); + e.button.x = reinterpret_cast(evnt.user.data1); + e.button.y = reinterpret_cast(evnt.user.data2); + return e; + + case SDL_USEREVENT: + // a signal that resolution has changed + if (dlg != NULL) + SDLGui_DrawDialog(dlg); // re-draw dialog + break; + } + } + else + { + // No special event occured. + // Wait a little to avoid eating CPU. + SDL_Delay(50); + if (cursor != NULL) { + cursor->blink_counter++; + if (cursor->blink_counter >= 10) { + cursor->blink_counter = 0; + cursor->blink_state = !cursor->blink_state; + if (dlg != NULL) + SDLGui_DrawCursor(dlg, cursor); + } + } + } + } +} + +/*-----------------------------------------------------------------------*/ +/* + Show and process a dialog. Returns the button number that has been + pressed. Does NOT handle SDL_QUIT - you must handle it before you + pass the input event to the SDL GUI. +*/ +int SDLGui_DoDialog(SGOBJ *dlg) +{ + int return_obj = -1; + int obj; + int x, y; + // int keysym, mod; + cursor_state cursor; + + // Is the left mouse button still pressed? Yes -> Handle TOUCHEXIT objects here + bool stillPressed = (SDL_GetMouseState(&x, &y) & SDL_BUTTON(1)); + obj = SDLGui_FindObj(dlg, x, y); + if (stillPressed && (obj >= 0) && (dlg[obj].flags & SG_TOUCHEXIT)) + { + // Mouse button is pressed over a TOUCHEXIT Button + // Toogle its state before drawing anything (it has been deselected before). + dlg[obj].state ^= SG_SELECTED; + + return_obj = obj; + } + + cursor.object = SDLGui_FindEditField(dlg, -1, SG_FIRST_EDITFIELD); + cursor.position = (cursor.object != -1) ? strlen(dlg[cursor.object].txt) : 0; + cursor.blink_counter = 0; + cursor.blink_state = true; + + SDLGui_DrawDialog(dlg); + + /* The main loop */ + while (return_obj < 0) + { + fprintf(stderr, "Debug SDL main loop\n"); + SDL_Event evnt = getEvent(dlg, &cursor); + fprintf(stderr, "Debug SDL main loop got event\n"); + switch(evnt.type) + { + case SDL_KEYDOWN: + return_obj = SDLGui_KeyPress(dlg, evnt.key.keysym.sym, evnt.key.keysym.mod, &cursor); + break; + + case SDL_MOUSEBUTTONDOWN: + return_obj = SDLGui_MouseClick(dlg, evnt.button.x, evnt.button.y, &cursor); + break; + } + } + fprintf(stderr, "Debug SDL main loop finished\n"); + if (dlg[return_obj].type == SGBUTTON) + { + // Deselect button... + // BUG: This should be caller responsibility + dlg[return_obj].state ^= SG_SELECTED; + } + + return return_obj; +} diff --git a/Src/sdlgui.h b/Src/sdlgui.h new file mode 100644 index 0000000..b45a62a --- /dev/null +++ b/Src/sdlgui.h @@ -0,0 +1,83 @@ +/* + * + * This file is taken from the ARAnyM project which builds a new and powerful + * TOS/FreeMiNT compatible virtual machine running on almost any hardware. + * + * This file is distributed under the GNU Public License, version 2 or at + * your option any later version. Read the file gpl.txt for details. + * + */ + +#ifndef _SDLGUI_H +#define _SDLGUI_H + +#include + +enum +{ + SGBOX, + SGTEXT, + SGEDITFIELD, + SGBUTTON, + SGCHECKBOX, + SGPOPUP +}; + + +/* Object flags: */ +#define SG_TOUCHEXIT 1 +#define SG_EXIT 2 +#define SG_BUTTON_RIGHT 4 +#define SG_DEFAULT 8 +#define SG_SELECTABLE 16 +#define SG_BACKGROUND 32 +#define SG_RADIO 64 + +/* Object states: */ +#define SG_SELECTED 1 +#define SG_HIDDEN 2 +#define SG_DISABLED 4 + +/* Special characters: */ +#define SGCHECKBOX_RADIO_NORMAL 12 +#define SGCHECKBOX_RADIO_SELECTED 13 +#define SGCHECKBOX_NORMAL 14 +#define SGCHECKBOX_SELECTED 15 +#define SGARROWUP 1 +#define SGARROWDOWN 2 +#define SGFOLDER 5 + + +typedef struct +{ + int type; /* What type of object */ + int flags; /* Object flags */ + int state; /* Object state */ + int x, y; /* The offset to the upper left corner */ + unsigned int w, h; /* Width and height */ + char *txt; /* Text string */ +} SGOBJ; + +typedef struct +{ + int object; + int position; + int blink_counter; + bool blink_state; +} cursor_state; + +extern void screenlock(); +extern void screenunlock(); + +bool SDLGui_Init(SDL_Surface *GUISurface); +int SDLGui_UnInit(void); +int SDLGui_DoDialog(SGOBJ *dlg); +int SDLGui_PrepareFont(void); +void SDLGui_FreeFont(void); + +SDL_Rect *SDLGui_GetFirstBackgroundRect(void); +SDL_Rect *SDLGui_GetNextBackgroundRect(void); + +SDL_Event getEvent(SGOBJ *dlg, cursor_state *cursor); + +#endif /* _SDLGUI_H */ diff --git a/Src/setarmenv b/Src/setarmenv new file mode 100755 index 0000000..2a14ff3 --- /dev/null +++ b/Src/setarmenv @@ -0,0 +1,6 @@ +export PATH=$PATH:/opt/Embedix/tools/bin/ +export CXX=arm-linux-g++ +export CC=arm-linux-gcc +export LD=arm-linux-ld +export LDFLAGS="-L/opt/Qtopia/sharp/lib/ -L/opt/Embedix/tools/arm-linux/lib" +export CPPFLAGS="-DQTOPIA" diff --git a/Src/sysconfig.h.Acorn b/Src/sysconfig.h.Acorn new file mode 100644 index 0000000..05b2a26 --- /dev/null +++ b/Src/sysconfig.h.Acorn @@ -0,0 +1,144 @@ +/* Src/sysconfig.h for Acorn RISC OS */ + + +#ifndef _ALL_SOURCE +/* #undef _ALL_SOURCE */ +#endif + +/* Define if your struct stat has st_blocks. */ +/* #undef HAVE_ST_BLOCKS */ + +/* Define if utime(file, NULL) sets file's timestamp to the present. */ +/*#define HAVE_UTIME_NULL 1 */ + +/* Define to `int' if doesn't define. */ +/* #undef mode_t */ + +/* Define to `long' if doesn't define. */ +/* #undef off_t */ + +/* Define to `int' if doesn't define. */ +/* #undef pid_t */ + +/* Define if you need to in order for stat and other things to work. */ +/* #undef _POSIX_SOURCE */ + +/* Define as the return type of signal handlers (int or void). */ +#define RETSIGTYPE void + +/* Define if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define if you can safely include both and . */ +/*#define TIME_WITH_SYS_TIME 1*/ + +/* Define if your declares struct tm. */ +/* #undef TM_IN_SYS_TIME */ + +/* The number of bytes in a char. */ +#define SIZEOF_CHAR 1 + +/* The number of bytes in a int. */ +#define SIZEOF_INT 4 + +/* The number of bytes in a long. */ +#define SIZEOF_LONG 4 + +/* The number of bytes in a long long. */ +/* #undef SIZEOF_LONG_LONG */ + +/* The number of bytes in a short. */ +#define SIZEOF_SHORT 2 + +/* Define if you have the gettimeofday function. */ +/*#define HAVE_GETTIMEOFDAY 1*/ + +/* Define if you have the mkdir function. */ +/*#define HAVE_MKDIR 1*/ + +/* Define if you have the rmdir function. */ +/*#define HAVE_RMDIR 1*/ + +/* Define if you have the select function. */ +/*#define HAVE_SELECT 1*/ + +/* Define if you have the sigaction function. */ +/*#define HAVE_SIGACTION 1*/ + +/* Define if you have the signal function */ +#define HAVE_SIGNAL + +/* Define if you have the statfs function. */ +/* #undef HAVE_STATFS */ + +/* Define if you have the strerror function. */ +#define HAVE_STRERROR 1 + +/* Define if you have the strstr function. */ +#define HAVE_STRSTR 1 + +/* Define if you have the usleep function. */ +/* #undef HAVE_USLEEP */ + +/* Define if you have the header file. */ +/*#define HAVE_DIRENT_H 1*/ + +/* Define if you have the header file. */ +/*#define HAVE_FCNTL_H 1*/ + +/* Define if you have the header file. */ +/* #undef HAVE_LINUX_JOYSTICK_H */ + +/* Define if you have the header file. */ +/* #undef HAVE_NCURSES_H */ + +/* Define if you have the header file. */ +/* #undef HAVE_NDIR_H */ + +/* Define if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define if you have the header file. */ +/* #undef HAVE_STRINGS_H */ + +/* Define if you have the header file. */ +/*#define HAVE_SYS_DIR_H 1*/ + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_MOUNT_H */ + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_NDIR_H */ + +/* Define if you have the header file. */ +/*#define HAVE_SYS_PARAM_H 1*/ + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_SELECT_H */ + +/* Define if you have the header file. */ +/*#define HAVE_SYS_STAT_H 1*/ + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_STATFS_H */ + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_STATVFS_H */ + +/* Define if you have the header file. */ +/*#define HAVE_SYS_TIME_H 1*/ + +/* Define if you have the header file. */ +/*#define HAVE_SYS_TYPES_H 1*/ + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_VFS_H */ + +/* Define if you have the header file. */ +/*#define HAVE_UNISTD_H 1*/ + +/* Define if you have the header file. */ +/*#define HAVE_UTIME_H 1*/ + +/* Define if you have the header file. */ +/* #undef HAVE_VALUES_H */ diff --git a/Src/sysconfig.h.Amiga b/Src/sysconfig.h.Amiga new file mode 100644 index 0000000..bae2295 --- /dev/null +++ b/Src/sysconfig.h.Amiga @@ -0,0 +1,143 @@ +/* Src/sysconfig.h for AmigaOS */ + +/* Define if on AIX 3. + System headers sometimes define this. + We just want to avoid a redefinition error message. */ +#ifndef _ALL_SOURCE +/* #undef _ALL_SOURCE */ +#endif + +/* Define if your struct stat has st_blocks. */ +#define HAVE_ST_BLOCKS 1 + +/* Define if utime(file, NULL) sets file's timestamp to the present. */ +#define HAVE_UTIME_NULL 1 + +/* Define to `int' if doesn't define. */ +/* #undef mode_t */ + +/* Define to `long' if doesn't define. */ +/* #undef off_t */ + +/* Define to `int' if doesn't define. */ +/* #undef pid_t */ + +/* Define if you need to in order for stat and other things to work. */ +#define _POSIX_SOURCE 1 + +/* Define as the return type of signal handlers (int or void). */ +#define RETSIGTYPE void + +/* Define if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define if you can safely include both and . */ +#define TIME_WITH_SYS_TIME 1 + +/* Define if your declares struct tm. */ +/* #undef TM_IN_SYS_TIME */ + +/* The number of bytes in a char. */ +#define SIZEOF_CHAR 1 + +/* The number of bytes in a int. */ +#define SIZEOF_INT 4 + +/* The number of bytes in a long. */ +#define SIZEOF_LONG 4 + +/* The number of bytes in a long long. */ +#define SIZEOF_LONG_LONG 8 + +/* The number of bytes in a short. */ +#define SIZEOF_SHORT 2 + +/* Define if you have the gettimeofday function. */ +#define HAVE_GETTIMEOFDAY 1 + +/* Define if you have the mkdir function. */ +#define HAVE_MKDIR 1 + +/* Define if you have the rmdir function. */ +#define HAVE_RMDIR 1 + +/* Define if you have the select function. */ +#define HAVE_SELECT 1 + +/* Define if you have the sigaction function. */ +#define HAVE_SIGACTION 1 + +/* Define if you have the statfs function. */ +/* #undef HAVE_STATFS */ + +/* Define if you have the strerror function. */ +#define HAVE_STRERROR 1 + +/* Define if you have the strstr function. */ +#define HAVE_STRSTR 1 + +/* Define if you have the usleep function. */ +/* #undef HAVE_USLEEP */ + +/* Define if you have the header file. */ +#define HAVE_DIRENT_H 1 + +/* Define if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define if you have the header file. */ +/* #undef HAVE_LINUX_JOYSTICK_H */ + +/* Define if you have the header file. */ +/* #undef HAVE_NCURSES_H */ + +/* Define if you have the header file. */ +#define HAVE_NDIR_H 1 + +/* Define if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define if you have the header file. */ +/* #undef HAVE_STRINGS_H */ + +/* Define if you have the header file. */ +#define HAVE_SYS_DIR_H 1 + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_MOUNT_H */ + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_NDIR_H */ + +/* Define if you have the header file. */ +#define HAVE_SYS_PARAM_H 1 + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_SELECT_H */ + +/* Define if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_STATFS_H */ + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_STATVFS_H */ + +/* Define if you have the header file. */ +#define HAVE_SYS_TIME_H 1 + +/* Define if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_VFS_H */ + +/* Define if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define if you have the header file. */ +#define HAVE_UTIME_H 1 + +/* Define if you have the header file. */ +/* #undef HAVE_VALUES_H */ diff --git a/Src/sysconfig.h.Be b/Src/sysconfig.h.Be new file mode 100644 index 0000000..6ebcf39 --- /dev/null +++ b/Src/sysconfig.h.Be @@ -0,0 +1,143 @@ +/* Src/sysconfig.h for BeOS */ + +/* Define if on AIX 3. + System headers sometimes define this. + We just want to avoid a redefinition error message. */ +#ifndef _ALL_SOURCE +/* #undef _ALL_SOURCE */ +#endif + +/* Define if your struct stat has st_blocks. */ +/* #undef HAVE_ST_BLOCKS */ + +/* Define if utime(file, NULL) sets file's timestamp to the present. */ +#define HAVE_UTIME_NULL 1 + +/* Define to `int' if doesn't define. */ +/* #undef mode_t */ + +/* Define to `long' if doesn't define. */ +/* #undef off_t */ + +/* Define to `int' if doesn't define. */ +/* #undef pid_t */ + +/* Define if you need to in order for stat and other things to work. */ +/* #undef _POSIX_SOURCE */ + +/* Define as the return type of signal handlers (int or void). */ +#define RETSIGTYPE void + +/* Define if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define if you can safely include both and . */ +#define TIME_WITH_SYS_TIME 1 + +/* Define if your declares struct tm. */ +/* #undef TM_IN_SYS_TIME */ + +/* The number of bytes in a char. */ +#define SIZEOF_CHAR 1 + +/* The number of bytes in a int. */ +#define SIZEOF_INT 4 + +/* The number of bytes in a long. */ +#define SIZEOF_LONG 4 + +/* The number of bytes in a long long. */ +#define SIZEOF_LONG_LONG 8 + +/* The number of bytes in a short. */ +#define SIZEOF_SHORT 2 + +/* Define if you have the gettimeofday function. */ +#define HAVE_GETTIMEOFDAY 1 + +/* Define if you have the mkdir function. */ +#define HAVE_MKDIR 1 + +/* Define if you have the rmdir function. */ +#define HAVE_RMDIR 1 + +/* Define if you have the select function. */ +#define HAVE_SELECT 1 + +/* Define if you have the sigaction function. */ +#define HAVE_SIGACTION 1 + +/* Define if you have the statfs function. */ +/* #undef HAVE_STATFS */ + +/* Define if you have the strerror function. */ +#define HAVE_STRERROR 1 + +/* Define if you have the strstr function. */ +#define HAVE_STRSTR 1 + +/* Define if you have the usleep function. */ +/* #undef HAVE_USLEEP */ + +/* Define if you have the header file. */ +#define HAVE_DIRENT_H 1 + +/* Define if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define if you have the header file. */ +/* #undef HAVE_LINUX_JOYSTICK_H */ + +/* Define if you have the header file. */ +/* #undef HAVE_NCURSES_H */ + +/* Define if you have the header file. */ +/* #undef HAVE_NDIR_H */ + +/* Define if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define if you have the header file. */ +/* #undef HAVE_STRINGS_H */ + +/* Define if you have the header file. */ +#define HAVE_SYS_DIR_H 1 + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_MOUNT_H */ + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_NDIR_H */ + +/* Define if you have the header file. */ +#define HAVE_SYS_PARAM_H 1 + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_SELECT_H */ + +/* Define if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_STATFS_H */ + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_STATVFS_H */ + +/* Define if you have the header file. */ +#define HAVE_SYS_TIME_H 1 + +/* Define if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_VFS_H */ + +/* Define if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define if you have the header file. */ +#define HAVE_UTIME_H 1 + +/* Define if you have the header file. */ +/* #undef HAVE_VALUES_H */ diff --git a/Src/sysconfig.h.Host-SDL b/Src/sysconfig.h.Host-SDL new file mode 100644 index 0000000..b98afb2 --- /dev/null +++ b/Src/sysconfig.h.Host-SDL @@ -0,0 +1,187 @@ +/* sysconfig.h. Generated from sysconfig.h.in by configure. */ +/* sysconfig.h.in. Generated from configure.ac by autoheader. */ + +/* Define to 1 if you have the header file, and it defines `DIR'. + */ +#define HAVE_DIRENT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define to 1 if you have the `gettimeofday' function. */ +#define HAVE_GETTIMEOFDAY 1 + +/* Glade support is enabled */ + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the `mkdir' function. */ +#define HAVE_MKDIR 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_NCURSES_H 1 + +/* Define to 1 if you have the header file, and it defines `DIR'. */ +/* #undef HAVE_NDIR_H */ + +/* Define to 1 if you have the `rmdir' function. */ +#define HAVE_RMDIR 1 + +/* Define to 1 if you have the `select' function. */ +#define HAVE_SELECT 1 + +/* Define to 1 if you have the `sigaction' function. */ +#define HAVE_SIGACTION 1 + +/* Define to 1 if you have the `statfs' function. */ +#define HAVE_STATFS 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the `strerror' function. */ +#define HAVE_STRERROR 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the `strstr' function. */ +#define HAVE_STRSTR 1 + +/* Define to 1 if `st_blocks' is member of `struct stat'. */ +#define HAVE_STRUCT_STAT_ST_BLOCKS 1 + +/* Define to 1 if your `struct stat' has `st_blocks'. Deprecated, use + `HAVE_STRUCT_STAT_ST_BLOCKS' instead. */ +#define HAVE_ST_BLOCKS 1 + +/* Define to 1 if you have the header file, and it defines `DIR'. + */ +/* #undef HAVE_SYS_DIR_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_MOUNT_H 1 + +/* Define to 1 if you have the header file, and it defines `DIR'. + */ +/* #undef HAVE_SYS_NDIR_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_PARAM_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_SELECT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STATFS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STATVFS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_VFS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if you have the `usleep' function. */ +#define HAVE_USLEEP 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UTIME_H 1 + +/* Define to 1 if `utime(file, NULL)' sets file's timestamp to the present. */ +#define HAVE_UTIME_NULL 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_VALUES_H 1 + +/* Extension for Maemo is enabled */ +/* #undef MAEMO */ + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "Christian.Bauer@uni-mainz.de" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "Frodo" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "Frodo 4.2" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "Frodo" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "4.2" + +/* Extension for Qtopia is enabled */ +/* #undef QTOPIA */ + +/* Define as the return type of signal handlers (`int' or `void'). */ +#define RETSIGTYPE void + +/* The size of `char', as computed by sizeof. */ +#define SIZEOF_CHAR 1 + +/* The size of `int', as computed by sizeof. */ +#define SIZEOF_INT 4 + +/* The size of `long', as computed by sizeof. */ +#define SIZEOF_LONG 4 + +/* The size of `long long', as computed by sizeof. */ +#define SIZEOF_LONG_LONG 8 + +/* The size of `short', as computed by sizeof. */ +#define SIZEOF_SHORT 2 + +/* The size of `void *', as computed by sizeof. */ +#define SIZEOF_VOID_P 4 + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define to 1 if you can safely include both and . */ +#define TIME_WITH_SYS_TIME 1 + +/* Define to 1 if your declares `struct tm'. */ +/* #undef TM_IN_SYS_TIME */ + +/* Define to 1 if the X Window System is missing or not being used. */ +/* #undef X_DISPLAY_MISSING */ + +/* Define to 1 if on AIX 3. + System headers sometimes define this. + We just want to avoid a redefinition error message. */ +#ifndef _ALL_SOURCE +/* # undef _ALL_SOURCE */ +#endif + +/* Define to `int' if does not define. */ +/* #undef mode_t */ + +/* Define to `long int' if does not define. */ +/* #undef off_t */ + +/* Define to `int' if does not define. */ +/* #undef pid_t */ diff --git a/Src/sysconfig.h.WIN32 b/Src/sysconfig.h.WIN32 new file mode 100644 index 0000000..f33f634 --- /dev/null +++ b/Src/sysconfig.h.WIN32 @@ -0,0 +1,143 @@ +/* Src/sysconfig.h for AmigaOS */ + +/* Define if on AIX 3. + System headers sometimes define this. + We just want to avoid a redefinition error message. */ +#ifndef _ALL_SOURCE +/* #undef _ALL_SOURCE */ +#endif + +/* Define if your struct stat has st_blocks. */ +/* #define HAVE_ST_BLOCKS 1 */ + +/* Define if utime(file, NULL) sets file's timestamp to the present. */ +#define HAVE_UTIME_NULL 1 + +/* Define to `int' if doesn't define. */ +#define mode_t int + +/* Define to `long' if doesn't define. */ +/* #undef off_t */ + +/* Define to `int' if doesn't define. */ +#define pid_t int + +/* Define if you need to in order for stat and other things to work. */ +/* #undef _POSIX_SOURCE */ + +/* Define as the return type of signal handlers (int or void). */ +#define RETSIGTYPE void + +/* Define if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define if you can safely include both and . */ +/* #undef TIME_WITH_SYS_TIME */ + +/* Define if your declares struct tm. */ +/* #undef TM_IN_SYS_TIME */ + +/* The number of bytes in a char. */ +#define SIZEOF_CHAR 1 + +/* The number of bytes in a int. */ +#define SIZEOF_INT 4 + +/* The number of bytes in a long. */ +#define SIZEOF_LONG 4 + +/* The number of bytes in a long long. */ +#define SIZEOF_LONG_LONG 8 + +/* The number of bytes in a short. */ +#define SIZEOF_SHORT 2 + +/* Define if you have the gettimeofday function. */ +#define HAVE_GETTIMEOFDAY 1 + +/* Define if you have the mkdir function. */ +#define HAVE_MKDIR 1 + +/* Define if you have the rmdir function. */ +#define HAVE_RMDIR 1 + +/* Define if you have the select function. */ +#define HAVE_SELECT 1 + +/* Define if you have the sigaction function. */ +/* #undef HAVE_SIGACTION */ + +/* Define if you have the statfs function. */ +/* #undef HAVE_STATFS */ + +/* Define if you have the strerror function. */ +#define HAVE_STRERROR 1 + +/* Define if you have the strstr function. */ +#define HAVE_STRSTR 1 + +/* Define if you have the usleep function. */ +/* #undef HAVE_USLEEP */ + +/* Define if you have the header file. */ +/* #undef HAVE_DIRENT_H */ + +/* Define if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define if you have the header file. */ +/* #undef HAVE_LINUX_JOYSTICK_H */ + +/* Define if you have the header file. */ +/* #undef HAVE_NCURSES_H */ + +/* Define if you have the header file. */ +#define HAVE_NDIR_H 1 + +/* Define if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define if you have the header file. */ +/* #undef HAVE_STRINGS_H */ + +/* Define if you have the header file. */ +/* #define HAVE_SYS_DIR_H */ + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_MOUNT_H */ + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_NDIR_H */ + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_PARAM_H */ + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_SELECT_H */ + +/* Define if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_STATFS_H */ + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_STATVFS_H */ + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_TIME_H */ + +/* Define if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_VFS_H */ + +/* Define if you have the header file. */ +/* #undef HAVE_UNISTD_H */ + +/* Define if you have the header file. */ +/* #undef HAVE_UTIME_H */ + +/* Define if you have the header file. */ +/* #undef HAVE_VALUES_H */ diff --git a/Src/sysconfig.h.Wii b/Src/sysconfig.h.Wii new file mode 100644 index 0000000..247b594 --- /dev/null +++ b/Src/sysconfig.h.Wii @@ -0,0 +1,191 @@ +/* Src/sysconfig.h for Wii */ + +/* Define if on AIX 3. + System headers sometimes define this. + We just want to avoid a redefinition error message. */ +#ifndef _ALL_SOURCE +/* #undef _ALL_SOURCE */ +#endif + +/* Define to empty if the keyword does not work. */ +/* #undef const */ + +/* Define if your struct stat has st_blocks. */ +#define HAVE_ST_BLOCKS 1 + +/* Define if utime(file, NULL) sets file's timestamp to the present. */ +#define HAVE_UTIME_NULL 1 + +/* Define as __inline if that's what the C compiler calls it. */ +/* #undef inline */ + +/* Define to `int' if doesn't define. */ +/* #undef mode_t */ + +/* Define to `long' if doesn't define. */ +/* #undef off_t */ + +/* Define to `int' if doesn't define. */ +/* #undef pid_t */ + +/* Define if you need to in order for stat and other things to work. */ +#define _POSIX_SOURCE 1 + +/* Define as the return type of signal handlers (int or void). */ +#define RETSIGTYPE void + +/* Define if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define if you can safely include both and . */ +#define TIME_WITH_SYS_TIME 1 + +/* Define if your declares struct tm. */ +/* #undef TM_IN_SYS_TIME */ + +/* The number of bytes in a char. */ +#define SIZEOF_CHAR 1 + +/* The number of bytes in a int. */ +#define SIZEOF_INT 4 + +/* The number of bytes in a long. */ +#define SIZEOF_LONG 4 + +/* The number of bytes in a long long. */ +#define SIZEOF_LONG_LONG 8 + +/* The number of bytes in a short. */ +#define SIZEOF_SHORT 2 + +/* Define if you have the gettimeofday function. */ +#define HAVE_GETTIMEOFDAY 1 + +/* Define if you have SDL. */ +#define HAVE_SDL 1 + +/* Define if you have the mkdir function. */ +#define HAVE_MKDIR 1 + +/* Define if you have the rmdir function. */ +#define HAVE_RMDIR 1 + +/* Define if you have the select function. */ +#define HAVE_SELECT 1 + +/* Define if you have the sigaction function. */ +/*#define HAVE_SIGACTION 1*/ + +/* Define if you have the statfs function. */ +/* #undef HAVE_STATFS */ + +/* Define if you have the strerror function. */ +#define HAVE_STRERROR 1 + +/* Define if you have the strstr function. */ +#define HAVE_STRSTR 1 + +/* Define if you have the usleep function. */ +/* #undef HAVE_USLEEP */ + +/* Define if you have the header file. */ +#define HAVE_DIRENT_H 1 + +/* Define if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define if you have the header file. */ +#undef HAVE_LINUX_JOYSTICK_H + +/* Define if you have the header file. */ +/* #undef HAVE_NCURSES_H */ + +/* Define if you have the header file. */ +#define HAVE_NDIR_H 1 + +/* Define if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define if you have the header file. */ +/* #undef HAVE_STRINGS_H */ + +/* Define if you have the header file. */ +#define HAVE_SYS_DIR_H 1 + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_MOUNT_H */ + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_NDIR_H */ + +/* Define if you have the header file. */ +#define HAVE_SYS_PARAM_H 1 + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_SELECT_H */ + +/* Define if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_STATFS_H */ + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_STATVFS_H */ + +/* Define if you have the header file. */ +#define HAVE_SYS_TIME_H 1 + +/* Define if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_VFS_H */ + +/* Define if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define if you have the header file. */ +#define HAVE_UTIME_H 1 + +/* Define if you have the header file. */ +/* #undef HAVE_VALUES_H */ + +/* The size of `char', as computed by sizeof. */ +#define SIZEOF_CHAR 1 + +/* The size of `int', as computed by sizeof. */ +#define SIZEOF_INT 4 + +/* The size of `long', as computed by sizeof. */ +#define SIZEOF_LONG 4 + +/* The size of `long long', as computed by sizeof. */ +#define SIZEOF_LONG_LONG 8 + +/* The size of `short', as computed by sizeof. */ +#define SIZEOF_SHORT 2 + +/* The size of `void *', as computed by sizeof. */ +#define SIZEOF_VOID_P 4 + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define to 1 if you can safely include both and . */ +#define TIME_WITH_SYS_TIME 1 + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "simon.kagstrom@gmail.com" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "Frodo" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "Frodo 4.2" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "Frodo" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "4.2" diff --git a/Src/sysdeps.h b/Src/sysdeps.h new file mode 100644 index 0000000..1c346d9 --- /dev/null +++ b/Src/sysdeps.h @@ -0,0 +1,208 @@ +/* + * sysdeps.h - Try to include the right system headers and get other + * system-specific stuff right + * + * Frodo (C) 1994-1997,2002-2005 Christian Bauer + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "sysconfig.h" + +#include +#include +#include +#include +#include +#include + +#include +using std::vector; + +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#ifdef HAVE_VALUES_H +#include +#endif + +#ifdef HAVE_STRINGS_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif + +#ifdef HAVE_UTIME_H +#include +#endif + +#ifdef HAVE_SYS_PARAM_H +#include +#endif + +#ifdef HAVE_SYS_SELECT_H +#include +#endif + +#ifdef HAVE_SYS_VFS_H +#include +#endif + +#ifdef HAVE_SYS_STAT_H +#include +#endif + +#ifdef HAVE_SYS_MOUNT_H +#include +#endif + +#ifdef HAVE_SYS_STATFS_H +#include +#endif + +#ifdef HAVE_SYS_STATVFS_H +#include +#endif + +#if TIME_WITH_SYS_TIME +# include +# include +#else +# if HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif + +#if HAVE_DIRENT_H +# include +#else +# define dirent direct +# if HAVE_SYS_NDIR_H +# include +# endif +# if HAVE_SYS_DIR_H +# include +# endif +# if HAVE_NDIR_H +# include +# endif +#endif + +#include + +#if EEXIST == ENOTEMPTY +#define BROKEN_OS_PROBABLY_AIX +#endif + +#ifdef __NeXT__ +#define S_IRUSR S_IREAD +#define S_IWUSR S_IWRITE +#define S_IXUSR S_IEXEC +#define S_ISDIR(val) (S_IFDIR & val) +struct utimbuf +{ + time_t actime; + time_t modtime; +}; +#endif + +#ifdef __DOS__ +#include +#include +#else +#undef O_BINARY +#define O_BINARY 0 +#endif + +#ifdef __mac__ +#define bool Boolean +#endif + +#ifdef __riscos +#define bool int +#endif + +#ifdef WIN32 +#include +#include +#if !defined(M_PI) +#define M_PI 3.14159265358979323846 +#endif +#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#if _MSC_VER < 1100 +#define bool char +#endif +#define LITTLE_ENDIAN_UNALIGNED 1 +#endif + +/* If char has more then 8 bits, good night. */ +#ifndef __BEOS__ +typedef unsigned char uint8; +typedef signed char int8; + +#if SIZEOF_SHORT == 2 +typedef unsigned short uint16; +typedef short int16; +#elif SIZEOF_INT == 2 +typedef unsigned int uint16; +typedef int int16; +#else +#error No 2 byte type, you lose. +#endif + +#if SIZEOF_INT == 4 +typedef unsigned int uint32; +typedef int int32; +#elif SIZEOF_LONG == 4 +typedef unsigned long uint32; +typedef long int32; +#else +#error No 4 byte type, you lose. +#endif + +#if SIZEOF_LONG == 8 +typedef unsigned long uint64; +typedef long int64; +#elif SIZEOF_LONG_LONG == 8 +typedef unsigned long long uint64; +typedef long long int64; +#else +#error No 8 byte type, you lose. +#endif + +#if SIZEOF_VOID_P == 4 +typedef uint32 uintptr; +typedef int32 intptr; +#elif SIZEOF_VOID_P == 8 +typedef uint64 uintptr; +typedef int64 intptr; +#else +#error Unsupported size of pointer +#endif + +#else +#include +#endif // __BEOS__