From cf770f0a3d0a3813eea849d35849b840f8359ab6 Mon Sep 17 00:00:00 2001 From: Mark Aufflick Date: Tue, 18 Oct 2011 14:49:35 +1100 Subject: [PATCH] v0.86 sources and binaries (earliest available on website) --- CHANGES | 410 ++ KEGSMAC.app/Contents/Info.plist | 22 + KEGSMAC.app/Contents/MacOS/KEGSMAC | Bin 0 -> 370360 bytes KEGSMAC.app/Contents/PkgInfo | 1 + .../Resources/English.lproj/InfoPlist.strings | Bin 0 -> 520 bytes .../English.lproj/main.nib/classes.nib | 4 + .../Resources/English.lproj/main.nib/info.nib | 19 + .../English.lproj/main.nib/objects.xib | 169 + KEGSMAC.app/Contents/Resources/kegsicon.icns | Bin 0 -> 70755 bytes KEGSMAC.app/Icon? | 0 README.compile | 113 + README.kegs | 989 +++++ README.linux.partitions | 252 ++ README.mac | 41 + README.win32 | 51 + config.kegs | 11 + kegswin.exe | Bin 0 -> 388517 bytes src/INTERNALS.iwm | 239 ++ src/INTERNALS.overview | 96 + src/INTERNALS.xdriver | 148 + src/Info.plist | 22 + src/InfoPlist.strings | Bin 0 -> 520 bytes src/Makefile | 123 + src/adb.c | 1760 +++++++++ src/adb.h | 160 + src/classes.nib | 4 + src/clock.c | 392 ++ src/compile_time.c | 5 + src/config.c | 2560 +++++++++++++ src/config.h | 34 + src/defc.h | 265 ++ src/defcomm.h | 200 + src/defs.h | 63 + src/defs_instr.h | 1622 ++++++++ src/dis.c | 1342 +++++++ src/disas.h | 210 ++ src/engine_c.c | 1007 +++++ src/engine_s.s | 2491 ++++++++++++ src/info.nib | 19 + src/instable.h | 2703 +++++++++++++ src/iwm.c | 2329 ++++++++++++ src/iwm.h | 116 + src/iwm_35_525.h | 310 ++ src/joystick_driver.c | 227 ++ src/kegsfont.h | 514 +++ src/kegsicon.icns | Bin 0 -> 70755 bytes src/macdriver.c | 955 +++++ src/macsnd_driver.c | 154 + src/make_inst | 31 + src/make_size | 30 + src/make_win | 4 + src/moremem.c | 2353 ++++++++++++ src/objects.xib | 169 + src/op_routs.h | 466 +++ src/paddles.c | 114 + src/partls.c | 135 + src/prodos.h | 116 + src/prodos_protos.h | 46 + src/protos.h | 495 +++ src/protos_engine_c.h | 39 + src/protos_macdriver.h | 40 + src/protos_macsnd_driver.h | 20 + src/protos_windriver.h | 36 + src/protos_xdriver.h | 36 + src/scc.c | 1153 ++++++ src/scc.h | 78 + src/scc_macdriver.c | 211 ++ src/scc_socket_driver.c | 255 ++ src/scc_windriver.c | 252 ++ src/sim65816.c | 2285 +++++++++++ src/size_tab.h | 274 ++ src/smartport.c | 783 ++++ src/sound.c | 2031 ++++++++++ src/sound.h | 60 + src/sound_driver.c | 469 +++ src/superhires.h | 204 + src/to_pro.c | 897 +++++ src/vars | 1 + src/vars_c | 20 + src/vars_hp | 20 + src/vars_linuxppc | 20 + src/vars_mac | 10 + src/vars_solaris | 20 + src/vars_win32 | 10 + src/vars_x86linux | 19 + src/vars_x86solaris | 20 + src/video.c | 3354 +++++++++++++++++ src/win32.rc | 109 + src/win32snd_driver.c | 215 ++ src/windriver.c | 637 ++++ src/winresource.h | 42 + src/xdriver.c | 1306 +++++++ 92 files changed, 41037 insertions(+) create mode 100644 CHANGES create mode 100644 KEGSMAC.app/Contents/Info.plist create mode 100755 KEGSMAC.app/Contents/MacOS/KEGSMAC create mode 100644 KEGSMAC.app/Contents/PkgInfo create mode 100644 KEGSMAC.app/Contents/Resources/English.lproj/InfoPlist.strings create mode 100755 KEGSMAC.app/Contents/Resources/English.lproj/main.nib/classes.nib create mode 100644 KEGSMAC.app/Contents/Resources/English.lproj/main.nib/info.nib create mode 100644 KEGSMAC.app/Contents/Resources/English.lproj/main.nib/objects.xib create mode 100644 KEGSMAC.app/Contents/Resources/kegsicon.icns create mode 100644 KEGSMAC.app/Icon? create mode 100644 README.compile create mode 100644 README.kegs create mode 100644 README.linux.partitions create mode 100644 README.mac create mode 100644 README.win32 create mode 100644 config.kegs create mode 100755 kegswin.exe create mode 100644 src/INTERNALS.iwm create mode 100644 src/INTERNALS.overview create mode 100644 src/INTERNALS.xdriver create mode 100644 src/Info.plist create mode 100644 src/InfoPlist.strings create mode 100644 src/Makefile create mode 100644 src/adb.c create mode 100644 src/adb.h create mode 100755 src/classes.nib create mode 100644 src/clock.c create mode 100644 src/compile_time.c create mode 100644 src/config.c create mode 100644 src/config.h create mode 100644 src/defc.h create mode 100644 src/defcomm.h create mode 100644 src/defs.h create mode 100644 src/defs_instr.h create mode 100644 src/dis.c create mode 100644 src/disas.h create mode 100644 src/engine_c.c create mode 100644 src/engine_s.s create mode 100644 src/info.nib create mode 100644 src/instable.h create mode 100644 src/iwm.c create mode 100644 src/iwm.h create mode 100644 src/iwm_35_525.h create mode 100644 src/joystick_driver.c create mode 100644 src/kegsfont.h create mode 100644 src/kegsicon.icns create mode 100644 src/macdriver.c create mode 100644 src/macsnd_driver.c create mode 100755 src/make_inst create mode 100755 src/make_size create mode 100755 src/make_win create mode 100644 src/moremem.c create mode 100644 src/objects.xib create mode 100644 src/op_routs.h create mode 100644 src/paddles.c create mode 100755 src/partls.c create mode 100755 src/prodos.h create mode 100755 src/prodos_protos.h create mode 100644 src/protos.h create mode 100644 src/protos_engine_c.h create mode 100644 src/protos_macdriver.h create mode 100644 src/protos_macsnd_driver.h create mode 100644 src/protos_windriver.h create mode 100644 src/protos_xdriver.h create mode 100644 src/scc.c create mode 100644 src/scc.h create mode 100644 src/scc_macdriver.c create mode 100644 src/scc_socket_driver.c create mode 100644 src/scc_windriver.c create mode 100644 src/sim65816.c create mode 100644 src/size_tab.h create mode 100644 src/smartport.c create mode 100644 src/sound.c create mode 100644 src/sound.h create mode 100644 src/sound_driver.c create mode 100644 src/superhires.h create mode 100755 src/to_pro.c create mode 120000 src/vars create mode 100644 src/vars_c create mode 100644 src/vars_hp create mode 100644 src/vars_linuxppc create mode 100644 src/vars_mac create mode 100644 src/vars_solaris create mode 100644 src/vars_win32 create mode 100644 src/vars_x86linux create mode 100644 src/vars_x86solaris create mode 100644 src/video.c create mode 100644 src/win32.rc create mode 100644 src/win32snd_driver.c create mode 100644 src/windriver.c create mode 100644 src/winresource.h create mode 100644 src/xdriver.c diff --git a/CHANGES b/CHANGES new file mode 100644 index 0000000..56bedb5 --- /dev/null +++ b/CHANGES @@ -0,0 +1,410 @@ + +Changes in KEGS v0.86 since v0.85 (03/23/04) +- Add patch for Solaris sound by Jonathan Kalbfeld. +- Fix so that F4 enters config panel even while running Prosel-16 +- Major mouse pointer changes, based on some ideas from Geoff Weiss. + The GSOS mouse now exactly tracks the host pointer automatically. +- Fixed an accidental debug halt when Prosel-16 disables the keyboard/mouse. + +Changes in KEGS v0.85 since v0.84 (01/09/04) +- Fix some minor 65816 bank-crossing bugs. +- Add -noignhalt to allow user to stop on code red halts. +- Fix Win32 capslock problem as reported by Edward Moore +- Fixed DreamVoir app on the sample image (it was corrupt) + +Changes in KEGS v0.84 since v0.83 (11/21/03) +- Add new speed, 8.0MHz directly using right-clicking or F6. +- Sim speed and Video update interval added to Config panel. +- Various cycle timing bugs in engine_c.c fixed. +- Add Config Panel entry to mask serial output to 7-bit, to enable PR#2 to + work better with an external telnet. +- In Config Panel file selection, typing a letter jumps to the first file + beginning with that letter. +- Fixed various serial socket bugs. Now you can disconnect a telnet session + and start a new one, and a Linux hang is fixed. +- Default GS memory size increased to 8MB. +- Small fix to double-hires color table. +- X windows can now send displays to other-endian X servers. + +Changes in KEGS v0.83 since v0.82 (11/19/03) +- Add Memory Size to config panel, with support for up to 14MB of memory + (Geoff Weiss) +- Add $C04F EMUBYTE support which Bernie II the Rescue defined. (Geoff Weiss) +- Fix $CFFF code red's reported by David Wilson. +- Add smartport $C70A Format routine (David Wilson). + +Changes in KEGS v0.82 since v0.81 (11/06/03) +- Fix superhires display glitch introduced in v0.81. +- Improved border handling--XMAS demo looks great. +- Fix some X build problems introduced in v0.81. + +Changes in KEGS v0.81 since v0.80 (11/04/03) +- Code Red/Yellow warnings about emulation stability +- Windows file browsing fixes +- Built-in C600 ROM for Apple II 5.25" game compatibility +- Turns key repeat back on when exiting from X-windows version +- Windows F8 captures the cursor + +Changes in KEGS v0.80 since v0.71 (10/31/03) +- Configuration Panel means no more hand-editing configuration files +- All emulator state is now saved in "config.kegs" +- 3200 color pictures! Video system much improved for display accuracy. +- F8 Pointer grabbing works on Mac +- ZipGS emulation + +Changes in KEGS v0.71 since v0.70 (11/20/02) +- Improved double-hires colors a lot. -dhr140 is no longer the default +- Airheart relies on the PC going from 0xffff to 0x0000, so I undid the + change from KEGS v0.54 which allowed PC to overflow to 0x10000. + This slows KEGS down by about 5%. +- Fixed X shared memory bug in KEGS v0.70 with fix from Jonathan Stark. + +Changes in KEGS v0.70 since v0.60 (11/18/02) +- New buttons: Middle button is enter-debugger, and right button changes speed +- New function key mapping (see README.kegs) +- Mac OS X port +- Win32 port +- Centralized much of what had been "xdriver.c" code into video.c, to move + true platform-specific stuff into the various *driver.c codes. + Kimage struct tracks video display buffers in a dev-independent way. + From video.c, the calls to the platform code start with "x_" mostly. + Code in video.c cleaned up somewhat. + Borders are now always in native buffer format, while text/hires/ + and superhires are in 8-bit buffers and translated to native later. +- Mac and Windows sound are all done in one process--no child sound process. +- Revamped key press handling and mouse clicks--all is now handled in + adb.c for a consistent user interface. Now KEGS implements the + same function keys on all platforms. See README.kegs for fn key maps. +- I copied the debugger help from Frederic Devernay's KEGS-SDL port. +- Fixed an old IWM bug causing bad nibblization due to using uninit vars. +- Gilles Tschopp workaround to use corrupted 2IMG files (from KEGS-OSX). +- Gilles Tschopp provided code to zero //gs memory at startup (from KEGS-OSX) +- Simple code to try to use Mac Diskcopy format disks +- Ignore writes to 0xc0a8 +- Search in $HOME and the launch directory (for mac) for kegs_conf/ROM +- Remove font65.sim file by integrating it into kegsfont.h. +- "-bw" option forces black and white hires mode. + + +Changes in KEGS v0.60 since v0.59 (10/03/00) +- The 16-bit colors were still wrong due to another coding error. It would + be much easier to get this right if I had a 16-bit color display... + A user says it works now. + +Changes in KEGS v0.59 since v0.58 (7/07/00) +- Added support for multiple paths to the default files and also multiple + names for many default files. This should make the .rpm distribution + work better. +- Add another keycode to mean break according to mic@research.nj.nec.com. +- Add support for various ROMs to get plugged into slot 1-7. +- Fix code so that it should compile on 64-bit platforms. + +Changes in KEGS v0.58 since v0.57 (2/08/00) +- Setting the execute bit on the disk image no longer means no-write-thru. + Too many new users were getting confused by this. +- Fixed another bug with Apple //e bank switching created by v0.56 + Reported by phoenyx. +- Add command line option "-v" to turn on some verbose debugging flags. +- Fixed potential core-dump bug with non-8 bit visuals. +- Fixed double-lo-res color problem. +- The X driver should work with any visual depth display now and get the + colors right. Ian Schmidt reported his 16-bit card had bad colors. + +Changes in KEGS v0.57 since v0.56 (12/27/99) +- Another try at making timezone stuff work across all Unix variants. + Let me know if the time in the Apple //gs control panel doesn't + match your real local time. +- Fix a bug created in v0.56 where the fast //e bank switch code had a typo. + This prevented ZBasic from working correctly. + +Changes in KEGS v0.56 since v0.55 (10/31/99) +- Faster Apple //e bank switch emulation. +- Simplified number of global variables for various softswitches. +- Fixed a bug which made 3.5" and 5.25" disk access much slower than necessary. +- Improved scan-line interrupt accuracy (lets MEGADEMO run). +- Improved sound interrupt accuracy (was hoping this would fix some sound + issues, but it doesn't seem to help). +- Add Mode_switch as an alias for the Option key +- I noticed the //gs self-tests were broken again--fixed. + +Changes in KEGS v0.55 since v0.54 (10/19/99) +- In LOG_PC debug aid, add cycles to the trace +- Fix MEGADEMO bug where 3.5" disks weren't properly ejected. Needed to + look at iwm.motor_on35 not iwm.motor_on. +- Temp fix for MEGADEMO to not halt if shadow-in-all-banks is on in $c036. +- Another MEGADEMO fix to not take a scan-line int if the SCB was cleared + right before the raster got to this line. +- Fix bug in smartport.c that was causing core dumps if you tried to init + a disk is s7dx. + +Changes in KEGS v0.54 since v0.53 (10/10/99) +- Add support for Out Of This World's direct reading of ADB RAM loc 0xb to + get key status. This lets shift/control work in OOTW. +- Code simplification to get rid of most set_halt() calls and use halt_printf. +- Speed improvement: track kpc (merged kbank and pc in one 32 bit variable) + which makes the inner loop faster. This does make KEGS not + accurately model a 65816 code crossing bank boundaries, but just + about every other emulator gets it wrong, and the speed improvement + is 5-10%. And I don't know of any code which relies on it + working correctly. +- Fix to allow better GS/OS compatibility: after each smartport call, + set 0x7f8 = 0xc7. +- Fixed ZipGS emulation bug where KEGS was not re-locking Zip at the right + time, which made double-hires not work after booting GS/OS. + +Changes in KEGS v0.53 since v0.52 (8/3/99) +- Move all the "fcycles" timing calculations to use double instead of float. +- Fix display shadowing bug reported by "phoenyx" which caused the text + display to not always be updated correctly with funny bank switching. +- Added the "Home" key as an alias for the '=' on the keypad. +- Changed the way X modifiers are interpreted to increase compatibility of + Caps Lock to more X servers. +- Add -dhr140 option to use old double-hires color mode that results in + exactly 140 horizontal pixels with no bleeding. It's set default + to "on" for now while I work out double-hires colors. +- Started adding some ZipGS compatibility--control panels run, but all + the controls are effectively ignored by KEGS. + +Changes in KEGS v0.52 since v0.51 (6/27/99) +- Small speed-up of interpreter loop to avoid checking the global variable + "halt_sim" after every instruction. +- Smartport fixes to avoid halts when the SCSI CD player NDA is installed. +- Fix to autodetect X visual depth (it didn't work at all in v0.51). +- Fix to HP binary--KEGS v0.51 hit an HP linker bug which caused the + executable to not run correctly. (It didn't obey an assembly- + language alignment command correctly). Re-ordering the object + list works around the problem. + +Changes in KEGS v0.51 since v0.50 (6/1/99) +- Fixed many bugs that crept into scanline interrupts over the last few months. +- RAM size is now settable on the commandline: -mem 0x400000 will use + a 4MB expansion RAM card (giving you 4.25MB of memory with ROM 01). +- VBL time used to be a variable (which was wrong)--it's now always the + same number of cycles. +- Typo preventing joystick_driver.c from compiling fixed. +- Auto senses X visual depth, searching for 8 bit, then 15 bit, then 24, + then 16 bit visuals. Can still override this with commandline. + +Changes in KEGS v0.50 since v0.49 (5/31/99) +- Added Linux joystick support with code provided by Jonathan Stark. + Activate with "-joystick" command line option. +- Small improvements in s7 device handling. If you have no s7 devices or no + bootable devices, KEGS launches Applesoft. +- Bug fix in scan-line interrupts--they were occurring at the wrong time + previously. +- Rewrote double-hires color routines. They're still not quite right, + but it's a lot better than it used to be. + +Changes in KEGS v0.49 since v0.48 (5/3/99) +- Fixed a key-repeat bug in v0.48 caused usually with shift-key sequences. +- Fixed bug where GNO would not work with ROM 03. ROM area at $C071-$C07F + is different from ROM 01. +- Ian Schmidt pointed out a special Ensoniq case where an oscillator in + one-shot mode can cause it's partner to start if it is in swap mode. +- Integrated in Geoff Weiss's Solaris x86 ports. I might have broken it + making a few last-minute changes... + +Changes in KEGS v0.48 since v0.47 (4/13/99) +- Even better ADB key repeat--key rollover works more like a real Apple //gs. +- IWM fix: some "smarport" modes were being activated sometimes during + normal 3.5" accesses, resulting in some games not loading correctly. +- Some fixes to serial port emulation to handle programs writing to + the serial port in MIDI mode when the chars will not be consumed. +- Smartport fix to set zero-page locations $42-$47, needed by some poorly- + written game loaders +- The "oscilloscope" effect in some sound-demos now shows the sounds + being played. + +Changes in KEGS v0.47 since v0.46 (4/7/99) +- ADB fix #1: reading $c010 should give key-down status better +- ADB fix #2: key repeat was stopping if modifier key pressed +- ADB fix #3: The game "Pirates" was crashing on startup due to a small bug. +- Bard's Tale 2 was freezing on startup due to a bug in the WAI instruction. +- Major serial port rewrite. Diversi-Tune now runs and sound OK (but there + are some small problems) and serial port emulation is better. + +Changes in KEGS v0.46 since v0.45 (3/21/99) +- Fix for undefined var in engine_c.c. Oops. +- Fix for old bug in engine_c.c causing KEGS to sometimes misinterpret + instructions which cross page boundaries. Was causing Thexder not + to work, at least. + +Changes in KEGS v0.45 since v0.44 (3/20/99) +- Fix for COP instruction in engine_c.c. Pointed out by Kelvin Sherlock. +- Major fixes to Ensoniq emulation, SynthLab sounds much better. +- Fix to iwm.c to deal with corrupt 2IMG archives a little better. + +Changes in KEGS v0.44 since v0.43 (2/23/99) +- -audio 0 option would often cause programs to hang. Bug was that the + audio rate was defaulting to '0' which confused KEGS. +- Made keycode 0x072 be the XK_Break key for XFree86 + +Changes in KEGS v0.43 since v0.42 (2/19/99) +- Support .nib 5.25" format as read-only +- Faster 3.5" nibblization routines (should make startup faster) +- Fixed a very-old 3.5" disk writing bug that made bit-copiers not work + +Changes in KEGS v0.42 since v0.41 (2/1/99) +- Include to fix Linux compile problem +- Fix relative branch timing bug that was making IWM emulation flaky + (backward branches should count as 3 cycles if to the same page, + and 4 if to a different page in emulation mode. Bug always counted + them as 4) +- Gave up on fast 5.25" writes--KEGS always slows to 1MHz for 5.25" + writes since the timing and kludges just got too annoying. +- add "-arate 22050" option to change audio sample rate on the command-line. + Slower audio rates can hit more audio bugs (I'm working on them). +- fixed little-endian bug in smartport.c and partls.c +- fixed side border redraw bug that would sometimes leave super-hires + images on the right-side border. + +Changes in KEGS v0.41 since v0.40 (1/19/99) +- Fixed bug where fill-line mode would not always redraw the screen correctly +- Changed some // comments to /* */ to help David Wilson's Solaris port +- Fixed little-endian bugs in smartport.c preventing mounting of + parititioned disks. Fix submitted by Jonathan Stark. +- Christopher Neufeld noted that fast space/delete option in the control + panel caused KEGS to hit breakpoints. I fixed this and fast arrows and + fast mouse options (they are now just ignored). +- Solaris port by David Wilson now provides a Makefile_solaris + +Changes in KEGS v0.40 since v0.39 (10/25/98) +- 15 and 24 bit depth displays now supported (though somewhat slower than + 8 bit displays). But Super-hires displays now show 256 + simultaneous colors on a 16- or 24-bit X display. + Select a 15-bit display with the cmd line option "-15" and + a 24-bit display with "-24". Otherwise, KEGS defaults to looking + for an 8-bit display, and fails if it cannot find one. +- Some border fixes--border colors now update correctly when palette + changes occur (like via F10). +- Alias F1 to ESC for OS/2. + +Changes in KEGS v0.39 since v0.38 (9/13/98) +- OS/2 port by Tschopp Gilles + - handle cr&lf better in disk_conf + - Drive letters work and are not confused with partition names, so + s7d1 = D:\images\cd:1 will open partition 1 correctly. + - KEGS no longer uses system() to do file copies, it does it all + using POSIX calls. + - Unix-specific socket calls moved from scc.c to scc_driver.h + - Default X handler re-installed properly now for better debug +- Nasty core dump bug found and fixed by Tschopp Gilles in disk switch code + +Changes in KEGS v0.38 since v0.37 (7/28/98) +- IWM bugs: + - fast_disk_emul off under GS/OS caused I/O errors. + KEGS was always slowing down to 1MHz when 5.25" drive was on, when + it should have been obeying the $C036 register. + - bug in IWM on little-endian processors +- disk ejection should now work, but a beta user claimed some bugs on + x86 Linux. +- 2IMG support, but only lightly tested. +- Removed some internal breaks on access to $C0B0 for tool033. +- Modulae also stumbled into some breakpoints by writing to $C02F, + which does nothing. +- Screen refresh simplified (for me) by redrawing the screen while + raster is on first scan-line, rather than line 200. + However, a side effect is some of the graphics during the XMAS DEMO + look a bit choppier. +- More SCC fixes to avoid breakpoints under GNO. +- Start support for sound under Linux, but it sounds horrible right now. + Any Linux sound gurus want to help out? +- Fixed possible array-overrun bug in video.c around border effects. + Maybe shared memory works under x86 Linux now? +- Made changes for OS/2 port to fopen() text files. From Blue Neon. + + +Changes in KEGS v0.37 since v0.36 (7/13/98) +- Linux PPC port completed and functional. KEGS has been tested to + run quite well and quite fast on a 240MHz 604e running + MkLinux pre-DR3. +- Change LITTLE_ENDIAN define to KEGS_LITTLE_ENDIAN since Linux + always defines LITTLE_ENDIAN as a silly macro. +- Dumb bug in IWM 3.5" routines could cause core dumps if disk arm moved + from outer track to inner track very quickly. +- Deleted some breakpoints that some Second Sight searching code would hit. +- Ignore some SCC reset commands GNO would use that caused KEGS to stop. +- Handle odd partitions better--some //gs formatted Zips had a blocksize + of 0, which defaults to 512 now. +- Handle some keysyms better to avoid MkLinux bug with keysym 0. + +Changes in KEGS v0.36 since v0.35 (5/30/98) + +- Linux x86 port completed and functional with help from Karl Pfleger +- Linux clock fixes--should handle daylight savings better on Linux +- LITTLE_ENDIAN defines +- Start making fixes for NeXTStep due to Eric Sunshine +- Fixed bug in HP asm code with I/O fetches--caused //gs selftests to fail + and a bug in scc.c was also causing self-tests to fail. + +Changes in KEGS v0.35 since v0.34 (5/17/98) + +- engine_c.c fully implemented--KEGS now has a version completely written + in C, and now portable to other Unix machines. +- KEGS got another 5% faster with more tweaks to the asm dispatch loop. + +Changes in KEGS v0.34 since v0.33 + +- KEGS is 10-15% faster due to finally implementing a planned recoding + of the dispatch loop. + +Changes in KEGS v0.33 since v0.32 (5/7/98) + +- Fixed bug in engine_s.s that prevented compiling on pre-10.20 systems. +- ADB mouse interrupts work now. Fixed "bug" where GSHK would think + mouse button was depressed at startup. (GS/OS is looking at mouse + button 1 status, which accidentally was reading as down). +- ADB emulation of read char_sets and read_kbd_layouts now matches a real + //gs. +- optimization to allow dereferencing page_info[] even if BANK_IO is set, + to get a small speed improvement in engines_s:dispatch(). +- SCC logs are 'Z' at the disas prompt. +- Tool decoded is 'T' at the disas prompt. +- SCC changes to support slot 1 == port 6501 and slot 2 == port 6502, + with limited interrupt support. Most serial tasks won't work still, + but some do. PR#1/2 and IN#1/2 work fine. getty under GNO doesn't. +- -audio [0/1] forces audio off/on. This just stops the sound playing-- + internally all Ensoniq interrupts/etc are fully emulated. If display + is not using shared memory (i.e., it's remote), audio defaults to off. + (but can be forced on with -audio 1). +- -display {foo} sends X display to {foo}. + +Changes in KEGS v0.32 since v0.31 (10/23/97) + +- Faster dispatch loop, for a 10-15% overall performance improvement +- Fixed sound bug where Oversampler would make KEGS halt (Oversampler + said turn on 128 oscillators, and KEGS tried to...) +- Fixed bug where KEGS would not work on 24-bit displays due to a typo. +- Added frame skipping support (-skip n) and auto frame skipping if you + are not using shared memory (like displaying KEGS to a remote machine). +- Added -noshm support for forcing off shared memory, so you can see how + much it helps. + +Changes in KEGS v0.31 since v0.30 (9/23/97) + +- New mouse handling--Press F8 to hide X windows cursor and constrain + cursor inside window. Makes using the mouse much easier. + F8 toggles back to normal. +- Add revision to status area. +- Remove "slow memory" calculation. KEGS was emulating slowing down to + 1MHz to write to slow memory (bank $E0 or $E1). But true //gs + accelerators have a smarter trick, so I just removed it from + KEGS. KEGS still slows down for I/O reads and writes. + This eliminates the confusing 40MHz speed numbers you'd sometimes get. + KEGS can also now run faster when it would have slowed down to + 1MHz before. +- Turn off accurate IWM emulation be default, for much faster emulation. + Bit copiers won't work by default now. Toggle accurate IWM + with F7. Accurate IWM forces 1MHz speed for 5.25" and 2.5MHz for + 3.5", but less accurate IWM runs as fast as possible. +- Add optional size to s7dx entries in disk_conf, to allow using /dev/rfloppy. +- Allow mounting partitions by number, instead of just by name, since some + Mac-formatted Zip disks don't have partition names. +- Add -ignbadacc to ignore bad memory accesses. +- Increase MAX_C030_TIMES. Otherwise, fast workstations could generate too + many clicks per VBL, causing an assertion to fail. +- Small speed increase detecting changes in the superhires screen. +- Alt_L is now Open-Apple, and Alt_R is Closed-Apple. +- KEGS now uses just one private colormap, so xwd can get screendumps. + + diff --git a/KEGSMAC.app/Contents/Info.plist b/KEGSMAC.app/Contents/Info.plist new file mode 100644 index 0000000..15a616a --- /dev/null +++ b/KEGSMAC.app/Contents/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + KEGSMAC + CFBundleIconFile + kegsicon.icns + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + APPL + CFBundleSignature + ???? + CFBundleVersion + 0.1 + CSResourcesFileMapped + + + diff --git a/KEGSMAC.app/Contents/MacOS/KEGSMAC b/KEGSMAC.app/Contents/MacOS/KEGSMAC new file mode 100755 index 0000000000000000000000000000000000000000..81fafe87085272b05a0d3603be3b5b6524f0b966 GIT binary patch literal 370360 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 + + + + IBDocumentLocation + 152 85 356 240 0 0 1280 832 + IBEditorPositions + + 29 + 69 252 182 44 0 0 1280 832 + + IBFramework Version + 291.0 + IBSystem Version + 6R73 + targetFramework + IBCarbonFramework + + diff --git a/KEGSMAC.app/Contents/Resources/English.lproj/main.nib/objects.xib b/KEGSMAC.app/Contents/Resources/English.lproj/main.nib/objects.xib new file mode 100644 index 0000000..616ea10 --- /dev/null +++ b/KEGSMAC.app/Contents/Resources/English.lproj/main.nib/objects.xib @@ -0,0 +1,169 @@ + + + IBCarbonFramework + + NSApplication + + + + main + + + KEGSMAC + + KEGSMAC + + + About KEGSMAC + 0 + abou + + + TRUE + + + Quit + 0 + quit + + + _NSAppleMenu + + + + File + + File + + + Configuration F4 + 0 + KCFG + Enter KEGS Configuration Panel + + + + + + Window + + Window + + + Zoom Window + zoom + + + TRUE + Minimize Window + 0 + mini + + + TRUE + Minimize All Windows + 0 + mina + + + TRUE + + + TRUE + Bring All to Front + bfrt + + + TRUE + Arrange in Front + 1572864 + frnt + + + _NSWindowsMenu + + + + _NSMainMenu + + + + + Window + + Window + + + TRUE + Minimize Window + m + mini + + + TRUE + Minimize All Windows + m + 1572864 + mini + + + TRUE + + + TRUE + Bring All to Front + frnt + + + TRUE + Bring in Front + 1572864 + frnt + + + _NSWindowsMenu + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Files Owner + + MenuBar + + + 201 + diff --git a/KEGSMAC.app/Contents/Resources/kegsicon.icns b/KEGSMAC.app/Contents/Resources/kegsicon.icns new file mode 100644 index 0000000000000000000000000000000000000000..72ea1720399aefb0e22a255031b9c00afd333727 GIT binary patch literal 70755 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 or so), you shouldn't need to mess with this. +-dhr140: Will use the old Double-hires color algorithm that results in + exactly 140 colors across the screen, as opposed to the blending + being done by default. + +X-Windows/Linux options +-15: KEGS will only look for a 15-bit X-Window display. +-16: KEGS will only look for a 16-bit X-Window display (not tested, probably + will get red colors wrong). +-24: KEGS will only look for a 24-bit X-Window display. +-display {machine:0.0}: Same as setting the environment variable DISPLAY. + Sends X display to {machine:0.0}. +-joystick: Will use /dev/js0 as the joystick. +-noshm: KEGS will not try to used shared memory for the X graphics display. + This will make KEGS much slower on graphics-intensive tasks, + by as much as a factor of 10! By default, -noshm causes an + effective -skip of 3 which is 15 fps. You can override this + default by specifying a -skip explicitly. + + +Command/Option keys: +------------------- + +If you have a workstation keyboard with the new Windows keys, you can +use them as the command/option keys. This is what I use. Since many people +don't have the PC keyboard, there are several alternatives. + +The following keys are Option (closed-apple) (not all keyboards have all +keys): F2, Meta_R, Alt_R, Cancel, Print_screen, Mode_switch, Option, +or the Windows key just to the right of the spacebar. The following keys are +Command (open-apple): F1, Meta_L, Alt_L, Menu, Scroll_lock, Command, +the Windows key left of the spacebar, and the Windows key on the far right +that looks like a pull-down menu. You can use F1 and F2 if you cannot make +anything else work. + +If you can't get any of these to work on your machine, let me know. +Note that X Windows often has other things mapped to Meta- and Alt- +key sequences, so they often don't get passed through to KEGS. So it's +best to use another key instead of Alt or Meta. + +The joystick/paddle buttons are just the Command and Option keys. + +Reset: +----- + +The reset key is Pause/Break or F12. You must hit it with Ctrl to get it to +take effect (just like a real Apple IIgs). Ctrl-Command-Reset +forces a reboot. Ctrl-Command-Option-Reset enters selftests. +Selftests will pass if you force speed to 2.8MHz using the middle +button. Watch out for ctrl-shift-Break--it will likely kill your +X Windows session. + +Control Panel: +------------- + +You can get to the Apple IIgs control panel (unless some application +has locked it out) using Ctrl-Command-ESC. + + +How to use "to_pro": +------------------- + +This lame utility serves two purposes: It "formats" large disk images, +and lets you move files from Unix into the simulator. It does this +by taking the files you provide, and putting them onto Unix file called +"POOF1" that is an image in ProDOS format. + +So, if you have a wolfdemo.bxy file from an FTP site, you can get it +into the emulator by: + +to_pro -800 wolfdemo.bxy + +which creates an 800K Unix file called "POOF1". POOF1 is now an +image that can be loaded into KEGS, and when you catalog it, it will +have wolfdemo.bxy on it. + +To create a 4MB image: + +to_pro -4096 wolfdemo.bxy + +which puts wolfdemo.bxy on a much larger image. + +I don't know what happens if the file, wolfdemo.bxy, is bigger than +the image...it probably crashes. + +Even if you want to format a "blank" image, you have to put something in it. +Like: + +echo "This is a lame utility" > foo +to_pro -16384 foo + +...creates a 16MB POOF1 with the file foo on it. Just delete foo +from within KEGS. + +See? I told you it was a lame utility! + +to_pro can handle up to 51 files at a time--for example: + +to_pro -32000 *.shk + +...would put all *.shk files in the current Unix directory into a 31.25MB +image called POOF1. + +To_pro tries to truncate Unix filenames to the 15 character ProDOS +limit, and converts all punctuation to dots. I've tested it enough +that it has worked for my purposes. + +The algorithm to_pro uses to create a disk volume is possibly suspect. +I recommend reformatting any images again inside KEGS (using GS/OS, for +instance) just to make sure the directory structure is good. To_pro +is intended to put files into images quickly and easily, and then to +copy the files off of those images onto images formatted from within +KEGS by an Apple IIgs OS. + +Since ProDOS cannot handle > 32MB images, make sure you run to_pro with +arguments under 32767. I personally haven't tried a partition bigger +than 30000K (about 2.5MB short of the maximum). Well, you can use bigger +images if you format them HFS, but I don't trust the GS/OS HFS driver. + +To_pro automatically sets the ProDOS filetype of files ending in ".shk" +to $E0. + +Details on config.kegs and disk images +-------------------------------------- + +The file "config.kegs" describes the images KEGS will use. The sample +file has all the lines commented out with '#' to show sample uses. +Remember, KEGS will boot s7d1 (unless you've changed that using the +Apple IIgs control panel), so you must put an image in that slot. + +Changing disks in slot 7 does not work, but you can move around +disks in slots 5 and 6. This allows you to "eject" disks and change them. +This is especially useful for multi-disk 5.25" programs. + +KEGS uses the Unix permissions on raw disk images to decide how to load +it into the emulator. If the file is unreadable, it cannot load the +image (duh). + +KEGS, by default, runs the IWM (3.5" and 5.25" disks) emulation in an +"approximate" mode, called "fast_disk_emul". In this mode, KEGS +emulates the hardware "faster" than real, meaning the data the code +being emulated expects is made available much faster than on a real +Apple IIgs, providing a nice speed boost. For instance, the 5.25" +drives run 10x the real speed usually. Almost everything will work +except for nibble copiers, which don't like the data coming this fast. +(Meaning, unless you're using a nibble copier, you shouldn't run into an +issue. All games/demos/etc run fine in this mode). To make nibble +copiers work, Press F7. + +KEGS can read in the ".nib" nibblized disk format, but as read-only mode. If +the emulated image is no longer ProDOS or DOS 3.3 standard, KEGS will +automatically treat the image as "Not-write-through-to-Image" from then +on. This mode means KEGS will continue to emulate the disk properly in +memory, but it cannot encode the changes in the standard .dsk or .nib +image format. It prints a message saying it has done so. However, +the "disk" in emulation is fully useable as long as KEGS is running. A +standard reformatting will not cause an image to flip to not-write- +through-to-Image, but running things like a "drive-speed" test will cause +further changes not to propagate to the Unix file. You will need +to "eject" the image and re-insert it before writes will take effect. + +In full accuracy mode (i.e., not fast_disk_emul), 5.25" drive accesses +force KEGS to run at 1MHz, and 3.5" drive accesses force KEGS to run at +2.5MHz. + +KEGS Timing: +----------- + +KEGS supports running at four speeds: 1MHz, 2.8MHz, 8.0MHz, and Unlimited. +Pressing the middle mouse button cycles between these modes. The 1MHz +and 2.8MHz speeds force KEGS to run at exactly those speeds, providing +accurate reproduction of a real Apple IIgs. + +KEGS will always run at 1MHz at least. If it is unable to keep up, +it will extend the emulated time to maintain the illusion of running +at 1MHz. That is, it may do just 40 screen refreshes per real second, +instead of the usual 60. This happens rarely. + +If you force KEGS to run at 1MHz, it will strive to run at exactly +1MHz (well, really 1.024MHz). If it is running faster (almost always), +it will pause briefly several times a second to maintain the 1MHz speed. It +does this in a friendly way that makes time available to other tasks. +This makes older Apple II games very playable just like a +real Apple IIgs on slow speed. KEGS is running at exactly the same +speed as an Apple //e when in 1MHz mode. The 1MHz mode you set +through the right mouse button overrides the "fast" mode you can access +through the control panel. But, 3.5" accesses will "speed up" to 2.8MHz +to enable that code to operate correctly while the 3.5" disk is being +accessed. + +If you force KEGS to run at 2.8MHz, KEGS tries to run at exactly 2.8MHz. But +like a real unaccelerated Apple IIgs, if you set the control panel to +"slow", it will really be running at 1MHz. Accesses to 5.25" disk +automatically slow down to 1MHz, when running the IWM in accurate +mode (F7). KEGS may not be able to keep up with some programs running +at 2.8MHz due to video and sound overheads on lower-end machines. If +that happens, it effectively runs slower by extending the emulated +"second", like in the 1MHz mode. You can tell this is happening +when Eff MHz in the status area falls below 2.5MHz. If KEGS is running +faster than 2.8MHz, it takes small pauses to slow down, just like in +1MHz. Many Apple IIgs demos must be run at 2.8MHz. The built-in +selftests (cmd-option-ctrl-Reset) must run at 2.8MHz. Many Apple IIgs +action games are more playable at 2.8MHz. + +The 8.0MHz setting means follow the ZipGS-selected speed, but don't go +faster than 8.0MHz. If your host computer cannot keep up, then the +emulated second will be extended. You can use the ZipGS control panel, +or ZIPPY.GS on the sample disk image to set the emulated ZipGS speed to +anything from 1MHz to 8MHz in .5MHz increments. + +The Unlimited setting means run as fast as possible, whatever speed that +is (but always above 1MHz). Eff MHz gives you the current Apple IIgs +equivalent speed. Many games will be unplayable at the unlimited +setting. Setting the IIgs control panel speed to "slow" will slow down +to 1MHz. + +Sound output has an interesting relationship to KEGS timing. KEGS must +play one second of sound per second of emulated time. Normally, this +works out exactly right. But as noted above, if KEGS can't maintain the +needed speed, it extends the emulated second. If it extends the second +to 1.4 real seconds, that means KEGS only produces 1.0 second of sound +data every 1.4 seconds--the sound breaks up! + +In all cases, 1MHz to KEGS is 1.024MHz. And 2.8MHz to KEGS is 2.52MHz +(trying to approximate the slowdown causes by memory refresh on a real +Apple IIgs). It's just easier to say 1MHz and 2.8MHz. + + +KEGS SAMPLE_DISK: +---------------- + +I'm providing a sample disk of freely available utilities/programs to +demonstrate a little of what KEGS can do. I'm also including my simple +changes to a benchmark called "SPEEDTEST" to make it run under ProDOS and +time itself automatically. The SAMPLE_DISK is not bootable since I'm +not sure if I can distribute PRODOS (the OS). + + + SPEEDTEST: + --------- + +In the folder "SPEEDTEST", there are two BASIC programs. OLD.SPEEDTEST +is the old, unmodified DOS 3.3 emulator benchmark by Clayten Hamacher. +It does not run properly under ProDOS 8. My modified version is +SPEED.PRO, meaning converted to ProDOS. I made few modifications, other +than to make the benchmarks time themselves. + +To run, just say "RUN SPEED.PRO". To run benchmarks, press "B". If +you say "A)ll tests", make sure you have a 5.25" disk image in s6d1! +(A blank 140K image will work fine). + +This modified SPEED.PRO can run on ANY Apple IIgs emulator (or on the real +thing). + + GSOS7, GSOS5, BYE.SYSTEM: + ------------------------ + +These are handy utilities I use on my s7d1 boot disk. Get a GS/OS 6.x +bootable disk image. (See GSOS.INFO file for how to get GS/OS). +Remove "PRODOS" from that disk's root directory, and copy GSOS7 to +the root directory. Then copy SYSTEM/P8 to PRODOS. Then move +BASIC.System into SYSTEM/. Then copy BYE.SYSTEM to the root directory, +then move BASIC.SYSTEM back to the root directory. + +What all this means is that now the root directory of your system disk +is: GSOS7, (other stuff), PRODOS, BYE.SYSTEM, and BASIC.SYSTEM. +When you boot, ProDOS will boot (this is PRODOS 8) and will search +for the first *.SYSTEM file, and run it. BYE.SYSTEM just does a BYE +command, which puts you in the PRODOS 8 textual launcher. +If you now select GSOS7 (the first entry, already highlighted, just +hit return), it will boot GSOS on slot 7. (Use GSOS5 to boot slot5). +Or, just move down and select BASIC.SYSTEM to go to BASIC. A very simple +program launcher!? + +Note that I didn't write GSOS5 or GSOS7--I just made a one byte hack +to the default GS/OS launcher. No real wizardry is going on here. + + + SHRINKIT3.4, GSHK1.1: + -------------------- + +Useful for unpacking .SHK files you can download off of the net. +Always use GSHK (GS/OS version of ShrinkIt) for GS programs since +they may have resource forks. It's also faster. GSHK must be run from GS/OS. + + LISTV2.0: + -------- + +ProDOS 8 text file lister, useful for viewing text files. + + Wolfenstein3D: + ------ + +Wolfenstein 3D for the Apple IIgs. No kidding! Must be run from GS/OS. + + SOUND22: + ------- + +Cool little ProDOS 8 program (SOUND.EDITOR) that plays hi-fidelity +(relatively) through the old Apple II speaker. This is included as a +demonstration of how accurate KEGS sound emulation is. + + Sound.Smith.95: + -------------- + +GS/OS application that plays SoundSmith songs, which are spreadsheet music, +like MODs. I included some sample songs--FILE.11, FILE.16, FILE.17, and +SPACE.HARRIER. Enjoy! + + SOLITAIRE: + --------- + +Klondike. I like the interface on this game. + + CAT.DOCTOR: + ---------- + +From Prosel8 (which is now public domain), this utility is very handy for +sorting directories (among other things). Useful for arranging GSOS7, +and BYE.SYSTEM mentioned above. + + BGSOUND: + ------- + +This CDA lets you play Soundsmith songs in the background while other +applications are running. Very handy for playing Solitaire with some music. + + DOCVu.CDA: + --------- + +This CDA shows the current DOC contents in real-time. It has neat visual +effects while playing Soundsmith songs. + + Zippy.gs + -------- + +Very useful ProDOS 8 program by Andy McFadden for setting ZipGS parameters. +In KEGS, you'll want to use this to change the Zip speed to less than +100% to make the "Unlimited" speed become limited to 7.5MHz, which is +useful for some games. + + +KEGS: What works: +----------------- + +Basically, just about every Apple II program works. + +KEGS is EXTREMELY compatible. But, I haven't tested everything. Let +me know if you find a program which is not working correctly. + +Some old Apple II 5.25" games require the old C600 ROM image, and don't work +with the default Apple IIgs ROM. This is not KEGS's fault--these games +don't run on a real Apple IIgs either. KEGS has built-in the old Apple II +Disk PROM which you can enable by using the IIgs control panel to set +Slot 6 to "Your Card". This allows many more Apple II games to run, and +is the recommended setting. + +The NinjaForce Megademo mostly works, but sometimes hangs in the BBS Demo. +Just skip that demo if it happens. + +The California Demo hangs at startup unless you use the IIgs control panel +to boot from slot 5, and then do a ctrl-Open_Apple-Reset to boot--doing +the above lets it work fine. This seems to be a bug in the demo. + + +KEGS bugs: +--------- + +KEGS's serial port emulation is very limited now, and only for +adventurous souls. + +On a ROM03, KEGS makes a patch to the ROM image (inside emulation, not +to the Unix file) to fix a bug in the ROM code. Both ROM01 and ROM03 +are patched to enable use of more than 8MB of memory. I then patch the ROM +self-tests to make the ROM checksum pass. But other programs, like +the Apple IIgs Diagnostic Disk, will detect a ROM checksum mismatch. +Don't worry about it. + +Sound breaks up if KEGS is unable to keep up--it should only be happening +if you are trying to force KEGS to run at 2.8MHz, but cannot due to +sound and video overhead. + + +Sound emulation: +--------------- + +KEGS supports very accurate classic Apple II sound (clicking of the +speaker using $C030) and fairly accurate Ensoniq sound. + +When KEGS determines that no sound has been produced for more than +5 seconds, it turns off the sound calculation routines for a small +speedup. It describes that it has done this by saying "Pausing sound" +in the debug window. However, when sound restarts, it sometimes +"breaks-up" a little. I will work on fixes for this. + +If your display is not using shared memory, audio defaults to off unless +you override it with "-audio 1". + +SCC emulation: +------------- + +KEGS emulates the two serial ports on a IIgs as being two Unix sockets. +Port 1 (printer port) is at socket address 6501, and port 2 (modem) +is at socket address 6502. + +In KEGS, from APPLESOFT, if you PR#1, all output will then be sent to +socket port 6501. You can see it by connecting to the port using +any method you like, but a simple, easy way is to use telnet. In +another Unix window, do: "telnet localhost 6501" and then you +will see all the output going to the "printer". + +Under APPLESOFT, you can PR#1 and IN#1. This gets input from the +socket also. You can type in the telnet window, it will be sent on +to the emulated IIgs. Telnet on Unix defaults to "line mode" which +buffers keys you type until you hit return. This can be a bit distracting, +and can be disabled by hitting Ctrl-] and then "mode char". This +causes a few {{ chars to show up in KEGS--just ignore this for now. +You may want to go to the F4 Config Panel and set "mask off high bit" +for serial port accesses to make PR#2 work a little nicer. + +That's about it. Proterm and Appleworks GS can talk to the modem port +fine, but it's limited in its usefulness. I have printed from +Printshop, but it's a bit pointless since it's sending out Imagewriter +printer codes which doesn't look like anything. You can "print" from +BASIC by using something like PR#1 in KEGS and +"telnet localhost 6501 | tee file.out" in another window. + +Feel free to let me know what doesn't work, but a lot is known not +to work. GNO's tty interface may work, but I'm having problems +testing it. + + +KEGS status area: +---------------- + +The status area is updated once each second. It displays info I am +(or was at some time) interested in seeing. + +Line 1: (Emulation speed info) +dcycs: number of seconds since KEGS was started +sim MHz: Effective speed of KEGS instruction emulation, not counting + overhead for video or sound routines. +Eff MHz: Above, but with overhead accounted for. Eff MHz is the + speed of an equivalent true Apple IIgs. This is extremely + accurate. +sec: The number of real seconds that have passed during on of KEGS's + emulated seconds. Should be 1.00 +/- .01. Under 1 + means KEGS is running a bit fast, over 1 means KEGS is + running slow. When you force speed to 2.5MHz, if KEGS + can't keep up, it extends sec, so you can see how slow + it's really going here. +vol: Apple IIgs main audio volume control, in hex, from 0-F. +pal: Super-hires palette that is unavailable. KEGS needs one palette + for the standard Apple // graphics mode on an 8-bit display, + and it grabs the least-used palette. Defaults to 0xe. + You can try changing it with F10. If you change it to a + palette that is not least used, KEGS changes it back in + one second. Any superhires lines using the unavailable + palette will have their colors mapped into the + closest-matching "lores" colors, to minimize visual + impact. +Limit: Prints which speed setting the user has requested: 1MHz, 2.8MHz, + or Unlimited. + +Line 2: (Video and X info) +xfer: In hex, number of bytes transferred to the X screen per second. +xred_cs: Percentage of Unix processor cycles that were spent in the X + server (or other processes on the machine). +ch_in: Percentage of Unix processor cycles spent checking for X input Events. +ref_l: Percentage of Unix processor cycles spent scanning the Apple IIgs + memory for changes to the current display screen memory, + and copying those changes to internal XImage buffers. +ref_x: Percentage of Unix processor cycles spent sending those XImage buffers + to the X server. Very similar to xred_cs. + +Line 3: (Interpreter overhead) +Ints: Number of Apple IIgs interrupts over the last second. +I/O: Rate of I/O through the fake smartport interface (hard drives). + Does not count 3.5" or 5.25" disk accesses. +BRK: Number of BRKs over the last second. +COP: Number of COPs over the last second. +Eng: Number of calls to the main instruction interpreter loop in the + last second. All "interrupts" or other special behavior + causes the main interpreter loop to exit. A high call + rate here indicates a lot of overhead. 12000-15000 is normal. + 20000+ indicates some sort of problem. +act: Some instructions are handled by the main interpreter loop returning + special status "actions" to main event loop. This is the + number over the last second. Should be low. +hev: This tracks HALT_EVENTs. KEGS returns to the main loop to recalc + effective speed whenever any speed-changing I/O location is + touched. See the code, mostly in moremem.c +esi: This counts the number of superhires scan-line interrupts + taken in the last second. +edi: This counts the number of Ensoniq "special events" over the last + second. A sound that stops playing always causes a KEGS + event, even if it doesn't cause a IIgs interrupt. + +Line 4: (Ensoniq DOC info) +snd1,2,3,4: Percentage of Unix processor cycles spent handling various + sound activities. snd1 is the total sum of all sound overhead. +st: Percentage of Unix cycles spent starting new Ensoniq oscillators. +est: Percentage of Unix cycles spent looking for 0 bytes in sounds. +x.yz: This final number is the average number of oscillators playing + over the last second. Up to 4.00 is low overhead, over + 20.0 is high overhead. + +Line 5: (Ensoniq DOC info) +snd_plays: Number of calls to a routine called sound_play, which + plays Ensoniq sounds. Always called at least 60 times per sec. +doc_ev: Number of Ensoniq (DOC) events in the last second. A sound + stopping is an event, but changing a parameter of a sound + while it is playing is also an event. +st_snd: Number of sounds that were started in the last second. +snd_parms: Number of times a sound parameter was changed while it + was playing. + +Line 6: (IWM info) +For each IWM device, this line displays the current track (and side for +3.5" disks). If a disk is spinning, there will be an "*" next to the +track number. Only updated once a second, so the disk arm moving may +appear to jump by several tracks. "fast_disk_emul:1" shows that KEGS +is using less accurate, but faster, IWM emulation. Press F7 to toggle +to accurate disk emulation. + + +Documentation To-Do: +------------------- + +Describe the tracing and breakpoint debug features. +Describe the debug interface. +Describe how the code works. +Describe more of what's known to work. +Describe my changes to SPEEDTEST. + +KEGS To-Do: +---------- + +Better serial port emulation (printing, comm) +Better nibblized images. +Fix the Ensoniq bugs to make sound more accurate. + +------------------- + +If you have any problems/questions/etc., just let me know. + +Special thanks to Jeff Smoot of climbingwashington.com for letting me use +the picture of a keg in the Mac icon. + +Kent Dickey +kadickey@alumni.princeton.edu + + +X Window (Linux) interface information: +-------------------------------------------- + +Every version of Linux is different. Supporting this is very difficult +especially since I do not run Linux myself. + +If KEGS fails to start, try the following options: + +kegs -audio 0 -noshm + +There may be a bug with drawing the border on x86 Linux with Shared Memory-- +add the options "-noshm -skip 0" to fix this up (but lose some graphics +performance, sorry). Try KEGS without these options first, but use +this as a workaround if necessary. + +If you want the display to go somewhere different, make sure the shell +environment variable $DISPLAY is set, or give the command-line argument +"-display {foo}". + +KEGS also forks off a subprocess to help handle the sound if audio is +active. If KEGS crashes in a unusual way (a core dump, for instance), +you may have to manually kill the subprocess. ("ps -ef| grep kegs;kill +xxxxx"). + +User geoff@gwlink.net adds some notes for mounting disks/floppies/CDs under +Solaris: + + To use a CDROM, insert the CD and let Volume Management mount it. + Edit kegs_conf and use the filesystem that shows up in the "df -k" + listing. The volume name of the CDROM must be included. For example, + a CDROM in an IDE drive would look like this: + + /vol/dev/dsk/c1t0d0/ciscocd + + A CDROM in a SCSI drive would look like this: + + /vol/dev/dsk/c0t6d0/j1170_10804 + +To provide low-level ADB emulation, KEGS turns off Unix key repeat when the +focus is in the KEGS window. It should be turned back on every time +the pointer leaves the KEGS window, but sometimes it doesn't. Re-running +KEGS (and then quitting it quickly) should turn key-repeat back on, +or you can type 'xset r' in another terminal window. + +Sometimes the converse is true--key repeat is "on" when the cursor is +in the KEGS window. Moving the cursor out of the window and then +back in should solve it. This is sometimes noticeable when running +Wolfenstein 3D GS. I haven't spent much time debugging the problem. +I think it may be the X Server. + +KEGS uses a private color-map for its X-window in 8-bit mode. This +may cause colormap "flash" when your cursor enters the window. + +KEGS details/troubleshooting +---------------------------- + +KEGS will work on all platforms with a 15/16-bit, 24-bit, or 32-bit +color display. KEGS also supports an 8-bit display on X windows only. +On all platforms, it autodetects the color depth--no color switching +is necessary as long as you're at a supported depth. + + +Disk Image Details + +Images loaded into slot 6 (drive 1 or 2) are assumed to be 140K +5.25" disks, which is usually have the extension ".dsk". Images +loaded into slot 5 (drive 1 or 2) are assumed to be 800K disk images +and can be in any supported imahe format (including partitions, if +you have 800K partitions). Images loaded into slot 7 (drives 1 +through 32) can be in any format and can be any size up to 4GB. + +KEGS boots s7d1 by default. You can change this using the emulated IIgs +control panel, just like a real Apple IIgs. KEGS emulates a IIgs with +two 5.25" drives in slot 6, two 3.5" drives in slot 5, and up to 32 +"hard drives" in slot 7. However, the current Configuration Panel only +lets you set through s7d11. + +Config.kegs file +---------------- + +KEGS saves your preferences and disk image names in the file config.kegs. +KEGS searches for this file in the directory KEGS was started in, in +your home directory, or in the Resources directory (on a Mac) of the app. +It needs to find one someplace, so putting it in your home directory is +usually the easiest. + +The config.kegs file is a simple text file. You need to quit KEGS before +editing the file. The BRAM data is also kept in this file, with separate +BRAM contents for ROM 01 and ROM 03 (so if you switch ROM versions, you +don't lose all your BRAM preferences). + +If you're trying to use a real host device (CD-ROM, or hard drive, or +floppy), you should make the permissions on the /dev/disk* files something +like (meaning, everyone should have read permission): + +brw-r--r-- 1 root operator 14, 0 Jun 10 00:01 /dev/disk2 + +You can do this on a Mac with: + +sudo chmod 644 /dev/disk2 + +Running KEGS as root is NOT recommended. + +The s6d* and s5d* drives support disk swapping and disk ejecting, but +the s7d* drives do not. + diff --git a/README.linux.partitions b/README.linux.partitions new file mode 100644 index 0000000..abfa8ed --- /dev/null +++ b/README.linux.partitions @@ -0,0 +1,252 @@ +[ This info provided by Mike Thomas ] +[ Updated 10/30/2003 by Kent: This file mentions editing "kegs.conf" to ] +[ mount images--this is now replaced by the built-in Configuration Panel. ] + +Setup and configuration for x86 Linux: +-------------------------------------- + +KEGS is very easy to setup on your Linux box, but not foolproof. First +you will need to decide where it will live. When doing this you will +have to take into consideration any users of your machine. It really +doesn't matter where it goes but it should have it's own directory and +any supplied sub-directories should be there. You may decide to use the +/usr/local path where most systems recommend you install applications. +Again, this is entirely up to you. On my system I have a separate +partition for KEGS and all the miscellaneous files I've accumulated for +it. This makes it easy for me to reinstall the system should the need +arise. Since I fool around with a large variety of software and OS's +this happens more often than it would for normal users. + +Once you have put the files into the proper place you will need to +compile it. You should not need to be 'root' to do this part. The file +README.compile explains the steps required for this. Basically all you +should need to do is set the vars link to point to the file +vars_x86linux. You will want to check the file and make sure it is +accurate for your system. On my Redhat 6.0 system the default compile +setup works fine. I use the pentium GCC compiler instead of the +supplied EGCS since it seems to build better binaries. I do not +recommend using optimization levels higher than 2. Once you have +successfully built the binaries you will need to copy them to the KEGS +directory. At a minimum copy the file kegs and to_pro. + +Ok, now that you have the binaries you're almost ready. You will need a +copy of the IIgs rom placed in the KEGS directory. It should be named +ROM. You will also need some disk images. This is the hardest part. +You will need to create an HD image to work with. Kent mentions an easy +way in his readme. From the shell type this: + +echo "testfile" > testfile +to_pro -16384 testfile + +If you're using bash try this: + +echo "testfile" > testfile +./to_pro -16384 testfile + +This should create a 16 megabyte HD image. This image will NOT be properly +formatted to boot a system. The block zero is not properly setup. There is no +easy way to fix this with the current KEGS/Linux system. There seems to be a +problem formating HD files for Prodos using KEGS. The system will easily erase +them but this doesn't help you. One solution is to make the primary boot drive +use a disk partition. This is more involved and will be explained later. +Another solution is to have access to the utility Block.Warden or some other +P8 block utility. What you need to do is copy the boot blocks (blocks 0 and 1) +from any bootable disk to the HD image. With that done you can now install +GS/OS. + +Make sure you set the proper file permissions on files needed by KEGS. You +will not be able to properly use it while logged on as root. KEGS uses the +file permissions to test if it should write the image to disk or the memory +image. As root, KEGS will never write the image since it thinks root +always has execute privilege. The main files which you will need read/write +access to are bram.data.1 and disk_conf. I suggest you have read access to all +the other files in the directory. + +Once you've got all the proper permissions set, log onto the system with your +normal account. Start up X and a shell and cd to the KEGS directory. Assuming +you have the disk images for GS/OS edit your disk_conf file so it looks +similar to this: + +# boot from install disk +s7d1 = /usr/local/fst/gsos1 + +# our HD image +# you should rename POOF1 file created with to_pro +# I use *.hdv to be compatible with other utilities +s7d2 = /usr/local/fst/boot.hdv + +# other GSOS images +s7d3 = /usr/local/fst/gsos2 +... + +If you include all the GSOS images this way you will have a simple setup. +Execute KEGS. For now go with the simplest command line possible. Since the +newer versions support auto detect on video resolutions this should be the +command kegs by itself. Hopefully you will boot into the emulator. If so, +install GSOS and you're ready to go. + + +Sound +----- + +Kent says sound doesn't work under Linux. This is partially true and much +depends on the sound system you have installed. I have been successful with +sound on both Soundblaster and Soundpro systems. For long compositions the +sound may break up but it is functional for games and system sounds. + + +System Video Setup +------------------ + +This is rather personal and based upon system hardware so I'll just give you my +thoughts and setup info on this. My normal X system is configured +@ 1152x864 15bpp due to constraints in the X server for my video system. I +have custom configured it to boot into this mode and then I change to 800x600 +by using the CTRL+ALT+(keypad)PLUS sequence when I use KEGS. This makes the +system much easier to read. + + +KEGS and disk partitions +------------------------ + +Kent mentions using partitions in his readme file but doesn't go into much +details. I suspect this is mostly for accessing CD-roms. But it also works +well for HFS and Prodos formatted partitions also. Linux can also handle HFS +partitions but not Prodos. To accomplish this you will need some free space on +your hard disk to create the partitions. You should not attempt this unless you +know what you are doing. You have been warned! + +This task is not easy, nor is it supported by Kent. This was done and tested +on my own system so I know it works. You will need the HFS support utilities +for Linux. These are available on several Linux software sites. The primary +need for these utilities is to change an ext2fs partition to an HFS partition. +You can also use them to format the HFS volumes and copy files to and from +the partition. Newer versions of the Linux kernel support HFS file systems but +you will still need the utilities to create the original volume. + +You must decide how you want to divide this partition. You can use it all for +HFS or you can create Prodos volumes and HFS volumes. There are pros and cons +for using Prodos partitions instead of files. The pros, it is a little faster +than using an HD file. It is a real Prodos partition, formatted by KEGS. The +cons, It must be backed up by using software on the emulator. You can't just +copy the HD file to another drive. + +You must weigh these pros and cons and decide for yourself. Of course you +are not limited to using partitions. I have a mix of partitions and files +which works quite well. I like the P8 partitions for holding my boot system +and applications. I back them up with GSHK to an HFS volume which I can then +transfer to another drive if I need even more security. + +If you decide to use the whole partition for HFS then all you need to do is +run the HFS utilities and setup the HFS volume. Read the documentation which +comes with the utility package and follow it faithfully. + +If you want to use P8 and HFS partitions you have some more work to do. If +you have never worked with low level partitions or are worried about destroying +your HD then you should probably forget this. For this to work you will have +to change the partition table on your HD. This can and most likely will ruin +any data you already have on there. I can not state this enough. Back up any +important data on the hard drive! It is possible to change the partitions in +Linux and not destroy the system. I have done this on mine but I also used +the last defined partition to make the changes and designed the system for +change should it be necessary. + +For those of you who know how to handle this, take the partition you have +decided to use for KEGS and divide it into at least one 32 meg partition. +More is better but you will have to use the emulator to back up any drives +setup this way since Linux can't access a Prodos partition (yet). I have setup +4 32 meg P8 partitions and several smaller HFS partitions of about 96 megs. +The reason I use smaller HFS partitions is because HFS isn't real efficient +with larger drives, but that's another story. The reason for the separate +HFS partitions is so Linux can mount the HFS volumes like any other file system. +I find this works quite easily for accessing files instead of using the HFS +utilities. Just my opinion though. For P8 utilities you will still need to +use the HFS utility and configure the drive as an HFS volume. This lets KEGS +read the partition when it loads the partition the first time. KEGS doesn't +like the Linux file system. + +Ok, everybody got their partitions defined? You want to use the HFS tools +and setup all the partitions (P8 and HFS) as HFS volumes. Next you will have +to setup the HFS partitions. No, I'm not repeating myself. This is not the same +thing as the low level partitions. HFS volumes have their own partition table. +For our purposes the table will only hold one partition on each whole volume. +The utility will give you the block count when you setup the partitions, +write it down so you don't forget. After you have the volume partition setup +you can format the drive. Yeah I know you made a Prodos partition but it +doesn't hurt anything and KEGS will be able to read the partition when it +boots up. + +Well, I think I covered everything needed to set them up. Now you will need to +edit the /etc/fstab file. Make sure there are no references to the original +partition. If you want to access the HFS volumes you will need to add them to +this file. You will also need to make sure that your Linux can understand the +HFS format. Most newer kernels will as long as you've compiled it into the +kernel or set it up as a module. KEGS doesn't need these entries to access +the volumes, they are just here for your convenience. In fact, if you don't +need Linux access I suggest you either leave them out or set them up as +NOAUTO and mount them only when needed. Unmount them when finished to avoid +any potential problems also. Do not give Linux access to any P8 partitions. +For one thing it can't recognize them and most likely will give you problems +when you boot the system. For safety's sake the only partition I have listed +in my /etc/fstab is a volume called transfer. You must set the filetype to HFS +so Linux doesn't complain about the partitions if you mount them. + +Ok, all partitions are defined, the /etc/fstab is setup correct, now you need +to change the permissions on the device files associated with the partitions. +This allows you to access the files as a normal user. (Thanks Kent, guess I +got too involved and forgot it should be treated like the CD). You will need +to reboot to ensure the system sees the new partitions and has the correct +/dev/hd?# device files. If you setup the partitions with fdisk you should know +the correct hd info to select the files. For the sake of example let's assume +the device is HDB and the partitions numbers are 1,2,3. From the shell, + +cd /dev +chmod 666 /dev/hdb1 +chmod 666 /dev/hdb2 +chmod 666 /dev/hdb3 + +After you start KEGS you can format the Prodos partitions. If you use the +method mentioned earlier for installing GS/OS you will want to quit the +installer and run the advanced disk utilities on the utilities disk, then +quit back to the installer program or reboot. + +Now I know this all sounds like a lot of trouble but (IMHO) it's worth it. For +one thing, KEGS will format a Prodos partition without problems (unlike an HD +file image) which makes a great boot system. And with GS/OS 6.01 you can access +large HFS volumes for storage and GS applications. You can also download from +the net to the HFS volume (if it's mounted) and avoid the trouble of copying +files to an image with to_pro. Not to mention the fact that the newest GNO +works with HFS drives. + +One more note, if you use HFS you will need to patch the HFS fst. There is a +one byte bug which mis-calculates HFS block sizes and WILL trash your HFS +drive. The patch is at several places on the net or ask someone in one of +the comp.sys.apple2 news groups. + +Miscellanea +----------- + +To ease access to the KEGS binary, make an executable script which contains +any command line parms you need. Then put this script somewhere in the path +so you can execute it from any shell. Depending on the desktop you use you +may want to setup links for editing the disk_conf file also. With GNO/ME you +can launch KEGS without the shell but I don't recommend this since you won't +know what happened when it dies. With KDE you can set up the launcher to use +a shell, this is much better but until you have your setup stable you will +want to use a regular terminal so you can keep track of what's going on. Like +GNO/ME, the KDE shell will close as soon as KEGS quits with an error. + + +Thanks +------ + +I hope this info helps you enjoy KEGS. Many thanks to Kent for creating this +fine emulator and for putting up with me bugging him with 'bug' reports. Many +of these weren't actually bugs but were my own fault due to lack of knowledge +about *nix systems. But Kent was prompt in fixing the ones which truly were +bugs. Thanks to him I can now play my favorite game, Questron 2 (gs version) +which requires a player disk in slot 5. I know no other emulator which can +actually play this game. + +Mike Thomas + diff --git a/README.mac b/README.mac new file mode 100644 index 0000000..92c5c0a --- /dev/null +++ b/README.mac @@ -0,0 +1,41 @@ + +MAC OS X port of KEGS (KEGSMAC): http://kegs.sourceforge.net +------------------------------------------------------------- + +There is a different port of KEGS to Mac OS X called KEGS-OSX. +You can get it from http://casags.net/. + +This port is not leveraged from KEGS-OSX (which is based on SDL). + +This is a Mac OS X Carbon port. It will not work on Mac OS 9. + +Everything pretty much works, but 8-bit color doesn't work. Make sure your +Mac is set to Thousands or Millions of colors. + +Usage: +----- + +Like most other KEGS versions, KEGSMAC is usually run from a Terminal +window. Just type "./KEGSMAC.app/Contents/MacOS/KEGSMAC" in the directory +you installed/compiled it in. You need to have a ROM file (named +ROM, ROM.01, or ROM.03) and a config.kegs in the same directory or in your +home directory (read the README--these files are searched for in various +places). + +KEGSMAC can also be run from the Finder, but if you do this, there +will be no debug window at all. This is not well tested, yet. + +To quit, either click the window close box, or select Quit from the menu. +You can also middle-click (if you have a 3-button mouse) or +Shift-F6 to get the debugger in the terminal window, and then type "q". + +Compile directions +------------------ + +In order to compile, + +1) cd into the src/ directory +2) copy vars_mac to vars +3) run make + +You can contact me at kadickey@alumni.princeton.edu diff --git a/README.win32 b/README.win32 new file mode 100644 index 0000000..182958a --- /dev/null +++ b/README.win32 @@ -0,0 +1,51 @@ + +WIN32 port of KEGS (KEGSWIN) +---------------------------- + +There is a different port of KEGS by akilgard called KEGS32. +You can get it from http://www.geocities.com/akilgard/kegs32. +This port is leveraged from KEGS32, but mostly a rewrite (perhaps +for the worse). The joystick code was taken without too many +modifications. + +This port is alpha quality. Seriously. Don't expect much. + +This is a bare-bones Win32 port. It was compiled with Mingw2.0, +which you can download at: http://www.mingw.org/. I also had +previously installed cygwin at http://www.cygwin.com/. Installing +these two beasts is a bit of a pain, so I'll eventually write up +directions (I hope). + +Sound works, the mouse works, and a joystick might work (ported +from KEGS32). The user-interface is just like every other KEGS +version (i.e., bad), so you can just read the standard README file. + +Only tested on a 32-bit graphics display, although I think 16-bit and +24-bit will work. 8-bit definitely does not work. There are many +other bugs I just haven't had time to list yet. + +Usage: +----- + +Like most other KEGS versions, KEGSWIN must be run from a terminal +window (command.com is fine). Just type "KEGSWIN" in the directory +you installed/compiled it in. You need to have a ROM file (named +ROM, ROM.01, or ROM.03) and a kegs_conf in the same directory (or +read the README--these files are searched for in various places). + +To quit, either click the close box, or force quit the application. +You can also middle-click (if you have a 3-button mouse) or +Shift-F6 to get the debugger in the terminal window, and then type "q". + +Compile directions +------------------ + +In order to compile, + +1) cd into the src/ directory +2) rm vars +3) ln -s vars_win32 vars +3) ./make_win + + +You can contact me at kadickey@alumni.princeton.edu diff --git a/config.kegs b/config.kegs new file mode 100644 index 0000000..bef86d2 --- /dev/null +++ b/config.kegs @@ -0,0 +1,11 @@ +# KEGS configuration file version 0.80 + +s5d1 = XMAS_DEMO +s5d2 = + +s6d1 = #dos33.dsk +s6d2 = + +s7d1 = NUCLEUS03 + + diff --git a/kegswin.exe b/kegswin.exe new file mode 100755 index 0000000000000000000000000000000000000000..b6c6563ee01dd6fbb51b106928bba99e42781737 GIT binary patch literal 388517 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 z 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Μ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 b!>^699C-mweHLv86C*eICsQP)v)2Cw@@GJb literal 0 HcmV?d00001 diff --git a/src/INTERNALS.iwm b/src/INTERNALS.iwm new file mode 100644 index 0000000..3be8c16 --- /dev/null +++ b/src/INTERNALS.iwm @@ -0,0 +1,239 @@ + +KEGS's Apple //gs IWM emulation routines. + +The IWM code does 5.25" and 3.5" reads & writes, and updates the Unix disk +image on writes. It is also nearly cycle-accurate--Let me know if you +have a program which can detect it's not a real Apple II. There are +a few 5.25" features missing (No 1/4 or 1/2 tracks, no support for Unix nibble +images, limited disk switching), but what's there is pretty accurate. +The low-level code support 1/4 and 1/2 tracks--it's the arm movement +and image-handling routines which don't. And lack of Unix nibble images +are also due to lack of higher-level routines to make those features work. + +How my disk emulation works: The routines have a nibblized image of each +track of each drive (two 5.25" and two 3.5" drives are supported) in memory. +The nibble images are declared as arrays, but it could be made to use +more dynamic memory allocation. + +Each track's data format is a series of two-byte pairs. The first byte +of the pair is the number of bits in this disk byte, and the second byte +is the value. So a size of 8 is normal. A size of 10 means that there +are 2 sync bits written before this byte on the disk. So for 5.25" disk +accesses, 40 cycles need to pass in the simulator before providing a +valid nibble. Partial nibbles are correctly formed if a read happens +too early (this actually makes things slower, but is required if you +want to make nibble copiers work). Similarly, writing to the disk +watches timing carefully to write out the correct number of bits per +disk byte. These routines will definitely test out your emulator's cycle +counting ability. + +If a long delay occurs between a read (or a write) the routines skips +the correct number of bits to return the correctly formed disk byte. +After a long delay, for efficiency, I always return a full disk byte, +instead of a partial one, even if the timing would put it in the middle +of a disk byte. + +The arm stepping is really lame. I will clean it up soon. + +Smartport support is sufficient to claim that there are no smartport +devices. This is necessary since the ROM tries to see if there are +smartport devices at power-on. + +I tested my 5.25" drive routines on EDD, which could correctly measure +drive speed and other disk factors. I also nibble-copied some disks, +which also worked fine. I tested the 3.5" routines using Copy II+, +which successfully nibble-copied several disks. + + +Code description: + +Most code is in iwm.c, with some defines in iwm.h, and some stuff in +iwm_35_525.h. + +Code only supports DOS3.3 ordered 5.25" images now, and ProDOS-ordered 3.5" +images. Well, the code supports ProDOS-order 5.25" also, but has no +mechanism to tell it an image is prodos-order yet. :-) + +Iwm state is encoded in the Iwm structure. + + drive525[2]: Disk structure for each 5.25" disk + drive35[2]: Disk structure for each 3.5" disk + smarport[32]: Disk structure for each "smartport" device emulated + via slot 7 (this code not included) + motor_on: True if IWM motor_on signal (c0e9) is asserted. Some + drive is on. + motor_off: True if motor has been turned off in software, but the + 1 second timeout has not expired yet. + motor_on35: True if 3.5" motor is on (controlled differently than + 5.25" c0e9). + motor_off_vbl_count: VBL count to turn motor off. + head35, step_direction35: 3.5" controls, useless. + iwm_phase[4]: Has '1' for each 5.25" phase that is on. + iwm_mode: IWM mode register. + drive_select: 0 = drive 1, 1 = drive 2. + q6, q7: IWM q6, q7 registers. + enable2: Smartport /ENABLE2 asserted. + reset: Smartport /RESET asserted. + previous_write_val: Partial write value. + previous_write_bits: How many bits are valid in previous_write_val. + +Each disk (3.5" and 5.25") is encoded in the Disk struct: + fd: Unix file descriptor. If < 0, no disk. + name_ptr: Unix file name for this disk. + image_start: offset from beginning of file for this partition. + image_size: size of this partition. + smartport: 1 if this is a smartport image, 0 if it is 5.25" or 3.5" + disk_525: 1 if this is a 5.25" image, 0 if it is 3.5" + drive: 0 = drive 1, 1 = drive 2. + cur_qtr_track: Current qtr track. So track 1 == qtr_track 4. + For 3.5", cur_qtr_track encodes the side also, so track 3 + side 1 would be qtr_track 7. + prodos_order: True if Unix image is ProDOS order. + vol_num: DOS3.3 volume number to use. Always 254. + write_prot: True if disk is write protected. + write_through_to_unix: True if writes should be passed through to + the unix image. If this is false, you can write + to the image in memory, but it won't get reflected + into the Unix file. If you create a non-DOS3.3 + or ProDOS format image, it automatically sets this + false. + disk_dirty: Some track has dirty data that need to be flushed. + just_ejected: Ejection flag. + dcycs_last_read: Cycle count of last disk data register access. + last_phase: Phase number last accessed. + nib_pos: Nibble offset ptr--points to a byte. + num_tracks: Number of tracks: 140 for 5.25" and 160 for 3.5" + track[MAX_TRACKS]: nibble image of all possible tracks. + +Each track is represented by the Track structure: + track_dirty: Contains data that needs to be written back to + the Unix image file. + overflow_size: Count of overflow bits, used in writing. + track_len: Number of nibbles on this track. + dsk: Handy pointer to parent Disk structure. + nib_area[]: ptr to memory containing pairs of [size,data], + encoding disk data bytes. + pad1: If the structure is 32 bytes long, some array + indexing is done better by my compiler. + + +Externally callable routines: +iwm_init(): Init various data structures at simulation start. +iwm_reset(): Called at Apple //gs reset time. +iwm_vbl_update(): Called every VBL (60 Hz) period. Used to turn motor + off, and flush out dirty data. + g_vbl_count is the count of VBL ticks (so it counts + at 60 times a second). +iwm_read_c0ec(double dcycs): Optimized routine to handle reading $C0EC + faster. Exactly the same as read_iwm(0xc, dcycs); +read_iwm(loc, dcycs): + Read from 0xc0e0 + loc. Loc is between 0x0 and 0xf. + Dcycs is an artifact from my simulator. Dcycs is a + double holding the number of Apple //gs cycles since the + emulator started. Dcycs always counts at 1.024MHz. If + you are running at 2.5MHz, it increments by 0.4 every + "cycle". This is a very convenient timing strategy. It + also allows emulating the delay caused by synchronizing + the fast part of a real Apple //gs with slow memory, + which means my emulator knows that reading softswitches + takes longer than reading fast memory. +write_iwm(int loc, int val, double dcycs): + Write to 0xc0e0 + loc. Just like read_iwm, but write "val" into + loc. + + +Tricky routines: + +IWM_READ_ROUT(): called by read_iwm() if q6,q7 = 0,0. + This is actually in the file iwm_35_525.h. This is so I + write the basic code once for 5.25" and 3.5" disk reads, + but then include the file with some macros set to create + the correct function optimized for 5.25" or 3.5" + accesses. The function for 5.25" is called + iwm_read_data_525, and iwm_read_data_35 for 3.5". + Returns next disk byte. + Takes three arguments: ptr to the Disk structure for + the active drive, fast_disk_emul, and dcycs. dcycs is + so that it can see how many cycles have passed since + the last read (stored in dsk->dcycs_last_read). + 16.0 dcycs need to pass for an 8 bit nibble for 3.5" + accesses, and 32.0 dcycs for an 8 bit nibble for 5.25". + Fast_disk_emul == 1 says don't mess around with accuracy, + and always return the next fully-formed nibble. + There is a lot of complexity in this routine. All IWM + routines must skip over nibbles (stored as byte pairs in + dsk->nib_area[]) which have a size of 0 (special padding + trick, described later). It then determines how much + time has passed, and so how many bits are valid. + If too many bits have gone by (11 cycs is almost 3 5.25" + bit times, which is about the nibble valid time in + the Apple //gs IWM hardware latch), it tries to skip + to the correct position. + Handles IWM latch mode for 3.5" or 5.25" accesses. If a + partial read is indicated, it ensures that the high bit + is clear by shifting the nibble to the right + appropriately. Again, nib_area[] is an array of bytes, + which are treated as pairs. Byte 0 = size, byte 1 = + disk nibble. + +IWM_WRITE_ROUT(): called by write_iwm() if q6,q7 = 1,1. + Similar to above. Handles async and sync mode writes. + Handles partial writes. Handles the ROM writing + 0xff, 0x3f, 0xcf, 0xf3, 0xfc to be four 10-bit nibbles. + Routine disk_nib_out(dsk, val, bits_read) does the + actual work of merging the bits into the track image. + +disk_nib_out(): called by IWM_WRITE_ROUTE() and iwm_nibblize_track_*(). + Writes byte into nib_area[]. If size > 10, makes it 10. + If high order bit not set, it sets it (makes certain routines + in EDD happy). + +overflow_size: + Writing to the disk creates some problems. I need to + maintain 2 things at all times on the track: + 1) Constant number of bits for the entire track. + 2) know where each synchronized byte starts on + the track. + If the track was just stored as raw bits, then correctly + simulating a delay of 300*4 cycles is tough, since it has to + be done by reading through all 300 bits on the track, + so that we keep in sync with where bytes really start. + But if you just store the bytes themselves, then sync + bytes look like every other byte. And if you now add + the size field, you have a situation where a track could + gain or lose bits when rewritten. Here's the case: + Assume the track contains: 10,ff 10,ff 10,ff 10,ff. + (That is 4 self-sync disk bytes of 10 bits each). + If we rewrite that area of the track with 'D5 AA 96 FF', + where each byte is 8 bits, we would have: + 8,D5 8,AA, 8,96, 8,FF. + Looks OK, but we just lost 8 bits! The original 4 nibbles + were using 40 bits of space on the disk. Our new 4 nibbles + are using 32 bits. 8 bits are lost. + Solution: log these missing bits via overflow_size. + When writing, if overflow_size gets > 8, force out a 0,0 + nibble. So sync bytes get written as: + 10,FF 10,FF 10,FF 10,FF 0,0 10,FF 10,FF 10,FF 10,FF, 0,0. + So when they get re-written with 8,xx, we don't lose any + bytes on the disk. + + Unfortunately, it doesn't quite work that easily, and bits + can still be lost when sync fields are partially overwritten. + This happens when all the 0,0's end up in a place on the + track where few overwrites occur, but other sync bytes + are being turned into 8,xx. So overflow_size goes negative, + saying we've got too much on the track. + The code prints an error when it gains more than 64 bits. + If someone can come up with a better scheme, I'd love to + hear it. A partial solution would be to have a routine + re-space the track to spread the needed 0,0's around + a little better when overflow_size gets too negative. + + +In iwm_nibblize_track_35(), the comments with hex numbers correspond +to the ROM 01 addresses which I disassembled to determine the checksum +algorithm. The code is not well written--it's basically hand-translated +65816 assembly code. I'll clean it up someday. + +Much of the code is not well-optimized. I'll get to that someday, but +the speed has been adequate for me so far. diff --git a/src/INTERNALS.overview b/src/INTERNALS.overview new file mode 100644 index 0000000..ef92495 --- /dev/null +++ b/src/INTERNALS.overview @@ -0,0 +1,96 @@ + +KEGS Internals +-------------- + +The INTERNALS* files describe the internal structure of KEGS and how +it works. It is meant to be useful to those who would attempt to +port KEGS to non-Unix platforms, or just want to know how KEGS works. + +Documentation files: +-------------------- + +INTERNALS.overview: Provides overview of KEGS's file structure + + +KEGS SOURCE FILES: +----------------- + +adb.c: ADB emulation routines. This includes keyboard, mouse, and + joystick. +adb.h: Defines for ADB routines. +clock.c: Clock, BRAM, and some timing routines. +compile_time.c: Trick file to put the time you compiled in g_compile_time[]. +defc.h: Global defines included by all .c files. Useful trick + to avoid complex multi-level include problems. +defs.h: Global defines included by all .s files (assembly language). +defcomm.h: Global defines included by all files (must be assembly and + C safe, such as #defines). +defs_instr.h: C and assembly definitions for various addressing modes + and for some repeated instructions (like LDA, STA, etc). +dis.c: Disassembler and debugger interface. The debugger interface + is similar to the Apple // monitor, but has many + more commands. +disas.h: Tables for disassembling 65816 instructions. Not very + efficient, but it works. +engine_c.c: C main instruction dispatch loop. +engine_s.s: Assembly main instruction dispatch loop. +instable.h: Instruction table. It is C and assembly-safe through + the make_inst script. make_inst turns instable.h into + 8inst_c and 16inst_c, which are both valid C code. +iwm.c: IWM routines for 5.25" and 3.5" disks. See INTERNALS.iwm +iwm.h: IWM defines +iwm_35_525.h: File for reading and writing a disk byte, which is included + in iwm.c twice, once for 5.25" and again for 3.5". + Forcing out compile-time constants this way makes it + faster. +moremem.c: Awful name--this file contains the page table change + routines (fixup_*) and io_read() and io_write() to + emulate all $C000 I/O accesses. +op_routs.h: More macros for 65816 emulation. +protos.h: Prototypes for all C functions. Auto-generated through + the "cproto" program (not included). +protos_xdriver.h: Prototypes for functions in xdriver.c. +scc.c: Serial chip emulation. +scc_driver.h: Unix-specific socket routines, stubbed out if you're not + on Linux or HP-UX. +scc.h: Defines for scc.c. +sim65816.c: main() is here along with many other housekeeping + functions, such as events, and interrupts. + The main loop of KEGS is run_prog(). +size_tab.h: Used by assembly for a jump table (make_size script + turns it into 8size_s and 16size_s) and by both C and + assembly to get the size of instructions. +smartport.c: Smartport emulation, emulates s7dx devices. +sound.c: Sound emulation. Builds sound samples, but does not + send sound to output device. +sound.h: Header file for sound.c. +sound_driver.c: Sound driver routines. Takes samples generated by sound.c + and sends them to the correct output device. Supports + HP Alib, HP /dev/audio, and Linux /dev/dsp currently. +superhires.h: "macro" routine used by video.c so that I could write + one generic superhires routine, but then include it + multiple times to get optimized 320 vs 640 mode routines. +video.c: Display routines. Builds 8 bit video buffers in a + device independent way. Functions here know nothing + about X windows. +xdriver.c: X windows driver routines. Takes buffers from video.c + and sends them on to X windows. Get keypresses from + X and sends them to adb.c. + +Porting Advice: +-------------- + +To a non-unix platform, disabling scc emulation would be a good start. +Just make sure you get the dummy stub routines in scc_driver.h. + +If you don't have gettimeofday(), clock.c will need to modified with +whatever timer facilities are available. A high-resolution clock works +best, but even a low-resolution clock will work. + +Modify sound_driver.c to get it to compile, if necessary. Always run with +"-audio 0" to turn audio off. No routines in sound_driver.c will get +called if you run with -audio 0. + +Replace xdriver.c with new routines for whatever platform you are porting to. +See INTERNALS.xdriver for more information. + diff --git a/src/INTERNALS.xdriver b/src/INTERNALS.xdriver new file mode 100644 index 0000000..bea9011 --- /dev/null +++ b/src/INTERNALS.xdriver @@ -0,0 +1,148 @@ + +xdriver.c contains the routines for interfacing to X windows. The rest +of KEGS interacts with X windows only through xdriver.c routines. + +Externally called routines are: +show_xcolor_array(): Debug routine, it does not need to do anything. +dev_video_init(): Called at startup, it should open up the + window and do other initialization. +update_physical_colormap(): Updates the X windows palette with the colors + from xcolor_a2vid_array[], which is maintained by + other xdriver routines. +update_status_line(): Call to update the internal array of chars + representing the status lines at the bottom of + the window. Does not draw the chars to the screen. +xdriver_end(): Shutdown routine +check_input_events(): Called up to 60 times a second (see video_update() in + video.c) to handle any X window events and get + keypresses. + On a mouse press, call update_mouse() with the + new x, y coordinates, and the status of the mouse + button. + If g_warp_pointer is set, constrain mouse within + the window. + On keypress, calls handle_keysym(). + handle_keysym(): Takes X keysym, and converts to the appropriate + a2code using the a2_key_to_xsym[] lookup table. + The a2codes are the Apple // ADB keycodes. + Special work is done to handle shift and control + properly since Apple only has one keycode for both + shift and control keys. Then call + adb_physical_key_update() with the a2 keycode and + is_up = 1 if keyup, 0 = if key down. + In addition, this routine handles all the Function + keys doing special actions, which should be easy to + port. +x_refresh_ximage(): Redraws the window using the a2_line_* arrays. + Described in more detail below. +update_color_array(): Interface to the color map. Sets color[col_num] + of the internal colormap array to a2_color. + a2_color is the 12 bit apple color of the form: + (red << 8) + (green << 4) + (blue). + There are 16 palettes of 16 colors each, managed as + one 256-color colormap. See discussion of + g_a2vid_palette below. +x_auto_repeat_on(): The X routines turn off key repeat when the cursor + enters the graphics window automatically, and turn + it back on when the cursor leaves. But if the + debugger gets control due to a breakpoint, keyrepeat + would be left off. So the debugger calls this + routine to make sure key repeat is back on. +redraw_status_lines(): Draw the status lines from the g_status_buf[][] array + to the graphics window. + +Externally referenced data: + +g_use_shmem: Set by main() to enable/disable MIT-SHM for X. + Also used by sound routines to auto-turn-off sound + if not using MIT-SHM. + +Bytes representing screen data: +byte *data_text[2]: Array of bytes for the lores and text pages 1 and 2. + Just 400*640 bytes. +byte *data_hires[2]: Array of bytes for the hires pages 1 and 2. +byte *data_superhires: Array of bytes for superhires screen. +byte *data_border_sides: Array of bytes representing the border sides. + Basically just A2_WINDOW_HEIGHT*EFF_BORDER_WIDTH bytes. +byte *data_border_special: Top and bottom border bytes. + (X_A2_WINDOW_HEIGHT - A2_WINDOW_HEIGHT + 2*8) * + (X_A2_WINDOW_WIDTH) bytes. + +Handles used for X windows drawing: +XImage *ximage_hires[2]: Opaque handle to XImage object for hires page 1 and + page 2. +XImage *ximage_text[2]: Text pages 1 and 2. +XImage *ximage_superhires: Superhires graphics XImage +XImage *ximage_border_special: Top and bottom border XImage. +XImage *ximage_border_sides: Left and right sides (only one copy, it is + drawn at two different locations to be both sides). + +Basic operation of xdriver: +-------------------------- + +X windows can push arrays of bytes to the screen through structures +called XImages. An XImage is a structure describing an offscreen bitmap. +For efficiency of page flipping, KEGS maintains separate bitmaps for the +two lores/text screens, the two hires screens, and the superhires screen. +It also maintains bitmaps for the border. For MIT-SHM to work, X +requires a unique XImage for each bitmap, and the bitmap must be allocated +within xdriver.c since it must be obtained through an shmat() call. +The X code also has non-MIT-SHM code which allocates the data_* buffers +just through malloc(). + +All bitmaps are 8-bits of Pseudo-color. The color arrays are managed +through the update_color_array() and update_physical_colormap() routines. +KEGS manages all 256 colors in the colormap as 16 palettes of 16 colors. +One of the palettes is reserved for the 16 lores colors, and is +indicated by the variable g_a2vid_palette. It defaults to 0xe. +Update_color_array() is called to update superhires colormap entries. +Update_color_array must not update colors corresponding to g_a2vid_palette. +Update_physical_colormap() pushes the color array managed by +update_color_array() to the screen, but first forces the lores colors into +the g_a2vid_palette palette. g_installed_full_superhires_colormap is +always false in KEGS for now. video.c calls update_color_array and changes +g_a2vid_palette. No xdriver routines gets notified when g_a2vid_palette +changes, so update_physical_colormap must handle the case where +g_a2vid_palette might have changed since it was last called. + +x_redraw_ximage(): +Routines in video.c are free to draw into the corresponding data_* +arrays to change any byte at any time. video.c manages remembering +which lines need to be redrawn and which parts of the screen are in +which video mode via the a2_line_* arrays. + +KEGS divides the video screen up into 25 groups, corresponding to each +text line. Each of these groups consists of 16 sublines. 25*8 = 400 lines. +(video.c has already doubled the vertical resolution in all video modes). +KEGS can allow any group to be from any of the five screens it manages: +The two text/lores pages, the two hires pages, and the superhires screen. +For each group, KEGS keeps track of what part of it needs to be redrawn. +g_a2_screen_buffer_changed has a bit set for each group which has changed +since the last call to x_redraw_ximage(). The rightmost bit (bit 0) +corresponds to group 0. If g_a2_screen_buffer_changed == 0, no groups +need to be redrawn. x_redraw_ximage clears out g_a2_screen_buffer_changed +after drawing the screen. + +For each group, a2_line_left_edge[] and a2_line_right_edge give the pixel +offsets of what should be redrawn. a2_line_xim[] gives the ximage handle +of what needs to be redrawn. KEGS always redraws 8 verticals of a group. +g_full_refresh_needed also has one bit set in it for each group, which +indicates overriding the a2_line_*_edge functions and redraw from 0 to +640 pixels of each group that needs to be redrawn. x_redraw_ximage() +interprets this information now using a simple algorithm: Skip over +groups which have not changed (using g_a2_screen_buffer_changed). +Save the ximage of this group, the left pixel and the right pixel. +Continue with the next group if it has changed. Widen the pixel region +and keep sucking up new groups to the same ximage. At group 25, or +when the ximage changes, call x_refresh_lines to redraw this large +rectangle from this ximage. x_refresh_lines() knows the ximage +corresponding to the border for the last group has to be handled +specially since the border group is not 640*400 pixels like the others. + +Other porting info: +a2_key_to_xsym[][3] contains the mapping function from X keysyms to +a2 keycodes. The first element is the a2 keycode, the second element +is the unshifted X keysym, and the third element is the shifted keysym. +A port must make the conversion to a2 keycodes, and provide up and +down events. + diff --git a/src/Info.plist b/src/Info.plist new file mode 100644 index 0000000..15a616a --- /dev/null +++ b/src/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + KEGSMAC + CFBundleIconFile + kegsicon.icns + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + APPL + CFBundleSignature + ???? + CFBundleVersion + 0.1 + CSResourcesFileMapped + + + diff --git a/src/InfoPlist.strings b/src/InfoPlist.strings new file mode 100644 index 0000000000000000000000000000000000000000..036ca1e23a43b6c58f15c4bd041c59f6c2bf1217 GIT binary patch literal 520 zcmbu5OKZYV7(~yyzaqF4u_7XZxM;19Vs&A_eSEf5qeNS_@&cHeM`|RJ`b&*ZV-?9^Zm%UF}c{T5RnzVW3 zL-thOjDh+qXF$U@M~@aALi#+&%~8)d6Y{E4qQmG{nouM$Sw-gW+fl8I|nZ#Z?- z-~HU}Kl%j|HBG&-k8{R_cQ`jxHbIGstDatQiaohMxpY^&BpcXC*WOn46*H6@* ../KEGSMAC.app/Contents/PkgInfo + cp -f Info.plist ../KEGSMAC.app/Contents/ + cp -f InfoPlist.strings ../KEGSMAC.app/Contents/Resources/English.lproj/ + cp -f info.nib ../KEGSMAC.app/Contents/Resources/English.lproj/main.nib + cp -f classes.nib ../KEGSMAC.app/Contents/Resources/English.lproj/main.nib + cp -f objects.xib ../KEGSMAC.app/Contents/Resources/English.lproj/main.nib + cp -f kegsicon.icns ../KEGSMAC.app/Contents/Resources/ + touch '../KEGSMAC.app/Icon?' + +# Linux for X builds: +xkegs: $(OBJECTS) compile_time.o + $(CC) $(CCOPTS) $(LDOPTS) $(OBJECTS) compile_time.o $(LDFLAGS) -o $(NAME)$(SUFFIX) $(XLIBS) $(EXTRA_LIBS) -lX11 + mv xkegs .. + +# Cygwin for X builds: +kegs.exe: $(OBJECTS) compile_time.o + $(CC) $(CCOPTS) $(LDOPTS) $(OBJECTS) compile_time.o $(LDFLAGS) -o $(NAME)$(SUFFIX) $(XLIBS) $(EXTRA_LIBS) -lXext -lX11 -lm + mv kegs.exe .. + +# Mingw32 (native windows) builds: +kegswin.exe: $(OBJECTS) compile_time.o + $(CC) $(CCOPTS) $(LDOPTS) $(OBJECTS) compile_time.o $(LDFLAGS) -o $(NAME)$(SUFFIX) $(EXTRA_LIBS) -lwinmm -lgdi32 -ldsound -lcomctl32 + mv $(NAME)$(SUFFIX) .. + + +8inst_c.h: instable.h + $(PERL) make_inst c 8 instable.h > 8inst_c.h + +16inst_c.h: instable.h + $(PERL) make_inst c 16 instable.h > 16inst_c.h + +size_c.h: size_tab.h + $(PERL) make_size c size_tab.h > size_c.h + +engine_c.o: 8inst_c.h 16inst_c.h size_c.h + +8inst_s.h: instable.h + $(PERL) make_inst s 8 instable.h > 8inst_s.h + +16inst_s.h: instable.h + $(PERL) make_inst s 16 instable.h > 16inst_s.h + +size_s.h: size_tab.h + $(PERL) make_size s size_tab.h > size_s.h + +8size_s.h: size_tab.h + $(PERL) make_size 8 size_tab.h > 8size_s.h + +16size_s.h: size_tab.h + $(PERL) make_size 16 size_tab.h > 16size_s.h + +engine_s.o: 8inst_s.h 16inst_s.h 8size_s.h 16size_s.h size_s.h + +.s.o: + $(AS) -c $(OPTS) -I. $*.s + +.c.o: + $(CC) $(CCOPTS) $(XOPTS) -c $(OPTS) -I. $*.c + +partls: partls.c + cc $(CCOPTS) $(XOPTS) $(OPTS) -o partls partls.c + +to_pro: prodos.h prodos_protos.h to_pro.c + cc $(CCOPTS) $(XOPTS) $(OPTS) -o to_pro to_pro.c + + +compile_time.o: $(OBJECTS) + + +# dependency stuff +adb.o: adb.c adb.h defc.h defcomm.h iwm.h protos.h +engine_c.o: engine_c.c defc.h defcomm.h iwm.h protos.h protos_engine_c.h size_c.h op_routs.h defs_instr.h 8inst_c.h 16inst_c.h +clock.o: clock.c defc.h defcomm.h iwm.h protos.h +compile_time.o: compile_time.c +config.o: config.c defc.h defcomm.h iwm.h protos.h config.h +dis.o: dis.c defc.h defcomm.h iwm.h protos.h disas.h +scc.o: scc.c defc.h defcomm.h iwm.h protos.h scc.h +scc_socket_driver.o: scc_socket_driver.c defc.h defcomm.h iwm.h protos.h scc.h +scc_windriver.o: scc_windriver.c defc.h defcomm.h iwm.h protos.h scc.h +scc_macdriver.o: scc_macdriver.c defc.h defcomm.h iwm.h protos.h scc.h +iwm.o: iwm.c defc.h defcomm.h iwm.h protos.h iwm_35_525.h +joystick_driver.o: joystick_driver.c defc.h defcomm.h iwm.h protos.h +moremem.o: moremem.c defc.h defcomm.h iwm.h protos.h +paddles.o: paddles.c defc.h defcomm.h iwm.h protos.h +sim65816.o: sim65816.c defc.h defcomm.h iwm.h protos.h +smartport.o: smartport.c defc.h defcomm.h iwm.h protos.h +sound.o: sound.c defc.h defcomm.h iwm.h protos.h sound.h +sound_driver.o: sound_driver.c defc.h defcomm.h iwm.h protos.h sound.h +video.o: video.c defc.h defcomm.h iwm.h protos.h superhires.h kegsfont.h +macdriver.o: macdriver.c defc.h defcomm.h iwm.h protos.h protos_macdriver.h +macsnd_driver.o: macsnd_driver.c defc.h defcomm.h iwm.h protos.h sound.h +windriver.o: windriver.c defc.h defcomm.h iwm.h protos.h protos_windriver.h winresource.h +win32snd_driver.o: win32snd_driver.c defc.h defcomm.h iwm.h protos.h sound.h diff --git a/src/adb.c b/src/adb.c new file mode 100644 index 0000000..0ebb103 --- /dev/null +++ b/src/adb.c @@ -0,0 +1,1760 @@ +/************************************************************************/ +/* KEGS: Apple //gs Emulator */ +/* Copyright 2002 by Kent Dickey */ +/* */ +/* This code is covered by the GNU GPL */ +/* */ +/* The KEGS web page is kegs.sourceforge.net */ +/* You may contact the author at: kadickey@alumni.princeton.edu */ +/************************************************************************/ + +const char rcsid_adb_c[] = "@(#)$KmKId: adb.c,v 1.63 2004-03-23 18:46:25-05 kentd Exp $"; + +/* adb_mode bit 3 and bit 2 (faster repeats for arrows and space/del) not done*/ + +#include "adb.h" + +extern int Verbose; +extern word32 g_vbl_count; +extern int g_num_lines_prev_superhires640; +extern int g_num_lines_prev_superhires; +extern int g_rom_version; +extern int g_fast_disk_emul; +extern int g_limit_speed; +extern int g_swap_paddles; +extern int g_invert_paddles; +extern int g_a2vid_palette; +extern int g_config_control_panel; +extern word32 g_cfg_vbl_count; + +extern byte *g_slow_memory_ptr; +extern byte *g_memory_ptr; +extern word32 g_mem_size_total; + +enum { + ADB_IDLE = 0, + ADB_IN_CMD, + ADB_SENDING_DATA, +}; + +#define ADB_C027_MOUSE_DATA 0x80 +#define ADB_C027_MOUSE_INT 0x40 +#define ADB_C027_DATA_VALID 0x20 +#define ADB_C027_DATA_INT 0x10 +#define ADB_C027_KBD_VALID 0x08 +#define ADB_C027_KBD_INT 0x04 +#define ADB_C027_MOUSE_COORD 0x02 +#define ADB_C027_CMD_FULL 0x01 + +#define ADB_C027_NEG_MASK ( ~ ( \ + ADB_C027_MOUSE_DATA | ADB_C027_DATA_VALID | \ + ADB_C027_KBD_VALID | ADB_C027_MOUSE_COORD | \ + ADB_C027_CMD_FULL)) + + +int halt_on_all_c027 = 0; + +word32 g_adb_repeat_delay = 45; +word32 g_adb_repeat_rate = 3; +word32 g_adb_repeat_info = 0x23; +word32 g_adb_char_set = 0x0; +word32 g_adb_layout_lang = 0x0; + +word32 g_adb_interrupt_byte = 0; +int g_adb_state = ADB_IDLE; + +word32 g_adb_cmd = (word32)-1; +int g_adb_cmd_len = 0; +int g_adb_cmd_so_far = 0; +word32 g_adb_cmd_data[16]; + +#define MAX_ADB_DATA_PEND 16 + +word32 g_adb_data[MAX_ADB_DATA_PEND]; +int g_adb_data_pending = 0; + +word32 g_c027_val = 0; +word32 g_c025_val = 0; + +byte adb_memory[256]; + +word32 g_adb_mode = 0; /* mode set via set_modes, clear_modes */ + +int g_warp_pointer = 0; +int g_hide_pointer = 0; +int g_unhide_pointer = 0; + + +int g_mouse_a2_x = 0; +int g_mouse_a2_y = 0; +int g_mouse_a2_button = 0; +int g_mouse_fifo_pos = 0; + +#define ADB_MOUSE_FIFO 8 + +int g_mouse_fifo_x[ADB_MOUSE_FIFO] = { 0 }; +int g_mouse_fifo_y[ADB_MOUSE_FIFO] = { 0 }; +int g_mouse_fifo_buttons[ADB_MOUSE_FIFO] = { 0 }; + +int g_mouse_warp_x = 0; +int g_mouse_warp_y = 0; + +int g_adb_mouse_valid_data = 0; +int g_adb_mouse_coord = 0; + +int g_adb_data_int_sent = 0; +int g_adb_mouse_int_sent = 0; +int g_adb_kbd_srq_sent = 0; + + +#define MAX_KBD_BUF 8 + +int g_key_down = 0; +int g_hard_key_down = 0; +int g_a2code_down = 0; +int g_kbd_read_no_update = 0; +int g_kbd_chars_buffered = 0; +int g_kbd_buf[MAX_KBD_BUF]; +word32 g_adb_repeat_vbl = 0; + +int g_kbd_dev_addr = 2; /* ADB physical kbd addr */ +int g_mouse_dev_addr = 3; /* ADB physical mouse addr */ + +int g_kbd_ctl_addr = 2; /* ADB microcontroller's kbd addr */ +int g_mouse_ctl_addr = 3; /* ADB ucontroller's mouse addr*/ + /* above are ucontroller's VIEW of where mouse/kbd */ + /* are...if they are moved, mouse/keyboard funcs */ + /* should stop (c025, c000, c024, etc). */ + +word32 g_virtual_key_up[4]; /* bitmask of all possible 128 a2codes */ + /* indicates which keys are up=1 by bit */ + +#define SHIFT_DOWN ( (g_c025_val & 0x01) ) +#define CTRL_DOWN ( (g_c025_val & 0x02) ) +#define CAPS_LOCK_DOWN ( (g_c025_val & 0x04) ) +#define OPTION_DOWN ( (g_c025_val & 0x40) ) +#define CMD_DOWN ( (g_c025_val & 0x80) ) + + +#define MAX_ADB_KBD_REG3 16 + +int g_kbd_reg0_pos = 0; +int g_kbd_reg0_data[MAX_ADB_KBD_REG3]; +int g_kbd_reg3_16bit = 0x602; /* also set in adb_reset()! */ + + +int g_adb_init = 0; + +void +adb_init() +{ + int keycode; + int i; + + if(g_adb_init) { + halt_printf("g_adb_init = %d!\n", g_adb_init); + } + g_adb_init = 1; + + for(i = 0; i < 128; i++) { + keycode = a2_key_to_ascii[i][0]; + if(keycode != i) { + printf("ADB keycode lost/skipped: i=%x: keycode=%x\n", + i, keycode); + my_exit(1); + } + } + + g_c025_val = 0; + + for(i = 0; i < 4; i++) { + g_virtual_key_up[i] = -1; + } + + adb_reset(); +} + + +void +adb_reset() +{ + + g_c027_val = 0; + + g_key_down = 0; + + g_kbd_dev_addr = 2; + g_mouse_dev_addr = 3; + + g_kbd_ctl_addr = 2; + g_mouse_ctl_addr = 3; + + g_adb_data_int_sent = 0; + g_adb_mouse_int_sent = 0; + g_adb_kbd_srq_sent = 0; + + g_adb_data_pending = 0; + g_adb_interrupt_byte = 0; + g_adb_state = ADB_IDLE; + g_adb_mouse_coord = 0; + g_adb_mouse_valid_data = 0; + + g_kbd_reg0_pos = 0; + g_kbd_reg3_16bit = 0x602; +} + + +#define LEN_ADB_LOG 16 +STRUCT(Adb_log) { + word32 addr; + int val; + int state; +}; + +Adb_log g_adb_log[LEN_ADB_LOG]; +int g_adb_log_pos = 0; + +void +adb_log(word32 addr, int val) +{ + int pos; + + pos = g_adb_log_pos; + g_adb_log[pos].addr = addr; + g_adb_log[pos].val = val; + g_adb_log[pos].state = g_adb_state; + pos++; + if(pos >= LEN_ADB_LOG) { + pos = 0; + } + g_adb_log_pos = pos; +} + +void +show_adb_log(void) +{ + int pos; + int i; + + pos = g_adb_log_pos; + printf("ADB log pos: %d\n", pos); + for(i = 0; i < LEN_ADB_LOG; i++) { + pos--; + if(pos < 0) { + pos = LEN_ADB_LOG - 1; + } + printf("%d:%d: addr:%04x = %02x, st:%d\n", i, pos, + g_adb_log[pos].addr, g_adb_log[pos].val, + g_adb_log[pos].state); + } + printf("kbd: dev: %x, ctl: %x; mouse: dev: %x, ctl: %x\n", + g_kbd_dev_addr, g_kbd_ctl_addr, + g_mouse_dev_addr, g_mouse_ctl_addr); + printf("adb_data_int_sent: %d, adb_kbd_srq_sent: %d, mouse_int: %d\n", + g_adb_data_int_sent, g_adb_kbd_srq_sent, g_adb_mouse_int_sent); + printf("g_adb_state: %d, g_adb_interrupt_byte: %02x\n", + g_adb_state, g_adb_interrupt_byte); +} + +void +adb_error(void) +{ + halt_printf("Adb Error\n"); + + show_adb_log(); +} + + + +void +adb_add_kbd_srq() +{ + if(g_kbd_reg3_16bit & 0x200) { + /* generate SRQ */ + g_adb_interrupt_byte |= 0x08;; + if(!g_adb_kbd_srq_sent) { + g_adb_kbd_srq_sent = 1; + add_irq(); + } + } else { + printf("Got keycode but no kbd SRQ!\n"); + } +} + +void +adb_clear_kbd_srq() +{ + if(g_adb_kbd_srq_sent) { + remove_irq(); + g_adb_kbd_srq_sent = 0; + } + + /* kbd SRQ's are the only ones to handle now, so just clean it out */ + g_adb_interrupt_byte &= (~(0x08)); +} + +void +adb_add_data_int() +{ + if(g_c027_val & ADB_C027_DATA_INT) { + if(!g_adb_data_int_sent) { + g_adb_data_int_sent = 1; + add_irq(); + } + } +} + +void +adb_add_mouse_int() +{ + if(g_c027_val & ADB_C027_MOUSE_INT) { + if(!g_adb_mouse_int_sent) { + /* printf("Mouse int sent\n"); */ + g_adb_mouse_int_sent = 1; + add_irq(); + } + } +} + +void +adb_clear_data_int() +{ + if(g_adb_data_int_sent) { + remove_irq(); + g_adb_data_int_sent = 0; + } +} + +void +adb_clear_mouse_int() +{ + if(g_adb_mouse_int_sent) { + remove_irq(); + g_adb_mouse_int_sent = 0; + /* printf("Mouse int clear, button: %d\n", g_mouse_a2_button);*/ + } +} + + +void +adb_send_bytes(int num_bytes, word32 val0, word32 val1, word32 val2) +{ + word32 val; + int shift_amount; + int i; + + if((num_bytes >= 12) || (num_bytes >= MAX_ADB_DATA_PEND)) { + halt_printf("adb_send_bytes: %d is too many!\n", num_bytes); + } + + g_adb_state = ADB_SENDING_DATA; + g_adb_data_pending = num_bytes; + adb_add_data_int(); + + for(i = 0; i < num_bytes; i++) { + if(i < 4) { + val = val0; + } else if(i < 8) { + val = val1; + } else { + val = val2; + } + + shift_amount = 8*(3 - i); + g_adb_data[i] = (val >> shift_amount) & 0xff; + adb_printf("adb_send_bytes[%d] = %02x\n", i, g_adb_data[i]); + } +} + + +void +adb_send_1byte(word32 val) +{ + + if(g_adb_data_pending != 0) { + halt_printf("g_adb_data_pending: %d\n", g_adb_data_pending); + } + + adb_send_bytes(1, val << 24, 0, 0); +} + + + +void +adb_response_packet(int num_bytes, word32 val) +{ + + if(g_adb_data_pending != 0) { + halt_printf("adb_response_packet, but pending: %d\n", + g_adb_data_pending); + } + + g_adb_state = ADB_IDLE; + g_adb_data_pending = num_bytes; + g_adb_data[0] = val & 0xff; + g_adb_data[1] = (val >> 8) & 0xff; + g_adb_data[2] = (val >> 16) & 0xff; + g_adb_data[3] = (val >> 24) & 0xff; + if(num_bytes) { + g_adb_interrupt_byte |= 0x80 + num_bytes - 1; + } else { + g_adb_interrupt_byte |= 0x80; + } + + adb_printf("adb_response packet: %d: %08x\n", + num_bytes, val); + + adb_add_data_int(); +} + + +void +adb_kbd_reg0_data(int a2code, int is_up) +{ + if(g_kbd_reg0_pos >= MAX_ADB_KBD_REG3) { + /* too many keys, toss */ + halt_printf("Had to toss key: %02x, %d\n", a2code, is_up); + return; + } + + g_kbd_reg0_data[g_kbd_reg0_pos] = a2code + (is_up << 7); + + adb_printf("g_kbd_reg0_data[%d] = %02x\n", g_kbd_reg0_pos, + g_kbd_reg0_data[g_kbd_reg0_pos]); + + g_kbd_reg0_pos++; + + adb_add_kbd_srq(); +} + +void +adb_kbd_talk_reg0() +{ + word32 val0, val1; + word32 reg; + int num_bytes; + int num; + int i; + + num = 0; + val0 = g_kbd_reg0_data[0]; + val1 = g_kbd_reg0_data[1]; + + num_bytes = 0; + if(g_kbd_reg0_pos > 0) { + num_bytes = 2; + num = 1; + if((val0 & 0x7f) == 0x7f) { + /* reset */ + val1 = val0; + } else if(g_kbd_reg0_pos > 1) { + num = 2; + if((val1 & 0x7f) == 0x7f) { + /* If first byte some other key, don't */ + /* put RESET next! */ + num = 1; + val1 = 0xff; + } + } else { + val1 = 0xff; + } + } + + if(num) { + for(i = num; i < g_kbd_reg0_pos; i++) { + g_kbd_reg0_data[i-1] = g_kbd_reg0_data[i]; + } + g_kbd_reg0_pos -= num; + } + + reg = (val0 << 8) + val1; + + adb_printf("adb_kbd_talk0: %04x\n", reg); + + adb_response_packet(num_bytes, reg); + if(g_kbd_reg0_pos == 0) { + adb_clear_kbd_srq(); + } +} + +void +adb_set_config(word32 val0, word32 val1, word32 val2) +{ + int new_mouse; + int new_kbd; + int tmp1; + + new_mouse = val0 >> 4; + new_kbd = val0 & 0xf; + if(new_mouse != g_mouse_ctl_addr) { + printf("ADB config: mouse from %x to %x!\n", + g_mouse_ctl_addr, new_mouse); + adb_error(); + g_mouse_ctl_addr = new_mouse; + } + if(new_kbd != g_kbd_ctl_addr) { + printf("ADB config: kbd from %x to %x!\n", + g_kbd_ctl_addr, new_kbd); + adb_error(); + g_kbd_ctl_addr = new_kbd; + } + + + tmp1 = val2 >> 4; + if(tmp1 == 4) { + g_adb_repeat_delay = 0; + } else if(tmp1 < 4) { + g_adb_repeat_delay = (tmp1 + 1) * 15; + } else { + halt_printf("Bad ADB repeat delay: %02x\n", tmp1); + } + + tmp1 = val2 & 0xf; + if(g_rom_version >= 3) { + tmp1 = 9 - tmp1; + } + + switch(tmp1) { + case 0: + g_adb_repeat_rate = 1; + break; + case 1: + g_adb_repeat_rate = 2; + break; + case 2: + g_adb_repeat_rate = 3; + break; + case 3: + g_adb_repeat_rate = 3; + break; + case 4: + g_adb_repeat_rate = 4; + break; + case 5: + g_adb_repeat_rate = 5; + break; + case 6: + g_adb_repeat_rate = 7; + break; + case 7: + g_adb_repeat_rate = 15; + break; + case 8: + /* I don't know what this should be, ROM 03 uses it */ + g_adb_repeat_rate = 30; + break; + case 9: + /* I don't know what this should be, ROM 03 uses it */ + g_adb_repeat_rate = 60; + break; + default: + halt_printf("Bad repeat rate: %02x\n", tmp1); + } + +} + +void +adb_set_new_mode(word32 val) +{ + if(val & 0x03) { + printf("Disabling keyboard/mouse:%02x!\n", val); + } + + if(val & 0xa2) { + halt_printf("ADB set mode: %02x!\n", val); + adb_error(); + } + + g_adb_mode = val; +} + + +int +adb_read_c026() +{ + word32 ret; + int i; + + ret = 0; + switch(g_adb_state) { + case ADB_IDLE: + ret = g_adb_interrupt_byte; + g_adb_interrupt_byte = 0; + if(g_adb_kbd_srq_sent) { + g_adb_interrupt_byte |= 0x08; + } + if(g_adb_data_pending == 0) { + if(ret & 0x80) { + halt_printf("read_c026: ret:%02x, pend:%d\n", + ret, g_adb_data_pending); + } + adb_clear_data_int(); + } + if(g_adb_data_pending) { + if(g_adb_state != ADB_IN_CMD) { + g_adb_state = ADB_SENDING_DATA; + } + } + break; + case ADB_IN_CMD: + ret = 0; + break; + case ADB_SENDING_DATA: + ret = g_adb_data[0]; + for(i = 1; i < g_adb_data_pending; i++) { + g_adb_data[i-1] = g_adb_data[i]; + } + g_adb_data_pending--; + if(g_adb_data_pending <= 0) { + g_adb_data_pending = 0; + g_adb_state = ADB_IDLE; + adb_clear_data_int(); + } + break; + default: + halt_printf("Bad ADB state: %d!\n", g_adb_state); + adb_clear_data_int(); + break; + } + + adb_printf("Reading c026. Returning %02x, st: %02x, pend: %d\n", + ret, g_adb_state, g_adb_data_pending); + + adb_log(0xc026, ret); + return (ret & 0xff); +} + + +void +adb_write_c026(int val) +{ + word32 tmp; + int dev; + + adb_printf("Writing c026 with %02x\n", val); + adb_log(0x1c026, val); + + + switch(g_adb_state) { + case ADB_IDLE: + g_adb_cmd = val; + g_adb_cmd_so_far = 0; + g_adb_cmd_len = 0; + + dev = val & 0xf; + switch(val) { + case 0x01: /* Abort */ + adb_printf("Performing adb abort\n"); + /* adb_abort() */ + break; + case 0x03: /* Flush keyboard buffer */ + adb_printf("Flushing adb keyboard buffer\n"); + /* Do nothing */ + break; + case 0x04: /* Set modes */ + adb_printf("ADB set modes\n"); + g_adb_state = ADB_IN_CMD; + g_adb_cmd_len = 1; + break; + case 0x05: /* Clear modes */ + adb_printf("ADB clear modes\n"); + g_adb_state = ADB_IN_CMD; + g_adb_cmd_len = 1; + break; + case 0x06: /* Set config */ + adb_printf("ADB set config\n"); + g_adb_state = ADB_IN_CMD; + g_adb_cmd_len = 3; + break; + case 0x07: /* Sync */ + adb_printf("Performing sync cmd!\n"); + g_adb_state = ADB_IN_CMD; + if(g_rom_version == 1) { + g_adb_cmd_len = 4; + } else { + g_adb_cmd_len = 8; + } + break; + case 0x08: /* Write mem */ + adb_printf("Starting write_mem cmd\n"); + g_adb_state = ADB_IN_CMD; + g_adb_cmd_len = 2; + break; + case 0x09: /* Read mem */ + adb_printf("Performing read_mem cmd!\n"); + g_adb_state = ADB_IN_CMD; + g_adb_cmd_len = 2; + break; + case 0x0a: /* Read modes byte */ + printf("Performing read_modes cmd!\n"); + /* set_halt(1); */ + adb_send_1byte(g_adb_mode); + break; + case 0x0b: /* Read config bytes */ + printf("Performing read_configs cmd!\n"); + tmp = (g_mouse_ctl_addr << 20) + + (g_kbd_ctl_addr << 16) + + (g_adb_char_set << 12) + + (g_adb_layout_lang << 8) + + (g_adb_repeat_info << 0); + tmp = (0x82U << 24) + tmp; + adb_send_bytes(4, tmp, 0, 0); + break; + case 0x0d: /* Get Version */ + adb_printf("Performing get_version cmd!\n"); + val = 0; + if(g_rom_version == 1) { + /* ROM 01 = revision 5 */ + val = 5; + } else { + /* ROM 03 checks for rev >= 6 */ + val = 6; + } + adb_send_1byte(val); + break; + case 0x0e: /* Read avail char sets */ + adb_printf("Performing read avail char sets cmd!\n"); + adb_send_bytes(2, /* just 2 bytes */ + 0x08000000, /* number of ch sets=0x8 */ + 0, 0); + /* set_halt(1); */ + break; + case 0x0f: /* Read avail kbd layouts */ + adb_printf("Performing read avail kbd layouts cmd!\n"); + adb_send_bytes(0x2, /* number of kbd layouts=0xa */ + 0x0a000000, 0, 0); + /* set_halt(1); */ + break; + case 0x10: /* Reset */ + printf("ADB reset, cmd 0x10\n"); + do_reset(); + break; + case 0x11: /* Send ADB keycodes */ + adb_printf("Sending ADB keycodes\n"); + g_adb_state = ADB_IN_CMD; + g_adb_cmd_len = 1; + break; + case 0x12: /* ADB cmd 12: ROM 03 only! */ + if(g_rom_version >= 3) { + g_adb_state = ADB_IN_CMD; + g_adb_cmd_len = 2; + } else { + printf("ADB cmd 12, but not ROM 3!\n"); + adb_error(); + } + break; + case 0x13: /* ADB cmd 13: ROM 03 only! */ + if(g_rom_version >= 3) { + g_adb_state = ADB_IN_CMD; + g_adb_cmd_len = 2; + } else { + printf("ADB cmd 13, but not ROM 3!\n"); + adb_error(); + } + break; + case 0x73: /* Disable SRQ device 3: mouse */ + adb_printf("Disabling Mouse SRQ's (device 3)\n"); + /* HACK HACK...should deal with SRQs on mouse */ + break; + case 0xb0: case 0xb1: case 0xb2: case 0xb3: + case 0xb4: case 0xb5: case 0xb6: case 0xb7: + case 0xb8: case 0xb9: case 0xba: case 0xbb: + case 0xbc: case 0xbd: case 0xbe: case 0xbf: + /* Listen dev x reg 3 */ + adb_printf("Sending data to dev %x reg 3\n", dev); + g_adb_state = ADB_IN_CMD; + g_adb_cmd_len = 2; + break; + case 0xc0: case 0xc1: case 0xc2: case 0xc3: + case 0xc4: case 0xc5: case 0xc6: case 0xc7: + case 0xc8: case 0xc9: case 0xca: case 0xcb: + case 0xcc: case 0xcd: case 0xce: case 0xcf: + /* Talk dev x reg 0 */ + adb_printf("Performing talk dev %x reg 0\n", dev); + if(dev == g_kbd_dev_addr) { + adb_kbd_talk_reg0(); + } else { + printf("Unknown talk dev %x reg 0!\n", dev); + /* send no data, on SRQ, system polls devs */ + /* so we don't want to send anything */ + adb_error(); + } + break; + case 0xf0: case 0xf1: case 0xf2: case 0xf3: + case 0xf4: case 0xf5: case 0xf6: case 0xf7: + case 0xf8: case 0xf9: case 0xfa: case 0xfb: + case 0xfc: case 0xfd: case 0xfe: case 0xff: + /* Talk dev x reg 3 */ + adb_printf("Performing talk dev %x reg 3\n", dev); + if(dev == g_kbd_dev_addr) { + adb_response_packet(2, g_kbd_reg3_16bit); + } else { + printf("Performing talk dev %x reg 3!!\n", dev); + adb_error(); + } + break; + default: + halt_printf("ADB ucontroller cmd %02x unknown!\n", val); + /* The Gog's says ACS Demo 2 has a bug and writes to */ + /* c026 */ + break; + } + break; + case ADB_IN_CMD: + adb_printf("Setting byte %d of cmd %02x to %02x\n", + g_adb_cmd_so_far, g_adb_cmd, val); + + g_adb_cmd_data[g_adb_cmd_so_far] = val; + g_adb_cmd_so_far++; + if(g_adb_cmd_so_far >= g_adb_cmd_len) { + adb_printf("Finished cmd %02x\n", g_adb_cmd); + do_adb_cmd(); + } + + break; + default: + printf("adb_state: %02x is unknown! Setting it to ADB_IDLE\n", + g_adb_state); + g_adb_state = ADB_IDLE; + adb_error(); + halt_on_all_c027 = 1; + break; + } + return; +} + +void +do_adb_cmd() +{ + int dev; + int new_kbd; + int addr; + int val; + + dev = g_adb_cmd & 0xf; + + g_adb_state = ADB_IDLE; + + switch(g_adb_cmd) { + case 0x04: /* Set modes */ + adb_printf("Performing ADB set mode: OR'ing in %02x\n", + g_adb_cmd_data[0]); + + val = g_adb_cmd_data[0] | g_adb_mode; + adb_set_new_mode(val); + + break; + case 0x05: /* clear modes */ + adb_printf("Performing ADB clear mode: AND'ing in ~%02x\n", + g_adb_cmd_data[0]); + + val = g_adb_cmd_data[0]; + val = g_adb_mode & (~val); + adb_set_new_mode(val); + break; + case 0x06: /* Set config */ + adb_printf("Set ADB config to %02x %02x %02x\n", + g_adb_cmd_data[0], g_adb_cmd_data[1],g_adb_cmd_data[2]); + + adb_set_config(g_adb_cmd_data[0], g_adb_cmd_data[1], + g_adb_cmd_data[2]); + + break; + case 0x07: /* SYNC */ + adb_printf("Performing ADB SYNC\n"); + adb_printf("data: %02x %02x %02x %02x\n", + g_adb_cmd_data[0], g_adb_cmd_data[1], g_adb_cmd_data[2], + g_adb_cmd_data[3]); + + adb_set_new_mode(g_adb_cmd_data[0]); + adb_set_config(g_adb_cmd_data[1], g_adb_cmd_data[2], + g_adb_cmd_data[3]); + + if(g_rom_version >= 3) { + adb_printf(" and cmd12:%02x %02x cmd13:%02x %02x\n", + g_adb_cmd_data[4], g_adb_cmd_data[5], + g_adb_cmd_data[6], g_adb_cmd_data[7]); + } + break; + case 0x08: /* Write mem */ + addr = g_adb_cmd_data[0]; + val = g_adb_cmd_data[1]; + write_adb_ram(addr, val); + break; + case 0x09: /* Read mem */ + addr = (g_adb_cmd_data[1] << 8) + g_adb_cmd_data[0]; + adb_printf("Performing mem read to addr %04x\n", addr); + adb_send_1byte(read_adb_ram(addr)); + break; + case 0x11: /* Send ADB keycodes */ + val = g_adb_cmd_data[0]; + adb_printf("Performing send ADB keycodes: %02x\n", val); + adb_virtual_key_update(val & 0x7f, val >> 7); + break; + case 0x12: /* ADB cmd12 */ + adb_printf("Performing ADB cmd 12\n"); + adb_printf("data: %02x %02x\n", g_adb_cmd_data[0], + g_adb_cmd_data[1]); + break; + case 0x13: /* ADB cmd13 */ + adb_printf("Performing ADB cmd 13\n"); + adb_printf("data: %02x %02x\n", g_adb_cmd_data[0], + g_adb_cmd_data[1]); + break; + case 0xb0: case 0xb1: case 0xb2: case 0xb3: + case 0xb4: case 0xb5: case 0xb6: case 0xb7: + case 0xb8: case 0xb9: case 0xba: case 0xbb: + case 0xbc: case 0xbd: case 0xbe: case 0xbf: + /* Listen dev x reg 3 */ + if(dev == g_kbd_dev_addr) { + if(g_adb_cmd_data[1] == 0xfe) { + /* change keyboard addr? */ + new_kbd = g_adb_cmd_data[0] & 0xf; + if(new_kbd != dev) { + printf("Moving kbd to dev %x!\n", + new_kbd); + adb_error(); + } + g_kbd_dev_addr = new_kbd; + } else if(g_adb_cmd_data[1] != 1) { + /* see what new device handler id is */ + printf("KBD listen to dev %x reg 3: 1:%02x\n", + dev, g_adb_cmd_data[1]); + adb_error(); + } + if(g_adb_cmd_data[0] != (word32)g_kbd_dev_addr) { + /* see if app is trying to change addr */ + printf("KBD listen to dev %x reg 3: 0:%02x!\n", + dev, g_adb_cmd_data[0]); + adb_error(); + } + g_kbd_reg3_16bit = ((g_adb_cmd_data[0] & 0xf) << 12) + + (g_kbd_reg3_16bit & 0x0fff); + } else if(dev == g_mouse_dev_addr) { + if(g_adb_cmd_data[0] != (word32)dev) { + /* see if app is trying to change mouse addr */ + printf("MOUS listen to dev %x reg3: 0:%02x!\n", + dev, g_adb_cmd_data[0]); + adb_error(); + } + if(g_adb_cmd_data[1] != 1 && g_adb_cmd_data[1] != 2) { + /* see what new device handler id is */ + printf("MOUS listen to dev %x reg 3: 1:%02x\n", + dev, g_adb_cmd_data[1]); + adb_error(); + } + } else { + printf("Listen cmd to dev %x reg3????\n", dev); + printf("data0: %02x, data1: %02x ????\n", + g_adb_cmd_data[0], g_adb_cmd_data[1]); + adb_error(); + } + break; + default: + printf("Doing adb_cmd %02x: UNKNOWN!\n", g_adb_cmd); + break; + } +} + + +int +adb_read_c027() +{ + word32 ret; + + if(halt_on_all_c027) { + halt_printf("halting on all c027 reads!\n"); + } + + if(g_c027_val & (~ADB_C027_NEG_MASK)) { + halt_printf("read_c027: g_c027_val: %02x\n", g_c027_val); + } + + ret = (g_c027_val & ADB_C027_NEG_MASK); + + if(g_adb_mouse_valid_data) { + ret |= ADB_C027_MOUSE_DATA; + } + + if(g_adb_interrupt_byte != 0) { + ret |= ADB_C027_DATA_VALID; + } else if(g_adb_data_pending > 0) { + if((g_adb_state != ADB_IN_CMD)) { + ret |= ADB_C027_DATA_VALID; + } + } + + if(g_adb_mouse_coord) { + ret |= ADB_C027_MOUSE_COORD; + } + +#if 0 + adb_printf("Read c027: %02x, int_byte: %02x, d_pend: %d\n", + ret, g_adb_interrupt_byte, g_adb_data_pending); +#endif + +#if 0 + adb_log(0xc027, ret); +#endif + return ret; +} + +void +adb_write_c027(int val) +{ + word32 old_val; + word32 new_int; + word32 old_int; + + adb_printf("Writing c027 with %02x\n", val); + adb_log(0x1c027, val); + + + old_val = g_c027_val; + + g_c027_val = (val & ADB_C027_NEG_MASK); + new_int = g_c027_val & ADB_C027_MOUSE_INT; + old_int = old_val & ADB_C027_MOUSE_INT; + if(!new_int && old_int) { + adb_clear_mouse_int(); + } + + new_int = g_c027_val & ADB_C027_DATA_INT; + old_int = old_val & ADB_C027_DATA_INT; + if(!new_int && old_int) { + /* ints were on, now off */ + adb_clear_data_int(); + } + + if(g_c027_val & ADB_C027_KBD_INT) { + halt_printf("Can't support kbd interrupts!\n"); + } + + return; +} + +int +read_adb_ram(word32 addr) +{ + int val; + + adb_printf("Reading adb ram addr: %02x\n", addr); + + if(addr >= 0x100) { + if(addr >= 0x1000 && addr < 0x2000) { + /* ROM self-test checksum */ + if(addr == 0x1400) { + val = 0x72; + } else if(addr == 0x1401) { + val = 0xf7; + } else { + val = 0; + } + } else { + printf("adb ram addr out of range: %04x!\n", addr); + val = 0; + } + } else { + val = adb_memory[addr]; + if((addr == 0xb) && (g_rom_version == 1)) { + // read special key state byte for Out of This World + val = (g_c025_val >> 1) & 0x43; + val |= (g_c025_val << 2) & 0x4; + val |= (g_c025_val >> 2) & 0x10; + } + if((addr == 0xc) && (g_rom_version >= 3)) { + // read special key state byte for Out of This World + val = g_c025_val & 0xc7; + printf("val is %02x\n", val); + } + } + + adb_printf("adb_ram returning %02x\n", val); + return val; +} + +void +write_adb_ram(word32 addr, int val) +{ + + adb_printf("Writing adb_ram addr: %02x: %02x\n", addr, val); + + if(addr >= 0x100) { + printf("write adb_ram addr: %02x: %02x!\n", addr, val); + adb_error(); + } else { + adb_memory[addr] = val; + } +} + +int +update_mouse(int x, int y, int button_states, int buttons_valid) +{ + int button1_changed; + int mouse_moved; + int unhide; + int pos; + int i; + + unhide = 0; + if(x < 0) { + x = 0; + unhide = 1; + } + if(x >= 640) { + x = 639; + unhide = 1; + } + if(y < 0) { + y = 0; + unhide = 1; + } + if(y >= 400) { + y = 399; + unhide = 1; + } + + + g_unhide_pointer = unhide && !g_warp_pointer; + + if(!g_warp_pointer) { + if(g_hide_pointer && g_unhide_pointer) { + /* cursor has left a2 window, show it */ + g_hide_pointer = 0; + x_hide_pointer(0); + } + if((g_num_lines_prev_superhires == 200) && + (g_num_lines_prev_superhires640 == 0) && + ((g_slow_memory_ptr[0x19d00] & 0x80) == 0)) { + // In 320-mode superhires, cut mouse range in half + x = x >> 1; + } + y = y >> 1; + } + +#if 0 + printf("Update Mouse called with buttons:%d x,y:%d,%d, fifo:%d,%d, " + " a2: %d,%d\n", buttons_valid, x, y, + g_mouse_fifo_x[0], g_mouse_fifo_y[0], + g_mouse_a2_x, g_mouse_a2_y); +#endif + + if((buttons_valid < 0) && g_warp_pointer) { + /* Warping the pointer causes it to jump here...this is not */ + /* real motion, just update info and get out */ + g_mouse_a2_x += (x - g_mouse_fifo_x[0]); + g_mouse_a2_y += (y - g_mouse_fifo_y[0]); + g_mouse_fifo_x[0] = x; + g_mouse_fifo_y[0] = y; + return 0; + } + +#if 0 + printf("...real move, warp: %d, %d, new x: %d, %d, a2:%d,%d\n", + g_mouse_warp_x, g_mouse_warp_y, g_mouse_fifo_x[0], + g_mouse_fifo_y[0], g_mouse_a2_x, g_mouse_a2_y); +#endif + + mouse_moved = (g_mouse_fifo_x[0] != x) || (g_mouse_fifo_y[0] != y); + + g_mouse_a2_x += g_mouse_warp_x; + g_mouse_a2_y += g_mouse_warp_y; + g_mouse_fifo_x[0] = x; + g_mouse_fifo_y[0] = y; + g_mouse_warp_x = 0; + g_mouse_warp_y = 0; + + button1_changed = (buttons_valid & 1) && + ((button_states & 1) != (g_mouse_fifo_buttons[0] & 1)); + + if((button_states & 4) && !(g_mouse_fifo_buttons[0] & 4) && + (buttons_valid & 4)) { + /* right button pressed */ + adb_increment_speed(); + } + if((button_states & 2) && !(g_mouse_fifo_buttons[0] & 2) && + (buttons_valid & 2)) { + /* middle button pressed */ + halt2_printf("Middle button pressed\n"); + } + + pos = g_mouse_fifo_pos; + if((pos < (ADB_MOUSE_FIFO - 2)) && button1_changed) { + /* copy delta to overflow, set overflow */ + /* overflow ensures the mouse button state is precise at */ + /* button up/down times. Using a mouse event list where */ + /* deltas accumulate until a button change would work, too */ + for(i = pos; i >= 0; i--) { + g_mouse_fifo_x[i + 1] = g_mouse_fifo_x[i]; + g_mouse_fifo_y[i + 1] = g_mouse_fifo_y[i]; + g_mouse_fifo_buttons[i + 1] = g_mouse_fifo_buttons[i]&1; + } + g_mouse_fifo_pos = pos + 1; + } + + g_mouse_fifo_buttons[0] = (button_states & buttons_valid) | + (g_mouse_fifo_buttons[0] & ~buttons_valid); + + if(mouse_moved || button1_changed) { + if( (g_mouse_ctl_addr == g_mouse_dev_addr) && + ((g_adb_mode & 0x2) == 0)) { + g_adb_mouse_valid_data = 1; + adb_add_mouse_int(); + } + } + + return mouse_moved; +} + +int +mouse_read_c024(double dcycs) +{ + word32 ret; + word32 tool_start; + int em_active; + int target_x, target_y; + int delta_x, delta_y; + int a2_x, a2_y; + int mouse_button; + int clamped; + int pos; + + if(((g_adb_mode & 0x2) != 0) || (g_mouse_dev_addr != g_mouse_ctl_addr)){ + /* mouse is off, return 0, or mouse is not autopoll */ + g_adb_mouse_valid_data = 0; + adb_clear_mouse_int(); + return 0; + } + + pos = g_mouse_fifo_pos; + target_x = g_mouse_fifo_x[pos]; + target_y = g_mouse_fifo_y[pos]; + mouse_button = (g_mouse_fifo_buttons[pos] & 1); + delta_x = target_x - g_mouse_a2_x; + delta_y = target_y - g_mouse_a2_y; + + clamped = 0; + if(delta_x > 0x3f) { + delta_x = 0x3f; + clamped = 1; + } else if(delta_x < -0x3f) { + delta_x = -0x3f; + clamped = 1; + } + if(delta_y > 0x3f) { + delta_y = 0x3f; + clamped = 1; + } else if(delta_y < -0x3f) { + delta_y = -0x3f; + clamped = 1; + } + + if(pos > 0) { + /* peek into next entry's button info if we are not clamped */ + /* and we're returning the y-coord */ + if(!clamped && g_adb_mouse_coord) { + mouse_button = g_mouse_fifo_buttons[pos - 1] & 1; + } + } + + if(g_adb_mouse_coord) { + /* y coord */ + delta_x = 0; /* clear unneeded x delta */ + } else { + delta_y = 0; /* clear unneeded y delta */ + } + + + adb_printf(" pre a2_x:%02x,%02x,%02x,%02x\n", + g_slow_memory_ptr[0x100e9], g_slow_memory_ptr[0x100ea], + g_slow_memory_ptr[0x100eb], g_slow_memory_ptr[0x100ec]); + adb_printf(" pre a2_x:%02x,%02x,%02x,%02x\n", + g_slow_memory_ptr[0x10190], g_slow_memory_ptr[0x10192], + g_slow_memory_ptr[0x10191], g_slow_memory_ptr[0x10193]); + + /* Update event manager internal state */ + tool_start = (g_slow_memory_ptr[0x103ca] << 16) + + (g_slow_memory_ptr[0x103c9] << 8) + + g_slow_memory_ptr[0x103c8]; + + em_active = 0; + if((tool_start >= 0x20000) && (tool_start < (g_mem_size_total - 28)) ) { + /* seems to be valid ptr to addr of mem space for tools */ + /* see if event manager appears to be active */ + em_active = g_memory_ptr[tool_start + 6*4] + + (g_memory_ptr[tool_start + 6*4 + 1] << 8); + if(g_warp_pointer) { + em_active = 0; + } + } + + a2_x = g_mouse_a2_x; + a2_y = g_mouse_a2_y; + + if(em_active) { + if((!g_hide_pointer) && (g_num_lines_prev_superhires == 200) && + !g_unhide_pointer) { + /* if super-hires and forcing tracking, then hide */ + g_hide_pointer = 1; + x_hide_pointer(1); + } + if(g_adb_mouse_coord == 0) { + /* update x coord values */ + g_slow_memory_ptr[0x47c] = a2_x & 0xff; + g_slow_memory_ptr[0x57c] = a2_x >> 8; + g_memory_ptr[0x47c] = a2_x & 0xff; + g_memory_ptr[0x57c] = a2_x >> 8; + + g_slow_memory_ptr[0x10190] = a2_x & 0xff; + g_slow_memory_ptr[0x10192] = a2_x >> 8; + } else { + g_slow_memory_ptr[0x4fc] = a2_y & 0xff; + g_slow_memory_ptr[0x5fc] = a2_y >> 8; + g_memory_ptr[0x4fc] = a2_y & 0xff; + g_memory_ptr[0x5fc] = a2_y >> 8; + + g_slow_memory_ptr[0x10191] = a2_y & 0xff; + g_slow_memory_ptr[0x10193] = a2_y >> 8; + } + } else { + if(g_hide_pointer && !g_warp_pointer) { + g_hide_pointer = 0; + x_hide_pointer(0); + } + } + + ret = ((!mouse_button) << 7) + ((delta_x | delta_y) & 0x7f); + if(g_adb_mouse_coord) { + g_mouse_a2_button = mouse_button; /* y coord has button*/ + } else { + ret |= 0x80; /* mouse button not down on x coord rd */ + } + + a2_x += delta_x; + a2_y += delta_y; + g_mouse_a2_x = a2_x; + g_mouse_a2_y = a2_y; + if(g_mouse_fifo_pos) { + if((target_x == a2_x) && (target_y == a2_y) && + (g_mouse_a2_button == mouse_button)) { + g_mouse_fifo_pos--; + } + } + + + adb_printf("Read c024, mouse is_y:%d, %02x, vbl:%08x, dcyc:%f, em:%d\n", + g_adb_mouse_coord, ret, g_vbl_count, dcycs, em_active); + adb_printf("...mouse targ_x:%d,%d delta_x,y:%d,%d fifo:%d, a2:%d,%d\n", + target_x, target_y, delta_x, delta_y, g_mouse_fifo_pos, + a2_x, a2_y); + adb_printf(" post a2_x:%02x,%02x,%02x,%02x\n", + g_slow_memory_ptr[0x10190], g_slow_memory_ptr[0x10192], + g_slow_memory_ptr[0x10191], g_slow_memory_ptr[0x10193]); + + if((g_mouse_fifo_pos == 0) && (g_mouse_fifo_x[0] == a2_x) && + (g_mouse_fifo_y[0] == a2_y) && + ((g_mouse_fifo_buttons[0] & 1) == g_mouse_a2_button)) { + g_adb_mouse_valid_data = 0; + adb_clear_mouse_int(); + } + + g_adb_mouse_coord = !g_adb_mouse_coord; + return ret; +} + +void +adb_key_event(int a2code, int is_up) +{ + word32 special; + word32 vbl_count; + int key; + int hard_key; + int pos; + int tmp_ascii; + int ascii; + + if(is_up) { + adb_printf("adb_key_event, key:%02x, is up, g_key_down: %02x\n", + a2code, g_key_down); + } + + if(a2code < 0 || a2code > 0x7f) { + halt_printf("add_key_event: a2code: %04x!\n", a2code); + return; + } + + if(!is_up && a2code == 0x35) { + /* ESC pressed, see if ctrl & cmd key down */ + if(CTRL_DOWN && CMD_DOWN) { + /* Desk mgr int */ + printf("Desk mgr int!\n"); + + g_adb_interrupt_byte |= 0x20; + adb_add_data_int(); + } + } + + /* convert key to ascii, if possible */ + hard_key = 0; + if(a2_key_to_ascii[a2code][1] & 0xef00) { + /* special key */ + } else { + /* we have ascii */ + hard_key = 1; + } + + pos = 1; + ascii = a2_key_to_ascii[a2code][1]; + if(CAPS_LOCK_DOWN && (ascii >= 'a' && ascii <= 'z')) { + pos = 2; + if(SHIFT_DOWN && (g_adb_mode & 0x40)) { + /* xor shift mode--capslock and shift == lowercase */ + pos = 1; + } + } else if(SHIFT_DOWN) { + pos = 2; + } + + ascii = a2_key_to_ascii[a2code][pos]; + if(CTRL_DOWN) { + tmp_ascii = a2_key_to_ascii[a2code][3]; + if(tmp_ascii >= 0) { + ascii = tmp_ascii; + } + } + key = (ascii & 0x7f) + 0x80; + + special = (ascii >> 8) & 0xff; + if(ascii < 0) { + printf("ascii1: %d, a2code: %02x, pos: %d\n", ascii,a2code,pos); + ascii = 0; + special = 0; + } + + + if(!is_up) { + if(hard_key) { + g_kbd_buf[g_kbd_chars_buffered] = key; + g_kbd_chars_buffered++; + if(g_kbd_chars_buffered >= MAX_KBD_BUF) { + g_kbd_chars_buffered = MAX_KBD_BUF - 1; + } + g_key_down = 1; + g_a2code_down = a2code; + + /* first key down, set up autorepeat */ + vbl_count = g_vbl_count; + if(g_config_control_panel) { + vbl_count = g_cfg_vbl_count; + } + g_adb_repeat_vbl = vbl_count + g_adb_repeat_delay; + if(g_adb_repeat_delay == 0) { + g_key_down = 0; + } + g_hard_key_down = 1; + } + + g_c025_val = g_c025_val | special; + adb_printf("new c025_or: %02x\n", g_c025_val); + } else { + if(hard_key && (a2code == g_a2code_down)) { + g_hard_key_down = 0; + /* Turn off repeat */ + g_key_down = 0; + } + + g_c025_val = g_c025_val & (~ special); + adb_printf("new c025_and: %02x\n", g_c025_val); + } + + if(g_key_down) { + g_c025_val = g_c025_val & (~0x20); + } else { + /* If no hard key down, set update mod latch */ + g_c025_val = g_c025_val | 0x20; + } + +} + +word32 +adb_read_c000() +{ + word32 vbl_count; + + if( ((g_kbd_buf[0] & 0x80) == 0) && (g_key_down == 0)) { + /* nothing happening, just get out */ + return g_kbd_buf[0]; + } + if(g_kbd_buf[0] & 0x80) { + /* got one */ + if((g_kbd_read_no_update++ > 5) && (g_kbd_chars_buffered > 1)) { + /* read 5 times, keys pending, let's move it along */ + printf("Read %02x 3 times, tossing\n", g_kbd_buf[0]); + adb_access_c010(); + } + } else { + vbl_count = g_vbl_count; + if(g_config_control_panel) { + vbl_count = g_cfg_vbl_count; + } + if(g_key_down && vbl_count >= g_adb_repeat_vbl) { + /* repeat the g_key_down */ + g_c025_val |= 0x8; + adb_key_event(g_a2code_down, 0); + g_adb_repeat_vbl = vbl_count + g_adb_repeat_rate; + } + } + + return g_kbd_buf[0]; +} + +word32 +adb_access_c010() +{ + int tmp; + int i; + + g_kbd_read_no_update = 0; + + tmp = g_kbd_buf[0] & 0x7f; + g_kbd_buf[0] = tmp; + + tmp = tmp | (g_hard_key_down << 7); + if(g_kbd_chars_buffered) { + for(i = 1; i < g_kbd_chars_buffered; i++) { + g_kbd_buf[i - 1] = g_kbd_buf[i]; + } + g_kbd_chars_buffered--; + } + + g_c025_val = g_c025_val & (~ (0x08)); + + return tmp; +} + +word32 +adb_read_c025() +{ + return g_c025_val; +} + +int +adb_is_cmd_key_down() +{ + return CMD_DOWN; +} + +int +adb_is_option_key_down() +{ + return OPTION_DOWN; +} + +void +adb_increment_speed() +{ + const char *str; + + g_limit_speed++; + if(g_limit_speed > 3) { + g_limit_speed = 0; + } + + str = ""; + switch(g_limit_speed) { + case 0: + str = "...as fast as possible!"; + break; + case 1: + str = "...1.024MHz!"; + break; + case 2: + str = "...2.8MHz!"; + break; + case 3: + str = "...8.0MHz!"; + break; + } + printf("Toggling g_limit_speed to %d%s\n", g_limit_speed, str); +} + +void +adb_physical_key_update(int a2code, int is_up) +{ + int autopoll; + int special; + int tmp; + + /* this routine called by xdriver to pass raw codes--handle */ + /* ucontroller and ADB bus protocol issues here */ + /* if autopoll on, pass it on through to c025,c000 regs */ + /* else only put it in kbd reg 3, and pull SRQ if needed */ + + adb_printf("adb_phys_key_update: %02x, %d\n", a2code, is_up); + + adb_printf("Handle a2code: %02x, is_up: %d\n", a2code, is_up); + + if(a2code < 0 || a2code > 0x7f) { + halt_printf("a2code: %04x!\n", a2code); + return; + } + + /* Remap 0x7b-0x7e to 0x3b-0x3e (arrow keys on new mac keyboards */ + if(a2code >= 0x7b && a2code <= 0x7e) { + a2code = a2code - 0x40; + } + + /* Now check for special keys (function keys, etc) */ + tmp = a2_key_to_ascii[a2code][1]; + special = 0; + if((tmp & 0xf000) == 0x8000) { + /* special function key */ + special = tmp & 0xff; + switch(special) { + case 0x01: /* F1 - remap to cmd */ + a2code = 0x37; + special = 0; + break; + case 0x02: /* F2 - remap to option */ + a2code = 0x3a; + special = 0; + break; + case 0x03: /* F3 - remap to escape for OS/2 */ + a2code = 0x35; + special = 0; + break; + case 0x0c: /* F12 - remap to reset */ + a2code = 0x7f; + special = 0; + break; + default: + break; + } + } + + /* Only process reset requests here */ + if(is_up == 0 && a2code == 0x7f && CTRL_DOWN) { + /* Reset pressed! */ + printf("Reset pressed since CTRL_DOWN: %d\n", CTRL_DOWN); + do_reset(); + return; + } + + if(special && !is_up) { + switch(special) { + case 0x04: /* F4 - Emulator config panel */ + g_config_control_panel = !g_config_control_panel; + break; + case 0x06: /* F6 - emulator speed */ + if(SHIFT_DOWN) { + halt2_printf("Shift-F6 pressed\n"); + } else { + adb_increment_speed(); + } + break; + case 0x07: /* F7 - fast disk emul */ + g_fast_disk_emul = !g_fast_disk_emul; + printf("g_fast_disk_emul is now %d\n", + g_fast_disk_emul); + break; + case 0x08: /* F8 - warp pointer */ + g_warp_pointer = !g_warp_pointer; + if(g_hide_pointer != g_warp_pointer) { + g_hide_pointer = g_warp_pointer; + x_hide_pointer(g_hide_pointer); + } + break; + case 0x09: /* F9 - swap paddles */ + if(SHIFT_DOWN) { + g_swap_paddles = !g_swap_paddles; + printf("Swap paddles is now: %d\n", + g_swap_paddles); + } else { + g_invert_paddles = !g_invert_paddles; + printf("Invert paddles is now: %d\n", + g_invert_paddles); + } + break; + case 0x0a: /* F10 - change a2vid paletter */ + change_a2vid_palette((g_a2vid_palette + 1) & 0xf); + break; + case 0x0b: /* F11 - full screen */ + /* g_fullscreen = !g_fullscreen; */ + /* x_full_screen(g_full_screen); */ + break; + } + + return; + } + + autopoll = 1; + if(g_adb_mode & 1) { + /* autopoll is explicitly off */ + autopoll = 0; + } + if(g_kbd_dev_addr != g_kbd_ctl_addr) { + /* autopoll is off because ucontroller doesn't know kbd moved */ + autopoll = 0; + } + if(g_config_control_panel) { + /* always do autopoll */ + autopoll = 1; + } + + + if(is_up) { + if(!autopoll) { + /* no auto keys, generate SRQ! */ + adb_kbd_reg0_data(a2code, is_up); + } else { + adb_virtual_key_update(a2code, is_up); + } + } else { + if(!autopoll) { + /* no auto keys, generate SRQ! */ + adb_kbd_reg0_data(a2code, is_up); + } else { + /* was up, now down */ + adb_virtual_key_update(a2code, is_up); + } + } +} + +void +adb_virtual_key_update(int a2code, int is_up) +{ + int i; + int bitpos; + word32 mask; + + adb_printf("Virtual handle a2code: %02x, is_up: %d\n", a2code, is_up); + + if(a2code < 0 || a2code > 0x7f) { + halt_printf("a2code: %04x!\n", a2code); + return; + } + + i = (a2code >> 5) & 3; + bitpos = a2code & 0x1f; + mask = (1 << bitpos); + + if(is_up) { + if(g_virtual_key_up[i] & mask) { + /* already up, do nothing */ + } else { + g_virtual_key_up[i] |= mask; + adb_key_event(a2code, is_up); + } + } else { + if(g_virtual_key_up[i] & mask) { + g_virtual_key_up[i] &= (~mask); + adb_key_event(a2code, is_up); + } + } +} + +void +adb_kbd_repeat_off() +{ + g_key_down = 0; +} diff --git a/src/adb.h b/src/adb.h new file mode 100644 index 0000000..857aa33 --- /dev/null +++ b/src/adb.h @@ -0,0 +1,160 @@ +/************************************************************************/ +/* KEGS: Apple //gs Emulator */ +/* Copyright 2002 by Kent Dickey */ +/* */ +/* This code is covered by the GNU GPL */ +/* */ +/* The KEGS web page is kegs.sourceforge.net */ +/* You may contact the author at: kadickey@alumni.princeton.edu */ +/************************************************************************/ + +const char rcsid_adb_h[] = "@(#)$KmKId: adb.h,v 1.9 2002-11-19 03:10:38-05 kadickey Exp $"; + +#include "defc.h" + +/* Format: a2code, ascii if no shift, ascii if shift, ascii if ctl */ +const int a2_key_to_ascii[][4] = { + { 0x00, 'a', 'A', 0x01 }, + { 0x01, 's', 'S', 0x13 }, + { 0x02, 'd', 'D', 0x04 }, + { 0x03, 'f', 'F', 0x06 }, + { 0x04, 'h', 'H', 0x08 }, + { 0x05, 'g', 'G', 0x07 }, + { 0x06, 'z', 'Z', 0x1a }, + { 0x07, 'x', 'X', 0x18 }, + + { 0x08, 'c', 'C', 0x03 }, + { 0x09, 'v', 'V', 0x16 }, + { 0x0a, -1, -1, -1 }, + { 0x0b, 'b', 'B', 0x02 }, + { 0x0c, 'q', 'Q', 0x11 }, + { 0x0d, 'w', 'W', 0x17 }, + { 0x0e, 'e', 'E', 0x05 }, + { 0x0f, 'r', 'R', 0x12 }, + + { 0x10, 'y', 'Y', 0x19 }, + { 0x11, 't', 'T', 0x14 }, + { 0x12, '1', '!', -1 }, + { 0x13, '2', '@', 0x00 }, + { 0x14, '3', '#', -1 }, + { 0x15, '4', '$', -1 }, + { 0x16, '6', '^', 0x1e }, + { 0x17, '5', '%', -1 }, + + { 0x18, '=', '+', -1 }, + { 0x19, '9', '(', -1 }, + { 0x1a, '7', '&', -1 }, + { 0x1b, '-', '_', 0x1f }, + { 0x1c, '8', '*', -1 }, + { 0x1d, '0', ')', -1 }, + { 0x1e, ']', '}', 0x1d }, + { 0x1f, 'o', 'O', 0x0f }, + + { 0x20, 'u', 'U', 0x15 }, + { 0x21, '[', '{', 0x1b }, + { 0x22, 'i', 'I', 0x09 }, + { 0x23, 'p', 'P', 0x10 }, + { 0x24, 0x0d, 0x0d, -1 }, /* return */ + { 0x25, 'l', 'L', 0x0c }, + { 0x26, 'j', 'J', 0x0a }, + { 0x27, 0x27, '"', -1 }, /* single quote */ + + { 0x28, 'k', 'K', 0x0b }, + { 0x29, ';', ':', -1 }, + { 0x2a, 0x5c, '|', 0x1c }, /* \, | */ + { 0x2b, ',', '<', -1 }, + { 0x2c, '/', '?', 0x7f }, + { 0x2d, 'n', 'N', 0x0e }, + { 0x2e, 'm', 'M', 0x0d }, + { 0x2f, '.', '>', -1 }, + + { 0x30, 0x09, 0x09, -1 }, /* tab */ + { 0x31, ' ', ' ', -1 }, + { 0x32, '`', '~', -1 }, + { 0x33, 0x7f, 0x7f, -1 }, /* Delete */ + { 0x34, -1, -1, -1 }, + { 0x35, 0x1b, 0x1b, -1 }, /* Esc */ + { 0x36, 0x0200, 0x0200, -1 }, /* control */ + { 0x37, 0x8000, 0x8000, -1 }, /* Command */ + + { 0x38, 0x0100, 0x0100, -1 }, /* shift */ + { 0x39, 0x0400, 0x0400, -1 }, /* caps lock */ + { 0x3a, 0x4000, 0x4000, -1 }, /* Option */ + { 0x3b, 0x08, 0x08, -1 }, /* left */ + { 0x3c, 0x15, 0x15, -1 }, /* right */ + { 0x3d, 0x0a, 0x0a, -1 }, /* down */ + { 0x3e, 0x0b, 0x0b, -1 }, /* up arrow */ + { 0x3f, -1, -1, -1 }, + + { 0x40, -1, -1, -1 }, + { 0x41, 0x102e, 0x102e, -1 }, /* keypad . */ + { 0x42, -1, -1, -1 }, + { 0x43, 0x102a, 0x102a, -1 }, /* keypad * */ + { 0x44, -1, -1, -1 }, + { 0x45, 0x102b, 0x102b, -1 }, /* keypad + */ + { 0x46, -1, -1, -1 }, + { 0x47, 0x1018, 0x1018, -1 }, /* keypad Clear */ + + { 0x48, -1, -1, -1 }, + { 0x49, -1, -1, -1 }, + { 0x4a, -1, -1, -1 }, + { 0x4b, 0x102f, 0x102f, -1 }, /* keypad / */ + { 0x4c, 0x100d, 0x100d, -1 }, /* keypad enter */ + { 0x4d, -1, -1, -1 }, + { 0x4e, 0x102d, 0x102d, -1 }, /* keypad - */ + { 0x4f, -1, -1, -1 }, + + { 0x50, -1, -1, -1 }, + { 0x51, 0x103d, 0x103d, -1 }, /* keypad = */ + { 0x52, 0x1030, 0x1030, -1 }, /* keypad 0 */ + { 0x53, 0x1031, 0x1031, -1 }, /* keypad 1 */ + { 0x54, 0x1032, 0x1032, -1 }, /* keypad 2 */ + { 0x55, 0x1033, 0x1033, -1 }, /* keypad 3 */ + { 0x56, 0x1034, 0x1034, -1 }, /* keypad 4 */ + { 0x57, 0x1035, 0x1035, -1 }, /* keypad 5 */ + + { 0x58, 0x1036, 0x1036, -1 }, /* keypad 6 */ + { 0x59, 0x1037, 0x1037, -1 }, /* keypad 7 */ + { 0x5a, -1, -1, -1 }, + { 0x5b, 0x1038, 0x1038, -1 }, /* keypad 8 */ + { 0x5c, 0x1039, 0x1039, -1 }, /* keypad 9 */ + { 0x5d, -1, -1, -1 }, + { 0x5e, -1, -1, -1 }, + { 0x5f, -1, -1, -1 }, + + { 0x60, 0x8005, 0x1060, -1 }, /* F5 */ + { 0x61, 0x8006, 0x1061, -1 }, /* F6 */ + { 0x62, 0x8007, 0x1062, -1 }, /* F7 */ + { 0x63, 0x8003, 0x1063, -1 }, /* F3 */ + { 0x64, 0x8008, 0x1064, -1 }, /* F8 */ + { 0x65, 0x8009, 0x1065, -1 }, /* F9 */ + { 0x66, -1, -1, -1 }, + { 0x67, 0x800b, 0x1067, -1 }, /* F11 */ + + { 0x68, -1, -1, -1 }, + { 0x69, 0x800d, 0x1069, -1 }, /* F13 */ + { 0x6a, -1, -1, -1 }, + { 0x6b, 0x800e, 0x106b, -1 }, /* F14 */ + { 0x6c, -1, -1, -1 }, + { 0x6d, 0x800a, 0x106d, -1 }, /* F10 */ + { 0x6e, -1, -1, -1 }, + { 0x6f, 0x800c, 0x106f, -1 }, /* F12 */ + + { 0x70, -1, -1, -1 }, + { 0x71, 0x800f, 0x1071, -1 }, /* F15 */ + { 0x72, 0x1072, 0x1072, -1 }, /* Help, insert */ + { 0x73, 0x1073, 0x1073, -1 }, /* Home */ + { 0x74, 0x1074, 0x1074, -1 }, /* Page up */ + { 0x75, 0x1075, 0x1075, -1 }, /* keypad delete */ + { 0x76, 0x8004, 0x1076, -1 }, /* F4 */ + { 0x77, 0x1077, 0x1077, -1 }, /* keypad end */ + + { 0x78, 0x8002, 0x1078, -1 }, /* F2 */ + { 0x79, 0x1079, 0x1079, -1 }, /* keypad page down */ + { 0x7a, 0x8001, 0x107a, -1 }, /* F1 */ + { 0x7b, 0x08, 0x08, -1 }, /* left */ /* remapped to 0x3b */ + { 0x7c, 0x15, 0x15, -1 }, /* right */ /* remapped to 0x3c */ + { 0x7d, 0x0a, 0x0a, -1 }, /* down */ /* remapped to 0x3d */ + { 0x7e, 0x0b, 0x0b, -1 }, /* up arrow */ /* remapped to 0x3e */ + { 0x7f, -1, -1, -1 }, /* Reset */ +}; diff --git a/src/classes.nib b/src/classes.nib new file mode 100755 index 0000000..ea58db1 --- /dev/null +++ b/src/classes.nib @@ -0,0 +1,4 @@ +{ +IBClasses = (); +IBVersion = 1; +} diff --git a/src/clock.c b/src/clock.c new file mode 100644 index 0000000..442ed70 --- /dev/null +++ b/src/clock.c @@ -0,0 +1,392 @@ +/************************************************************************/ +/* KEGS: Apple //gs Emulator */ +/* Copyright 2002 by Kent Dickey */ +/* */ +/* This code is covered by the GNU GPL */ +/* */ +/* The KEGS web page is kegs.sourceforge.net */ +/* You may contact the author at: kadickey@alumni.princeton.edu */ +/************************************************************************/ + +const char rcsid_clock_c[] = "@(#)$KmKId: clock.c,v 1.29 2003-10-17 15:07:35-04 kentd Exp $"; + +#include "defc.h" +#include +#ifdef _WIN32 +# include +# include +#else +# include +#endif + +extern int Verbose; +extern int g_vbl_count; +extern int g_rom_version; +extern int g_config_kegs_update_needed; + +#define CLK_IDLE 1 +#define CLK_TIME 2 +#define CLK_INTERNAL 3 +#define CLK_BRAM1 4 +#define CLK_BRAM2 5 + +int g_clk_mode = CLK_IDLE; +int g_clk_read = 0; +int g_clk_reg1 = 0; + +word32 c033_data = 0; +word32 c034_val = 0; + +byte g_bram[2][256]; +byte *g_bram_ptr = &(g_bram[0][0]); + +word32 g_clk_cur_time = 0xa0000000; +int g_clk_next_vbl_update = 0; + +double +get_dtime() +{ +#ifndef _WIN32 + struct timeval tp1; + double dsec; + double dusec; +#endif + double dtime; + + /* Routine used to return actual system time as a double */ + /* No routine cares about the absolute value, only deltas--maybe */ + /* take advantage of that in future to increase usec accuracy */ + +#ifdef _WIN32 + dtime = timeGetTime() / 1000.0; +#else + +# ifdef SOLARIS + gettimeofday(&tp1, (void *)0); +# else + gettimeofday(&tp1, (struct timezone *)0); +# endif + + dsec = (double)tp1.tv_sec; + dusec = (double)tp1.tv_usec; + + dtime = dsec + (dusec / (1000.0 * 1000.0)); +#endif + + return dtime; +} + +int +micro_sleep(double dtime) +{ +#ifndef _WIN32 + struct timeval Timer; + int ret; +#endif + + if(dtime <= 0.0) { + return 0; + } + if(dtime >= 1.0) { + halt_printf("micro_sleep called with %f!!\n", dtime); + return -1; + } + +#if 0 + printf("usleep: %f\n", dtime); +#endif + +#ifdef _WIN32 + Sleep(dtime * 1000); +#else + Timer.tv_sec = 0; + Timer.tv_usec = (dtime * 1000000.0); + if( (ret = select(0, 0, 0, 0, &Timer)) < 0) { + fprintf(stderr, "micro_sleep (select) ret: %d, errno: %d\n", + ret, errno); + return -1; + } +#endif + return 0; +} + +void +clk_bram_zero() +{ + int i, j; + + /* zero out all bram */ + for(i = 0; i < 2; i++) { + for(j = 0; j < 256; j++) { + g_bram[i][j] = 0; + } + } + g_bram_ptr = &(g_bram[0][0]); +} + +void +clk_bram_set(int bram_num, int offset, int val) +{ + g_bram[bram_num][offset] = val; +} + +void +clk_setup_bram_version() +{ + if(g_rom_version < 3) { + g_bram_ptr = (&g_bram[0][0]); // ROM 01 + } else { + g_bram_ptr = (&g_bram[1][0]); // ROM 03 + } +} + +void +clk_write_bram(FILE *fconf) +{ + int i, j, k; + + for(i = 0; i < 2; i++) { + fprintf(fconf, "\n"); + for(j = 0; j < 256; j += 16) { + fprintf(fconf, "bram%d[%02x] =", 2*i + 1, j); + for(k = 0; k < 16; k++) { + fprintf(fconf, " %02x", g_bram[i][j+k]); + } + fprintf(fconf, "\n"); + } + } +} + +void +update_cur_time() +{ + struct tm *tm_ptr; + time_t cur_time; + unsigned int secs, secs2; + + cur_time = time(0); + + /* Figure out the timezone (effectively) by diffing two times. */ + /* this is probably not right for a few hours around daylight savings*/ + /* time transition */ + secs2 = mktime(gmtime(&cur_time)); + tm_ptr = localtime(&cur_time); + secs = mktime(tm_ptr); + + secs = (unsigned int)cur_time - (secs2 - secs); + + if(tm_ptr->tm_isdst) { + /* adjust for daylight savings time */ + secs += 3600; + } + + /* add in secs to make date based on Apple Jan 1, 1904 instead of */ + /* Unix's Jan 1, 1970 */ + /* So add in 66 years and 17 leap year days (1904 is a leap year) */ + secs += ((66*365) + 17) * (24*3600); + + g_clk_cur_time = secs; + + clk_printf("Update g_clk_cur_time to %08x\n", g_clk_cur_time); + g_clk_next_vbl_update = g_vbl_count + 5; +} + +/* clock_update called by sim65816 every VBL */ +void +clock_update() +{ + /* Nothing to do */ +} + +void +clock_update_if_needed() +{ + int diff; + + diff = g_clk_next_vbl_update - g_vbl_count; + if(diff < 0 || diff > 60) { + /* Been a while, re-read the clock */ + update_cur_time(); + } +} + +word32 +clock_read_c033() +{ + return c033_data; +} + +word32 +clock_read_c034() +{ + return c034_val; +} + +void +clock_write_c033(word32 val) +{ + c033_data = val; +} + +void +clock_write_c034(word32 val) +{ + c034_val = val & 0x7f; + if((val & 0x80) != 0) { + if((val & 0x20) == 0) { + printf("c034 write not last = 1\n"); + /* set_halt(1); */ + } + do_clock_data(); + } +} + + +void +do_clock_data() +{ + word32 mask; + int read; + int op; + + clk_printf("In do_clock_data, g_clk_mode: %02x\n", g_clk_mode); + + read = c034_val & 0x40; + switch(g_clk_mode) { + case CLK_IDLE: + g_clk_read = (c033_data >> 7) & 1; + g_clk_reg1 = (c033_data >> 2) & 3; + op = (c033_data >> 4) & 7; + if(!read) { + /* write */ + switch(op) { + case 0x0: /* Read/write seconds register */ + g_clk_mode = CLK_TIME; + clock_update_if_needed(); + break; + case 0x3: /* internal registers */ + g_clk_mode = CLK_INTERNAL; + if(g_clk_reg1 & 0x2) { + /* extend BRAM read */ + g_clk_mode = CLK_BRAM2; + g_clk_reg1 = (c033_data & 7) << 5; + } + break; + case 0x2: /* read/write ram 0x10-0x13 */ + g_clk_mode = CLK_BRAM1; + g_clk_reg1 += 0x10; + break; + case 0x4: /* read/write ram 0x00-0x0f */ + case 0x5: case 0x6: case 0x7: + g_clk_mode = CLK_BRAM1; + g_clk_reg1 = (c033_data >> 2) & 0xf; + break; + default: + halt_printf("Bad c033_data in CLK_IDLE: %02x\n", + c033_data); + } + } else { + printf("clk read from IDLE mode!\n"); + /* set_halt(1); */ + g_clk_mode = CLK_IDLE; + } + break; + case CLK_BRAM2: + if(!read) { + /* get more bits of bram addr */ + if((c033_data & 0x83) == 0x00) { + /* more address bits */ + g_clk_reg1 |= ((c033_data >> 2) & 0x1f); + g_clk_mode = CLK_BRAM1; + } else { + halt_printf("CLK_BRAM2: c033_data: %02x!\n", + c033_data); + g_clk_mode = CLK_IDLE; + } + } else { + halt_printf("CLK_BRAM2: clock read!\n"); + g_clk_mode = CLK_IDLE; + } + break; + case CLK_BRAM1: + /* access battery ram addr g_clk_reg1 */ + if(read) { + if(g_clk_read) { + /* Yup, read */ + c033_data = g_bram_ptr[g_clk_reg1]; + clk_printf("Reading BRAM loc %02x: %02x\n", + g_clk_reg1, c033_data); + } else { + halt_printf("CLK_BRAM1: said wr, now read\n"); + } + } else { + if(g_clk_read) { + halt_printf("CLK_BRAM1: said rd, now write\n"); + } else { + /* Yup, write */ + clk_printf("Writing BRAM loc %02x with %02x\n", + g_clk_reg1, c033_data); + g_bram_ptr[g_clk_reg1] = c033_data; + g_config_kegs_update_needed = 1; + } + } + g_clk_mode = CLK_IDLE; + break; + case CLK_TIME: + if(read) { + if(g_clk_read == 0) { + halt_printf("Reading time, but in set mode!\n"); + } + c033_data = (g_clk_cur_time >> (g_clk_reg1 * 8)) & 0xff; + clk_printf("Returning time byte %d: %02x\n", + g_clk_reg1, c033_data); + } else { + /* Write */ + if(g_clk_read) { + halt_printf("Write time, but in read mode!\n"); + } + clk_printf("Writing TIME loc %d with %02x\n", + g_clk_reg1, c033_data); + mask = 0xff << (8 * g_clk_reg1); + + g_clk_cur_time = (g_clk_cur_time & (~mask)) | + ((c033_data & 0xff) << (8 *g_clk_reg1)); + } + g_clk_mode = CLK_IDLE; + break; + case CLK_INTERNAL: + if(read) { + printf("Attempting to read internal reg %02x!\n", + g_clk_reg1); + } else { + switch(g_clk_reg1) { + case 0x0: /* test register */ + if(c033_data & 0xc0) { + printf("Writing test reg: %02x!\n", + c033_data); + /* set_halt(1); */ + } + break; + case 0x1: /* write protect reg */ + clk_printf("Writing clk wr_protect: %02x\n", + c033_data); + if(c033_data & 0x80) { + printf("Stop, wr clk wr_prot: %02x\n", + c033_data); + /* set_halt(1); */ + } + break; + default: + halt_printf("Writing int reg: %02x with %02x\n", + g_clk_reg1, c033_data); + } + } + g_clk_mode = CLK_IDLE; + break; + default: + halt_printf("clk mode: %d unknown!\n", g_clk_mode); + g_clk_mode = CLK_IDLE; + break; + } +} + diff --git a/src/compile_time.c b/src/compile_time.c new file mode 100644 index 0000000..e937797 --- /dev/null +++ b/src/compile_time.c @@ -0,0 +1,5 @@ + +const char rcsid_compile_time_c[] = "@(#)$KmKId: compile_time.c,v 1.2 2002-11-14 01:02:44-05 kadickey Exp $"; + +char g_compile_time[] = "Compiled: " __DATE__ " " __TIME__ ; + diff --git a/src/config.c b/src/config.c new file mode 100644 index 0000000..b9987b1 --- /dev/null +++ b/src/config.c @@ -0,0 +1,2560 @@ +/************************************************************************/ +/* KEGS: Apple //gs Emulator */ +/* Copyright 2003 by Kent Dickey */ +/* */ +/* This code is covered by the GNU GPL */ +/* */ +/* The KEGS web page is kegs.sourceforge.net */ +/* You may contact the author at: kadickey@alumni.princeton.edu */ +/************************************************************************/ + +const char rcsid_config_c[] = "@(#)$KmKId: config.c,v 1.30 2003-11-21 16:38:53-05 kentd Exp $"; + +#include "defc.h" +#include +#include "config.h" +#include + +extern int Verbose; +extern word32 g_vbl_count; +extern Iwm iwm; + +extern int g_track_bytes_35[]; +extern int g_track_nibs_35[]; +extern int g_apple35_sel; + +extern int g_cur_a2_stat; +extern byte *g_slow_memory_ptr; +extern double g_cur_dcycs; + +extern word32 g_adb_repeat_vbl; + +extern int g_limit_speed; +extern int g_force_depth; +extern int g_raw_serial; +extern int g_serial_out_masking; +extern word32 g_mem_size_exp; +extern int g_video_line_update_interval; + +extern int g_screen_index[]; +extern word32 g_full_refresh_needed; +extern word32 g_a2_screen_buffer_changed; +extern int g_a2_new_all_stat[]; +extern int g_new_a2_stat_cur_line; + +extern int g_key_down; +extern const char g_kegs_version_str[]; + +int g_config_control_panel = 0; +char g_config_kegs_name[256]; +char g_cfg_cwd_str[CFG_PATH_MAX] = { 0 }; + +int g_config_kegs_auto_update = 1; +int g_config_kegs_update_needed = 0; + +const char *g_config_kegs_name_list[] = { + "config.kegs", "kegs_conf", ".config.kegs", 0 +}; + +int g_highest_smartport_unit = -1; +int g_reparse_delay = 0; + +byte g_save_text_screen_bytes[0x800]; +word32 g_save_cur_a2_stat = 0; +char g_cfg_printf_buf[CFG_PRINTF_BUFSIZE]; +char g_config_kegs_buf[CONF_BUF_LEN]; + +word32 g_cfg_vbl_count = 0; + +int g_cfg_curs_x = 0; +int g_cfg_curs_y = 0; +int g_cfg_curs_inv = 0; +int g_cfg_curs_mousetext = 0; + +#define CFG_MAX_OPTS 16 +#define CFG_OPT_MAXSTR 100 + +int g_cfg_opts_vals[CFG_MAX_OPTS]; +char g_cfg_opts_strs[CFG_MAX_OPTS][CFG_OPT_MAXSTR]; +char g_cfg_opt_buf[CFG_OPT_MAXSTR]; + +#define MAX_PARTITION_BLK_SIZE 65536 + +extern Cfg_menu g_cfg_main_menu[]; + +#define KNMP(a) &a, #a, 0 + +Cfg_menu g_cfg_disk_menu[] = { +{ "Disk Configuration", g_cfg_disk_menu, 0, 0, CFGTYPE_MENU }, +{ "s5d1 = ", 0, 0, 0, CFGTYPE_DISK + 0x5000 }, +{ "s5d2 = ", 0, 0, 0, CFGTYPE_DISK + 0x5010 }, +{ "", 0, 0, 0, 0 }, +{ "s6d1 = ", 0, 0, 0, CFGTYPE_DISK + 0x6000 }, +{ "s6d2 = ", 0, 0, 0, CFGTYPE_DISK + 0x6010 }, +{ "", 0, 0, 0, 0 }, +{ "s7d1 = ", 0, 0, 0, CFGTYPE_DISK + 0x7000 }, +{ "s7d2 = ", 0, 0, 0, CFGTYPE_DISK + 0x7010 }, +{ "s7d3 = ", 0, 0, 0, CFGTYPE_DISK + 0x7020 }, +{ "s7d4 = ", 0, 0, 0, CFGTYPE_DISK + 0x7030 }, +{ "s7d5 = ", 0, 0, 0, CFGTYPE_DISK + 0x7040 }, +{ "s7d6 = ", 0, 0, 0, CFGTYPE_DISK + 0x7050 }, +{ "s7d7 = ", 0, 0, 0, CFGTYPE_DISK + 0x7060 }, +{ "s7d8 = ", 0, 0, 0, CFGTYPE_DISK + 0x7070 }, +{ "s7d9 = ", 0, 0, 0, CFGTYPE_DISK + 0x7080 }, +{ "s7d10 = ", 0, 0, 0, CFGTYPE_DISK + 0x7090 }, +{ "s7d11 = ", 0, 0, 0, CFGTYPE_DISK + 0x70a0 }, +{ "", 0, 0, 0, 0 }, +{ "Back to Main Config", g_cfg_main_menu, 0, 0, CFGTYPE_MENU }, +{ 0, 0, 0, 0, 0 }, +}; + +Cfg_menu g_cfg_main_menu[] = { +{ "KEGS Configuration", g_cfg_main_menu, 0, 0, CFGTYPE_MENU }, +{ "Disk Configuration", g_cfg_disk_menu, 0, 0, CFGTYPE_MENU }, +{ "Force X-windows display depth", KNMP(g_force_depth), CFGTYPE_INT }, +{ "Auto-update config.kegs,0,Manual,1,Immediately", + KNMP(g_config_kegs_auto_update), CFGTYPE_INT }, +{ "Speed,0,Unlimited,1,1.0MHz,2,2.8MHz,3,8.0MHz (Zip)", + KNMP(g_limit_speed), CFGTYPE_INT }, +{ "Expansion Mem Size,0,0MB,0x100000,1MB,0x200000,2MB,0x300000,3MB," + "0x400000,4MB,0x600000,6MB,0x800000,8MB,0xa00000,10MB,0xc00000,12MB," + "0xe00000,14MB", KNMP(g_mem_size_exp), CFGTYPE_INT }, +{ "Serial Ports,0,Only use sockets 6501-6502,1,Use real ports if avail", + KNMP(g_raw_serial), CFGTYPE_INT }, +{ "Serial Output,0,Send full 8-bit data,1,Mask off high bit", + KNMP(g_serial_out_masking), CFGTYPE_INT }, +{ "3200 Color Enable,0,Auto (Full if fast enough),1,Full (Update every line)," + "8,Off (Update video every 8 lines)", + KNMP(g_video_line_update_interval), CFGTYPE_INT }, +{ "Dump text screen to file", (void *)cfg_text_screen_dump, 0, 0, CFGTYPE_FUNC}, +{ "", 0, 0, 0, 0 }, +{ "Save changes to config.kegs", (void *)config_write_config_kegs_file, 0, 0, + CFGTYPE_FUNC }, +{ "", 0, 0, 0, 0 }, +{ "Exit Config (or press F4)", (void *)cfg_exit, 0, 0, CFGTYPE_FUNC }, +{ 0, 0, 0, 0, 0 }, +}; + + +#define CFG_MAX_DEFVALS 128 +Cfg_defval g_cfg_defvals[CFG_MAX_DEFVALS]; +int g_cfg_defval_index = 0; + +int g_cfg_slotdrive = -1; +int g_cfg_select_partition = -1; +char g_cfg_tmp_path[CFG_PATH_MAX]; +char g_cfg_file_path[CFG_PATH_MAX]; +char g_cfg_file_cachedpath[CFG_PATH_MAX]; +char g_cfg_file_cachedreal[CFG_PATH_MAX]; +char g_cfg_file_curpath[CFG_PATH_MAX]; +char g_cfg_file_shortened[CFG_PATH_MAX]; +char g_cfg_file_match[CFG_PATH_MAX]; + +Cfg_listhdr g_cfg_dirlist = { 0 }; +Cfg_listhdr g_cfg_partitionlist = { 0 }; + +int g_cfg_file_pathfield = 0; + +void +config_init_menus(Cfg_menu *menuptr) +{ + void *voidptr; + const char *name_str; + Cfg_defval *defptr; + int type; + int pos; + int val; + + if(menuptr[0].defptr != 0) { + return; + } + menuptr[0].defptr = (void *)1; + pos = 0; + while(pos < 100) { + type = menuptr->cfgtype; + voidptr = menuptr->ptr; + name_str = menuptr->name_str; + if(menuptr->str == 0) { + break; + } + if(name_str != 0) { + defptr = &(g_cfg_defvals[g_cfg_defval_index++]); + if(g_cfg_defval_index >= CFG_MAX_DEFVALS) { + printf("CFG_MAX_DEFVAL overflow\n"); + my_exit(5); + } + defptr->menuptr = menuptr; + defptr->intval = 0; + switch(type) { + case CFGTYPE_INT: + val = *((int *)voidptr); + defptr->intval = val; + menuptr->defptr = &(defptr->intval); + break; + default: + printf("name_str is %p = %s, but type: %d\n", + name_str, name_str, type); + my_exit(5); + } + } + if(type == CFGTYPE_MENU) { + config_init_menus((Cfg_menu *)voidptr); + } + pos++; + menuptr++; + } +} + +void +config_init() +{ + config_init_menus(g_cfg_main_menu); + + // Find the config.kegs file + setup_kegs_file(&g_config_kegs_name[0], sizeof(g_config_kegs_name), 0, + &g_config_kegs_name_list[0]); + + config_parse_config_kegs_file(); +} + +void +cfg_exit() +{ + /* printf("In cfg exit\n"); */ + g_config_control_panel = 0; +} + +void +cfg_text_screen_dump() +{ + char buf[85]; + char *filename; + FILE *ofile; + int offset; + int c; + int pos; + int i, j; + + filename = "kegs.screen.dump"; + printf("Writing text screen to the file %s\n", filename); + ofile = fopen(filename, "w"); + if(ofile == 0) { + printf("fopen ret 0, errno: %d\n", errno); + return; + } + + for(i = 0; i < 24; i++) { + pos = 0; + for(j = 0; j < 40; j++) { + offset = g_screen_index[i] + j; + if(g_save_cur_a2_stat & ALL_STAT_VID80) { + c = g_save_text_screen_bytes[0x400+offset]; + c = c & 0x7f; + if(c < 0x20) { + c += 0x40; + } + buf[pos++] = c; + } + c = g_save_text_screen_bytes[offset] & 0x7f; + if(c < 0x20) { + c += 0x40; + } + buf[pos++] = c; + } + while((pos > 0) && (buf[pos-1] == ' ')) { + /* try to strip out trailing spaces */ + pos--; + } + buf[pos++] = '\n'; + buf[pos++] = 0; + fputs(buf, ofile); + } + fclose(ofile); +} + +void +config_vbl_update(int doit_3_persec) +{ + if(doit_3_persec) { + if(g_config_kegs_auto_update && g_config_kegs_update_needed) { + config_write_config_kegs_file(); + } + } + return; +} + +void +config_parse_option(char *buf, int pos, int len, int line) +{ + Cfg_menu *menuptr; + Cfg_defval *defptr; + char *nameptr; + int *iptr; + int num_equals; + int type; + int val; + int c; + int i; + +// warning: modifies buf (turns spaces to nulls) +// parse buf from pos into option, "=" and then "rest" + if(pos >= len) { + /* blank line */ + return; + } + + if(strncmp(&buf[pos], "bram", 4) == 0) { + config_parse_bram(buf, pos+4, len); + return; + } + + // find "name" as first contiguous string + printf("...parse_option: line %d, %p,%p = %s (%s) len:%d\n", line, + &buf[pos], buf, &buf[pos], buf, len); + + nameptr = &buf[pos]; + while(pos < len) { + c = buf[pos]; + if(c == 0 || c == ' ' || c == '\t' || c == '\n') { + break; + } + pos++; + } + buf[pos] = 0; + pos++; + + // Eat up all whitespace and '=' + num_equals = 0; + while(pos < len) { + c = buf[pos]; + if((c == '=') && num_equals == 0) { + pos++; + num_equals++; + } else if(c == ' ' || c == '\t') { + pos++; + } else { + break; + } + } + + /* Look up nameptr to find type */ + type = -1; + defptr = 0; + menuptr = 0; + for(i = 0; i < g_cfg_defval_index; i++) { + defptr = &(g_cfg_defvals[i]); + menuptr = defptr->menuptr; + if(strcmp(menuptr->name_str, nameptr) == 0) { + type = menuptr->cfgtype; + break; + } + } + + switch(type) { + case CFGTYPE_INT: + /* use strtol */ + val = (int)strtol(&buf[pos], 0, 0); + iptr = (int *)menuptr->ptr; + *iptr = val; + break; + default: + printf("Config file variable %s is unknown type: %d\n", + nameptr, type); + } + +} + +void +config_parse_bram(char *buf, int pos, int len) +{ + int bram_num; + int offset; + int val; + + if((len < (pos+5)) || (buf[pos+1] != '[') || (buf[pos+4] != ']')) { + printf("Malformed bram statement: %s\n", buf); + return; + } + bram_num = buf[pos] - '0'; + if(bram_num != 1 && bram_num != 3) { + printf("Malformed bram number: %s\n", buf); + return; + } + + bram_num = bram_num >> 1; // turn 3->1 and 1->0 + + offset = strtoul(&(buf[pos+2]), 0, 16); + pos += 5; + while(pos < len) { + while(buf[pos] == ' ' || buf[pos] == '\t' || buf[pos] == 0x0a || + buf[pos] == 0x0d || buf[pos] == '=') { + pos++; + } + val = strtoul(&buf[pos], 0, 16); + clk_bram_set(bram_num, offset, val); + offset++; + pos += 2; + } +} + +void +config_parse_config_kegs_file() +{ + FILE *fconf; + char *buf; + char *ptr; + char *name_ptr; + char *partition_name; + int part_num; + int ejected; + int line; + int pos; + int slot; + int drive; + int size; + int len; + int ret; + int i; + + printf("Parsing config.kegs file\n"); + + clk_bram_zero(); + + g_highest_smartport_unit = -1; + + cfg_get_base_path(&g_cfg_cwd_str[0], g_config_kegs_name, 0); + if(g_cfg_cwd_str[0] != 0) { + ret = chdir(&g_cfg_cwd_str[0]); + if(ret != 0) { + printf("chdir to %s, errno:%d\n", g_cfg_cwd_str, errno); + } + } + + /* In any case, copy the directory path to g_cfg_cwd_str */ + (void)getcwd(&g_cfg_cwd_str[0], CFG_PATH_MAX); + + fconf = fopen(g_config_kegs_name, "rt"); + if(fconf == 0) { + printf("cannot open disk_conf! Stopping!\n"); + exit(3); + } + + line = 0; + while(1) { + buf = &g_config_kegs_buf[0]; + ptr = fgets(buf, CONF_BUF_LEN, fconf); + if(ptr == 0) { + iwm_printf("Done reading disk_conf\n"); + break; + } + + line++; + /* strip off newline(s) */ + len = strlen(buf); + for(i = len - 1; i >= 0; i--) { + if((buf[i] != 0x0d) && (buf[i] != 0x0a)) { + break; + } + len = i; + buf[i] = 0; + } + + iwm_printf("disk_conf[%d]: %s\n", line, buf); + if(len > 0 && buf[0] == '#') { + iwm_printf("Skipping comment\n"); + continue; + } + + /* determine what this is */ + pos = 0; + + while(pos < len && (buf[pos] == ' ' || buf[pos] == '\t') ) { + pos++; + } + if((pos + 4) > len || buf[pos] != 's' || buf[pos+2] != 'd' || + buf[pos+1] > '9' || buf[pos+1] < '0') { + config_parse_option(buf, pos, len, line); + continue; + } + + slot = buf[pos+1] - '0'; + drive = buf[pos+3] - '0'; + + /* skip over slot, drive */ + pos += 4; + if(buf[pos] >= '0' && buf[pos] <= '9') { + drive = drive * 10 + buf[pos] - '0'; + pos++; + } + + /* make s6d1 mean index 0 */ + drive--; + + while(pos < len && (buf[pos] == ' ' || buf[pos] == '\t' || + buf[pos] == '=') ) { + pos++; + } + + ejected = 0; + if(buf[pos] == '#') { + /* disk is ejected, but read all the info anyway */ + ejected = 1; + pos++; + } + + size = 0; + if(buf[pos] == ',') { + /* read optional size parameter */ + pos++; + while(pos < len && buf[pos] >= '0' && buf[pos] <= '9'){ + size = size * 10 + buf[pos] - '0'; + pos++; + } + size = size * 1024; + if(buf[pos] == ',') { + pos++; /* eat trailing ',' */ + } + } + + /* see if it has a partition name */ + partition_name = 0; + part_num = -1; + if(buf[pos] == ':') { + pos++; + /* yup, it's got a partition name! */ + partition_name = &buf[pos]; + while((pos < len) && (buf[pos] != ':')) { + pos++; + } + buf[pos] = 0; /* null terminate partition name */ + pos++; + } + if(buf[pos] == ';') { + pos++; + /* it's got a partition number */ + part_num = 0; + while((pos < len) && (buf[pos] != ':')) { + part_num = (10*part_num) + buf[pos] - '0'; + pos++; + } + pos++; + } + + /* Get filename */ + name_ptr = &(buf[pos]); + if(name_ptr[0] == 0) { + continue; + } + + insert_disk(slot, drive, name_ptr, ejected, size, + partition_name, part_num); + + } + + ret = fclose(fconf); + if(ret != 0) { + printf("Closing disk_conf ret: %d, errno: %d\n", ret, errno); + exit(4); + } + + iwm_printf("Done parsing disk_conf file\n"); +} + + +Disk * +cfg_get_dsk_from_slot_drive(int slot, int drive) +{ + Disk *dsk; + int max_drive; + + /* Get dsk */ + max_drive = 2; + switch(slot) { + case 5: + dsk = &(iwm.drive35[drive]); + break; + case 6: + dsk = &(iwm.drive525[drive]); + break; + default: + max_drive = MAX_C7_DISKS; + dsk = &(iwm.smartport[drive]); + } + + if(drive >= max_drive) { + dsk -= drive; /* move back to drive 0 effectively */ + } + + return dsk; +} + +void +config_generate_config_kegs_name(char *outstr, int maxlen, Disk *dsk, + int with_extras) +{ + char *str; + + str = outstr; + + if(with_extras && dsk->fd < 0) { + snprintf(str, maxlen - (str - outstr), "#"); + str = &outstr[strlen(outstr)]; + } + if(with_extras && dsk->force_size > 0) { + snprintf(str, maxlen - (str - outstr), ",%d,", dsk->force_size); + str = &outstr[strlen(outstr)]; + } + if(with_extras && dsk->partition_name != 0) { + snprintf(str, maxlen - (str - outstr), ":%s:", + dsk->partition_name); + str = &outstr[strlen(outstr)]; + } else if(with_extras && dsk->partition_num >= 0) { + snprintf(str, maxlen - (str - outstr), ";%d:", + dsk->partition_num); + str = &outstr[strlen(outstr)]; + } + snprintf(str, maxlen - (str - outstr), "%s", dsk->name_ptr); +} + +void +config_write_config_kegs_file() +{ + FILE *fconf; + Disk *dsk; + Cfg_defval *defptr; + Cfg_menu *menuptr; + int defval, curval; + int type; + int slot, drive; + int i; + + printf("Writing config.kegs file to %s\n", g_config_kegs_name); + + fconf = fopen(g_config_kegs_name, "w+"); + if(fconf == 0) { + halt_printf("cannot open %s! Stopping!\n"); + return; + } + + fprintf(fconf, "# KEGS configuration file version %s\n", + g_kegs_version_str); + + for(i = 0; i < MAX_C7_DISKS + 4; i++) { + slot = 7; + drive = i - 4; + if(i < 4) { + slot = (i >> 1) + 5; + drive = i & 1; + } + if(drive == 0) { + fprintf(fconf, "\n"); /* an extra blank line */ + } + + dsk = cfg_get_dsk_from_slot_drive(slot, drive); + if(dsk->name_ptr == 0 && (i > 4)) { + /* No disk, not even ejected--just skip */ + continue; + } + fprintf(fconf, "s%dd%d = ", slot, drive + 1); + if(dsk->name_ptr == 0) { + fprintf(fconf, "\n"); + continue; + } + config_generate_config_kegs_name(&g_cfg_tmp_path[0], + CFG_PATH_MAX, dsk, 1); + fprintf(fconf, "%s\n", &g_cfg_tmp_path[0]); + } + + fprintf(fconf, "\n"); + + /* See if any variables are different than their default */ + for(i = 0; i < g_cfg_defval_index; i++) { + defptr = &(g_cfg_defvals[i]); + menuptr = defptr->menuptr; + defval = defptr->intval; + type = menuptr->cfgtype; + if(type != CFGTYPE_INT) { + /* skip it */ + continue; + } + curval = *((int *)menuptr->ptr); + if(curval != defval) { + fprintf(fconf, "%s = %d\n", menuptr->name_str, curval); + } + } + + fprintf(fconf, "\n"); + + /* write bram state */ + clk_write_bram(fconf); + + fclose(fconf); + + g_config_kegs_update_needed = 0; +} + +void +insert_disk(int slot, int drive, const char *name, int ejected, int force_size, + const char *partition_name, int part_num) +{ + byte buf_2img[512]; + Disk *dsk; + char *name_ptr, *uncomp_ptr, *system_str; + char *part_ptr; + int size; + int system_len; + int part_len; + int cmp_o, cmp_p, cmp_dot; + int cmp_b, cmp_i, cmp_n; + int can_write; + int len; + int nibs; + int unix_pos; + int name_len; + int image_identified; + int exp_size; + int save_track; + int ret; + int tmp; + int i; + + g_config_kegs_update_needed = 1; + + if((slot < 5) || (slot > 7)) { + printf("insert_disk: Invalid slot: %d\n", slot); + return; + } + if(drive < 0 || ((slot == 7) && (drive >= MAX_C7_DISKS)) || + ((slot < 7) && (drive > 1))) { + printf("insert_disk: Invalid drive: %d\n", drive); + return; + } + + dsk = cfg_get_dsk_from_slot_drive(slot, drive); + +#if 0 + printf("Inserting disk %s (%s or %d) in slot %d, drive: %d\n", name, + partition_name, part_num, slot, drive); +#endif + + dsk->just_ejected = 0; + dsk->force_size = force_size; + + if(dsk->fd >= 0) { + eject_disk(dsk); + } + + /* Before opening, make sure no other mounted disk has this name */ + /* If so, unmount it */ + if(!ejected) { + for(i = 0; i < 2; i++) { + eject_named_disk(&iwm.drive525[i], name,partition_name); + eject_named_disk(&iwm.drive35[i], name, partition_name); + } + for(i = 0; i < MAX_C7_DISKS; i++) { + eject_named_disk(&iwm.smartport[i],name,partition_name); + } + } + + if(dsk->name_ptr != 0) { + /* free old name_ptr */ + free(dsk->name_ptr); + } + + name_len = strlen(name) + 1; + name_ptr = (char *)malloc(name_len); + strncpy(name_ptr, name, name_len); + dsk->name_ptr = name_ptr; + + dsk->partition_name = 0; + if(partition_name != 0) { + part_len = strlen(partition_name) + 1; + part_ptr = (char *)malloc(part_len); + strncpy(part_ptr, partition_name, part_len); + dsk->partition_name = part_ptr; + } + dsk->partition_num = part_num; + + iwm_printf("Opening up disk image named: %s\n", name_ptr); + + if(ejected) { + /* just get out of here */ + dsk->fd = -1; + return; + } + + dsk->fd = -1; + can_write = 1; + + if((name_len > 4) && (strcmp(&name_ptr[name_len - 4], ".gz") == 0)) { + + /* it's gzip'ed, try to gunzip it, then unlink the */ + /* uncompressed file */ + + can_write = 0; + + uncomp_ptr = (char *)malloc(name_len); + strncpy(uncomp_ptr, name_ptr, name_len); + uncomp_ptr[name_len - 4] = 0; + + system_len = name_len + 200; + system_str = (char *)malloc(system_len + 1); + snprintf(system_str, system_len, + "set -o noclobber;gunzip -c %s > %s", name_ptr, + uncomp_ptr); + printf("I am uncompressing %s into %s for mounting\n", + name_ptr, uncomp_ptr); + ret = system(system_str); + if(ret == 0) { + /* successfully ran */ + dsk->fd = open(uncomp_ptr, O_RDONLY | O_BINARY, 0x1b6); + iwm_printf("Opening .gz file %s is fd: %d\n", + uncomp_ptr, dsk->fd); + + /* and, unlink the temporary file */ + (void)unlink(uncomp_ptr); + } + free(system_str); + free(uncomp_ptr); + } + + if(dsk->fd < 0 && can_write) { + dsk->fd = open(name_ptr, O_RDWR | O_BINARY, 0x1b6); + } + + if(dsk->fd < 0 && can_write) { + printf("Trying to open %s read-only, errno: %d\n", name_ptr, + errno); + dsk->fd = open(name_ptr, O_RDONLY | O_BINARY, 0x1b6); + can_write = 0; + } + + iwm_printf("open returned: %d\n", dsk->fd); + + if(dsk->fd < 0) { + printf("Disk image %s does not exist!\n", name_ptr); + return; + } + + if(can_write != 0) { + dsk->write_prot = 0; + dsk->write_through_to_unix = 1; + } else { + dsk->write_prot = 1; + dsk->write_through_to_unix = 0; + } + + save_track = dsk->cur_qtr_track; /* save arm position */ + dsk->image_type = DSK_TYPE_PRODOS; + + /* See if it is in 2IMG format */ + ret = read(dsk->fd, (char *)&buf_2img[0], 512); + size = force_size; + if(size <= 0) { + size = cfg_get_fd_size(dsk->fd); + } + image_identified = 0; + if(buf_2img[0] == '2' && buf_2img[1] == 'I' && buf_2img[2] == 'M' && + buf_2img[3] == 'G') { + /* It's a 2IMG disk */ + printf("Image named %s is in 2IMG format\n", dsk->name_ptr); + image_identified = 1; + + if(buf_2img[12] == 0) { + printf("2IMG is in DOS 3.3 sector order\n"); + dsk->image_type = DSK_TYPE_DOS33; + } + if(buf_2img[19] & 0x80) { + /* disk is locked */ + printf("2IMG is write protected\n"); + dsk->write_prot = 1; + dsk->write_through_to_unix = 0; + } + if((buf_2img[17] & 1) && (dsk->image_type == DSK_TYPE_DOS33)) { + dsk->vol_num = buf_2img[16]; + printf("Setting DOS 3.3 vol num to %d\n", dsk->vol_num); + } + // Some 2IMG archives have the size byte reversed + size = (buf_2img[31] << 24) + (buf_2img[30] << 16) + + (buf_2img[29] << 8) + buf_2img[28]; + unix_pos = (buf_2img[27] << 24) + (buf_2img[26] << 16) + + (buf_2img[25] << 8) + buf_2img[24]; + if(size == 0x800c00) { + // Byte reversed 0x0c8000 + size = 0x0c8000; + } + if(size == 0) { + /* From KEGS-OS-X: Gilles Tschopp: */ + /* deal with corrupted 2IMG files */ + printf("Bernie corrupted size to 0...working around\n"); + + /* Just get the full size, and subtract 64, and */ + /* then round down to lower 0x1000 boundary */ + size = cfg_get_fd_size(dsk->fd) - 64; + size = size & -0x1000; + } + dsk->image_start = unix_pos; + dsk->image_size = size; + } + exp_size = 800*1024; + if(dsk->disk_525) { + exp_size = 140*1024; + } + if(!image_identified) { + /* See if it might be the Mac diskcopy format */ + tmp = (buf_2img[0x40] << 24) + (buf_2img[0x41] << 16) + + (buf_2img[0x42] << 8) + buf_2img[0x43]; + if((size >= (exp_size + 0x54)) && (tmp == exp_size)) { + /* It's diskcopy since data size field matches */ + printf("Image named %s is in Mac diskcopy format\n", + dsk->name_ptr); + image_identified = 1; + dsk->image_start = 0x54; + dsk->image_size = exp_size; + dsk->image_type = DSK_TYPE_PRODOS; /* ProDOS */ + } + } + if(!image_identified) { + /* Assume raw image */ + dsk->image_start = 0; + dsk->image_size = size; + dsk->image_type = DSK_TYPE_PRODOS; + if(dsk->disk_525) { + dsk->image_type = DSK_TYPE_DOS33; + if(name_len >= 5) { + cmp_o = dsk->name_ptr[name_len-2]; + cmp_p = dsk->name_ptr[name_len-3]; + cmp_dot = dsk->name_ptr[name_len-4]; + if(cmp_dot == '.' && + (cmp_p == 'p' || cmp_p == 'P') && + (cmp_o == 'o' || cmp_o == 'O')) { + dsk->image_type = DSK_TYPE_PRODOS; + } + + cmp_b = dsk->name_ptr[name_len-2]; + cmp_i = dsk->name_ptr[name_len-3]; + cmp_n = dsk->name_ptr[name_len-4]; + cmp_dot = dsk->name_ptr[name_len-5]; + if(cmp_dot == '.' && + (cmp_n == 'n' || cmp_n == 'N') && + (cmp_i == 'i' || cmp_i == 'I') && + (cmp_b == 'b' || cmp_b == 'B')) { + dsk->image_type = DSK_TYPE_NIB; + dsk->write_prot = 1; + dsk->write_through_to_unix = 0; + } + } + } + } + + dsk->disk_dirty = 0; + dsk->nib_pos = 0; + + if(dsk->smartport) { + g_highest_smartport_unit = MAX(dsk->drive, + g_highest_smartport_unit); + + if(partition_name != 0 || part_num >= 0) { + ret = cfg_partition_find_by_name_or_num(dsk->fd, + partition_name, part_num, dsk); + printf("partition %s (num %d) mounted, wr_prot: %d\n", + partition_name, part_num, dsk->write_prot); + + if(ret < 0) { + close(dsk->fd); + dsk->fd = -1; + return; + } + } + iwm_printf("adding smartport device[%d], size:%08x, " + "img_sz:%08x\n", dsk->drive, dsk->tracks[0].unix_len, + dsk->image_size); + } else if(dsk->disk_525) { + unix_pos = dsk->image_start; + size = dsk->image_size; + dsk->num_tracks = 4*35; + len = 0x1000; + nibs = NIB_LEN_525; + if(dsk->image_type == DSK_TYPE_NIB) { + len = dsk->image_size / 35;; + nibs = len; + } + if(size != 35*len) { + printf("Disk 5.25 error: size is %d, not %d\n",size, + 35*len); + } + for(i = 0; i < 35; i++) { + iwm_move_to_track(dsk, 4*i); + disk_unix_to_nib(dsk, 4*i, unix_pos, len, nibs); + unix_pos += len; + } + } else { + /* disk_35 */ + unix_pos = dsk->image_start; + size = dsk->image_size; + if(size != 800*1024) { + printf("Disk 3.5 error: size is %d, not 800K\n", size); + } + dsk->num_tracks = 2*80; + for(i = 0; i < 2*80; i++) { + iwm_move_to_track(dsk, i); + len = g_track_bytes_35[i >> 5]; + nibs = g_track_nibs_35[i >> 5]; + iwm_printf("Trk: %d.%d = unix: %08x, %04x, %04x\n", + i>>1, i & 1, unix_pos, len, nibs); + disk_unix_to_nib(dsk, i, unix_pos, len, nibs); + unix_pos += len; + + iwm_printf(" trk_len:%05x\n",dsk->tracks[i].track_len); + } + } + + iwm_move_to_track(dsk, save_track); + +} + +void +eject_named_disk(Disk *dsk, const char *name, const char *partition_name) +{ + + if(dsk->fd < 0) { + return; + } + + /* If name matches, eject the disk! */ + if(!strcmp(dsk->name_ptr, name)) { + /* It matches, eject it */ + if((partition_name != 0) && (dsk->partition_name != 0)) { + /* If both have partitions, and they differ, then */ + /* don't eject. Otherwise, eject */ + if(strcmp(dsk->partition_name, partition_name) != 0) { + /* Don't eject */ + return; + } + } + eject_disk(dsk); + } +} + +void +eject_disk_by_num(int slot, int drive) +{ + Disk *dsk; + + dsk = cfg_get_dsk_from_slot_drive(slot, drive); + + eject_disk(dsk); +} + +void +eject_disk(Disk *dsk) +{ + int motor_on; + int i; + + if(dsk->fd < 0) { + return; + } + + g_config_kegs_update_needed = 1; + + motor_on = iwm.motor_on; + if(g_apple35_sel) { + motor_on = iwm.motor_on35; + } + if(motor_on) { + halt_printf("Try eject dsk:%s, but motor_on!\n", dsk->name_ptr); + } + + iwm_flush_disk_to_unix(dsk); + + printf("Ejecting disk: %s\n", dsk->name_ptr); + + /* Free all memory, close file */ + + /* free the tracks first */ + for(i = 0; i < dsk->num_tracks; i++) { + if(dsk->tracks[i].nib_area) { + free(dsk->tracks[i].nib_area); + } + dsk->tracks[i].nib_area = 0; + dsk->tracks[i].track_len = 0; + } + dsk->num_tracks = 0; + + /* close file, clean up dsk struct */ + close(dsk->fd); + + dsk->image_start = 0; + dsk->image_size = 0; + dsk->nib_pos = 0; + dsk->disk_dirty = 0; + dsk->write_through_to_unix = 0; + dsk->write_prot = 1; + dsk->fd = -1; + dsk->just_ejected = 1; + + /* Leave name_ptr valid */ +} + +int +cfg_get_fd_size(int fd) +{ + struct stat stat_buf; + int ret; + + ret = fstat(fd, &stat_buf); + if(ret != 0) { + fprintf(stderr,"fstat returned %d on fd %d, errno: %d\n", + ret, fd, errno); + stat_buf.st_size = 0; + } + + return stat_buf.st_size; +} + +int +cfg_partition_read_block(int fd, void *buf, int blk, int blk_size) +{ + int ret; + + ret = lseek(fd, blk * blk_size, SEEK_SET); + if(ret != blk * blk_size) { + printf("lseek: %08x, wanted: %08x, errno: %d\n", ret, + blk * blk_size, errno); + return 0; + } + + ret = read(fd, (char *)buf, blk_size); + if(ret != blk_size) { + printf("ret: %08x, wanted %08x, errno: %d\n", ret, blk_size, + errno); + return 0; + } + return ret; +} + +int +cfg_partition_find_by_name_or_num(int fd, const char *partnamestr, int part_num, + Disk *dsk) +{ + Cfg_dirent *direntptr; + int match; + int num_parts; + int i; + + num_parts = cfg_partition_make_list(fd); + + if(num_parts <= 0) { + return -1; + } + + for(i = 0; i < g_cfg_partitionlist.last; i++) { + direntptr = &(g_cfg_partitionlist.direntptr[i]); + match = 0; + if((strncmp(partnamestr, direntptr->name, 32) == 0) && + (part_num < 0)) { + //printf("partition, match1, name:%s %s, part_num:%d\n", + // partnamestr, direntptr->name, part_num); + + match = 1; + } + if((partnamestr == 0) && (direntptr->part_num == part_num)) { + //printf("partition, match2, n:%s, part_num:%d == %d\n", + // direntptr->name, direntptr->part_num, part_num); + match = 1; + } + if(match) { + dsk->image_start = direntptr->image_start; + dsk->image_size = direntptr->size; + //printf("match with image_start: %08x, image_size: " + // "%08x\n", dsk->image_start, dsk->image_size); + + return i; + } + } + + return -1; +} + +int +cfg_partition_make_list(int fd) +{ + Driver_desc *driver_desc_ptr; + Part_map *part_map_ptr; + word32 *blk_bufptr; + word32 start; + word32 len; + word32 data_off; + word32 data_len; + word32 sig; + int size; + int image_start, image_size; + int is_dir; + int block_size; + int map_blks; + int cur_blk; + + block_size = 512; + + cfg_free_alldirents(&g_cfg_partitionlist); + + blk_bufptr = (word32 *)malloc(MAX_PARTITION_BLK_SIZE); + + cfg_partition_read_block(fd, blk_bufptr, 0, block_size); + + driver_desc_ptr = (Driver_desc *)blk_bufptr; + sig = GET_BE_WORD16(driver_desc_ptr->sig); + block_size = GET_BE_WORD16(driver_desc_ptr->blk_size); + if(block_size == 0) { + block_size = 512; + } + if(sig != 0x4552 || block_size < 0x200 || + (block_size > MAX_PARTITION_BLK_SIZE)) { + cfg_printf("Partition error: No driver descriptor map found\n"); + free(blk_bufptr); + return 0; + } + + map_blks = 1; + cur_blk = 0; + size = cfg_get_fd_size(fd); + cfg_file_add_dirent(&g_cfg_partitionlist, "None - Whole image", + is_dir=0, size, 0, -1); + + while(cur_blk < map_blks) { + cur_blk++; + cfg_partition_read_block(fd, blk_bufptr, cur_blk, block_size); + part_map_ptr = (Part_map *)blk_bufptr; + sig = GET_BE_WORD16(part_map_ptr->sig); + if(cur_blk <= 1) { + map_blks = MIN(20, + GET_BE_WORD32(part_map_ptr->map_blk_cnt)); + } + if(sig != 0x504d) { + printf("Partition entry %d bad sig\n", cur_blk); + free(blk_bufptr); + return g_cfg_partitionlist.last; + } + + /* found it, check for consistency */ + start = GET_BE_WORD32(part_map_ptr->phys_part_start); + len = GET_BE_WORD32(part_map_ptr->part_blk_cnt); + data_off = GET_BE_WORD32(part_map_ptr->data_start); + data_len = GET_BE_WORD32(part_map_ptr->data_cnt); + if(data_off + data_len > len) { + printf("Poorly formed entry\n"); + continue; + } + + if(data_len < 10 || start < 1) { + printf("Poorly formed entry %d, datalen:%d, " + "start:%08x\n", cur_blk, data_len, start); + continue; + } + + image_size = data_len * block_size; + image_start = (start + data_off) * block_size; + is_dir = 2*(image_size < 800*1024); +#if 0 + printf(" partition add entry %d = %s %d %08x %08x\n", + cur_blk, part_map_ptr->part_name, is_dir, + image_size, image_start); +#endif + + cfg_file_add_dirent(&g_cfg_partitionlist, + part_map_ptr->part_name, is_dir, image_size, + image_start, cur_blk); + } + + free(blk_bufptr); + return g_cfg_partitionlist.last; +} + +int +cfg_maybe_insert_disk(int slot, int drive, const char *namestr) +{ + int num_parts; + int fd; + + fd = open(namestr, O_RDONLY | O_BINARY, 0x1b6); + if(fd < 0) { + printf("Cannot open: %s\n", namestr); + return 0; + } + + num_parts = cfg_partition_make_list(fd); + close(fd); + + if(num_parts > 0) { + printf("Choose a partition\n"); + g_cfg_select_partition = 1; + } else { + insert_disk(slot, drive, namestr, 0, 0, 0, -1); + return 1; + } + return 0; +} + +int +cfg_stat(char *path, struct stat *sb) +{ + int removed_slash; + int len; + int ret; + + removed_slash = 0; + len = 0; + +#ifdef _WIN32 + /* Windows doesn't like to stat paths ending in a /, so remove it */ + len = strlen(path); + if((len > 1) && (path[len - 1] == '/') ) { + path[len - 1] = 0; /* remove the slash */ + removed_slash = 1; + } +#endif + + ret = stat(path, sb); + +#ifdef _WIN32 + /* put the slash back */ + if(removed_slash) { + path[len - 1] = '/'; + } +#endif + + return ret; +} + +void +cfg_htab_vtab(int x, int y) +{ + if(x > 79) { + x = 0; + } + if(y > 23) { + y = 0; + } + g_cfg_curs_x = x; + g_cfg_curs_y = y; + g_cfg_curs_inv = 0; + g_cfg_curs_mousetext = 0; +} + +void +cfg_home() +{ + int i; + + cfg_htab_vtab(0, 0); + for(i = 0; i < 24; i++) { + cfg_cleol(); + } +} + +void +cfg_cleol() +{ + g_cfg_curs_inv = 0; + g_cfg_curs_mousetext = 0; + cfg_putchar(' '); + while(g_cfg_curs_x != 0) { + cfg_putchar(' '); + } +} + +void +cfg_putchar(int c) +{ + int offset; + int x, y; + + if(c == '\n') { + cfg_cleol(); + return; + } + if(c == '\b') { + g_cfg_curs_inv = !g_cfg_curs_inv; + return; + } + if(c == '\t') { + g_cfg_curs_mousetext = !g_cfg_curs_mousetext; + return; + } + y = g_cfg_curs_y; + x = g_cfg_curs_x; + + offset = g_screen_index[g_cfg_curs_y]; + if((x & 1) == 0) { + offset += 0x10000; + } + if(g_cfg_curs_inv) { + if(c >= 0x40 && c < 0x60) { + c = c & 0x1f; + } + } else { + c = c | 0x80; + } + if(g_cfg_curs_mousetext) { + c = (c & 0x1f) | 0x40; + } + set_memory_c(0xe00400 + offset + (x >> 1), c, 0); + x++; + if(x >= 80) { + x = 0; + y++; + if(y >= 24) { + y = 0; + } + } + g_cfg_curs_y = y; + g_cfg_curs_x = x; +} + +void +cfg_printf(const char *fmt, ...) +{ + va_list ap; + int c; + int i; + + va_start(ap, fmt); + (void)vsnprintf(g_cfg_printf_buf, CFG_PRINTF_BUFSIZE, fmt, ap); + va_end(ap); + + for(i = 0; i < CFG_PRINTF_BUFSIZE; i++) { + c = g_cfg_printf_buf[i]; + if(c == 0) { + return; + } + cfg_putchar(c); + } +} + +void +cfg_print_num(int num, int max_len) +{ + char buf[64]; + char buf2[64]; + int len; + int cnt; + int c; + int i, j; + + /* Prints right-adjusted "num" in field "max_len" wide */ + snprintf(&buf[0], 64, "%d", num); + len = strlen(buf); + for(i = 0; i < 64; i++) { + buf2[i] = ' '; + } + j = max_len + 1; + buf2[j] = 0; + j--; + cnt = 0; + for(i = len - 1; (i >= 0) && (j >= 1); i--) { + c = buf[i]; + if(c >= '0' && c <= '9') { + if(cnt >= 3) { + buf2[j--] = ','; + cnt = 0; + } + cnt++; + } + buf2[j--] = c; + } + cfg_printf(&buf2[1]); +} + +void +cfg_get_disk_name(char *outstr, int maxlen, int type_ext, int with_extras) +{ + Disk *dsk; + int slot, drive; + + slot = type_ext >> 8; + drive = type_ext & 0xff; + dsk = cfg_get_dsk_from_slot_drive(slot, drive); + + outstr[0] = 0; + if(dsk->name_ptr == 0) { + return; + } + + config_generate_config_kegs_name(outstr, maxlen, dsk, with_extras); +} + +void +cfg_parse_menu(Cfg_menu *menu_ptr, int menu_pos, int highlight_pos, int change) +{ + char valbuf[CFG_OPT_MAXSTR]; + const char *menustr; + char *str; + char *outstr; + int *iptr; + int val; + int num_opts; + int opt_num; + int bufpos, outpos; + int curval, defval; + int type; + int type_ext; + int opt_get_str; + int separator; + int len; + int c; + int i; + + g_cfg_opt_buf[0] = 0; + + num_opts = 0; + opt_get_str = 0; + separator = ','; + + menu_ptr += menu_pos; /* move forward to entry menu_pos */ + + menustr = menu_ptr->str; + type = menu_ptr->cfgtype; + type_ext = (type >> 4); + type = type & 0xf; + len = strlen(menustr) + 1; + + bufpos = 0; + outstr = &(g_cfg_opt_buf[0]); + + outstr[bufpos++] = ' '; + outstr[bufpos++] = ' '; + outstr[bufpos++] = '\t'; + outstr[bufpos++] = '\t'; + outstr[bufpos++] = ' '; + outstr[bufpos++] = ' '; + + if(menu_pos == highlight_pos) { + outstr[bufpos++] = '\b'; + } + + opt_get_str = 2; + i = -1; + outpos = bufpos; +#if 0 + printf("cfg menu_pos: %d str len: %d\n", menu_pos, len); +#endif + while(++i < len) { + c = menustr[i]; + if(c == separator) { + if(i == 0) { + continue; + } + c = 0; + } + outstr[outpos++] = c; + outstr[outpos] = 0; + if(outpos >= CFG_OPT_MAXSTR) { + fprintf(stderr, "CFG_OPT_MAXSTR exceeded\n"); + my_exit(1); + } + if(c == 0) { + if(opt_get_str == 2) { + outstr = &(valbuf[0]); + bufpos = outpos - 1; + opt_get_str = 0; + } else if(opt_get_str) { +#if 0 + if(menu_pos == highlight_pos) { + printf("menu_pos %d opt %d = %s=%d\n", + menu_pos, num_opts, + g_cfg_opts_strs[num_opts], + g_cfg_opts_vals[num_opts]); + } +#endif + num_opts++; + outstr = &(valbuf[0]); + opt_get_str = 0; + if(num_opts >= CFG_MAX_OPTS) { + fprintf(stderr, "CFG_MAX_OPTS oflow\n"); + my_exit(1); + } + } else { + val = strtoul(valbuf, 0, 0); + g_cfg_opts_vals[num_opts] = val; + outstr = &(g_cfg_opts_strs[num_opts][0]); + opt_get_str = 1; + } + outpos = 0; + outstr[0] = 0; + } + } + + if(menu_pos == highlight_pos) { + g_cfg_opt_buf[bufpos++] = '\b'; + } + + g_cfg_opt_buf[bufpos] = 0; + + // Figure out if we should get a checkmark + curval = -1; + defval = -1; + if(type == CFGTYPE_INT) { + iptr = menu_ptr->ptr; + curval = *iptr; + iptr = menu_ptr->defptr; + defval = *iptr; + if(curval == defval) { + g_cfg_opt_buf[3] = 'D'; /* checkmark */ + g_cfg_opt_buf[4] = '\t'; + } + } + + // Decide what to display on the "right" side + str = 0; + opt_num = -1; + if(type == CFGTYPE_INT) { + g_cfg_opt_buf[bufpos++] = ' '; + g_cfg_opt_buf[bufpos++] = '='; + g_cfg_opt_buf[bufpos++] = ' '; + g_cfg_opt_buf[bufpos] = 0; + for(i = 0; i < num_opts; i++) { + if(curval == g_cfg_opts_vals[i]) { + opt_num = i; + break; + } + } + } + + if(change != 0) { + if(type == CFGTYPE_INT) { + if(num_opts > 0) { + opt_num += change; + if(opt_num >= num_opts) { + opt_num = 0; + } + if(opt_num < 0) { + opt_num = num_opts - 1; + } + curval = g_cfg_opts_vals[opt_num]; + } else { + curval += change; + /* HACK: min_val, max_val testing here */ + } + iptr = (int *)menu_ptr->ptr; + *iptr = curval; + } + g_config_kegs_update_needed = 1; + } + +#if 0 + if(menu_pos == highlight_pos) { + printf("menu_pos %d opt_num %d\n", menu_pos, opt_num); + } +#endif + + if(opt_num >= 0) { + str = &(g_cfg_opts_strs[opt_num][0]); + } else { + if(type == CFGTYPE_INT) { + str = &(g_cfg_opts_strs[0][0]); + snprintf(str, CFG_OPT_MAXSTR, "%d", curval); + } else if (type == CFGTYPE_DISK) { + str = &(g_cfg_opts_strs[0][0]), + cfg_get_disk_name(str, CFG_OPT_MAXSTR, type_ext, 1); + str = cfg_shorten_filename(str, 70); + } else { + str = ""; + } + } + +#if 0 + if(menu_pos == highlight_pos) { + printf("menu_pos %d buf_pos %d, str is %s, %02x, %02x, " + "%02x %02x\n", + menu_pos, bufpos, str, g_cfg_opt_buf[bufpos-1], + g_cfg_opt_buf[bufpos-2], + g_cfg_opt_buf[bufpos-3], + g_cfg_opt_buf[bufpos-4]); + } +#endif + + g_cfg_opt_buf[bufpos] = 0; + strncpy(&(g_cfg_opt_buf[bufpos]), str, CFG_OPT_MAXSTR - bufpos - 1); + g_cfg_opt_buf[CFG_OPT_MAXSTR-1] = 0; +} + +void +cfg_get_base_path(char *pathptr, const char *inptr, int go_up) +{ + const char *tmpptr; + char *slashptr; + char *outptr; + int add_dotdot, is_dotdot; + int len; + int c; + + /* Take full filename, copy it to pathptr, and truncate at last slash */ + /* inptr and pathptr can be the same */ + /* if go_up is set, then replace a blank dir with ".." */ + /* but first, see if path is currently just ../ over and over */ + /* if so, just tack .. onto the end and return */ + //printf("cfg_get_base start with %s\n", inptr); + + g_cfg_file_match[0] = 0; + tmpptr = inptr; + is_dotdot = 1; + while(1) { + if(tmpptr[0] == 0) { + break; + } + if(tmpptr[0] == '.' && tmpptr[1] == '.' && tmpptr[2] == '/') { + tmpptr += 3; + } else { + is_dotdot = 0; + break; + } + } + slashptr = 0; + outptr = pathptr; + c = -1; + while(c != 0) { + c = *inptr++; + if(c == '/') { + if(*inptr != 0) { /* if not a trailing slash... */ + slashptr = outptr; + } + } + *outptr++ = c; + } + if(!go_up) { + /* if not go_up, copy chopped part to g_cfg_file_match*/ + /* copy from slashptr+1 to end */ + tmpptr = slashptr+1; + if(slashptr == 0) { + tmpptr = pathptr; + } + strncpy(&g_cfg_file_match[0], tmpptr, CFG_PATH_MAX); + /* remove trailing / from g_cfg_file_match */ + len = strlen(&g_cfg_file_match[0]); + if((len > 1) && (len < (CFG_PATH_MAX - 1)) && + g_cfg_file_match[len - 1] == '/') { + g_cfg_file_match[len - 1] = 0; + } + //printf("set g_cfg_file_match to %s\n", &g_cfg_file_match[0]); + } + if(!is_dotdot && (slashptr != 0)) { + slashptr[0] = '/'; + slashptr[1] = 0; + outptr = slashptr + 2; + } + add_dotdot = 0; + if(pathptr[0] == 0 || is_dotdot) { + /* path was blank, or consisted of just ../ pattern */ + if(go_up) { + add_dotdot = 1; + } + } else if(slashptr == 0) { + /* no slashes found, but path was not blank--make it blank */ + if(pathptr[0] == '/') { + pathptr[1] = 0; + } else { + pathptr[0] = 0; + } + } + + if(add_dotdot) { + --outptr; + outptr[0] = '.'; + outptr[1] = '.'; + outptr[2] = '/'; + outptr[3] = 0; + } + + //printf("cfg_get_base end with %s, is_dotdot:%d, add_dotdot:%d\n", + // pathptr, is_dotdot, add_dotdot); +} + +void +cfg_file_init() +{ + int slot, drive; + int i; + + cfg_get_disk_name(&g_cfg_tmp_path[0], CFG_PATH_MAX, g_cfg_slotdrive, 0); + + slot = g_cfg_slotdrive >> 8; + drive = g_cfg_slotdrive & 1; + for(i = 0; i < 6; i++) { + if(g_cfg_tmp_path[0] != 0) { + break; + } + /* try to get a starting path from some mounted drive */ + drive = !drive; + if(i & 1) { + slot++; + if(slot >= 8) { + slot = 5; + } + } + cfg_get_disk_name(&g_cfg_tmp_path[0], CFG_PATH_MAX, + (slot << 8) + drive, 0); + } + cfg_get_base_path(&g_cfg_file_curpath[0], &g_cfg_tmp_path[0], 0); + g_cfg_dirlist.invalid = 1; +} + +void +cfg_free_alldirents(Cfg_listhdr *listhdrptr) +{ + int i; + + if(listhdrptr->max > 0) { + // Free the old directory listing + for(i = 0; i < listhdrptr->last; i++) { + free(listhdrptr->direntptr[i].name); + } + free(listhdrptr->direntptr); + } + + listhdrptr->direntptr = 0; + listhdrptr->last = 0; + listhdrptr->max = 0; + listhdrptr->invalid = 0; + + listhdrptr->topent = 0; + listhdrptr->curent = 0; +} + + +void +cfg_file_add_dirent(Cfg_listhdr *listhdrptr, const char *nameptr, int is_dir, + int size, int image_start, int part_num) +{ + Cfg_dirent *direntptr; + char *ptr; + int inc_amt; + int namelen; + + namelen = strlen(nameptr); + if(listhdrptr->last >= listhdrptr->max) { + // realloc + inc_amt = MAX(64, listhdrptr->max); + inc_amt = MIN(inc_amt, 1024); + listhdrptr->max += inc_amt; + listhdrptr->direntptr = realloc(listhdrptr->direntptr, + listhdrptr->max * sizeof(Cfg_dirent)); + } + ptr = malloc(namelen+1+is_dir); + strncpy(ptr, nameptr, namelen+1); + if(is_dir) { + strcat(ptr, "/"); + } +#if 0 + printf("...file entry %d is %s\n", g_cfg_dirlist.last, ptr); +#endif + direntptr = &(listhdrptr->direntptr[listhdrptr->last]); + direntptr->name = ptr; + direntptr->is_dir = is_dir; + direntptr->size = size; + direntptr->image_start = image_start; + direntptr->part_num = part_num; + listhdrptr->last++; +} + +int +cfg_dirent_sortfn(const void *obj1, const void *obj2) +{ + const Cfg_dirent *direntptr1, *direntptr2; + + /* Called by qsort to sort directory listings */ + direntptr1 = (const Cfg_dirent *)obj1; + direntptr2 = (const Cfg_dirent *)obj2; + return strcmp(direntptr1->name, direntptr2->name); +} + +int +cfg_str_match(const char *str1, const char *str2, int len) +{ + const byte *bptr1, *bptr2; + int c, c2; + int i; + + /* basically, work like strcmp, except if str1 ends first, return 0 */ + + bptr1 = (const byte *)str1; + bptr2 = (const byte *)str2; + for(i = 0; i < len; i++) { + c = *bptr1++; + c2 = *bptr2++; + if(c == 0) { + if(i > 0) { + return 0; + } else { + return c - c2; + } + } + if(c != c2) { + return c - c2; + } + } + + return 0; +} + +void +cfg_file_readdir(const char *pathptr) +{ + struct dirent *direntptr; + struct stat stat_buf; + DIR *dirptr; + mode_t fmt; + char *str; + const char *tmppathptr; + int ret; + int is_dir, is_gz; + int len; + int i; + + if(!strncmp(pathptr, &g_cfg_file_cachedpath[0], CFG_PATH_MAX) && + (g_cfg_dirlist.last > 0) && (g_cfg_dirlist.invalid==0)){ + return; + } + // No match, must read new directory + + // Free all dirents that were cached previously + cfg_free_alldirents(&g_cfg_dirlist); + + strncpy(&g_cfg_file_cachedpath[0], pathptr, CFG_PATH_MAX); + strncpy(&g_cfg_file_cachedreal[0], pathptr, CFG_PATH_MAX); + + str = &g_cfg_file_cachedreal[0]; + + for(i = 0; i < 200; i++) { + len = strlen(str); + if(len <= 0) { + break; + } else if(len < CFG_PATH_MAX-2) { + if(str[len-1] != '/') { + // append / to make various routines work + str[len] = '/'; + str[len+1] = 0; + } + } + ret = cfg_stat(str, &stat_buf); + is_dir = 0; + if(ret == 0) { + fmt = stat_buf.st_mode & S_IFMT; + if(fmt == S_IFDIR) { + /* it's a directory */ + is_dir = 1; + } + } + if(is_dir) { + break; + } else { + // user is entering more path, use base for display + cfg_get_base_path(str, str, 0); + } + } + + tmppathptr = str; + if(str[0] == 0) { + tmppathptr = "."; + } + cfg_file_add_dirent(&g_cfg_dirlist, "..", 1, 0, -1, -1); + + dirptr = opendir(tmppathptr); + if(dirptr == 0) { + printf("Could not open %s as a directory\n", tmppathptr); + return; + } + while(1) { + direntptr = readdir(dirptr); + if(direntptr == 0) { + break; + } + if(!strcmp(".", direntptr->d_name)) { + continue; + } + if(!strcmp("..", direntptr->d_name)) { + continue; + } + /* Else, see if it is a directory or a file */ + snprintf(&g_cfg_tmp_path[0], CFG_PATH_MAX, "%s%s", + &g_cfg_file_cachedreal[0], direntptr->d_name); + ret = cfg_stat(&g_cfg_tmp_path[0], &stat_buf); + len = strlen(g_cfg_tmp_path); + is_dir = 0; + is_gz = 0; + if((len > 3) && (strcmp(&g_cfg_tmp_path[len - 3], ".gz") == 0)){ + is_gz = 1; + } + if(ret != 0) { + printf("stat %s ret %d, errno:%d\n", &g_cfg_tmp_path[0], + ret, errno); + stat_buf.st_size = 0; + } else { + fmt = stat_buf.st_mode & S_IFMT; + if(fmt == S_IFDIR) { + /* it's a directory */ + is_dir = 1; + } else if((fmt == S_IFREG) && (is_gz == 0) && + (stat_buf.st_size < 140*1024)) { + /* skip it */ + continue; + } + } + cfg_file_add_dirent(&g_cfg_dirlist, direntptr->d_name, is_dir, + stat_buf.st_size, -1, -1); + } + + /* then sort the results (Mac's HFS+ is sorted, but other FS won't be)*/ + qsort(&(g_cfg_dirlist.direntptr[0]), g_cfg_dirlist.last, + sizeof(Cfg_dirent), cfg_dirent_sortfn); + + g_cfg_dirlist.curent = g_cfg_dirlist.last - 1; + for(i = g_cfg_dirlist.last - 1; i >= 0; i--) { + ret = cfg_str_match(&g_cfg_file_match[0], + g_cfg_dirlist.direntptr[i].name, CFG_PATH_MAX); + if(ret <= 0) { + /* set cur ent to closest filename to the match name */ + g_cfg_dirlist.curent = i; + } + } +} + +char * +cfg_shorten_filename(const char *in_ptr, int maxlen) +{ + char *out_ptr; + int len; + int c; + int i; + + /* Warning: uses a static string, not reentrant! */ + + out_ptr = &(g_cfg_file_shortened[0]); + len = strlen(in_ptr); + maxlen = MIN(len, maxlen); + for(i = 0; i < maxlen; i++) { + c = in_ptr[i] & 0x7f; + if(c < 0x20) { + c = '*'; + } + out_ptr[i] = c; + } + out_ptr[maxlen] = 0; + if(len > maxlen) { + for(i = 0; i < (maxlen/2); i++) { + c = in_ptr[len-i-1] & 0x7f; + if(c < 0x20) { + c = '*'; + } + out_ptr[maxlen-i-1] = c; + } + out_ptr[(maxlen/2) - 1] = '.'; + out_ptr[maxlen/2] = '.'; + out_ptr[(maxlen/2) + 1] = '.'; + } + + return out_ptr; +} + +void +cfg_fix_topent(Cfg_listhdr *listhdrptr) +{ + int num_to_show; + + num_to_show = listhdrptr->num_to_show; + + /* Force curent and topent to make sense */ + if(listhdrptr->curent >= listhdrptr->last) { + listhdrptr->curent = listhdrptr->last - 1; + } + if(listhdrptr->curent < 0) { + listhdrptr->curent = 0; + } + if(abs(listhdrptr->curent - listhdrptr->topent) >= num_to_show) { + listhdrptr->topent = listhdrptr->curent - (num_to_show/2); + } + if(listhdrptr->topent > listhdrptr->curent) { + listhdrptr->topent = listhdrptr->curent - (num_to_show/2); + } + if(listhdrptr->topent < 0) { + listhdrptr->topent = 0; + } +} + +void +cfg_file_draw() +{ + Cfg_listhdr *listhdrptr; + Cfg_dirent *direntptr; + char *str, *fmt; + int num_to_show; + int yoffset; + int x, y; + int i; + + cfg_file_readdir(&g_cfg_file_curpath[0]); + + for(y = 0; y < 21; y++) { + cfg_htab_vtab(0, y); + cfg_printf("\tZ\t"); + for(x = 1; x < 72; x++) { + cfg_htab_vtab(x, y); + cfg_putchar(' '); + } + cfg_htab_vtab(79, y); + cfg_printf("\t_\t"); + } + + cfg_htab_vtab(1, 0); + cfg_putchar('\b'); + for(x = 1; x < 79; x++) { + cfg_putchar(' '); + } + cfg_htab_vtab(30, 0); + cfg_printf("\bSelect image for s%dd%d\b", (g_cfg_slotdrive >> 8), + (g_cfg_slotdrive & 0xff) + 1); + + cfg_htab_vtab(2, 1); + cfg_printf("Current KEGS directory: %-50s", + cfg_shorten_filename(&g_cfg_cwd_str[0], 50)); + + cfg_htab_vtab(2, 2); + cfg_printf("config.kegs path: %-50s", + cfg_shorten_filename(&g_config_kegs_name[0], 50)); + + cfg_htab_vtab(2, 3); + + str = ""; + if(g_cfg_file_pathfield) { + str = "\b \b"; + } + cfg_printf("Path: %s%s", + cfg_shorten_filename(&g_cfg_file_curpath[0], 64), str); + + cfg_htab_vtab(0, 4); + cfg_printf(" \t"); + for(x = 1; x < 79; x++) { + cfg_putchar('\\'); + } + cfg_printf("\t "); + + + /* Force curent and topent to make sense */ + listhdrptr = &g_cfg_dirlist; + num_to_show = CFG_NUM_SHOWENTS; + yoffset = 5; + if(g_cfg_select_partition > 0) { + listhdrptr = &g_cfg_partitionlist; + num_to_show -= 2; + cfg_htab_vtab(2, yoffset); + cfg_printf("Select partition of %-50s\n", + cfg_shorten_filename(&g_cfg_file_path[0], 50), str); + yoffset += 2; + } + + listhdrptr->num_to_show = num_to_show; + cfg_fix_topent(listhdrptr); + for(i = 0; i < num_to_show; i++) { + y = i + yoffset; + if(listhdrptr->last > (i + listhdrptr->topent)) { + direntptr = &(listhdrptr-> + direntptr[i + listhdrptr->topent]); + cfg_htab_vtab(2, y); + if(direntptr->is_dir) { + cfg_printf("\tXY\t "); + } else { + cfg_printf(" "); + } + if(direntptr->part_num >= 0) { + cfg_printf("%3d: ", direntptr->part_num); + } + str = cfg_shorten_filename(direntptr->name, 45); + fmt = "%-45s"; + if(i + listhdrptr->topent == listhdrptr->curent) { + if(g_cfg_file_pathfield == 0) { + fmt = "\b%-45s\b"; + } else { + fmt = "%-44s\b \b"; + } + } + cfg_printf(fmt, str); + if(!direntptr->is_dir) { + cfg_print_num(direntptr->size, 13); + } + } + } + + cfg_htab_vtab(1, 21); + cfg_putchar('\t'); + for(x = 1; x < 79; x++) { + cfg_putchar('L'); + } + cfg_putchar('\t'); + +} + +void +cfg_partition_selected() +{ + char *str; + const char *part_str; + char *part_str2; + int pos; + int part_num; + + pos = g_cfg_partitionlist.curent; + str = g_cfg_partitionlist.direntptr[pos].name; + part_num = -2; + part_str = 0; + if(str[0] == 0 || (str[0] >= '0' && str[0] <= '9')) { + part_num = g_cfg_partitionlist.direntptr[pos].part_num; + } else { + part_str = str; + } + part_str2 = 0; + if(part_str != 0) { + part_str2 = (char *)malloc(strlen(part_str)+1); + strcpy(part_str2, part_str); + } + + insert_disk(g_cfg_slotdrive >> 8, g_cfg_slotdrive & 0xff, + &(g_cfg_file_path[0]), 0, 0, part_str2, part_num); + if(part_str2 != 0) { + free(part_str2); + } + g_cfg_slotdrive = -1; + g_cfg_select_partition = -1; +} + +void +cfg_file_selected() +{ + struct stat stat_buf; + char *str; + int fmt; + int ret; + + if(g_cfg_select_partition > 0) { + cfg_partition_selected(); + return; + } + + if(g_cfg_file_pathfield == 0) { + // in file section area of window + str = g_cfg_dirlist.direntptr[g_cfg_dirlist.curent].name; + if(!strcmp(str, "../")) { + /* go up one directory */ + cfg_get_base_path(&g_cfg_file_curpath[0], + &g_cfg_file_curpath[0], 1); + return; + } + + snprintf(&g_cfg_file_path[0], CFG_PATH_MAX, "%s%s", + &g_cfg_file_cachedreal[0], str); + } else { + // just use cfg_file_curpath directly + strncpy(&g_cfg_file_path[0], &g_cfg_file_curpath[0], + CFG_PATH_MAX); + } + + ret = cfg_stat(&g_cfg_file_path[0], &stat_buf); + fmt = stat_buf.st_mode & S_IFMT; + cfg_printf("Stat'ing %s, st_mode is: %08x\n", &g_cfg_file_path[0], + (int)stat_buf.st_mode); + + if(ret != 0) { + printf("stat %s returned %d, errno: %d\n", &g_cfg_file_path[0], + ret, errno); + } else { + if(fmt == S_IFDIR) { + /* it's a directory */ + strncpy(&g_cfg_file_curpath[0], &g_cfg_file_path[0], + CFG_PATH_MAX); + } else { + /* select it */ + ret = cfg_maybe_insert_disk(g_cfg_slotdrive >> 8, + g_cfg_slotdrive & 0xff, &g_cfg_file_path[0]); + if(ret > 0) { + g_cfg_slotdrive = -1; + } + } + } +} + +void +cfg_file_handle_key(int key) +{ + Cfg_listhdr *listhdrptr; + int len; + + if(g_cfg_file_pathfield) { + if(key >= 0x20 && key < 0x7f) { + len = strlen(&g_cfg_file_curpath[0]); + if(len < CFG_PATH_MAX-4) { + g_cfg_file_curpath[len] = key; + g_cfg_file_curpath[len+1] = 0; + } + return; + } + } + + listhdrptr = &g_cfg_dirlist; + if(g_cfg_select_partition > 0) { + listhdrptr = &g_cfg_partitionlist; + } + if( (g_cfg_file_pathfield == 0) && + ((key >= 'a' && key <= 'z') || (key >= 'A' && key <= 'Z')) ) { + /* jump to file starting with this letter */ + g_cfg_file_match[0] = key; + g_cfg_file_match[1] = 0; + g_cfg_dirlist.invalid = 1; /* re-read directory */ + } + + switch(key) { + case 0x1b: + eject_disk_by_num(g_cfg_slotdrive >> 8, g_cfg_slotdrive & 0xff); + g_cfg_slotdrive = -1; + g_cfg_select_partition = -1; + g_cfg_dirlist.invalid = 1; + break; + case 0x0a: /* down arrow */ + if(g_cfg_file_pathfield == 0) { + listhdrptr->curent++; + cfg_fix_topent(listhdrptr); + } + break; + case 0x0b: /* up arrow */ + if(g_cfg_file_pathfield == 0) { + listhdrptr->curent--; + cfg_fix_topent(listhdrptr); + } + break; + case 0x0d: /* return */ + cfg_file_selected(); + break; + case 0x09: /* tab */ + g_cfg_file_pathfield = !g_cfg_file_pathfield; + break; + case 0x08: /* left arrow */ + case 0x7f: /* delete key */ + if(g_cfg_file_pathfield) { + // printf("left arrow/delete\n"); + len = strlen(&g_cfg_file_curpath[0]) - 1; + if(len >= 0) { + g_cfg_file_curpath[len] = 0; + } + } + break; + default: + printf("key: %02x\n", key); + } +#if 0 + printf("curent: %d, topent: %d, last: %d\n", + g_cfg_dirlist.curent, g_cfg_dirlist.topent, g_cfg_dirlist.last); +#endif +} + +void +config_control_panel() +{ + void (*fn_ptr)(); + const char *str; + Cfg_menu *menu_ptr; + void *ptr; + int print_eject_help; + int line; + int type; + int match_found; + int menu_line; + int menu_inc; + int max_line; + int key; + int i, j; + + // First, save key text info + g_save_cur_a2_stat = g_cur_a2_stat; + for(i = 0; i < 0x400; i++) { + g_save_text_screen_bytes[i] = g_slow_memory_ptr[0x400+i]; + g_save_text_screen_bytes[0x400+i] =g_slow_memory_ptr[0x10400+i]; + } + + g_cur_a2_stat = ALL_STAT_TEXT | ALL_STAT_VID80 | ALL_STAT_ANNUNC3 | + (0xf << BIT_ALL_STAT_TEXT_COLOR) | ALL_STAT_ALTCHARSET; + g_a2_new_all_stat[0] = g_cur_a2_stat; + g_new_a2_stat_cur_line = 0; + + cfg_printf("In config_control_panel\n"); + + for(i = 0; i < 20; i++) { + if(adb_read_c000() & 0x80) { + (void)adb_access_c010(); + } + } + g_adb_repeat_vbl = 0; + g_cfg_vbl_count = 0; + // HACK: Force adb keyboard (and probably mouse) to "normal"... + + g_full_refresh_needed = -1; + g_a2_screen_buffer_changed = -1; + + cfg_home(); + j = 0; + + menu_ptr = g_cfg_main_menu; + menu_line = 1; + menu_inc = 1; + g_cfg_slotdrive = -1; + g_cfg_select_partition = -1; + + while(g_config_control_panel) { + cfg_home(); + line = 1; + max_line = 1; + match_found = 0; + print_eject_help = 0; + cfg_printf("%s\n\n", menu_ptr[0].str); + while(line < 24) { + str = menu_ptr[line].str; + type = menu_ptr[line].cfgtype; + ptr = menu_ptr[line].ptr; + if(str == 0) { + break; + } + if((type & 0xf) == CFGTYPE_DISK) { + print_eject_help = 1; + } + cfg_parse_menu(menu_ptr, line, menu_line, 0); + if(line == menu_line) { + if(type != 0) { + match_found = 1; + } else if(menu_inc) { + menu_line++; + } else { + menu_line--; + } + } + if(line > max_line) { + max_line = line; + } + + cfg_printf("%s\n", g_cfg_opt_buf); + line++; + } + if((menu_line < 1) && !match_found) { + menu_line = 1; + } + if((menu_line >= max_line) && !match_found) { + menu_line = max_line; + } + + cfg_htab_vtab(0, 23); + cfg_printf("Move: \tJ\t \tK\t Change: \tH\t \tU\t \tM\t"); + if(print_eject_help) { + cfg_printf(" Eject: "); + if(g_cfg_slotdrive >= 0) { + cfg_printf("\bESC\b"); + } else { + cfg_printf("E"); + } + } +#if 0 + cfg_htab_vtab(0, 22); + cfg_printf("menu_line: %d line: %d, vbl:%d, adb:%d key_dn:%d\n", + menu_line, line, g_cfg_vbl_count, g_adb_repeat_vbl, + g_key_down); +#endif + + if(g_cfg_slotdrive >= 0) { + cfg_file_draw(); + } + + key = -1; + while(g_config_control_panel) { + video_update(); + key = adb_read_c000(); + if(key & 0x80) { + key = key & 0x7f; + (void)adb_access_c010(); + break; + } else { + key = -1; + } + micro_sleep(1.0/60.0); + g_cfg_vbl_count++; + if(!match_found) { + break; + } + } + + if((key >= 0) && (g_cfg_slotdrive < 0)) { + // Normal menu system + switch(key) { + case 0x0a: /* down arrow */ + menu_line++; + menu_inc = 1; + break; + case 0x0b: /* up arrow */ + menu_line--; + menu_inc = 0; + if(menu_line < 1) { + menu_line = 1; + } + break; + case 0x15: /* right arrow */ + cfg_parse_menu(menu_ptr, menu_line,menu_line,1); + break; + case 0x08: /* left arrow */ + cfg_parse_menu(menu_ptr,menu_line,menu_line,-1); + break; + case 0x0d: + type = menu_ptr[menu_line].cfgtype; + ptr = menu_ptr[menu_line].ptr; + switch(type & 0xf) { + case CFGTYPE_MENU: + menu_ptr = (Cfg_menu *)ptr; + menu_line = 1; + break; + case CFGTYPE_DISK: + g_cfg_slotdrive = type >> 4; + cfg_file_init(); + break; + case CFGTYPE_FUNC: + fn_ptr = (void (*)())ptr; + (*fn_ptr)(); + break; + } + break; + case 0x1b: + // Jump to last menu entry + menu_line = max_line; + break; + case 'e': + case 'E': + type = menu_ptr[menu_line].cfgtype; + if((type & 0xf) == CFGTYPE_DISK) { + eject_disk_by_num(type >> 12, + (type >> 4) & 0xff); + } + break; + default: + printf("key: %02x\n", key); + } + } else if(key >= 0) { + cfg_file_handle_key(key); + } + } + + for(i = 0; i < 0x400; i++) { + set_memory_c(0xe00400+i, g_save_text_screen_bytes[i], 0); + set_memory_c(0xe10400+i, g_save_text_screen_bytes[0x400+i], 0); + } + + // And quit + g_config_control_panel = 0; + g_adb_repeat_vbl = g_vbl_count + 60; + g_cur_a2_stat = g_save_cur_a2_stat; + change_display_mode(g_cur_dcycs); + g_full_refresh_needed = -1; + g_a2_screen_buffer_changed = -1; +} + diff --git a/src/config.h b/src/config.h new file mode 100644 index 0000000..06106dc --- /dev/null +++ b/src/config.h @@ -0,0 +1,34 @@ +/************************************************************************/ +/* KEGS: Apple //gs Emulator */ +/* Copyright 2003 by Kent Dickey */ +/* */ +/* This code is covered by the GNU GPL */ +/* */ +/* The KEGS web page is kegs.sourceforge.net */ +/* You may contact the author at: kadickey@alumni.princeton.edu */ +/************************************************************************/ + +#ifdef INCLUDE_RCSID_C +const char rcsid_config_h[] = "@(#)$KmKId: config.h,v 1.8 2003-10-17 15:09:58-04 kentd Exp $"; +#endif + +#define CONF_BUF_LEN 1024 +#define COPY_BUF_SIZE 4096 +#define CFG_PRINTF_BUFSIZE 2048 + +#define CFG_PATH_MAX 1024 + +#define CFG_NUM_SHOWENTS 16 + +#define CFGTYPE_MENU 1 +#define CFGTYPE_INT 2 +#define CFGTYPE_DISK 3 +#define CFGTYPE_FUNC 4 +/* CFGTYPE limited to just 4 bits: 0-15 */ + +/* Cfg_menu, Cfg_dirent and Cfg_listhdr are defined in defc.h */ + +STRUCT(Cfg_defval) { + Cfg_menu *menuptr; + int intval; +}; diff --git a/src/defc.h b/src/defc.h new file mode 100644 index 0000000..1596fe9 --- /dev/null +++ b/src/defc.h @@ -0,0 +1,265 @@ +/************************************************************************/ +/* KEGS: Apple //gs Emulator */ +/* Copyright 2002 by Kent Dickey */ +/* */ +/* This code is covered by the GNU GPL */ +/* */ +/* The KEGS web page is kegs.sourceforge.net */ +/* You may contact the author at: kadickey@alumni.princeton.edu */ +/************************************************************************/ + +#ifdef INCLUDE_RCSID_C +const char rcsid_defc_h[] = "@(#)$KmKId: defc.h,v 1.91 2003-11-03 01:29:38-05 kentd Exp $"; +#endif + +#include "defcomm.h" + +#define STRUCT(a) typedef struct _ ## a a; struct _ ## a + +typedef unsigned char byte; +typedef unsigned short word16; +typedef unsigned int word32; +#if _MSC_VER +typedef unsigned __int64 word64; +#else +typedef unsigned long long word64; +#endif + +void U_STACK_TRACE(); + +/* 28MHz crystal, plus every 65th 1MHz cycle is stretched 140ns */ +#define CYCS_28_MHZ (28636360) +#define DCYCS_28_MHZ (1.0*CYCS_28_MHZ) +#define CYCS_3_5_MHZ (CYCS_28_MHZ/8) +#define DCYCS_1_MHZ ((DCYCS_28_MHZ/28.0)*(65.0*7/(65.0*7+1.0))) +#define CYCS_1_MHZ ((int)DCYCS_1_MHZ) + +#define DCYCS_IN_16MS_RAW (DCYCS_1_MHZ / 60.0) +#define DCYCS_IN_16MS ((double)((int)DCYCS_IN_16MS_RAW)) +#define DRECIP_DCYCS_IN_16MS (1.0 / (DCYCS_IN_16MS)) + +#ifdef KEGS_LITTLE_ENDIAN +# define BIGEND(a) ((((a) >> 24) & 0xff) + \ + (((a) >> 8) & 0xff00) + \ + (((a) << 8) & 0xff0000) + \ + (((a) << 24) & 0xff000000)) +# define GET_BE_WORD16(a) ((((a) >> 8) & 0xff) + (((a) << 8) & 0xff00)) +# define GET_BE_WORD32(a) (BIGEND(a)) +#else +# define BIGEND(a) (a) +# define GET_BE_WORD16(a) (a) +# define GET_BE_WORD32(a) (a) +#endif + +#define MAXNUM_HEX_PER_LINE 32 + +#ifdef __NeXT__ +# include +#endif + +#ifndef _WIN32 +# include +# include +# include +#endif + +#include +#include +#include +#include +#include +#include +#include +#ifdef HPUX +# include /* for GET_ITIMER */ +#endif + +#ifdef SOLARIS +# include +#endif + +#ifndef O_BINARY +/* work around some Windows junk */ +# define O_BINARY 0 +#endif + +STRUCT(Pc_log) { + double dcycs; + word32 dbank_kpc; + word32 instr; + word32 psr_acc; + word32 xreg_yreg; + word32 stack_direct; + word32 pad; +}; + +STRUCT(Event) { + double dcycs; + int type; + Event *next; +}; + +STRUCT(Fplus) { + double plus_1; + double plus_2; + double plus_3; + double plus_x_minus_1; +}; + +STRUCT(Engine_reg) { + double fcycles; + word32 kpc; + word32 acc; + + word32 xreg; + word32 yreg; + + word32 stack; + word32 dbank; + + word32 direct; + word32 psr; + Fplus *fplus_ptr; +}; + +STRUCT(Kimage) { + void *dev_handle; + void *dev_handle2; + byte *data_ptr; + int width_req; + int width_act; + int height; + int depth; + int mdepth; + int aux_info; +}; + +typedef byte *Pg_info; +STRUCT(Page_info) { + Pg_info rd_wr; +}; + +STRUCT(Cfg_menu) { + const char *str; + void *ptr; + const char *name_str; + void *defptr; + int cfgtype; +}; + +STRUCT(Cfg_dirent) { + char *name; + int is_dir; + int size; + int image_start; + int part_num; +}; + +STRUCT(Cfg_listhdr) { + Cfg_dirent *direntptr; + int max; + int last; + int invalid; + + int curent; + int topent; + + int num_to_show; +}; + +#ifdef __LP64__ +# define PTR2WORD(a) ((unsigned long)(a)) +#else +# define PTR2WORD(a) ((unsigned int)(a)) +#endif + + +#define ALTZP (statereg & 0x80) +#define PAGE2 (statereg & 0x40) +#define RAMRD (statereg & 0x20) +#define RAMWRT (statereg & 0x10) +#define RDROM (statereg & 0x08) +#define LCBANK2 (statereg & 0x04) +#define ROMB (statereg & 0x02) +#define INTCX (statereg & 0x01) + +#define EXTRU(val, pos, len) \ + ( ( (len) >= (pos) + 1) ? ((val) >> (31-(pos))) : \ + (((val) >> (31-(pos)) ) & ( (1<<(len) ) - 1) ) ) + +#define DEP1(val, pos, old_val) \ + (((old_val) & ~(1 << (31 - (pos))) ) | \ + ( ((val) & 1) << (31 - (pos))) ) + +#define set_halt(val) \ + if(val) { set_halt_act(val); } + +#define clear_halt() \ + clr_halt_act() + +#define GET_PAGE_INFO_RD(page) \ + (page_info_rd_wr[page].rd_wr) + +#define GET_PAGE_INFO_WR(page) \ + (page_info_rd_wr[0x10000 + PAGE_INFO_PAD_SIZE + (page)].rd_wr) + +#define SET_PAGE_INFO_RD(page,val) \ + ;page_info_rd_wr[page].rd_wr = (Pg_info)val; + +#define SET_PAGE_INFO_WR(page,val) \ + ;page_info_rd_wr[0x10000 + PAGE_INFO_PAD_SIZE + (page)].rd_wr = \ + (Pg_info)val; + +#define VERBOSE_DISK 0x001 +#define VERBOSE_IRQ 0x002 +#define VERBOSE_CLK 0x004 +#define VERBOSE_SHADOW 0x008 +#define VERBOSE_IWM 0x010 +#define VERBOSE_DOC 0x020 +#define VERBOSE_ADB 0x040 +#define VERBOSE_SCC 0x080 +#define VERBOSE_TEST 0x100 +#define VERBOSE_VIDEO 0x200 +#define VERBOSE_MAC 0x400 + +#ifdef NO_VERB +# define DO_VERBOSE 0 +#else +# define DO_VERBOSE 1 +#endif + +#define disk_printf if(DO_VERBOSE && (Verbose & VERBOSE_DISK)) printf +#define irq_printf if(DO_VERBOSE && (Verbose & VERBOSE_IRQ)) printf +#define clk_printf if(DO_VERBOSE && (Verbose & VERBOSE_CLK)) printf +#define shadow_printf if(DO_VERBOSE && (Verbose & VERBOSE_SHADOW)) printf +#define iwm_printf if(DO_VERBOSE && (Verbose & VERBOSE_IWM)) printf +#define doc_printf if(DO_VERBOSE && (Verbose & VERBOSE_DOC)) printf +#define adb_printf if(DO_VERBOSE && (Verbose & VERBOSE_ADB)) printf +#define scc_printf if(DO_VERBOSE && (Verbose & VERBOSE_SCC)) printf +#define test_printf if(DO_VERBOSE && (Verbose & VERBOSE_TEST)) printf +#define vid_printf if(DO_VERBOSE && (Verbose & VERBOSE_VIDEO)) printf +#define mac_printf if(DO_VERBOSE && (Verbose & VERBOSE_MAC)) printf + + +#define HALT_ON_SCAN_INT 0x001 +#define HALT_ON_IRQ 0x002 +#define HALT_ON_SHADOW_REG 0x004 +#define HALT_ON_C70D_WRITES 0x008 + +#define HALT_ON(a, msg) \ + if(Halt_on & a) { \ + halt_printf(msg); \ + } + + +#ifndef MIN +# define MIN(a,b) (((a) < (b)) ? (a) : (b)) +#endif +#ifndef MAX +# define MAX(a,b) (((a) < (b)) ? (b) : (a)) +#endif + +#define GET_ITIMER(dest) dest = get_itimer(); + +#include "iwm.h" +#include "protos.h" diff --git a/src/defcomm.h b/src/defcomm.h new file mode 100644 index 0000000..7de1042 --- /dev/null +++ b/src/defcomm.h @@ -0,0 +1,200 @@ +/************************************************************************/ +/* KEGS: Apple //gs Emulator */ +/* Copyright 2002 by Kent Dickey */ +/* */ +/* This code is covered by the GNU GPL */ +/* */ +/* The KEGS web page is kegs.sourceforge.net */ +/* You may contact the author at: kadickey@alumni.princeton.edu */ +/************************************************************************/ + +#ifdef INCLUDE_RCSID_C +const char rcsdif_defcomm_h[] = "@(#)$KmKId: defcomm.h,v 1.93 2002-11-19 03:10:38-05 kadickey Exp $"; +#endif + +#define USE_XIMAGE_CHANGED + +#if 0 +# define CHECK_BREAKPOINTS +#endif + +#define SHIFT_PER_CHANGE 3 +#define CHANGE_SHIFT (5 + SHIFT_PER_CHANGE) + +#define SLOW_MEM_CH_SIZE (0x10000 >> CHANGE_SHIFT) + +#define MAXNUM_HEX_PER_LINE 32 + +/* Different Joystick defines */ +#define JOYSTICK_MOUSE 1 +#define JOYSTICK_LINUX 2 +#define JOYSTICK_KEYPAD 3 +#define JOYSTICK_WIN32_1 4 +#define JOYSTICK_WIN32_2 5 + + +#define HALT_EVENT 0x10 + +#define MAX_BREAK_POINTS 0x20 + +#define MAX_BP_INDEX 0x100 +#define MAX_BP_PER_INDEX 3 /* 4 word32s total = 16 bytes */ +#define SIZE_BREAKPT_ENTRY_BITS 4 /* 16 bytes = 4 bits */ + +/* Warning--next defines used by asm! */ +#define PAGE_INFO_PAD_SIZE 0x800 +#define PAGE_INFO_WR_OFFSET 0x10000+PAGE_INFO_PAD_SIZE + +#define BANK_IO_BIT 31 +#define BANK_SHADOW_BIT 30 +#define BANK_SHADOW2_BIT 29 +#define BANK_IO2_BIT 28 +#define BANK_BREAK_BIT 27 +#define BANK_BREAK (1 << (31 - BANK_BREAK_BIT)) +#define BANK_IO2_TMP (1 << (31 - BANK_IO2_BIT)) +#define BANK_IO_TMP (1 << (31 - BANK_IO_BIT)) +#define BANK_SHADOW (1 << (31 - BANK_SHADOW_BIT)) +#define BANK_SHADOW2 (1 << (31 - BANK_SHADOW2_BIT)) +#define SET_BANK_IO \ + (&g_dummy_memory1_ptr[BANK_IO_TMP | BANK_IO2_TMP]) + +#define BANK_BAD_MEM (&g_dummy_memory1_ptr[0xff]) + + +#define LEN_FIFO_BUF 160 +#define LEN_KBD_BUF 160 + +#define FIFO_OK 0x1 +#define FIFO_INIT 0x2 +#define FIFO_END 0x3 +#define FIFO_40COLS 0x4 +#define FIFO_80COLS 0x5 +#define FIFO_SENDCHAR 0x6 +#define FIFO_SENDKEY 0x7 +#define FIFO_REFRESH 0x8 + +#define B_OP_SIZE 2 +#define B_OP_D_SIZE 5 +#define B_OP_DTYPE 12 +#define SIZE_OP_DTYPE 7 + + + +#define ENGINE_FCYCLES 0x00 +#define ENGINE_REG_KPC 0x08 +#define ENGINE_REG_ACC 0x0c +#define ENGINE_REG_XREG 0x10 +#define ENGINE_REG_YREG 0x14 +#define ENGINE_REG_STACK 0x18 +#define ENGINE_REG_DBANK 0x1c +#define ENGINE_REG_DIRECT 0x20 +#define ENGINE_REG_PSR 0x24 +#define ENGINE_FPLUS_PTR 0x28 + +#define LOG_PC_DCYCS 0x00 +#define LOG_PC_DBANK_KPC 0x08 +#define LOG_PC_INSTR 0x0c +#define LOG_PC_PSR_ACC 0x10 +#define LOG_PC_XREG_YREG 0x14 +#define LOG_PC_STACK_DIRECT 0x18 +#define LOG_PC_PAD 0x1c + +#define LOG_PC_SIZE 0x20 + + +#define FPLUS_PLUS_1 0x00 +#define FPLUS_PLUS_2 0x08 +#define FPLUS_PLUS_3 0x10 +#define FPLUS_PLUS_X_M1 0x18 + +#define RET_BREAK 0x1 +#define RET_COP 0x2 +#define RET_WDM 0x3 +#define RET_MVP 0x4 +#define RET_MVN 0x5 +#define RET_WAI 0x6 +#define RET_STP 0x7 +#define RET_ADD_DEC_8 0x8 +#define RET_ADD_DEC_16 0x9 +#define RET_C700 0xa +#define RET_C70A 0xb +#define RET_C70D 0xc +#define RET_IRQ 0xd + + +#define MODE_BORDER 0 +#define MODE_TEXT 1 +#define MODE_GR 2 +#define MODE_HGR 3 +#define MODE_SUPER_HIRES 4 + +#define BIT_ALL_STAT_TEXT 0 +#define BIT_ALL_STAT_VID80 1 +#define BIT_ALL_STAT_ST80 2 +#define BIT_ALL_STAT_COLOR_C021 3 +#define BIT_ALL_STAT_MIX_T_GR 4 +#define BIT_ALL_STAT_DIS_COLOR_DHIRES 5 /* special val, c029 */ +#define BIT_ALL_STAT_PAGE2 6 /* special val, statereg */ +#define BIT_ALL_STAT_SUPER_HIRES 7 /* special, c029 */ +#define BIT_ALL_STAT_HIRES 8 +#define BIT_ALL_STAT_ANNUNC3 9 +#define BIT_ALL_STAT_BG_COLOR 10 /* 4 bits */ +#define BIT_ALL_STAT_TEXT_COLOR 14 /* 4 bits */ + /* Text must be just above */ + /* bg to match c022 reg */ +#define BIT_ALL_STAT_ALTCHARSET 18 +#define BIT_ALL_STAT_FLASH_STATE 19 +#define BIT_ALL_STAT_A2VID_PALETTE 20 /* 4 bits */ + +#define ALL_STAT_SUPER_HIRES (1 << (BIT_ALL_STAT_SUPER_HIRES)) +#define ALL_STAT_TEXT (1 << (BIT_ALL_STAT_TEXT)) +#define ALL_STAT_VID80 (1 << (BIT_ALL_STAT_VID80)) +#define ALL_STAT_PAGE2 (1 << (BIT_ALL_STAT_PAGE2)) +#define ALL_STAT_ST80 (1 << (BIT_ALL_STAT_ST80)) +#define ALL_STAT_COLOR_C021 (1 << (BIT_ALL_STAT_COLOR_C021)) +#define ALL_STAT_DIS_COLOR_DHIRES (1 << (BIT_ALL_STAT_DIS_COLOR_DHIRES)) +#define ALL_STAT_MIX_T_GR (1 << (BIT_ALL_STAT_MIX_T_GR)) +#define ALL_STAT_HIRES (1 << (BIT_ALL_STAT_HIRES)) +#define ALL_STAT_ANNUNC3 (1 << (BIT_ALL_STAT_ANNUNC3)) +#define ALL_STAT_TEXT_COLOR (0xf << (BIT_ALL_STAT_TEXT_COLOR)) +#define ALL_STAT_BG_COLOR (0xf << (BIT_ALL_STAT_BG_COLOR)) +#define ALL_STAT_ALTCHARSET (1 << (BIT_ALL_STAT_ALTCHARSET)) +#define ALL_STAT_FLASH_STATE (1 << (BIT_ALL_STAT_FLASH_STATE)) +#define ALL_STAT_A2VID_PALETTE (0xf << (BIT_ALL_STAT_A2VID_PALETTE)) + +#define BORDER_WIDTH 32 + +#define EFF_BORDER_WIDTH (BORDER_WIDTH + (640-560)) + +/* BASE_MARGIN_BOTTOM+MARGIN_TOP must equal 62. There are 262 scan lines */ +/* at 60Hz (15.7KHz line rate) and so we just make 62 border lines */ +#define BASE_MARGIN_TOP 32 +#define BASE_MARGIN_BOTTOM 30 +#define BASE_MARGIN_LEFT BORDER_WIDTH +#define BASE_MARGIN_RIGHT BORDER_WIDTH + +#define A2_WINDOW_WIDTH 640 +#define A2_WINDOW_HEIGHT 400 + +#define X_A2_WINDOW_WIDTH (A2_WINDOW_WIDTH + BASE_MARGIN_LEFT + \ + BASE_MARGIN_RIGHT) +#define X_A2_WINDOW_HEIGHT (A2_WINDOW_HEIGHT + BASE_MARGIN_TOP + \ + BASE_MARGIN_BOTTOM) + +#define MAX_STATUS_LINES 7 +#define STATUS_LINE_LENGTH 88 + +#define BASE_WINDOW_WIDTH (X_A2_WINDOW_WIDTH) + + +#define A2_BORDER_COLOR_NUM 0xfe + +#if 0 +#define A2_TEXT_COLOR_ALT_NUM 0x01 +#define A2_BG_COLOR_ALT_NUM 0x00 +#define A2_TEXT_COLOR_PRIM_NUM 0x02 +#define A2_BG_COLOR_PRIM_NUM 0x00 +#define A2_TEXT_COLOR_FLASH_NUM 0x0c +#define A2_BG_COLOR_FLASH_NUM 0x08 +#endif + diff --git a/src/defs.h b/src/defs.h new file mode 100644 index 0000000..161f968 --- /dev/null +++ b/src/defs.h @@ -0,0 +1,63 @@ +/************************************************************************/ +/* KEGS: Apple //gs Emulator */ +/* Copyright 2002 by Kent Dickey */ +/* */ +/* This code is covered by the GNU GPL */ +/* */ +/* The KEGS web page is kegs.sourceforge.net */ +/* You may contact the author at: kadickey@alumni.princeton.edu */ +/************************************************************************/ + +#ifdef INCLUDE_RCSID_S + .data + .export rcsdif_defs_h,data +rcsdif_defs_h + .stringz "@(#)$KmKId: defs.h,v 1.22 2002-11-19 03:10:38-05 kadickey Exp $" + .code +#endif + +#include "defcomm.h" + +link .reg %r2 +acc .reg %r3 +xreg .reg %r4 +yreg .reg %r5 +stack .reg %r6 +dbank .reg %r7 +direct .reg %r8 +neg .reg %r9 +zero .reg %r10 +psr .reg %r11 +kpc .reg %r12 +const_fd .reg %r13 +instr .reg %r14 +#if 0 +cycles .reg %r13 +kbank .reg %r14 +#endif + +page_info_ptr .reg %r15 +inst_tab_ptr .reg %r16 +fcycles_stop_ptr .reg %r17 +addr_latch .reg %r18 + +scratch1 .reg %r19 +scratch2 .reg %r20 +scratch3 .reg %r21 +scratch4 .reg %r22 +;instr .reg %r23 ; arg3 + +fcycles .reg %fr12 +fr_plus_1 .reg %fr13 +fr_plus_2 .reg %fr14 +fr_plus_3 .reg %fr15 +fr_plus_x_m1 .reg %fr16 +fcycles_stop .reg %fr17 +fcycles_last_dcycs .reg %fr18 + +ftmp1 .reg %fr4 +ftmp2 .reg %fr5 +fscr1 .reg %fr6 + +#define LDC(val,reg) ldil L%val,reg ! ldo R%val(reg),reg + diff --git a/src/defs_instr.h b/src/defs_instr.h new file mode 100644 index 0000000..89a49c6 --- /dev/null +++ b/src/defs_instr.h @@ -0,0 +1,1622 @@ +/************************************************************************/ +/* KEGS: Apple //gs Emulator */ +/* Copyright 2002 by Kent Dickey */ +/* */ +/* This code is covered by the GNU GPL */ +/* */ +/* The KEGS web page is kegs.sourceforge.net */ +/* You may contact the author at: kadickey@alumni.princeton.edu */ +/************************************************************************/ + +#ifdef ASM +# ifdef INCLUDE_RCSID_S + .data + .export rcsdif_defs_instr_h,data +rcsdif_defs_instr_h + .stringz "@(#)$KmKId: defs_instr.h,v 1.57 2004-01-10 15:49:14-05 kentd Exp $" + .code +# endif + +# ifdef ACC8 + .export defs_instr_start_8,data +defs_instr_start_8 .word 0 +# else + .export defs_instr_start_16,data +defs_instr_start_16 .word 0 +# endif /* ACC8*/ +#endif /* ASM */ + + +#undef GET_DLOC_X_IND_RD + +#ifdef ASM +# ifdef ACC8 +# define GET_DLOC_X_IND_RD() \ + ldb 1(scratch1),arg0 ! \ + GET_DLOC_X_IND_WR() ! \ + bl get_mem_long_8,link ! \ + nop +# else +# define GET_DLOC_X_IND_RD() \ + ldb 1(scratch1),arg0 ! \ + GET_DLOC_X_IND_WR() ! \ + bl get_mem_long_16,link ! \ + nop +# endif /* ACC8 */ +#else /* C*/ +# ifdef ACC8 +# define GET_DLOC_X_IND_RD() \ + GET_1BYTE_ARG; \ + GET_DLOC_X_IND_WR(); \ + GET_MEMORY8(arg, arg); +# else +# define GET_DLOC_X_IND_RD() \ + GET_1BYTE_ARG; \ + GET_DLOC_X_IND_WR(); \ + GET_MEMORY16(arg, arg, 0); +# endif +#endif + +#undef GET_DISP8_S_RD + +#ifdef ASM +# ifdef ACC8 +# define GET_DISP8_S_RD() \ + ldb 1(scratch1),arg0 ! \ + GET_DISP8_S_WR() ! \ + bl get_mem_b0_8,link ! \ + nop +# else +# define GET_DISP8_S_RD() \ + ldb 1(scratch1),arg0 ! \ + GET_DISP8_S_WR() ! \ + bl get_mem_b0_16,link ! \ + nop +# endif +#else /* C */ +# ifdef ACC8 +# define GET_DISP8_S_RD() \ + GET_1BYTE_ARG; \ + GET_DISP8_S_WR(); \ + GET_MEMORY8(arg, arg); +# else +# define GET_DISP8_S_RD() \ + GET_1BYTE_ARG; \ + GET_DISP8_S_WR(); \ + GET_MEMORY16(arg, arg, 0); +# endif +#endif + + +#ifdef ASM +# define MUST_FIX \ + break +#endif + +#undef GET_DLOC_RD + +#ifdef ASM +# ifdef ACC8 +# define GET_DLOC_RD() \ + ldb 1(scratch1),arg0 ! \ + extru,= direct,31,8,0 ! \ + CYCLES_PLUS_1 ! \ + INC_KPC_2 ! \ + add direct,arg0,addr_latch ! \ + extru addr_latch,23,16,arg3 ! \ + CYCLES_PLUS_1 ! \ + ldwx,s arg3(page_info_ptr),scratch2 ! \ + extru addr_latch,31,8,scratch4 ! \ + extru addr_latch,31,16,addr_latch ! \ + ldbx scratch4(scratch2),ret0 ! \ + extru,= scratch2,BANK_IO_BIT,1,0 ! \ + bl get_memory_iocheck_stub_asm,link ! \ + copy addr_latch,arg0 +# else +# define GET_DLOC_RD() \ + ldb 1(scratch1),arg0 ! \ + extru,= direct,31,8,0 ! \ + CYCLES_PLUS_1 ! \ + INC_KPC_2 ! \ + add direct,arg0,arg0 ! \ + bl get_mem_b0_16,link ! \ + extru arg0,31,16,arg0 +# endif +#else /* C */ +# ifdef ACC8 +# define GET_DLOC_RD() \ + GET_1BYTE_ARG; \ + if(direct & 0xff) { \ + CYCLES_PLUS_1; \ + } \ + INC_KPC_2; \ + GET_MEMORY8((direct + arg) & 0xffff, arg); +# else +# define GET_DLOC_RD() \ + GET_1BYTE_ARG; \ + if(direct & 0xff) { \ + CYCLES_PLUS_1; \ + } \ + INC_KPC_2; \ + GET_MEMORY16((direct + arg) & 0xffff, arg, 1); +# endif +#endif + + +#undef GET_DLOC_L_IND_RD +#ifdef ASM +# ifdef ACC8 +# define GET_DLOC_L_IND_RD() \ + ldb 1(scratch1),arg0 ! \ + GET_DLOC_L_IND_WR() ! \ + bl get_mem_long_8,link ! \ + nop +# else +# define GET_DLOC_L_IND_RD() \ + ldb 1(scratch1),arg0 ! \ + GET_DLOC_L_IND_WR() ! \ + bl get_mem_long_16,link ! \ + nop +# endif +#else /* C */ +# ifdef ACC8 +# define GET_DLOC_L_IND_RD() \ + GET_1BYTE_ARG; \ + GET_DLOC_L_IND_WR(); \ + GET_MEMORY8(arg, arg); +# else +# define GET_DLOC_L_IND_RD() \ + GET_1BYTE_ARG; \ + GET_DLOC_L_IND_WR(); \ + GET_MEMORY16(arg, arg, 0); +# endif +#endif + + +#undef GET_IMM_MEM + +#ifdef ASM +# ifdef ACC8 +# define GET_IMM_MEM() \ + ldb 1(scratch1),ret0 ! \ + INC_KPC_2 +# else +# define GET_IMM_MEM() \ + ldb 2(scratch1),scratch2 ! \ + INC_KPC_3 ! \ + ldb 1(scratch1),ret0 ! \ + CYCLES_PLUS_1 ! \ + dep scratch2,23,8,ret0 +# endif +#else +# ifdef ACC8 +# define GET_IMM_MEM() \ + GET_1BYTE_ARG; \ + INC_KPC_2; +# else +# define GET_IMM_MEM() \ + GET_2BYTE_ARG; \ + CYCLES_PLUS_1; \ + INC_KPC_3; +# endif +#endif + + +#undef GET_ABS_RD + +#ifdef ASM +# ifdef ACC8 +# define GET_ABS_RD() \ + ldb 2(scratch1),arg3 ! \ + INC_KPC_3 ! \ + ldb 1(scratch1),addr_latch ! \ + CYCLES_PLUS_2 ! \ + dep dbank,23,24,arg3 ! \ + ldwx,s arg3(page_info_ptr),scratch2 ! \ + copy addr_latch,scratch4 ! \ + dep arg3,23,24,addr_latch ! \ + ldbx scratch4(scratch2),ret0 ! \ + extru,= scratch2,BANK_IO_BIT,1,0 ! \ + bl get_memory_iocheck_stub_asm,link ! \ + copy addr_latch,arg0 +# else +# define GET_ABS_RD() \ + ldb 1(scratch1),arg0 ! \ + CYCLES_PLUS_1 ! \ + ldb 2(scratch1),scratch2 ! \ + INC_KPC_3 ! \ + dep dbank,15,16,arg0 ! \ + bl get_mem_long_16,link ! \ + dep scratch2,23,8,arg0 +# endif +#else +# ifdef ACC8 +# define GET_ABS_RD() \ + GET_2BYTE_ARG; \ + CYCLES_PLUS_1; \ + GET_MEMORY8((dbank << 16) + arg, arg); \ + INC_KPC_3; +# else +# define GET_ABS_RD() \ + GET_2BYTE_ARG; \ + CYCLES_PLUS_1; \ + GET_MEMORY16((dbank << 16) + arg, arg, 0); \ + INC_KPC_3; +# endif +#endif + + +#undef GET_LONG_RD + + +#ifdef ASM +# ifdef ACC8 +# define GET_LONG_RD() \ + ldb 1(scratch1),arg0 ! \ + INC_KPC_4 ! \ + ldb 2(scratch1),scratch2 ! \ + CYCLES_PLUS_2 ! \ + ldb 3(scratch1),scratch1 ! \ + dep scratch2,23,8,arg0 ! \ + bl get_mem_long_8,link ! \ + dep scratch1,15,8,arg0 +# else +# define GET_LONG_RD() \ + ldb 1(scratch1),arg0 ! \ + INC_KPC_4 ! \ + ldb 2(scratch1),scratch2 ! \ + CYCLES_PLUS_2 ! \ + ldb 3(scratch1),scratch1 ! \ + dep scratch2,23,8,arg0 ! \ + bl get_mem_long_16,link ! \ + dep scratch1,15,8,arg0 +# endif +#else /* C */ +# ifdef ACC8 +# define GET_LONG_RD() \ + GET_3BYTE_ARG; \ + CYCLES_PLUS_2; \ + GET_MEMORY8(arg, arg); \ + INC_KPC_4; +# else +# define GET_LONG_RD() \ + GET_3BYTE_ARG; \ + CYCLES_PLUS_2; \ + GET_MEMORY16(arg, arg, 0); \ + INC_KPC_4; +# endif +#endif + + + +#undef GET_DLOC_IND_Y_RD + +#undef GET_DLOC_IND_Y_WR_SPECIAL2 + +#define GET_DLOC_IND_Y_WR_SPECIAL2() \ + add direct,arg0,arg0 ! \ + bl get_mem_b0_direct_page_16,link ! \ + extru arg0,31,16,arg0 ! \ + dep dbank,15,8,ret0 ! \ + extru,= direct,31,8,0 ! \ + CYCLES_PLUS_1 ! \ + add yreg,ret0,arg0 /* don't change this instr */ + /* or add any after */ + /* to preserve ret0 & arg0 */ + + +#ifdef ASM +# ifdef ACC8 +# define GET_DLOC_IND_Y_RD() \ + ldb 1(scratch1),arg0 ! \ + INC_KPC_2 ! \ + GET_DLOC_IND_Y_WR_SPECIAL2() ! \ + xor arg0,ret0,scratch1 ! \ + extru,= psr,27,1,0 ! \ + extru,= scratch1,23,8,0 ! \ + CYCLES_PLUS_1 ! \ + bl get_mem_long_8,link ! \ + nop +# else +# define GET_DLOC_IND_Y_RD() \ + ldb 1(scratch1),arg0 ! \ + INC_KPC_2 ! \ + GET_DLOC_IND_Y_WR_SPECIAL2() ! \ + xor arg0,ret0,scratch1 ! \ + extru,= psr,27,1,0 ! \ + extru,= scratch1,23,8,0 ! \ + CYCLES_PLUS_1 ! \ + bl get_mem_long_16,link ! \ + nop +# endif +#else /* C */ +# ifdef ACC8 +# define GET_DLOC_IND_Y_RD() \ + GET_1BYTE_ARG; \ + GET_MEMORY_DIRECT_PAGE16((direct + arg) & 0xffff, tmp1); \ + tmp1 += (dbank << 16); \ + if(direct & 0xff) { \ + CYCLES_PLUS_1; \ + } \ + tmp2 = tmp1 + yreg; \ + if(((psr & 0x10) == 0) || ((tmp1 ^ tmp2) & 0xff00)) { \ + CYCLES_PLUS_1; \ + } \ + INC_KPC_2; \ + GET_MEMORY8(tmp2, arg); +# else +# define GET_DLOC_IND_Y_RD() \ + GET_1BYTE_ARG; \ + GET_MEMORY_DIRECT_PAGE16((direct + arg) & 0xffff, tmp1); \ + tmp1 += (dbank << 16); \ + if(direct & 0xff) { \ + CYCLES_PLUS_1; \ + } \ + tmp2 = tmp1 + yreg; \ + if(((psr & 0x10) == 0) || ((tmp1 ^ tmp2) & 0xff00)) { \ + CYCLES_PLUS_1; \ + } \ + INC_KPC_2; \ + GET_MEMORY16(tmp2, arg, 0); +# endif +#endif + + +#undef GET_DLOC_IND_RD + +#ifdef ASM +# ifdef ACC8 +# define GET_DLOC_IND_RD() \ + ldb 1(scratch1),arg0 ! \ + extru,= direct,31,8,0 ! \ + CYCLES_PLUS_1 ! \ + INC_KPC_2 ! \ + add direct,arg0,arg0 ! \ + bl get_mem_b0_direct_page_16,link ! \ + extru arg0,31,16,arg0 ! \ + copy ret0,arg0 ! \ + bl get_mem_long_8,link ! \ + dep dbank,15,16,arg0 +# else +# define GET_DLOC_IND_RD() \ + ldb 1(scratch1),arg0 ! \ + extru,= direct,31,8,0 ! \ + CYCLES_PLUS_1 ! \ + INC_KPC_2 ! \ + add direct,arg0,arg0 ! \ + bl get_mem_b0_direct_page_16,link ! \ + extru arg0,31,16,arg0 ! \ + copy ret0,arg0 ! \ + bl get_mem_long_16,link ! \ + dep dbank,15,16,arg0 +# endif +#else +# ifdef ACC8 +# define GET_DLOC_IND_RD() \ + GET_1BYTE_ARG; \ + INC_KPC_2; \ + if(direct & 0xff) { \ + CYCLES_PLUS_1; \ + } \ + GET_MEMORY_DIRECT_PAGE16((direct + arg) & 0xffff, arg); \ + GET_MEMORY8((dbank << 16) + arg, arg); +# else +# define GET_DLOC_IND_RD() \ + GET_1BYTE_ARG; \ + INC_KPC_2; \ + if(direct & 0xff) { \ + CYCLES_PLUS_1; \ + } \ + GET_MEMORY_DIRECT_PAGE16((direct + arg) & 0xffff, arg); \ + GET_MEMORY16((dbank << 16) + arg, arg, 0); +# endif +#endif + + +#undef GET_DLOC_X_RD + +#ifdef ASM +# ifdef ACC8 +# define GET_DLOC_X_RD() \ + ldb 1(scratch1),arg0 ! \ + GET_DLOC_INDEX_WR_A(xreg) ! \ + bl get_mem_b0_8,link ! \ + GET_DLOC_INDEX_WR_B(xreg) +# else +# define GET_DLOC_X_RD() \ + ldb 1(scratch1),arg0 ! \ + GET_DLOC_INDEX_WR_A(xreg) ! \ + bl get_mem_b0_16,link ! \ + GET_DLOC_INDEX_WR_B(xreg) +# endif +#else +# ifdef ACC8 +# define GET_DLOC_X_RD() \ + GET_1BYTE_ARG; \ + CYCLES_PLUS_1; \ + if(direct & 0xff) { \ + CYCLES_PLUS_1; \ + } \ + INC_KPC_2; \ + arg = (arg + xreg + direct) & 0xffff; \ + if(psr & 0x100) { \ + if((direct & 0xff) == 0) { \ + arg = (direct & 0xff00) | (arg & 0xff); \ + } \ + } \ + GET_MEMORY8(arg & 0xffff, arg); +# else +# define GET_DLOC_X_RD() \ + GET_1BYTE_ARG; \ + CYCLES_PLUS_1; \ + if(direct & 0xff) { \ + CYCLES_PLUS_1; \ + } \ + INC_KPC_2; \ + arg = (arg + xreg + direct) & 0xffff; \ + if(psr & 0x100) { \ + if((direct & 0xff) == 0) { \ + arg = (direct & 0xff00) | (arg & 0xff); \ + } \ + } \ + GET_MEMORY16(arg, arg, 1); +# endif +#endif + + +#undef GET_DISP8_S_IND_Y_RD + +#ifdef ASM +# ifdef ACC8 +# define GET_DISP8_S_IND_Y_RD() \ + ldb 1(scratch1),arg0 ! \ + GET_DISP8_S_IND_Y_WR() ! \ + bl get_mem_long_8,link ! \ + nop +# else +# define GET_DISP8_S_IND_Y_RD() \ + ldb 1(scratch1),arg0 ! \ + GET_DISP8_S_IND_Y_WR() ! \ + bl get_mem_long_16,link ! \ + nop +# endif +#else +# ifdef ACC8 +# define GET_DISP8_S_IND_Y_RD() \ + GET_1BYTE_ARG; \ + GET_DISP8_S_IND_Y_WR(); \ + GET_MEMORY8(arg, arg); +# else +# define GET_DISP8_S_IND_Y_RD() \ + GET_1BYTE_ARG; \ + GET_DISP8_S_IND_Y_WR(); \ + GET_MEMORY16(arg, arg, 0); +# endif +#endif + + +#undef GET_DLOC_L_IND_Y_RD + +#ifdef ASM +# ifdef ACC8 +# define GET_DLOC_L_IND_Y_RD() \ + ldb 1(scratch1),arg0 ! \ + GET_DLOC_L_IND_Y_WR() ! \ + bl get_mem_long_8,link ! \ + nop +# else +# define GET_DLOC_L_IND_Y_RD() \ + ldb 1(scratch1),arg0 ! \ + GET_DLOC_L_IND_Y_WR() ! \ + bl get_mem_long_16,link ! \ + nop +# endif +#else /* C */ +# ifdef ACC8 +# define GET_DLOC_L_IND_Y_RD() \ + GET_1BYTE_ARG; \ + GET_DLOC_L_IND_Y_WR(); \ + GET_MEMORY8(arg, arg); +# else +# define GET_DLOC_L_IND_Y_RD() \ + GET_1BYTE_ARG; \ + GET_DLOC_L_IND_Y_WR(); \ + GET_MEMORY16(arg, arg, 0); +# endif +#endif + + +#undef GET_ABS_INDEX_ADDR_FOR_RD + +#ifdef ASM +/* to get cycle right: add 1 cycle */ +/* if (x is 16bit) || (carry into high byte), add another cycle */ +/* So, if x==16bit, add 1. If x==8bit then add 1 if carry */ +# define GET_ABS_INDEX_ADDR_FOR_RD(index_reg) \ + ldb 1(scratch1),ret0 ! \ + CYCLES_PLUS_1 ! \ + ldb 2(scratch1),scratch1 ! \ + dep dbank,15,16,ret0 ! \ + INC_KPC_3 ! \ + dep scratch1,23,8,ret0 ! \ + add ret0,index_reg,arg0 ! \ + xor arg0,ret0,scratch1 ! \ + extru,= psr,27,1,0 ! \ + extru,= scratch1,23,8,0 ! \ + CYCLES_PLUS_1 +#else /* C */ +# define GET_ABS_INDEX_ADDR_FOR_RD(index_reg) \ + GET_2BYTE_ARG; \ + CYCLES_PLUS_1; \ + INC_KPC_3; \ + tmp1 = (dbank << 16) + arg; \ + arg = tmp1 + index_reg; \ + if(((psr & 0x10) == 0) || ((tmp1 ^ arg) & 0xff00)) { \ + CYCLES_PLUS_1; \ + } +#endif + +#undef GET_ABS_Y_RD + +#ifdef ASM +# ifdef ACC8 +# define GET_ABS_Y_RD() \ + GET_ABS_INDEX_ADDR_FOR_RD(yreg) ! \ + bl get_mem_long_8,link ! \ + extru arg0,31,24,arg0 +# else +# define GET_ABS_Y_RD() \ + GET_ABS_INDEX_ADDR_FOR_RD(yreg) ! \ + bl get_mem_long_16,link ! \ + extru arg0,31,24,arg0 +# endif +#else /* C */ +# ifdef ACC8 +# define GET_ABS_Y_RD() \ + GET_ABS_INDEX_ADDR_FOR_RD(yreg); \ + GET_MEMORY8(arg, arg); +# else +# define GET_ABS_Y_RD() \ + GET_ABS_INDEX_ADDR_FOR_RD(yreg); \ + GET_MEMORY16(arg, arg, 0); +# endif +#endif + + + + +#undef GET_ABS_X_RD +#undef GET_ABS_X_RD_WR + +#ifdef ASM +# ifdef ACC8 +# define GET_ABS_X_RD() \ + GET_ABS_INDEX_ADDR_FOR_RD(xreg) ! \ + bl get_mem_long_8,link ! \ + extru arg0,31,24,arg0 + +# define GET_ABS_X_RD_WR() \ + ldb 1(scratch1),ret0 ! \ + INC_KPC_3 ! \ + ldb 2(scratch1),scratch1 ! \ + dep dbank,15,16,ret0 ! \ + CYCLES_PLUS_2 ! \ + dep scratch1,23,8,ret0 ! \ + add ret0,xreg,arg0 ! \ + bl get_mem_long_8,link ! \ + extru arg0,31,24,arg0 +# else +# define GET_ABS_X_RD() \ + GET_ABS_INDEX_ADDR_FOR_RD(xreg) ! \ + bl get_mem_long_16,link ! \ + extru arg0,31,24,arg0 + +# define GET_ABS_X_RD_WR() \ + ldb 1(scratch1),ret0 ! \ + INC_KPC_3 ! \ + ldb 2(scratch1),scratch1 ! \ + dep dbank,15,16,ret0 ! \ + CYCLES_PLUS_2 ! \ + dep scratch1,23,8,ret0 ! \ + add ret0,xreg,arg0 ! \ + bl get_mem_long_16,link ! \ + extru arg0,31,24,arg0 +# endif +#else /* C */ +# ifdef ACC8 +# define GET_ABS_X_RD() \ + GET_ABS_INDEX_ADDR_FOR_RD(xreg); \ + GET_MEMORY8(arg, arg); + +# define GET_ABS_X_RD_WR() \ + GET_2BYTE_ARG; \ + INC_KPC_3; \ + CYCLES_PLUS_2; \ + arg = (dbank << 16) + ((arg + xreg) & 0xffff); \ + GET_MEMORY8(arg, arg); +# else +# define GET_ABS_X_RD() \ + GET_ABS_INDEX_ADDR_FOR_RD(xreg); \ + GET_MEMORY16(arg, arg, 0); + +# define GET_ABS_X_RD_WR() \ + GET_2BYTE_ARG; \ + INC_KPC_3; \ + CYCLES_PLUS_2; \ + arg = (dbank << 16) + ((arg + xreg) & 0xffff); \ + GET_MEMORY16(arg, arg, 0); +# endif +#endif + + +#undef GET_LONG_X_RD + +#ifdef ASM +# ifdef ACC8 +# define GET_LONG_X_RD() \ + ldb 1(scratch1),ret0 ! \ + ldb 2(scratch1),scratch2 ! \ + CYCLES_PLUS_2 ! \ + ldb 3(scratch1),scratch1 ! \ + INC_KPC_4 ! \ + dep scratch2,23,8,ret0 ! \ + dep scratch1,15,8,ret0 ! \ + add ret0,xreg,arg0 ! \ + bl get_mem_long_8,link ! \ + extru arg0,31,24,arg0 +# else +# define GET_LONG_X_RD() \ + ldb 1(scratch1),ret0 ! \ + ldb 2(scratch1),scratch2 ! \ + CYCLES_PLUS_2 ! \ + ldb 3(scratch1),scratch1 ! \ + INC_KPC_4 ! \ + dep scratch2,23,8,ret0 ! \ + dep scratch1,15,8,ret0 ! \ + add ret0,xreg,arg0 ! \ + bl get_mem_long_16,link ! \ + extru arg0,31,24,arg0 +# endif +#else /* C */ +# ifdef ACC8 +# define GET_LONG_X_RD() \ + GET_3BYTE_ARG; \ + arg = (arg + xreg) & 0xffffff; \ + INC_KPC_4; \ + CYCLES_PLUS_2; \ + GET_MEMORY8(arg, arg); +# else +# define GET_LONG_X_RD() \ + GET_3BYTE_ARG; \ + arg = (arg + xreg) & 0xffffff; \ + INC_KPC_4; \ + CYCLES_PLUS_2; \ + GET_MEMORY16(arg, arg, 0); +# endif +#endif + + +#define SET_NEG_ZERO16(val) \ + zero = val; \ + neg = (val) >> 15; + +# define SET_NEG_ZERO8(val) \ + zero = val; \ + neg = (val) >> 7; + +#define SET_CARRY8(val) \ + psr = (psr & ~1) + (((val) >> 8) & 1); + +#define SET_CARRY16(val) \ + psr = (psr & ~1) + (((val) >> 16) & 1); + +#if 0 +# define NEGZERO8(val) SET_NEG_ZERO8(val) +#else +# define NEGZERO16(val) SET_NEG_ZERO16(val) +#endif + +#define SET_INDEX_REG(src, dest) \ + if(psr & 0x10) { \ + dest = (src) & 0xff; \ + SET_NEG_ZERO8(dest); \ + } else { \ + dest = (src) & 0xffff; \ + SET_NEG_ZERO16(dest); \ + } + +#define LD_INDEX_INST(index_reg, in_bank) \ + if(psr & 0x10) { \ + GET_MEMORY8(arg, arg); \ + } else { \ + GET_MEMORY16(arg, arg, in_bank);\ + } \ + SET_INDEX_REG(arg, index_reg); + +#define LDX_INST(in_bank) LD_INDEX_INST(xreg, in_bank) +#define LDY_INST(in_bank) LD_INDEX_INST(yreg, in_bank) + +#undef ORA_INST + +#ifdef ASM +# ifdef ACC8 +# define ORA_INST() \ + ldi 0xff,scratch1 ! \ + or acc,ret0,arg0 ! \ + and arg0,scratch1,zero ! \ + extru arg0,24,1,neg ! \ + b dispatch ! \ + dep arg0,31,8,acc +# else +# define ORA_INST() \ + zdepi -1,31,16,scratch1 ! \ + or acc,ret0,arg0 ! \ + and arg0,scratch1,zero ! \ + extru arg0,16,1,neg ! \ + b dispatch ! \ + dep arg0,31,16,acc +# endif +#else /* C */ +# ifdef ACC8 +# define ORA_INST() \ + tmp1 = (acc | arg) & 0xff; \ + acc = (acc & 0xff00) + tmp1; \ + SET_NEG_ZERO8(tmp1); +# else +# define ORA_INST() \ + acc = (acc | arg); \ + SET_NEG_ZERO16(acc); +# endif +#endif + +#undef AND_INST + +#ifdef ASM +# ifdef ACC8 +# define AND_INST() \ + ldi 0xff,scratch1 ! \ + and acc,ret0,arg0 ! \ + and arg0,scratch1,zero ! \ + extru arg0,24,1,neg ! \ + b dispatch ! \ + dep arg0,31,8,acc +# else +# define AND_INST() \ + zdepi -1,31,16,scratch1 ! \ + and acc,ret0,arg0 ! \ + and arg0,scratch1,zero ! \ + extru arg0,16,1,neg ! \ + b dispatch ! \ + dep arg0,31,16,acc +# endif +#else /* C */ +# ifdef ACC8 +# define AND_INST() \ + tmp1 = (acc & arg) & 0xff; \ + acc = (acc & 0xff00) + tmp1; \ + SET_NEG_ZERO8(tmp1); +# else +# define AND_INST() \ + acc = (acc & arg); \ + SET_NEG_ZERO16(acc); +# endif +#endif + +#undef EOR_INST + +#ifdef ASM +# ifdef ACC8 +# define EOR_INST() \ + ldi 0xff,scratch1 ! \ + xor acc,ret0,arg0 ! \ + and arg0,scratch1,zero ! \ + extru arg0,24,1,neg ! \ + b dispatch ! \ + dep arg0,31,8,acc +# else +# define EOR_INST() \ + zdepi -1,31,16,scratch1 ! \ + xor acc,ret0,arg0 ! \ + and arg0,scratch1,zero ! \ + extru arg0,16,1,neg ! \ + b dispatch ! \ + dep arg0,31,16,acc +# endif +#else /* C */ +# ifdef ACC8 +# define EOR_INST() \ + tmp1 = (acc ^ arg) & 0xff; \ + acc = (acc & 0xff00) + tmp1; \ + SET_NEG_ZERO8(tmp1); +# else +# define EOR_INST() \ + acc = (acc ^ arg); \ + SET_NEG_ZERO16(acc); +# endif +#endif + +# undef LDA_INST + +#ifdef ASM +# ifdef ACC8 +# define LDA_INST() \ + extru ret0,31,8,zero ! \ + extru ret0,24,1,neg ! \ + b dispatch ! \ + dep zero,31,8,acc +# else +# define LDA_INST() \ + extru ret0,31,16,zero ! \ + extru ret0,16,1,neg ! \ + b dispatch ! \ + dep zero,31,16,acc +# endif +#else /* C*/ +# ifdef ACC8 +# define LDA_INST() \ + acc = (acc & 0xff00) + (arg & 0xff); \ + SET_NEG_ZERO8(arg & 0xff); +# else +# define LDA_INST() \ + acc = (arg & 0xffff); \ + SET_NEG_ZERO16(acc); +# endif +#endif + +# undef ADC_INST + +#ifdef ASM +# ifdef ACC8 +# define ADC_INST() \ + b adc_binary_8 ! \ + extru acc,31,8,arg0 +# else +# define ADC_INST() \ + b adc_binary_16 ! \ + extru acc,31,16,arg0 +# endif +#else /* C */ +# ifdef ACC8 +# define ADC_INST() \ + tmp1 = do_adc_sbc8(acc & 0xff, arg & 0xff, psr, 0); \ + acc = (acc & 0xff00) + (tmp1 & 0xff); \ + psr = (tmp1 >> 16); \ + zero = !(psr & 0x2); \ + neg = (psr >> 7) & 1; +# else +# define ADC_INST() \ + tmp1 = do_adc_sbc16(acc, arg & 0xffff, psr, 0); \ + acc = (tmp1 & 0xffff); \ + psr = (tmp1 >> 16); \ + zero = !(psr & 0x2); \ + neg = (psr >> 7) & 1; +# endif +#endif + + +# undef SBC_INST + +#ifdef ASM +# ifdef ACC8 +# define SBC_INST() \ + uaddcm 0,ret0,ret0 ! \ + b sbc_binary_8 ! \ + extru acc,31,8,arg0 +# else +# define SBC_INST() \ + uaddcm 0,ret0,ret0 ! \ + b sbc_binary_16 ! \ + extru acc,31,16,arg0 +# endif +#else /* C */ +# ifdef ACC8 +# define SBC_INST() \ + tmp1 = do_adc_sbc8(acc & 0xff, arg & 0xff, psr, 1); \ + acc = (acc & 0xff00) + (tmp1 & 0xff); \ + psr = (tmp1 >> 16); \ + zero = !(psr & 0x2); \ + neg = (psr >> 7) & 1; +# else +# define SBC_INST() \ + tmp1 = do_adc_sbc16(acc, arg & 0xffff, psr, 1); \ + acc = (tmp1 & 0xffff); \ + psr = (tmp1 >> 16); \ + zero = !(psr & 0x2); \ + neg = (psr >> 7) & 1; +# endif +#endif + + +# undef CMP_INST + +#ifdef ASM +# ifdef ACC8 +# define CMP_INST() ! \ + extru acc,31,8,scratch1 ! \ + subi 0x100,ret0,ret0 ! \ + ldi 0xff,scratch2 ! \ + add ret0,scratch1,ret0 ! \ + extru ret0,23,1,scratch1 ! \ + and ret0,scratch2,zero ! \ + extru ret0,24,1,neg ! \ + b dispatch ! \ + dep scratch1,31,1,psr +# else +# define CMP_INST() ! \ + ldil l%0x10000,scratch3 ! \ + zdepi -1,31,16,scratch2 ! \ + sub scratch3,ret0,ret0 ! \ + add ret0,acc,ret0 ! \ + extru ret0,15,1,scratch1 ! \ + and ret0,scratch2,zero ! \ + extru ret0,16,1,neg ! \ + b dispatch ! \ + dep scratch1,31,1,psr +# endif +#else /* C */ +# ifdef ACC8 +# define CMP_INST() \ + arg = (acc & 0xff) + (0x100 - arg); \ + SET_CARRY8(arg); \ + arg = arg & 0xff; \ + SET_NEG_ZERO8(arg & 0xff); +# else +# define CMP_INST() \ + arg = (acc & 0xffff) + (0x10000 - arg); \ + SET_CARRY16(arg); \ + arg = arg & 0xffff; \ + SET_NEG_ZERO16(arg & 0xffff); +# endif +#endif + +# undef BIT_INST + +#ifdef ASM +# ifdef ACC8 +# define BIT_INST() \ + ldi 0xff,scratch1 ! \ + and acc,ret0,arg0 ! \ + extru ret0,24,1,neg ! \ + and arg0,scratch1,zero ! \ + extru ret0,25,1,scratch2 ! \ + b dispatch ! \ + dep scratch2,25,1,psr +# else +# define BIT_INST() \ + zdepi -1,31,16,scratch1 ! \ + and acc,ret0,arg0 ! \ + extru ret0,16,1,neg ! \ + and arg0,scratch1,zero ! \ + extru ret0,17,1,scratch2 ! \ + b dispatch ! \ + dep scratch2,25,1,psr +# endif +#else /* C */ +# ifdef ACC8 +# define BIT_INST() \ + neg = arg >> 7; \ + zero = (acc & arg & 0xff); \ + psr = (psr & (~0x40)) | (arg & 0x40); +# else +# define BIT_INST() \ + neg = arg >> 15; \ + zero = (acc & arg & 0xffff); \ + psr = (psr & (~0x40)) | ((arg >> 8) & 0x40); +# endif +#endif + +# undef STA_INST + +#ifdef ASM +# ifdef ACC8 +# define STA_INST() \ + extru acc,31,8,arg1 ! \ + ldil l%dispatch,link ! \ + b set_mem_long_8 ! \ + ldo r%dispatch(link),link +# else +# define STA_INST() \ + extru acc,31,16,arg1 ! \ + ldil l%dispatch,link ! \ + b set_mem_long_16 ! \ + ldo r%dispatch(link),link +# endif +#else /* C*/ +# ifdef ACC8 +# define STA_INST(in_bank) \ + SET_MEMORY8(arg, acc); +# else +# define STA_INST(in_bank) \ + SET_MEMORY16(arg, acc, in_bank); +# endif +#endif + + +#undef TSB_INST + +#ifdef ASM +# ifdef ACC8 +/* Uses addr_latch as full apple addr to write data to */ +# define TSB_INST() \ + or acc,ret0,arg1 ! \ + CYCLES_PLUS_1 ! \ + extru arg1,31,8,arg1 ! \ + and ret0,acc,zero ! \ + copy addr_latch,arg0 ! \ + extru zero,31,8,zero ! \ + ldil l%dispatch,link ! \ + b set_mem_long_8 ! \ + ldo r%dispatch(link),link +# else +/* Uses addr_latch as full apple addr to write data to */ +# define TSB_INST() \ + or acc,ret0,arg1 ! \ + CYCLES_PLUS_1 ! \ + extru arg1,31,16,arg1 ! \ + and ret0,acc,zero ! \ + copy addr_latch,arg0 ! \ + extru zero,31,16,zero ! \ + ldil l%dispatch,link ! \ + b set_mem_long_16 ! \ + ldo r%dispatch(link),link +# endif +#else /* C*/ +# ifdef ACC8 +# define TSB_INST(in_bank) \ + tmp1 = arg | acc; \ + CYCLES_PLUS_1; \ + zero = arg & acc; \ + SET_MEMORY8(addr_latch, tmp1); +# else +# define TSB_INST(in_bank) \ + tmp1 = arg | acc; \ + CYCLES_PLUS_1; \ + zero = arg & acc; \ + SET_MEMORY16(addr_latch, tmp1, in_bank); +# endif +#endif + + +#undef ASL_INST + +#ifdef ASM +# ifdef ACC8 +/* Uses addr_latch as full apple addr to write data to */ +# define ASL_INST() \ + ldi 0xff,scratch1 ! \ + copy addr_latch,arg0 ! \ + sh1add ret0,0,scratch3 ! \ + CYCLES_PLUS_1 ! \ + extru scratch3,24,1,neg ! \ + and scratch3,scratch1,zero ! \ + extru scratch3,23,1,scratch2 ! \ + copy zero,arg1 ! \ + dep scratch2,31,1,psr /* set carry */ ! \ + ldil l%dispatch,link ! \ + b set_mem_long_8 ! \ + ldo r%dispatch(link),link +# else +/* Uses addr_latch as full apple addr to write data to */ +# define ASL_INST() \ + zdepi -1,31,16,scratch1 ! \ + copy addr_latch,arg0 ! \ + sh1add ret0,0,scratch3 ! \ + CYCLES_PLUS_1 ! \ + extru scratch3,16,1,neg ! \ + and scratch3,scratch1,zero ! \ + extru scratch3,15,1,scratch2 ! \ + copy zero,arg1 ! \ + dep scratch2,31,1,psr /* set carry */ ! \ + ldil l%dispatch,link ! \ + b set_mem_long_16 ! \ + ldo r%dispatch(link),link +# endif +#else /* C */ +# ifdef ACC8 +# define ASL_INST(in_bank) \ + psr = (psr & 0x1fe) + ((arg >> 7) & 1); \ + tmp1 = (arg << 1) & 0xff; \ + CYCLES_PLUS_1; \ + SET_NEG_ZERO8(tmp1); \ + SET_MEMORY8(addr_latch, tmp1); +# else +# define ASL_INST(in_bank) \ + psr = (psr & 0x1fe) + ((arg >> 15) & 1);\ + tmp1 = (arg << 1) & 0xffff; \ + CYCLES_PLUS_1; \ + SET_NEG_ZERO16(tmp1); \ + SET_MEMORY16(addr_latch, tmp1, in_bank); +# endif +#endif + +# undef ROL_INST + +#ifdef ASM +# ifdef ACC8 +/* Uses addr_latch as full apple addr to write data to */ +# define ROL_INST() \ + extru psr,31,1,scratch2 ! \ + ldi 0xff,scratch1 ! \ + copy addr_latch,arg0 ! \ + sh1add ret0,scratch2,scratch3 ! \ + CYCLES_PLUS_1 ! \ + extru scratch3,24,1,neg ! \ + and scratch3,scratch1,zero ! \ + extru scratch3,23,1,scratch2 ! \ + copy zero,arg1 ! \ + dep scratch2,31,1,psr /* set carry */ ! \ + ldil l%dispatch,link ! \ + b set_mem_long_8 ! \ + ldo r%dispatch(link),link +# else +/* Uses addr_latch as full apple addr to write data to */ +# define ROL_INST() \ + extru psr,31,1,scratch2 ! \ + copy addr_latch,arg0 ! \ + zdepi -1,31,16,scratch1 ! \ + sh1add ret0,scratch2,scratch3 ! \ + CYCLES_PLUS_1 ! \ + extru scratch3,16,1,neg ! \ + and scratch3,scratch1,zero ! \ + extru scratch3,15,1,scratch2 ! \ + copy zero,arg1 ! \ + dep scratch2,31,1,psr /* set carry */ ! \ + ldil l%dispatch,link ! \ + b set_mem_long_16 ! \ + ldo r%dispatch(link),link +# endif +#else /* C */ +# ifdef ACC8 +# define ROL_INST(in_bank) \ + arg = (arg << 1) | (psr & 1); \ + SET_MEMORY8(addr_latch, arg); \ + CYCLES_PLUS_1; \ + SET_NEG_ZERO8(arg & 0xff); \ + SET_CARRY8(arg); +# else +# define ROL_INST(in_bank) \ + arg = (arg << 1) | (psr & 1); \ + SET_MEMORY16(addr_latch, arg, in_bank); \ + CYCLES_PLUS_1; \ + SET_NEG_ZERO16(arg & 0xffff); \ + SET_CARRY16(arg); +# endif +#endif + +# undef LSR_INST + +#ifdef ASM +# ifdef ACC8 +/* Uses addr_latch as full apple addr to write data to */ +# define LSR_INST() \ + copy addr_latch,arg0 ! \ + extru ret0,30,7,zero ! \ + CYCLES_PLUS_1 ! \ + ldi 0,neg ! \ + dep ret0,31,1,psr /* set carry */ ! \ + copy zero,arg1 ! \ + ldil l%dispatch,link ! \ + b set_mem_long_8 ! \ + ldo r%dispatch(link),link +# else +/* Uses addr_latch as full apple addr to write data to */ +# define LSR_INST() \ + copy addr_latch,arg0 ! \ + extru ret0,30,15,zero ! \ + CYCLES_PLUS_1 ! \ + ldi 0,neg ! \ + dep ret0,31,1,psr /* set carry */ ! \ + copy zero,arg1 ! \ + ldil l%dispatch,link ! \ + b set_mem_long_16 ! \ + ldo r%dispatch(link),link +# endif +#else /* C */ +# ifdef ACC8 +# define LSR_INST(in_bank) \ + SET_CARRY8(arg << 8); \ + arg = (arg >> 1) & 0x7f; \ + SET_MEMORY8(addr_latch, arg); \ + CYCLES_PLUS_1; \ + SET_NEG_ZERO8(arg); +# else +# define LSR_INST(in_bank) \ + SET_CARRY16(arg << 16); \ + arg = (arg >> 1) & 0x7fff; \ + SET_MEMORY16(addr_latch, arg, in_bank); \ + CYCLES_PLUS_1; \ + SET_NEG_ZERO16(arg); +# endif +#endif + + +# undef ROR_INST + +#ifdef ASM +# ifdef ACC8 +# define ROR_INST() ! \ + extru psr,31,1,neg ! \ + copy addr_latch,arg0 ! \ + extru ret0,30,7,zero ! \ + CYCLES_PLUS_1 ! \ + dep neg,24,1,zero ! \ + copy zero,arg1 ! \ + dep ret0,31,1,psr ! \ + ldil l%dispatch,link ! \ + b set_mem_long_8 ! \ + ldo r%dispatch(link),link +# else +# define ROR_INST() ! \ + extru psr,31,1,neg ! \ + copy addr_latch,arg0 ! \ + extru ret0,30,15,zero ! \ + CYCLES_PLUS_1 ! \ + dep neg,16,1,zero ! \ + copy zero,arg1 ! \ + dep ret0,31,1,psr ! \ + ldil l%dispatch,link ! \ + b set_mem_long_16 ! \ + ldo r%dispatch(link),link +# endif +#else /* C */ +# ifdef ACC8 +# define ROR_INST(in_bank) \ + tmp1 = psr & 1; \ + SET_CARRY8(arg << 8); \ + arg = ((arg >> 1) & 0x7f) | (tmp1 << 7); \ + SET_MEMORY8(addr_latch, arg); \ + CYCLES_PLUS_1; \ + SET_NEG_ZERO8(arg); +# else +# define ROR_INST(in_bank) \ + tmp1 = psr & 1; \ + SET_CARRY16(arg << 16); \ + arg = ((arg >> 1) & 0x7fff) | (tmp1 << 15); \ + SET_MEMORY16(addr_latch, arg, in_bank); \ + CYCLES_PLUS_1; \ + SET_NEG_ZERO16(arg); +# endif +#endif + +# undef TRB_INST + +#ifdef ASM +# ifdef ACC8 +/* Uses addr_latch as full apple addr to write data to */ +# define TRB_INST() \ + andcm ret0,acc,arg1 ! \ + copy addr_latch,arg0 ! \ + and ret0,acc,zero ! \ + extru arg1,31,8,arg1 ! \ + CYCLES_PLUS_1 ! \ + extru zero,31,8,zero ! \ + ldil l%dispatch,link ! \ + b set_mem_long_8 ! \ + ldo r%dispatch(link),link +# else +/* Uses addr_latch as full apple addr to write data to */ +# define TRB_INST() \ + andcm ret0,acc,arg1 ! \ + CYCLES_PLUS_1 ! \ + extru arg1,31,16,arg1 ! \ + and ret0,acc,zero ! \ + copy addr_latch,arg0 ! \ + extru zero,31,16,zero ! \ + ldil l%dispatch,link ! \ + b set_mem_long_16 ! \ + ldo r%dispatch(link),link +# endif +#else /* C */ +# ifdef ACC8 +# define TRB_INST(in_bank) \ + arg = arg & 0xff; \ + tmp1 = arg & ~acc; \ + CYCLES_PLUS_1; \ + zero = arg & acc; \ + SET_MEMORY8(addr_latch, tmp1); +# else +# define TRB_INST(in_bank) \ + tmp1 = arg & ~acc; \ + CYCLES_PLUS_1; \ + zero = arg & acc; \ + SET_MEMORY16(addr_latch, tmp1, in_bank); +# endif +#endif + +# undef DEC_INST + +#ifdef ASM +# ifdef ACC8 +# define DEC_INST() ! \ + addi -1,ret0,ret0 ! \ + copy addr_latch,arg0 ! \ + extru ret0,24,1,neg ! \ + CYCLES_PLUS_1 ! \ + extru ret0,31,8,zero ! \ + ldil l%dispatch,link ! \ + copy zero,arg1 ! \ + b set_mem_long_8 ! \ + ldo r%dispatch(link),link +# else +# define DEC_INST() ! \ + addi -1,ret0,ret0 ! \ + copy addr_latch,arg0 ! \ + extru ret0,16,1,neg ! \ + CYCLES_PLUS_1 ! \ + extru ret0,31,16,zero ! \ + ldil l%dispatch,link ! \ + copy zero,arg1 ! \ + b set_mem_long_16 ! \ + ldo r%dispatch(link),link +# endif +#else /* C */ +# ifdef ACC8 +# define DEC_INST(in_bank) \ + CYCLES_PLUS_1; \ + arg = (arg - 1) & 0xff; \ + SET_MEMORY8(addr_latch, arg); \ + SET_NEG_ZERO8(arg); +# else +# define DEC_INST(in_bank) \ + CYCLES_PLUS_1; \ + arg = (arg - 1) & 0xffff; \ + SET_MEMORY16(addr_latch, arg, in_bank); \ + SET_NEG_ZERO16(arg); +# endif +#endif + + +# undef INC_INST + +#ifdef ASM +# ifdef ACC8 +# define INC_INST() ! \ + addi 1,ret0,ret0 ! \ + copy addr_latch,arg0 ! \ + extru ret0,24,1,neg ! \ + CYCLES_PLUS_1 ! \ + extru ret0,31,8,zero ! \ + ldil l%dispatch,link ! \ + copy zero,arg1 ! \ + b set_mem_long_8 ! \ + ldo r%dispatch(link),link +# else +# define INC_INST() ! \ + addi 1,ret0,ret0 ! \ + copy addr_latch,arg0 ! \ + extru ret0,16,1,neg ! \ + CYCLES_PLUS_1 ! \ + extru ret0,31,16,zero ! \ + ldil l%dispatch,link ! \ + copy zero,arg1 ! \ + b set_mem_long_16 ! \ + ldo r%dispatch(link),link +# endif +#else /* C */ +# ifdef ACC8 +# define INC_INST(in_bank) \ + CYCLES_PLUS_1; \ + arg = (arg + 1) & 0xff; \ + SET_MEMORY8(addr_latch, arg); \ + SET_NEG_ZERO8(arg); +# else +# define INC_INST(in_bank) \ + CYCLES_PLUS_1; \ + arg = (arg + 1) & 0xffff; \ + SET_MEMORY16(addr_latch, arg, in_bank); \ + SET_NEG_ZERO16(arg); +# endif +#endif + +# undef STZ_INST + +#ifdef ASM +# ifdef ACC8 +# define STZ_INST() \ + ldi 0,arg1 ! \ + ldil l%dispatch,link ! \ + b set_mem_long_8 ! \ + ldo r%dispatch(link),link +# else +# define STZ_INST() \ + ldi 0,arg1 ! \ + ldil l%dispatch,link ! \ + b set_mem_long_16 ! \ + ldo r%dispatch(link),link +# endif +#else /* C */ +# ifdef ACC8 +# define STZ_INST(in_bank) \ + SET_MEMORY8(arg, 0); +# else +# define STZ_INST(in_bank) \ + SET_MEMORY16(arg, 0, in_bank); +# endif +#endif + + +#undef COND_BR1 +#undef COND_BR2 +#undef COND_BR_UNTAKEN + +#ifdef ASM +# define COND_BR1 \ + ldb 1(scratch1),arg0 + +/* be careful about modifying kpc as first instr of COND_Br2 since it */ +/* is in the delay slot of a branch! */ +# define COND_BR2 \ + addi 2,kpc,scratch3 ! \ + ldi 0x100,scratch4 ! \ + extrs arg0,31,8,ret0 ! \ + and scratch4,psr,scratch4 ! \ + add scratch3,ret0,ret0 ! \ + CYCLES_PLUS_1 ! \ + xor scratch3,ret0,scratch3 ! \ + and,= scratch4,scratch3,0 ! \ + CYCLES_PLUS_1 ! \ + b dispatch ! \ + dep ret0,31,16,kpc + + +# define COND_BR_UNTAKEN \ + b dispatch ! \ + INC_KPC_2 +#else /* C */ +# define BRANCH_DISP8(cond) \ + GET_1BYTE_ARG; \ + tmp2 = kpc & 0xff0000; \ + kpc += 2; \ + tmp1 = kpc; \ + if(cond) { \ + kpc = kpc + arg - ((arg & 0x80) << 1); \ + CYCLES_PLUS_1; \ + if((tmp1 ^ kpc) & psr & 0x100) { \ + CYCLES_PLUS_1; \ + } \ + } \ + kpc = tmp2 + (kpc & 0xffff); +#endif + +#undef STY_INST +#undef STX_INST + +#ifdef ASM +# define STY_INST() \ + ldil l%dispatch,link ! \ + b set_mem_yreg ! \ + ldo r%dispatch(link),link + +# define STX_INST() \ + ldil l%dispatch,link ! \ + b set_mem_xreg ! \ + ldo r%dispatch(link),link +#else /* C */ +# define STY_INST(in_bank) \ + if(psr & 0x10) { \ + SET_MEMORY8(arg, yreg); \ + } else { \ + SET_MEMORY16(arg, yreg, in_bank);\ + } +# define STX_INST(in_bank) \ + if(psr & 0x10) { \ + SET_MEMORY8(arg, xreg); \ + } else { \ + SET_MEMORY16(arg, xreg, in_bank);\ + } +#endif + +#ifndef ASM + +# define C_LDX_ABS_Y() \ + GET_ABS_INDEX_ADDR_FOR_RD(yreg); \ + LDX_INST(0); + +# define C_LDY_ABS_X() \ + GET_ABS_INDEX_ADDR_FOR_RD(xreg); \ + LDY_INST(0); + +# define C_LDX_ABS() \ + GET_ABS_ADDR(); \ + LDX_INST(0); + +# define C_LDY_ABS() \ + GET_ABS_ADDR(); \ + LDY_INST(0); + +# define C_LDX_DLOC() \ + GET_DLOC_ADDR(); \ + LDX_INST(1); + +# define C_LDY_DLOC() \ + GET_DLOC_ADDR(); \ + LDY_INST(1); + +# define C_LDY_DLOC_X() \ + GET_DLOC_X_ADDR(); \ + LDY_INST(1); + +# define C_LDX_DLOC_Y() \ + GET_DLOC_Y_ADDR(); \ + LDX_INST(1); + +# define CP_INDEX_VAL(index_reg) \ + arg = 0x100 - arg + index_reg; \ + if((psr & 0x10) == 0) { \ + arg += 0xff00; \ + SET_NEG_ZERO16(arg & 0xffff); \ + SET_CARRY16(arg); \ + } else { \ + SET_NEG_ZERO8(arg & 0xff);\ + SET_CARRY8(arg); \ + } + +# define CP_INDEX_LOAD(index_reg, in_bank) \ + if((psr & 0x10) != 0) { \ + GET_MEMORY8(arg, arg); \ + } else { \ + GET_MEMORY16(arg, arg, in_bank);\ + } \ + CP_INDEX_VAL(index_reg) + +# define CPX_INST(in_bank) \ + CP_INDEX_LOAD(xreg, in_bank); + +# define CPY_INST(in_bank) \ + CP_INDEX_LOAD(yreg, in_bank); + +# define C_CPX_IMM() \ + INC_KPC_2; \ + if((psr & 0x10) == 0) { \ + GET_2BYTE_ARG; \ + CYCLES_PLUS_1; \ + INC_KPC_1; \ + } else { \ + GET_1BYTE_ARG; \ + } \ + CP_INDEX_VAL(xreg); + +# define C_CPY_IMM() \ + INC_KPC_2; \ + if((psr & 0x10) == 0) { \ + GET_2BYTE_ARG; \ + CYCLES_PLUS_1; \ + INC_KPC_1; \ + } else { \ + GET_1BYTE_ARG; \ + } \ + CP_INDEX_VAL(yreg); + +# define C_CPX_DLOC() \ + GET_DLOC_ADDR(); \ + CPX_INST(1); + +# define C_CPY_DLOC() \ + GET_DLOC_ADDR(); \ + CPY_INST(1); + +# define C_CPX_ABS() \ + GET_ABS_ADDR(); \ + CPX_INST(0); + +# define C_CPY_ABS() \ + GET_ABS_ADDR(); \ + CPY_INST(0); + +#endif + + + + +/* This is here to make sure all the macros expand to no instrs */ +/* if defs_instr_end_8 - start != 0, then something did expand */ + +#ifdef ASM +# ifdef ACC8 + .export defs_instr_end_8,data +defs_instr_end_8 .word 0 +# else + .export defs_instr_end_16,data +defs_instr_end_16 .word 0 +# endif +#endif diff --git a/src/dis.c b/src/dis.c new file mode 100644 index 0000000..352f135 --- /dev/null +++ b/src/dis.c @@ -0,0 +1,1342 @@ +/************************************************************************/ +/* KEGS: Apple //gs Emulator */ +/* Copyright 2002 by Kent Dickey */ +/* */ +/* This code is covered by the GNU GPL */ +/* */ +/* The KEGS web page is kegs.sourceforge.net */ +/* You may contact the author at: kadickey@alumni.princeton.edu */ +/************************************************************************/ + +const char rcsid_dis_c[] = "@(#)$KmKId: dis.c,v 1.90 2003-11-18 17:35:30-05 kentd Exp $"; + +#include +#include "defc.h" +#include + +#include "disas.h" + +#define LINE_SIZE 160 + +extern byte *g_memory_ptr; +extern byte *g_slow_memory_ptr; +extern byte *g_rom_fc_ff_ptr; +extern byte *g_rom_cards_ptr; +extern word32 g_mem_size_base, g_mem_size_exp; +extern int halt_sim; +extern int enter_debug; +extern int statereg; +extern word32 stop_run_at; +extern int stop_on_c03x; +extern int Verbose; +extern int Halt_on; +extern int g_rom_version; + +extern int g_testing_enabled; + +int g_num_breakpoints = 0; +word32 g_breakpts[MAX_BREAK_POINTS]; + +extern int g_irq_pending; + +extern Engine_reg engine; + +#define W_BUF_LEN 128 +char w_buff[W_BUF_LEN]; + +int g_stepping = 0; + +word32 list_kpc; +int hex_line_len; +word32 a1,a2,a3,a4; +int a1bank, a2bank, a3bank, a4bank; +char *line_ptr; +int mode,old_mode; +int got_num; + +int g_quit_sim_now = 0; + +int +get_num() +{ + int tmp1; + + a2 = 0; + got_num = 0; + while(1) { + if(mode == 0 && got_num != 0) { +/* + printf("In getnum, mode =0, setting a1,a3 = a2\n"); + printf("a2: %x\n", a2); +*/ + a3 = a2; + a3bank = a2bank; + a1 = a2; + a1bank = a2bank; + } + tmp1 = *line_ptr++ & 0x7f; + if(tmp1 >= '0' && tmp1 <= '9') { + a2 = (a2 << 4) + tmp1 - '0'; + got_num = 1; + continue; + } + if(tmp1 >= 'a' && tmp1 <= 'f') { + a2 = (a2 << 4) + tmp1 - 'a' + 10; + got_num = 1; + continue; + } + if(tmp1 == '/') { + a2bank = a2; + a2 = 0; + continue; + } + return tmp1; + } +} + +void +debugger_help() +{ + printf("KEGS Debugger help (courtesy Fredric Devernay\n"); + printf("General command syntax: [bank]/[address][command]\n"); + printf("e.g. 'e1/0010B' to set a breakpoint at the interrupt jump pt\n"); + printf("Enter all addresses using lower-case\n"); + printf("As with the IIgs monitor, you can omit the bank number after\n"); + printf("having set it: 'e1/0010B' followed by '14B' will set\n"); + printf("breakpoints at e1/0010 and e1/0014\n"); + printf("\n"); + printf("g Go\n"); + printf("[bank]/[addr]g Go from [bank]/[address]\n"); + printf("s Step one instruction\n"); + printf("[bank]/[addr]s Step one instr at [bank]/[address]\n"); + printf("[bank]/[addr]B Set breakpoint at [bank]/[address]\n"); + printf("B Show all breakpoints\n"); + printf("[bank]/[addr]D Delete breakpoint at [bank]/[address]\n"); + printf("[bank]/[addr1].[addr2] View memory\n"); + printf("[bank]/[addr]L Disassemble memory\n"); + + printf("P Dump the trace to 'pc_log_out'\n"); + printf("Z Dump SCC state\n"); + printf("I Dump IWM state\n"); + printf("[drive].[track]I Dump IWM state\n"); + printf("E Dump Ensoniq state\n"); + printf("[osc]E Dump oscillator [osc] state\n"); + printf("R Dump dtime array and events\n"); + printf("T Show toolbox log\n"); + printf("[bank]/[addr]T Dump tools using ptr [bank]/[addr]\n"); + printf(" as 'tool_set_info'\n"); + printf("[mode]V XOR verbose with 1=DISK, 2=IRQ,\n"); + printf(" 4=CLK,8=SHADOW,10=IWM,20=DOC,\n"); + printf(" 40=ABD,80=SCC, 100=TEST, 200=VIDEO\n"); + printf("[mode]H XOR halt_on with 1=SCAN_INT,\n"); + printf(" 2=IRQ, 4=SHADOW_REG, 8=C70D_WRITES\n"); + printf("r Reset\n"); + printf("[0/1]=m Changes m bit for l listings\n"); + printf("[0/1]=x Changes x bit for l listings\n"); + printf("[t]=z Stops at absolute time t (obsolete)\n"); + printf("S show_bankptr_bank0 & smartport errs\n"); + printf("P show_pmhz\n"); + printf("A show_a2_line_stuff show_adb_log\n"); + printf("Ctrl-e Dump registers\n"); + printf("[bank]/[addr1].[addr2]us[file] Save mem area to [file]\n"); + printf("[bank]/[addr1].[addr2]ul[file] Load mem area from [file]\n"); + printf("v Show video information\n"); + printf("q Exit Debugger (and KEGS)\n"); +} + +void +do_debug_intfc() +{ + char linebuf[LINE_SIZE]; + int slot_drive; + int track; + int osc; + int done; + int ret_val; + + hex_line_len = 0x10; + a1 = 0; a2 = 0; a3 = 0; a4 = 0; + a1bank = 0; a2bank = 0; a3bank = 0; a4bank = 0; + list_kpc = engine.kpc; + g_stepping = 0; + mode = 0; old_mode = 0; + done = 0; + stop_run_at = -1; + + x_auto_repeat_on(0); + + if(g_quit_sim_now) { + printf("Exiting immediately\n"); + return; + } + + printf("Type 'h' for help\n"); + + while(!done) { + printf("> "); fflush(stdout); + if(read_line(linebuf,LINE_SIZE-1) <= 0) { + done = 1; + continue; + } + line_ptr = linebuf; + +/* + printf("input line: :%s:\n", linebuf); + printf("mode: %d\n", mode); +*/ + mode = 0; + + while(*line_ptr != 0) { + ret_val = get_num(); +/* + printf("ret_val: %x, got_num= %d\n", ret_val, + got_num); +*/ + old_mode = mode; + mode = 0; + switch(ret_val) { + case 'h': + debugger_help(); + break; + case 'R': + show_dtime_array(); + show_all_events(); + break; + case 'I': + slot_drive = -1; + track = -1; + if(got_num) { + if(old_mode == '.') { + slot_drive = a1; + } + track = a2; + } + iwm_show_track(slot_drive, track); + iwm_show_stats(); + break; + case 'E': + osc = -1; + if(got_num) { + osc = a2; + } + doc_show_ensoniq_state(osc); + break; + case 'T': + if(got_num) { + show_toolset_tables(a2bank, a2); + } else { + show_toolbox_log(); + } + break; + case 'v': + video_show_debug_info(); + break; + case 'V': + printf("g_irq_pending: %d\n", g_irq_pending); + printf("Setting Verbose ^= %04x\n", a1); + Verbose ^= a1; + printf("Verbose is now: %04x\n", Verbose); + break; + case 'H': + printf("Setting Halt_on ^= %04x\n", a1); + Halt_on ^= a1; + printf("Halt_on is now: %04x\n", Halt_on); + break; + case 'r': + do_reset(); + list_kpc = engine.kpc; + break; + case 'm': + if(old_mode == '=') { + if(!a1) { + engine.psr &= ~0x20; + } else { + engine.psr |= 0x20; + } + if(engine.psr & 0x100) { + engine.psr |= 0x30; + } + } + break; + case 'x': + if(old_mode == '=') { + if(!a1) { + engine.psr &= ~0x10; + } else { + engine.psr |= 0x10; + } + if(engine.psr & 0x100) { + engine.psr |= 0x30; + } + } + break; + case 'z': + if(old_mode == '=') { + stop_run_at = a1; + printf("Calling add_event for t:%08x\n", + a1); + add_event_stop((double)a1); + printf("set stop_run_at = %x\n", a1); + } + break; + case 'l': case 'L': + do_debug_list(); + break; + case 'Z': + show_scc_log(); + show_scc_state(); + break; + case 'S': + show_bankptrs_bank0rdwr(); + smartport_error(); + break; + case 'C': + show_xcolor_array(); + break; + case 'P': + show_pc_log(); + break; + case 'M': + show_pmhz(); + break; + case 'A': + show_a2_line_stuff(); + show_adb_log(); + break; + case 's': + g_stepping = 1; + if(got_num) { + engine.kpc = (a2bank<<16) + (a2&0xffff); + } + mode = 's'; + list_kpc = engine.kpc; + break; + case 'B': + if(got_num) { + printf("got_num: %d, a2bank: %x, a2: %x\n", got_num, a2bank, a2); + set_bp((a2bank << 16) + a2); + } else { + show_bp(); + } + break; + case 'D': + if(got_num) { + printf("got_num: %d, a2bank: %x, a2: %x\n", got_num, a2bank, a2); + delete_bp((a2bank << 16) + a2); + } + break; + case 'g': + case 'G': + printf("Going..\n"); + g_stepping = 0; + if(got_num) { + engine.kpc = (a2bank<<16) + (a2&0xffff); + } + if(ret_val == 'G' && g_testing_enabled) { + do_gen_test(got_num, a2); + } else { + do_go(); + } + list_kpc = engine.kpc; + break; + case 'q': + case 'Q': + printf("Exiting debugger\n"); + return; + break; + case 'u': + printf("Unix commands\n"); + do_debug_unix(); + break; + case ':': case '.': + case '+': case '-': + case '=': case ',': + mode = ret_val; + printf("Setting mode = %x\n", mode); + break; + case ' ': case '\t': + if(!got_num) { + mode = old_mode; + break; + } + do_blank(); + break; + case 0x05: /* ctrl-e */ + show_regs(); + break; + case '\n': + *line_ptr = 0; + if(old_mode == 's') { + do_blank(); + break; + } + if(line_ptr == &linebuf[1]) { + a2 = a1 | (hex_line_len - 1); + show_hex_mem(a1bank,a1,a2bank,a2, -1); + a1 = a2 + 1; + } else { + if(got_num == 1 || mode == 's') { + do_blank(); + } + } + break; + case 'w': + read_line(w_buff, W_BUF_LEN); + break; + case 'X': + stop_on_c03x = !stop_on_c03x; + printf("stop_on_c03x set to %d\n",stop_on_c03x); + break; + default: + printf("\nUnrecognized command: %s\n",linebuf); + *line_ptr = 0; + break; + } + } + + } + printf("Console closed.\n"); +} + +word32 +dis_get_memory_ptr(word32 addr) +{ + word32 tmp1, tmp2, tmp3; + + tmp1 = get_memory_c(addr, 0); + tmp2 = get_memory_c(addr + 1, 0); + tmp3 = get_memory_c(addr + 2, 0); + + return (tmp3 << 16) + (tmp2 << 8) + tmp1; +} + +void +show_one_toolset(FILE *toolfile, int toolnum, word32 addr) +{ + word32 rout_addr; + int num_routs; + int i; + + num_routs = dis_get_memory_ptr(addr); + fprintf(toolfile, "Tool 0x%02x, table: 0x%06x, num_routs:%03x\n", + toolnum, addr, num_routs); + + for(i = 1; i < num_routs; i++) { + rout_addr = dis_get_memory_ptr(addr + 4*i); + fprintf(toolfile, "%06x = %02x%02x\n", rout_addr, i, toolnum); + } +} + +void +show_toolset_tables(word32 a2bank, word32 addr) +{ + FILE *toolfile; + word32 tool_addr; + int num_tools; + int i; + + addr = (a2bank << 16) + (addr & 0xffff); + + toolfile = fopen("tool_set_info", "wt"); + if(toolfile == 0) { + fprintf(stderr, "fopen of tool_set_info failed: %d\n", errno); + exit(2); + } + + num_tools = dis_get_memory_ptr(addr); + fprintf(toolfile, "There are 0x%02x tools using ptr at %06x\n", + num_tools, addr); + + for(i = 1; i < num_tools; i++) { + tool_addr = dis_get_memory_ptr(addr + 4*i); + show_one_toolset(toolfile, i, tool_addr); + } + + fclose(toolfile); +} + + +#ifndef TEST65 +void +do_gen_test(int got_num, int base_seed) +{ + /* dummy */ +} +#endif + +void +set_bp(word32 addr) +{ + int count; + + printf("About to set BP at %06x\n", addr); + count = g_num_breakpoints; + if(count >= MAX_BREAK_POINTS) { + printf("Too many (0x%02x) breakpoints set!\n", count); + return; + } + + g_breakpts[count] = addr; + g_num_breakpoints = count + 1; + fixup_brks(); +} + +void +show_bp() +{ + int i; + + printf("Showing breakpoints set\n"); + for(i = 0; i < g_num_breakpoints; i++) { + printf("bp:%02x: %06x\n", i, g_breakpts[i]); + } +} + +void +delete_bp(word32 addr) +{ + int count; + int hit; + int i; + + printf("About to delete BP at %06x\n", addr); + count = g_num_breakpoints; + + hit = -1; + for(i = 0; i < count; i++) { + if(g_breakpts[i] == addr) { + hit = i; + break; + } + } + + if(hit < 0) { + printf("Breakpoint not found!\n"); + } else { + printf("Deleting brkpoint #0x%02x\n", hit); + for(i = hit+1; i < count; i++) { + g_breakpts[i-1] = g_breakpts[i]; + } + g_num_breakpoints = count - 1; + setup_pageinfo(); + } + + show_bp(); +} + +void +do_blank() +{ + int tmp, i; + + switch(old_mode) { + case 's': + tmp = a2; + if(tmp == 0) tmp = 1; + enter_debug = 0; + for(i = 0; i < tmp; i++) { + g_stepping = 1; + do_step(); + if(enter_debug || halt_sim != 0) { + if(halt_sim != HALT_EVENT) { + break; + } + } + } + list_kpc = engine.kpc; + /* video_update_through_line(262); */ + break; + case ':': + set_memory_c(((a3bank << 16) + a3), a2, 0); + a3++; + mode = old_mode; + break; + case '.': + case 0: + xam_mem(-1); + break; + case ',': + xam_mem(16); + break; + case '+': + printf("%x\n", a1 + a2); + break; + case '-': + printf("%x\n", a1 - a2); + break; + default: + printf("Unknown mode at space: %d\n", old_mode); + break; + } +} + +void +do_go() +{ + clear_halt(); + + run_prog(); + show_regs(); +} + +void +do_step() +{ + int size; + int size_mem_imm, size_x_imm; + + clear_halt(); + + run_prog(); + + show_regs(); + size_mem_imm = 2; + if(engine.psr & 0x20) { + size_mem_imm = 1; + } + size_x_imm = 2; + if(engine.psr & 0x10) { + size_x_imm = 1; + } + size = do_dis(stdout, engine.kpc, size_mem_imm, size_x_imm, 0, 0); +} + +void +xam_mem(int count) +{ + show_hex_mem(a1bank, a1, a2bank, a2, count); + a1 = a2 + 1; +} + +void +show_hex_mem(int startbank, word32 start, int endbank, word32 end, int count) +{ + char ascii[MAXNUM_HEX_PER_LINE]; + word32 i; + int val, offset; + + if(count < 0) { + count = 16 - (start & 0xf); + } + + offset = 0; + ascii[0] = 0; + printf("Showing hex mem: bank: %x, start: %x, end: %x\n", + startbank, start, end); + for(i = start; i <= end; i++) { + if( (i==start) || (count == 16) ) { + printf("%04x:",i); + } + printf(" %02x", get_memory_c((startbank <<16) + i, 0)); + val = get_memory_c((startbank << 16) + i, 0) & 0x7f; + if(val < 32 || val >= 0x7f) { + val = '.'; + } + ascii[offset++] = val; + ascii[offset] = 0; + count--; + if(count <= 0) { + printf(" %s\n", ascii); + offset = 0; + ascii[0] = 0; + count = 16; + } + } + if(offset > 0) { + printf(" %s\n", ascii); + } +} + + +int +read_line(char *buf, int len) +{ + int space_left; + int ret; + + space_left = len; + + ret = 0; + buf[0] = 0; + while(space_left > 0) { + ret = read(0,buf,1); + if(ret <= 0) { + printf("read <= 0\n"); + return(len-space_left); + } + space_left -= ret; + if(buf[ret-1] == 0x0a) { + return(len-space_left); + } + buf = &buf[ret]; + } + return(len-space_left); +} + +void +do_debug_list() +{ + int i; + int size; + int size_mem_imm, size_x_imm; + + if(got_num) { + list_kpc = (a2bank << 16) + (a2 & 0xffff); + } + printf("%d=m %d=x %d=LCBANK\n", (engine.psr >> 5)&1, + (engine.psr >> 4) & 1, (statereg & 0x4) >> 2); + + size_mem_imm = 2; + if(engine.psr & 0x20) { + size_mem_imm = 1; + } + size_x_imm = 2; + if(engine.psr & 0x10) { + size_x_imm = 1; + } + for(i=0;i<20;i++) { + size = do_dis(stdout, list_kpc, size_mem_imm, + size_x_imm, 0, 0); + list_kpc += size; + } +} + +const char *g_kegs_rom_names[] = { "ROM", "ROM.01", "ROM.03", 0 }; + +const char *g_kegs_c1rom_names[] = { 0 }; +const char *g_kegs_c2rom_names[] = { 0 }; +const char *g_kegs_c3rom_names[] = { 0 }; +const char *g_kegs_c4rom_names[] = { 0 }; +const char *g_kegs_c5rom_names[] = { 0 }; +const char *g_kegs_c6rom_names[] = { "c600.rom", "controller.rom", "disk.rom", + "DISK.ROM", "diskII.prom", 0 }; +const char *g_kegs_c7rom_names[] = { 0 }; + +const char **g_kegs_rom_card_list[8] = { + 0, g_kegs_c1rom_names, + g_kegs_c2rom_names, g_kegs_c3rom_names, + g_kegs_c4rom_names, g_kegs_c5rom_names, + g_kegs_c6rom_names, g_kegs_c7rom_names }; + +byte g_rom_c600_rom01_diffs[256] = { + 0x00, 0x00, 0x00, 0x00, 0xc6, 0x00, 0xe2, 0x00, + 0xd0, 0x50, 0x0f, 0x77, 0x5b, 0xb9, 0xc3, 0xb1, + 0xb1, 0xf8, 0xcb, 0x4e, 0xb8, 0x60, 0xc7, 0x2e, + 0xfc, 0xe0, 0xbf, 0x1f, 0x66, 0x37, 0x4a, 0x70, + 0x55, 0x2c, 0x3c, 0xfc, 0xc2, 0xa5, 0x08, 0x29, + 0xac, 0x21, 0xcc, 0x09, 0x55, 0x03, 0x17, 0x35, + 0x4e, 0xe2, 0x0c, 0xe9, 0x3f, 0x9d, 0xc2, 0x06, + 0x18, 0x88, 0x0d, 0x58, 0x57, 0x6d, 0x83, 0x8c, + 0x22, 0xd3, 0x4f, 0x0a, 0xe5, 0xb7, 0x9f, 0x7d, + 0x2c, 0x3e, 0xae, 0x7f, 0x24, 0x78, 0xfd, 0xd0, + 0xb5, 0xd6, 0xe5, 0x26, 0x85, 0x3d, 0x8d, 0xc9, + 0x79, 0x0c, 0x75, 0xec, 0x98, 0xcc, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x39, 0x00, 0x35, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, + 0x6c, 0x44, 0xce, 0x4c, 0x01, 0x08, 0x00, 0x00 +}; + +void +load_roms() +{ + char name_buf[256]; + struct stat stat_buf; + const char **names_ptr; + int more_than_8mb; + int changed_rom; + int len; + int fd; + int ret; + int i; + + g_rom_version = 0; + setup_kegs_file(&name_buf[0], (int)sizeof(name_buf), 0, + &g_kegs_rom_names[0]); + fd = open(name_buf, O_RDONLY | O_BINARY); + if(fd < 0) { + printf("Open ROM file %s failed:%d, errno:%d\n", name_buf, fd, + errno); + my_exit(-3); + } + + ret = fstat(fd, &stat_buf); + if(ret != 0) { + fprintf(stderr, "fstat returned %d on fd %d, errno: %d\n", + ret, fd, errno); + my_exit(2); + } + + len = stat_buf.st_size; + if(len == 128*1024) { + g_rom_version = 1; + g_mem_size_base = 256*1024; + ret = read(fd, &g_rom_fc_ff_ptr[2*65536], len); + } else if(len == 256*1024) { + g_rom_version = 3; + g_mem_size_base = 1024*1024; + ret = read(fd, &g_rom_fc_ff_ptr[0], len); + } else { + fprintf(stderr, "ROM size %d not 128K or 256K\n", len); + my_exit(4); + } + + printf("Read: %d bytes of ROM\n", ret); + if(ret != len) { + printf("errno: %d\n", errno); + my_exit(-3); + } + close(fd); + + memset(&g_rom_cards_ptr[0], 0, 256*16); + + /* initialize c600 rom to be diffs from the real ROM, to build-in */ + /* Apple II compatibility without distributing ROMs */ + for(i = 0; i < 256; i++) { + g_rom_cards_ptr[0x600 + i] = g_rom_fc_ff_ptr[0x3c600 + i] ^ + g_rom_c600_rom01_diffs[i]; + } + if(g_rom_version >= 3) { + /* some patches */ + g_rom_cards_ptr[0x61b] ^= 0x40; + g_rom_cards_ptr[0x61c] ^= 0x33; + g_rom_cards_ptr[0x632] ^= 0xc0; + g_rom_cards_ptr[0x633] ^= 0x33; + } + + for(i = 1; i < 8; i++) { + names_ptr = g_kegs_rom_card_list[i]; + if(names_ptr == 0) { + continue; + } + if(*names_ptr == 0) { + continue; + } + + setup_kegs_file(&name_buf[0], (int)sizeof(name_buf), 1, + names_ptr); + + if(name_buf[0] != 0) { + fd = open(name_buf, O_RDONLY | O_BINARY); + if(fd < 0) { + printf("Open card ROM file %s failed: %d " + "err:%d\n", name_buf, fd, errno); + my_exit(-3); + } + + len = 256; + ret = read(fd, &g_rom_cards_ptr[i*0x100], len); + + if(ret != len) { + printf("errno: %d, expected %d, got %d\n", + errno, len, ret); + my_exit(-3); + } + close(fd); + } + } + + more_than_8mb = (g_mem_size_exp > 0x800000); + /* Only do the patch if users wants more than 8MB of expansion mem */ + + changed_rom = 0; + if(g_rom_version == 1) { + /* make some patches to ROM 01 */ +#if 0 + /* 1: Patch ROM selftest to not do speed test */ + printf("Patching out speed test failures from ROM 01\n"); + g_rom_fc_ff_ptr[0x3785a] = 0x18; + changed_rom = 1; +#endif + +#if 0 + /* 2: Patch ROM selftests not to do tests 2,4 */ + /* 0 = skip, 1 = do it, test 1 is bit 0 of LSByte */ + g_rom_fc_ff_ptr[0x371e9] = 0xf5; + g_rom_fc_ff_ptr[0x371ea] = 0xff; + changed_rom = 1; +#endif + + if(more_than_8mb) { + /* Geoff Weiss patch to use up to 14MB of RAM */ + g_rom_fc_ff_ptr[0x30302] = 0xdf; + g_rom_fc_ff_ptr[0x30314] = 0xdf; + g_rom_fc_ff_ptr[0x3031c] = 0x00; + changed_rom = 1; + } + + /* Patch ROM selftest to not do ROM cksum if any changes*/ + if(changed_rom) { + g_rom_fc_ff_ptr[0x37a06] = 0x18; + g_rom_fc_ff_ptr[0x37a07] = 0x18; + } + } else if(g_rom_version == 3) { + /* patch ROM 03 */ + printf("Patching ROM 03 smartport bug\n"); + /* 1: Patch Smartport code to fix a stupid bug */ + /* that causes it to write the IWM status reg into c036, */ + /* which is the system speed reg...it's "safe" since */ + /* IWM status reg bit 4 must be 0 (7MHz)..., otherwise */ + /* it might have turned on shadowing in all banks! */ + g_rom_fc_ff_ptr[0x357c9] = 0x00; + changed_rom = 1; + +#if 0 + /* patch ROM 03 to not to speed test */ + /* skip fast speed test */ + g_rom_fc_ff_ptr[0x36ad7] = 0x18; + g_rom_fc_ff_ptr[0x36ad8] = 0x18; + changed_rom = 1; +#endif + +#if 0 + /* skip slow speed test */ + g_rom_fc_ff_ptr[0x36ae7] = 0x18; + g_rom_fc_ff_ptr[0x36ae8] = 0x6b; + changed_rom = 1; +#endif + +#if 0 + /* 4: Patch ROM 03 selftests not to do tests 1-4 */ + g_rom_fc_ff_ptr[0x364a9] = 0xf0; + g_rom_fc_ff_ptr[0x364aa] = 0xff; + changed_rom = 1; +#endif + + /* ROM tests are in ff/6403-642x, where 6403 = addr of */ + /* test 1, etc. */ + + if(more_than_8mb) { + /* Geoff Weiss patch to use up to 14MB of RAM */ + g_rom_fc_ff_ptr[0x30b] = 0xdf; + g_rom_fc_ff_ptr[0x31d] = 0xdf; + g_rom_fc_ff_ptr[0x325] = 0x00; + changed_rom = 1; + } + + if(changed_rom) { + /* patch ROM 03 selftest to not do ROM cksum */ + g_rom_fc_ff_ptr[0x36cb0] = 0x18; + g_rom_fc_ff_ptr[0x36cb1] = 0x18; + } + + } +} + +void +do_debug_unix() +{ + char localbuf[LINE_SIZE]; + word32 offset, len; + int fd, ret; + int load, save; + int i; + + load = 0; save = 0; + switch(*line_ptr++) { + case 'l': case 'L': + printf("Loading.."); + load = 1; + break; + case 's': case 'S': + printf("Saving..."); + save = 1; + break; + default: + printf("Unknown unix command: %c\n", *(line_ptr-1)); + *line_ptr = 0; + return; + } + while(*line_ptr == ' ' || *line_ptr == '\t') { + line_ptr++; + } + i = 0; + while(i < LINE_SIZE) { + localbuf[i++] = *line_ptr++; + if(*line_ptr==' ' || *line_ptr=='\t' || *line_ptr == '\n') { + break; + } + } + localbuf[i] = 0; + + + printf("About to open: %s,len: %d\n", localbuf, (int)strlen(localbuf)); + if(load) { + fd = open(localbuf,O_RDONLY | O_BINARY); + } else { + fd = open(localbuf,O_WRONLY | O_CREAT | O_BINARY, 0x1b6); + } + if(fd < 0) { + printf("Open %s failed: %d\n", localbuf, fd); + printf("errno: %d\n", errno); + return; + } + if(load) { + offset = a1 & 0xffff; + len = 0x20000 - offset; + } else { + if(old_mode == '.') { + len = a2 - a1 + 1; + } else { + len = 0x100; + } + } + if(load) { + if(a1bank >= 0xe0 && a1bank < 0xe2) { + ret = read(fd,&g_slow_memory_ptr[((a1bank & 1)<<16)+a1],len); + } else { + ret = read(fd,&g_memory_ptr[(a1bank << 16) + a1],len); + } + } else { + if(a1bank >= 0xe0 && a1bank < 0xe2) { + ret = write(fd,&g_slow_memory_ptr[((a1bank & 1)<<16)+a1],len); + } else { + ret = write(fd,&g_memory_ptr[(a1bank << 16) + a1],len); + } + } + printf("Read/write: addr %06x for %04x bytes, ret: %x bytes\n", + (a1bank << 16) + a1, len, ret); + if(ret < 0) { + printf("errno: %d\n", errno); + } + a1 = a1 + ret; +} + +void +do_debug_load() +{ + printf("Sorry, can't load now\n"); +} + + +int +do_dis(FILE *outfile, word32 kpc, int accsize, int xsize, + int op_provided, word32 instr) +{ + char buffer[150]; + const char *out; + int args, type; + int opcode; + word32 val; + word32 oldkpc; + word32 dtype; + int signed_val; + + oldkpc = kpc; + if(op_provided) { + opcode = (instr >> 24) & 0xff; + } else { + opcode = (int)get_memory_c(kpc, 0) & 0xff; + } + + kpc++; + + dtype = disas_types[opcode]; + out = disas_opcodes[opcode]; + type = dtype & 0xff; + args = dtype >> 8; + + if(args > 3) { + if(args == 4) { + args = accsize; + } else if(args == 5) { + args = xsize; + } + } + + val = -1; + switch(args) { + case 0: + val = 0; + break; + case 1: + if(op_provided) { + val = instr & 0xff; + } else { + val = get_memory_c(kpc, 0); + } + break; + case 2: + if(op_provided) { + val = instr & 0xffff; + } else { + val = get_memory16_c(kpc, 0); + } + break; + case 3: + if(op_provided) { + val = instr & 0xffffff; + } else { + val = get_memory24_c(kpc, 0); + } + break; + default: + fprintf(stderr, "args out of rang: %d, opcode: %08x\n", + args, opcode); + break; + } + kpc += args; + + if(!op_provided) { + instr = (opcode << 24) | (val & 0xffffff); + } + + switch(type) { + case ABS: + if(args != 2) { + printf("arg # mismatch for opcode %x\n", opcode); + } + sprintf(buffer,"%s\t$%04x",out,val); + show_line(outfile, oldkpc,instr,args+1,buffer); + break; + case ABSX: + if(args != 2) { + printf("arg # mismatch for opcode %x\n", opcode); + } + sprintf(buffer,"%s\t$%04x,X",out,val); + show_line(outfile, oldkpc,instr,args+1,buffer); + break; + case ABSY: + if(args != 2) { + printf("arg # mismatch for opcode %x\n", opcode); + } + sprintf(buffer,"%s\t$%04x,Y",out,val); + show_line(outfile, oldkpc,instr,args+1,buffer); + break; + case ABSLONG: + if(args != 3) { + printf("arg # mismatch for opcode %x\n", opcode); + } + sprintf(buffer,"%s\t$%06x",out,val); + show_line(outfile, oldkpc,instr,args+1,buffer); + break; + case ABSIND: + if(args != 2) { + printf("arg # mismatch for opcode %x\n", opcode); + } + sprintf(buffer,"%s\t($%04x)",out,val); + show_line(outfile, oldkpc,instr,args+1,buffer); + break; + case ABSXIND: + if(args != 2) { + printf("arg # mismatch for opcode %x\n", opcode); + } + sprintf(buffer,"%s\t($%04x,X)",out,val); + show_line(outfile, oldkpc,instr,args+1,buffer); + break; + case IMPLY: + if(args != 0) { + printf("arg # mismatch for opcode %x\n", opcode); + } + sprintf(buffer,"%s",out); + show_line(outfile, oldkpc,instr,args+1,buffer); + break; + case ACCUM: + if(args != 0) { + printf("arg # mismatch for opcode %x\n", opcode); + } + sprintf(buffer,"%s",out); + show_line(outfile, oldkpc,instr,args+1,buffer); + break; + case IMMED: + if(args == 1) { + sprintf(buffer,"%s\t#$%02x",out,val); + } else if(args == 2) { + sprintf(buffer,"%s\t#$%04x",out,val); + } else { + printf("arg # mismatch for opcode %x\n", opcode); + } + show_line(outfile, oldkpc,instr,args+1,buffer); + break; + case JUST8: + if(args != 1) { + printf("arg # mismatch for opcode %x\n", opcode); + } + sprintf(buffer,"%s\t$%02x",out,val); + show_line(outfile, oldkpc,instr,args+1,buffer); + break; + case DLOC: + if(args != 1) { + printf("arg # mismatch for opcode %x\n", opcode); + } + sprintf(buffer,"%s\t$%02x",out,val); + show_line(outfile, oldkpc,instr,args+1,buffer); + break; + case DLOCX: + if(args != 1) { + printf("arg # mismatch for opcode %x\n", opcode); + } + sprintf(buffer,"%s\t$%02x,X",out,val); + show_line(outfile, oldkpc,instr,args+1,buffer); + break; + case DLOCY: + if(args != 1) { + printf("arg # mismatch for opcode %x\n", opcode); + } + sprintf(buffer,"%s\t$%02x,Y",out,val); + show_line(outfile, oldkpc,instr,args+1,buffer); + break; + case LONG: + if(args != 3) { + printf("arg # mismatch for opcode %x\n", opcode); + } + sprintf(buffer,"%s\t$%06x",out,val); + show_line(outfile, oldkpc,instr,args+1,buffer); + break; + case LONGX: + if(args != 3) { + printf("arg # mismatch for opcode %x\n", opcode); + } + sprintf(buffer,"%s\t$%06x,X",out,val); + show_line(outfile, oldkpc,instr,args+1,buffer); + break; + case DLOCIND: + if(args != 1) { + printf("arg # mismatch for opcode %x\n", opcode); + } + sprintf(buffer,"%s\t($%02x)",out,val); + show_line(outfile, oldkpc,instr,args+1,buffer); + break; + case DLOCINDY: + if(args != 1) { + printf("arg # mismatch for opcode %x\n", opcode); + } + sprintf(buffer,"%s\t($%02x),Y",out,val); + show_line(outfile, oldkpc,instr,args+1,buffer); + break; + case DLOCXIND: + if(args != 1) { + printf("arg # mismatch for opcode %x\n", opcode); + } + sprintf(buffer,"%s\t($%02x,X)",out,val); + show_line(outfile, oldkpc,instr,args+1,buffer); + break; + case DLOCBRAK: + if(args != 1) { + printf("arg # mismatch for opcode %x\n", opcode); + } + sprintf(buffer,"%s\t[$%02x]",out,val); + show_line(outfile, oldkpc,instr,args+1,buffer); + break; + case DLOCBRAKY: + if(args != 1) { + printf("arg # mismatch for opcode %x\n", opcode); + } + sprintf(buffer,"%s\t[$%02x],y",out,val); + show_line(outfile, oldkpc,instr,args+1,buffer); + break; + case DISP8: + if(args != 1) { + printf("arg # mismatch for opcode %x\n", opcode); + } + signed_val = (signed char)val; + sprintf(buffer,"%s\t$%04x",out, + (word32)(kpc+(signed_val)) & 0xffff); + show_line(outfile, oldkpc,instr,args+1,buffer); + break; + case DISP8S: + if(args != 1) { + printf("arg # mismatch for opcode %x\n", opcode); + } + sprintf(buffer,"%s\t$%02x,S",out,(word32)(byte)(val)); + show_line(outfile, oldkpc,instr,args+1,buffer); + break; + case DISP8SINDY: + if(args != 1) { + printf("arg # mismatch for opcode %x\n", opcode); + } + sprintf(buffer,"%s\t($%02x,S),Y",out,(word32)(byte)(val)); + show_line(outfile, oldkpc,instr,args+1,buffer); + break; + case DISP16: + if(args != 2) { + printf("arg # mismatch for opcode %x\n", opcode); + } + sprintf(buffer,"%s\t$%04x", out, + (word32)(kpc+(signed)(word16)(val)) & 0xffff); + show_line(outfile, oldkpc,instr,args+1,buffer); + break; + case MVPMVN: + if(args != 2) { + printf("arg # mismatch for opcode %x\n", opcode); + } + sprintf(buffer,"%s\t$%02x,$%02x",out,val&0xff,val>>8); + show_line(outfile, oldkpc,instr,args+1,buffer); + break; + case SEPVAL: + case REPVAL: + if(args != 1) { + printf("arg # mismatch for opcode %x\n", opcode); + } + sprintf(buffer,"%s\t#$%02x",out,val); + show_line(outfile, oldkpc,instr,args+1,buffer); + break; + default: + printf("argument type: %d unexpected\n", type); + break; + } + + return(args+1); +} + +void +show_line(FILE *outfile, word32 kaddr, word32 operand, int size, + char *string) +{ + int i; + int opcode; + + fprintf(outfile, "%02x/%04x: ", kaddr >> 16, kaddr & 0xffff); + opcode = (operand >> 24) & 0xff; + fprintf(outfile,"%02x ", opcode); + + for(i=1;i> 8; + } + for(;i<5;i++) { + fprintf(outfile, " "); + } + fprintf(outfile,"%s\n", string); +} + +void +halt_printf(const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); + + set_halt(1); +} + +void +halt2_printf(const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); + + set_halt(2); +} + diff --git a/src/disas.h b/src/disas.h new file mode 100644 index 0000000..f83cd90 --- /dev/null +++ b/src/disas.h @@ -0,0 +1,210 @@ +/************************************************************************/ +/* KEGS: Apple //gs Emulator */ +/* Copyright 2002 by Kent Dickey */ +/* */ +/* This code is covered by the GNU GPL */ +/* */ +/* The KEGS web page is kegs.sourceforge.net */ +/* You may contact the author at: kadickey@alumni.princeton.edu */ +/************************************************************************/ + +const char rcsid_disas_h[] = "@(#)$KmKId: disas.h,v 1.10 2002-11-19 03:10:38-05 kadickey Exp $"; + +enum { + ABS = 1, + ABSX, + ABSY, + ABSLONG, + ABSIND, + ABSXIND, + IMPLY, + ACCUM, + IMMED, + JUST8, + DLOC, + DLOCX, + DLOCY, + LONG, + LONGX, + DLOCIND, + DLOCINDY, + DLOCXIND, + DLOCBRAK, + DLOCBRAKY, + DISP8, + DISP8S, + DISP8SINDY, + DISP16, + MVPMVN, + REPVAL, + SEPVAL +}; + + +const char * const disas_opcodes[256] = { + "BRK", "ORA", "COP", "ORA", "TSB", "ORA", "ASL", "ORA", /* 00-07 */ + "PHP", "ORA", "ASL", "PHD", "TSB", "ORA", "ASL", "ORA", /* 08-0f */ + "BPL", "ORA", "ORA", "ORA", "TRB", "ORA", "ASL", "ORA", /* 10-17 */ + "CLC", "ORA", "INC", "TCS", "TRB", "ORA", "ASL", "ORA", /* 18-1f */ + "JSR", "AND", "JSL", "AND", "BIT", "AND", "ROL", "AND", /* 20-27 */ + "PLP", "AND", "ROL", "PLD", "BIT", "AND", "ROL", "AND", /* 28-2f */ + "BMI", "AND", "AND", "AND", "BIT", "AND", "ROL", "AND", /* 30-37 */ + "SEC", "AND", "DEC", "TSC", "BIT", "AND", "ROL", "AND", /* 38-3f */ + "RTI", "EOR", "WDM", "EOR", "MVP", "EOR", "LSR", "EOR", /* 40-47 */ + "PHA", "EOR", "LSR", "PHK", "JMP", "EOR", "LSR", "EOR", /* 48-4f */ + "BVC", "EOR", "EOR", "EOR", "MVN", "EOR", "LSR", "EOR", /* 50-57 */ + "CLI", "EOR", "PHY", "TCD", "JMP", "EOR", "LSR", "EOR", /* 58-5f */ + "RTS", "ADC", "PER", "ADC", "STZ", "ADC", "ROR", "ADC", /* 60-67 */ + "PLA", "ADC", "ROR", "RTL", "JMP", "ADC", "ROR", "ADC", /* 68-6f */ + "BVS", "ADC", "ADC", "ADC", "STZ", "ADC", "ROR", "ADC", /* 70-77 */ + "SEI", "ADC", "PLY", "TDC", "JMP", "ADC", "ROR", "ADC", /* 78-7f */ + "BRA", "STA", "BRL", "STA", "STY", "STA", "STX", "STA", /* 80-87 */ + "DEY", "BIT", "TXA", "PHB", "STY", "STA", "STX", "STA", /* 88-8f */ + "BCC", "STA", "STA", "STA", "STY", "STA", "STX", "STA", /* 90-97 */ + "TYA", "STA", "TXS", "TXY", "STZ", "STA", "STZ", "STA", /* 98-9f */ + "LDY", "LDA", "LDX", "LDA", "LDY", "LDA", "LDX", "LDA", /* a0-a7 */ + "TAY", "LDA", "TAX", "PLB", "LDY", "LDA", "LDX", "LDA", /* a8-af */ + "BCS", "LDA", "LDA", "LDA", "LDY", "LDA", "LDX", "LDA", /* b0-b7 */ + "CLV", "LDA", "TSX", "TYX", "LDY", "LDA", "LDX", "LDA", /* b8-bf */ + "CPY", "CMP", "REP", "CMP", "CPY", "CMP", "DEC", "CMP", /* c0-c7 */ + "INY", "CMP", "DEX", "WAI", "CPY", "CMP", "DEC", "CMP", /* c8-cf */ + "BNE", "CMP", "CMP", "CMP", "PEI", "CMP", "DEC", "CMP", /* d0-d7 */ + "CLD", "CMP", "PHX", "STP", "JML", "CMP", "DEC", "CMP", /* d8-df */ + "CPX", "SBC", "SEP", "SBC", "CPX", "SBC", "INC", "SBC", /* e0-e7 */ + "INX", "SBC", "NOP", "XBA", "CPX", "SBC", "INC", "SBC", /* e8-ef */ + "BEQ", "SBC", "SBC", "SBC", "PEA", "SBC", "INC", "SBC", /* f0-f7 */ + "SED", "SBC", "PLX", "XCE", "JSR", "SBC", "INC", "SBC", /* f8-ff */ +}; + + +const word32 disas_types[256] = { + JUST8+0x100, DLOCXIND+0x100, /* 00-01 */ + JUST8+0x100, DISP8S+0x100, /* 02-03 */ + DLOC+0x100, DLOC+0x100, /* 04-05 */ + DLOC+0x100, DLOCBRAK+0x100, /* 06-07 */ + IMPLY+0x000, IMMED+0x400, /* 08-9 */ + ACCUM+0x000, IMPLY+0x000, /* 0a-b */ + ABS+0x200, ABS+0x200, /* c-d */ + ABS+0x200, LONG+0x300, /* e-f */ + DISP8+0x100, DLOCINDY+0x100, /* 10-11 */ + DLOCIND+0x100, DISP8SINDY+0x100, /* 12-13 */ + DLOC+0x100, DLOCX+0x100, /* 14-15 */ + DLOCX+0x100, DLOCBRAKY+0x100, /* 16-17 */ + IMPLY+0x000, ABSY+0x200, /* 18-19 */ + ACCUM+0x000, IMPLY+0x000, /* 1a-1b */ + ABS+0x200, ABSX+0x200, /* 1c-1d */ + ABSX+0x200, LONGX+0x300, /* 1e-1f */ + ABS+0x200, DLOCXIND+0x100, /* 20-21 */ + ABSLONG+0x300, DISP8S+0x100, /* 22-23 */ + DLOC+0x100, DLOC+0x100, /* 24-25 */ + DLOC+0x100, DLOCBRAK+0x100, /* 26-27 */ + IMPLY+0x000, IMMED+0x400, /* 28-29 */ + ACCUM+0x000, IMPLY+0x000, /* 2a-2b */ + ABS+0x200, ABS+0x200, /* 2c-2d */ + ABS+0x200, LONG+0x300, /* 2e-2f */ + DISP8+0x100, DLOCINDY+0x100, /* 30-31 */ + DLOCIND+0x100, DISP8SINDY+0x100, /* 32-33 */ + DLOCX+0x100, DLOCX+0x100, /* 34-35 */ + DLOCX+0x100, DLOCBRAKY+0x100, /* 36-37 */ + IMPLY+0x000, ABSY+0x200, /* 38-39 */ + ACCUM+0x000, IMPLY+0x000, /* 3a-3b */ + ABSX+0x200, ABSX+0x200, /* 3c-3d */ + ABSX+0x200, LONGX+0x300, /* 3e-3f */ + IMPLY+0x000, DLOCXIND+0x100, /* 40-41 */ + JUST8+0x100, DISP8S+0x100, /* 42-43 */ + MVPMVN+0x200, DLOC+0x100, /* 44-45 */ + DLOC+0x100, DLOCBRAK+0x100, /* 46-47 */ + IMPLY+0x000, IMMED+0x400, /* 48-49 */ + ACCUM+0x000, IMPLY+0x000, /* 4a-4b */ + ABS+0x200, ABS+0x200, /* 4c-4d */ + ABS+0x200, LONG+0x300, /* 4e-4f */ + DISP8+0x100, DLOCINDY+0x100, /* 50-51 */ + DLOCIND+0x100, DISP8SINDY+0x100, /* 52-53 */ + MVPMVN+0x200, DLOCX+0x100, /* 54-55 */ + DLOCX+0x100, DLOCBRAKY+0x100, /* 56-57 */ + IMPLY+0x000, ABSY+0x200, /* 58-59 */ + IMPLY+0x000, IMPLY+0x000, /* 5a-5b */ + LONG+0x300, ABSX+0x200, /* 5c-5d */ + ABSX+0x200, LONGX+0x300, /* 5e-5f */ + IMPLY+0x000, DLOCXIND+0x100, /* 60-61 */ + DISP16+0x200, DISP8S+0x100, /* 62-63 */ + DLOC+0x100, DLOC+0x100, /* 64-65 */ + DLOC+0x100, DLOCBRAK+0x100, /* 66-67 */ + IMPLY+0x000, IMMED+0x400, /* 68-69 */ + ACCUM+0x000, IMPLY+0x000, /* 6a-6b */ + ABSIND+0x200, ABS+0x200, /* 6c-6d */ + ABS+0x200, LONG+0x300, /* 6e-6f */ + DISP8+0x100, DLOCINDY+0x100, /* 70-71 */ + DLOCIND+0x100, DISP8SINDY+0x100, /* 72-73 */ + DLOCX+0x100, DLOCX+0x100, /* 74-75 */ + DLOCX+0x100, DLOCBRAKY+0x100, /* 76-77 */ + IMPLY+0x000, ABSY+0x200, /* 78-79 */ + IMPLY+0x000, IMPLY+0x000, /* 7a-7b */ + ABSXIND+0x200, ABSX+0x200, /* 7c-7d */ + ABSX+0x200, LONGX+0x300, /* 7e-7f */ + DISP8+0x100, DLOCXIND+0x100, /* 80-81 */ + DISP16+0x200, DISP8S+0x100, /* 82-83 */ + DLOC+0x100, DLOC+0x100, /* 84-85 */ + DLOC+0x100, DLOCBRAK+0x100, /* 86-87 */ + IMPLY+0x000, IMMED+0x400, /* 88-89 */ + IMPLY+0x000, IMPLY+0x000, /* 8a-8b */ + ABS+0x200, ABS+0x200, /* 8c-8d */ + ABS+0x200, LONG+0x300, /* 8e-8f */ + DISP8+0x100, DLOCINDY+0x100, /* 90-91 */ + DLOCIND+0x100, DISP8SINDY+0x100, /* 92-93 */ + DLOCX+0x100, DLOCX+0x100, /* 94-95 */ + DLOCY+0x100, DLOCBRAKY+0x100, /* 96-97 */ + IMPLY+0x000, ABSY+0x200, /* 98-99 */ + IMPLY+0x000, IMPLY+0x000, /* 9a-9b */ + ABS+0x200, ABSX+0x200, /* 9c-9d */ + ABSX+0x200, LONGX+0x300, /* 9e-9f */ + IMMED+0x500, DLOCXIND+0x100, /* a0-a1 */ + IMMED+0x500, DISP8S+0x100, /* a2-a3 */ + DLOC+0x100, DLOC+0x100, /* a4-a5 */ + DLOC+0x100, DLOCBRAK+0x100, /* a6-a7 */ + IMPLY+0x000, IMMED+0x400, /* a8-a9 */ + IMPLY+0x000, IMPLY+0x000, /* aa-ab */ + ABS+0x200, ABS+0x200, /* ac-ad */ + ABS+0x200, LONG+0x300, /* ae-af */ + DISP8+0x100, DLOCINDY+0x100, /* b0-b1 */ + DLOCIND+0x100, DISP8SINDY+0x100, /* b2-b3 */ + DLOCX+0x100, DLOCX+0x100, /* b4-b5 */ + DLOCY+0x100, DLOCBRAKY+0x100, /* b6-b7 */ + IMPLY+0x000, ABSY+0x200, /* b8-b9 */ + IMPLY+0x000, IMPLY+0x000, /* ba-bb */ + ABSX+0x200, ABSX+0x200, /* bc-bd */ + ABSY+0x200, LONGX+0x300, /* be-bf */ + IMMED+0x500, DLOCXIND+0x100, /* c0-c1 */ + REPVAL+0x100, DISP8S+0x100, /* c2-c3 */ + DLOC+0x100, DLOC+0x100, /* c4-c5 */ + DLOC+0x100, DLOCBRAK+0x100, /* c6-c7 */ + IMPLY+0x000, IMMED+0x400, /* c8-c9 */ + IMPLY+0x000, IMPLY+0x000, /* ca-cb */ + ABS+0x200, ABS+0x200, /* cc-cd */ + ABS+0x200, LONG+0x300, /* ce-cf */ + DISP8+0x100, DLOCINDY+0x100, /* d0-d1 */ + DLOCIND+0x100, DISP8SINDY+0x100, /* d2-d3 */ + DLOC+0x100, DLOCX+0x100, /* d4-d5 */ + DLOCX+0x100, DLOCBRAKY+0x100, /* d6-d7 */ + IMPLY+0x000, ABSY+0x200, /* d8-d9 */ + IMPLY+0x000, IMPLY+0x000, /* da-db */ + ABSIND+0x200, ABSX+0x200, /* dc-dd */ + ABSX+0x200, LONGX+0x300, /* de-df */ + IMMED+0x500, DLOCXIND+0x100, /* e0-e1 */ + SEPVAL+0x100, DISP8S+0x100, /* e2-e3 */ + DLOC+0x100, DLOC+0x100, /* e4-e5 */ + DLOC+0x100, DLOCBRAK+0x100, /* e6-e7 */ + IMPLY+0x000, IMMED+0x400, /* e8-e9 */ + IMPLY+0x000, IMPLY+0x000, /* ea-eb */ + ABS+0x200, ABS+0x200, /* ec-ed */ + ABS+0x200, LONG+0x300, /* ee-ef */ + DISP8+0x100, DLOCINDY+0x100, /* f0-f1 */ + DLOCIND+0x100, DISP8SINDY+0x100, /* f2-f3 */ + IMMED+0x200, DLOCX+0x100, /* f4-f5 */ + DLOCX+0x100, DLOCBRAKY+0x100, /* f6-f7 */ + IMPLY+0x000, ABSY+0x200, /* f8-f9 */ + IMPLY+0x000, IMPLY+0x000, /* fa-fb */ + ABSXIND+0x200, ABSX+0x200, /* fc-fd */ + ABSX+0x200, LONGX+0x300, /* fe-ff */ +}; + diff --git a/src/engine_c.c b/src/engine_c.c new file mode 100644 index 0000000..cbeedec --- /dev/null +++ b/src/engine_c.c @@ -0,0 +1,1007 @@ +/************************************************************************/ +/* KEGS: Apple //gs Emulator */ +/* Copyright 2002 by Kent Dickey */ +/* */ +/* This code is covered by the GNU GPL */ +/* */ +/* The KEGS web page is kegs.sourceforge.net */ +/* You may contact the author at: kadickey@alumni.princeton.edu */ +/************************************************************************/ + +const char rcsid_engine_c_c[] = "@(#)$KmKId: engine_c.c,v 1.51 2004-01-10 15:50:15-05 kentd Exp $"; + +#include "defc.h" +#include "protos_engine_c.h" + +#if 0 +# define LOG_PC +/* define FCYCS_PTR_FCYCLES_ROUND_SLOW to get accurate 1MHz write to slow mem*/ +/* this might help joystick emulation in some Apple //gs games like */ +/* Madness */ +# define FCYCS_PTR_FCYCLES_ROUND_SLOW FCYCLES_ROUND; *fcycs_ptr = fcycles; +#endif + +#ifndef FCYCS_PTR_FCYCLES_ROUND_SLOW +# define FCYCS_PTR_FCYCLES_ROUND_SLOW +#endif + +extern int halt_sim; +extern int g_code_red; +extern int g_ignore_halts; +extern double g_fcycles_stop; +extern double g_last_vbl_dcycs; +extern int g_wait_pending; +extern int g_irq_pending; +extern int g_testing; +extern int g_num_brk; +extern int g_num_cop; +extern byte *g_slow_memory_ptr; +extern byte *g_memory_ptr; +extern byte *g_rom_fc_ff_ptr; +extern byte *g_rom_cards_ptr; +extern byte *g_dummy_memory1_ptr; + +extern int g_num_breakpoints; +extern word32 g_breakpts[]; + +extern Pc_log *log_pc_ptr; +extern Pc_log *log_pc_start_ptr; +extern Pc_log *log_pc_end_ptr; + +int size_tab[] = { +#include "size_c.h" +}; + +int bogus[] = { + 0, +#include "op_routs.h" +}; + +#define FINISH(arg1, arg2) g_ret1 = arg1; g_ret2 = arg2; goto finish; +#define INC_KPC_1 kpc = (kpc & 0xff0000) + ((kpc + 1) & 0xffff); +#define INC_KPC_2 kpc = (kpc & 0xff0000) + ((kpc + 2) & 0xffff); +#define INC_KPC_3 kpc = (kpc & 0xff0000) + ((kpc + 3) & 0xffff); +#define INC_KPC_4 kpc = (kpc & 0xff0000) + ((kpc + 4) & 0xffff); + +#define CYCLES_PLUS_1 fcycles += fplus_1; +#define CYCLES_PLUS_2 fcycles += fplus_2; +#define CYCLES_PLUS_3 fcycles += fplus_3; +#define CYCLES_PLUS_4 fcycles += (fplus_1 + fplus_3); +#define CYCLES_PLUS_5 fcycles += (fplus_2 + fplus_3); +#define CYCLES_MINUS_1 fcycles -= fplus_1; +#define CYCLES_MINUS_2 fcycles -= fplus_2; + +#define CYCLES_FINISH fcycles = g_fcycles_stop + fplus_1; + +#define FCYCLES_ROUND fcycles = (int)(fcycles + fplus_x_m1); + +#define LOG_PC_MACRO() \ + tmp_pc_ptr = log_pc_ptr++; \ + tmp_pc_ptr->dbank_kpc = (dbank << 24) + kpc; \ + tmp_pc_ptr->instr = (opcode << 24) + arg_ptr[1] + \ + (arg_ptr[2] << 8) + (arg_ptr[3] << 16); \ + tmp_pc_ptr->psr_acc = ((psr & ~(0x82)) << 16) + acc + \ + (neg << 23) + ((!zero) << 17); \ + tmp_pc_ptr->xreg_yreg = (xreg << 16) + yreg; \ + tmp_pc_ptr->stack_direct = (stack << 16) + direct; \ + tmp_pc_ptr->dcycs = fcycles + g_last_vbl_dcycs - fplus_2; \ + if(log_pc_ptr >= log_pc_end_ptr) { \ + /*halt2_printf("log_pc oflow %f\n", tmp_pc_ptr->dcycs);*/ \ + log_pc_ptr = log_pc_start_ptr; \ + } + +#define GET_1BYTE_ARG arg = arg_ptr[1]; +#define GET_2BYTE_ARG arg = arg_ptr[1] + (arg_ptr[2] << 8); +#define GET_3BYTE_ARG arg = arg_ptr[1] + (arg_ptr[2] << 8) + (arg_ptr[3]<<16); + +/* HACK HACK HACK */ +#define UPDATE_PSR(dummy, old_psr) \ + if(psr & 0x100) { \ + psr |= 0x30; \ + stack = 0x100 + (stack & 0xff); \ + } \ + if((old_psr ^ psr) & 0x10) { \ + if(psr & 0x10) { \ + xreg = xreg & 0xff; \ + yreg = yreg & 0xff; \ + } \ + } \ + if(((psr & 0x4) == 0) && g_irq_pending) { \ + FINISH(RET_IRQ, 0); \ + } \ + if((old_psr ^ psr) & 0x20) { \ + goto recalc_accsize; \ + } + +extern Page_info page_info_rd_wr[]; +extern word32 slow_mem_changed[]; + +#define GET_MEMORY8(addr,dest) \ + addr_latch = (addr); \ + CYCLES_PLUS_1; \ + stat = GET_PAGE_INFO_RD(((addr) >> 8) & 0xffff); \ + wstat = PTR2WORD(stat) & 0xff; \ + ptr = stat - wstat + ((addr) & 0xff); \ + if(wstat & (1 << (31 - BANK_IO_BIT))) { \ + fcycles_tmp1 = fcycles; \ + dest = get_memory8_io_stub((addr), stat, \ + &fcycles_tmp1, fplus_x_m1); \ + fcycles = fcycles_tmp1; \ + } else { \ + dest = *ptr; \ + } + +#define GET_MEMORY(addr,dest) GET_MEMORY8(addr, dest) + +#define GET_MEMORY16(addr, dest, in_bank) \ + save_addr = addr; \ + stat = GET_PAGE_INFO_RD(((addr) >> 8) & 0xffff); \ + wstat = PTR2WORD(stat) & 0xff; \ + ptr = stat - wstat + ((addr) & 0xff); \ + if((wstat & (1 << (31 - BANK_IO_BIT))) || (((addr) & 0xff) == 0xff)) { \ + fcycles_tmp1 = fcycles; \ + dest = get_memory16_pieces_stub((addr), stat, \ + &fcycles_tmp1, fplus_ptr, in_bank); \ + fcycles = fcycles_tmp1; \ + } else { \ + CYCLES_PLUS_2; \ + dest = ptr[0] + (ptr[1] << 8); \ + } \ + addr_latch = save_addr; + +#define GET_MEMORY24(addr, dest, in_bank) \ + save_addr = addr; \ + stat = GET_PAGE_INFO_RD(((addr) >> 8) & 0xffff); \ + wstat = PTR2WORD(stat) & 0xff; \ + ptr = stat - wstat + ((addr) & 0xff); \ + if((wstat & (1 << (31 - BANK_IO_BIT))) || (((addr) & 0xfe) == 0xfe)) { \ + fcycles_tmp1 = fcycles; \ + dest = get_memory24_pieces_stub((addr), stat, \ + &fcycles_tmp1, fplus_ptr, in_bank); \ + fcycles = fcycles_tmp1; \ + } else { \ + CYCLES_PLUS_3; \ + dest = ptr[0] + (ptr[1] << 8) + (ptr[2] << 16); \ + } \ + addr_latch = save_addr; + +#define GET_MEMORY_DIRECT_PAGE16(addr, dest) \ + save_addr = addr; \ + if(psr & 0x100) { \ + if((direct & 0xff) == 0) { \ + save_addr = (save_addr & 0xff) + direct; \ + } \ + } \ + if((psr & 0x100) && (((addr) & 0xff) == 0xff)) { \ + GET_MEMORY8(save_addr, getmem_tmp); \ + save_addr = (save_addr + 1) & 0xffff; \ + if((direct & 0xff) == 0) { \ + save_addr = (save_addr & 0xff) + direct; \ + } \ + GET_MEMORY8(save_addr, dest); \ + dest = (dest << 8) + getmem_tmp; \ + } else { \ + GET_MEMORY16(save_addr, dest, 1); \ + } + + +#define PUSH8(arg) \ + SET_MEMORY8(stack, arg); \ + stack--; \ + if(psr & 0x100) { \ + stack = 0x100 | (stack & 0xff); \ + } \ + stack = stack & 0xffff; + +#define PUSH16(arg) \ + if((stack & 0xfe) == 0) { \ + /* stack will cross page! */ \ + PUSH8((arg) >> 8); \ + PUSH8(arg); \ + } else { \ + stack -= 2; \ + stack = stack & 0xffff; \ + SET_MEMORY16(stack + 1, arg, 1); \ + } + +#define PUSH16_UNSAFE(arg) \ + save_addr = (stack - 1) & 0xffff; \ + stack -= 2; \ + if(psr & 0x100) { \ + stack = 0x100 | (stack & 0xff); \ + } \ + stack = stack & 0xffff; \ + SET_MEMORY16(save_addr, arg, 1); + +#define PUSH24_UNSAFE(arg) \ + save_addr = (stack - 2) & 0xffff; \ + stack -= 3; \ + if(psr & 0x100) { \ + stack = 0x100 | (stack & 0xff); \ + } \ + stack = stack & 0xffff; \ + SET_MEMORY24(save_addr, arg, 1); + +#define PULL8(dest) \ + stack++; \ + if(psr & 0x100) { \ + stack = 0x100 | (stack & 0xff); \ + } \ + stack = stack & 0xffff; \ + GET_MEMORY8(stack, dest); + +#define PULL16(dest) \ + if((stack & 0xfe) == 0xfe) { /* page cross */ \ + PULL8(dest); \ + PULL8(pull_tmp); \ + dest = (pull_tmp << 8) + dest; \ + } else { \ + GET_MEMORY16(stack + 1, dest, 1); \ + stack = (stack + 2) & 0xffff; \ + if(psr & 0x100) { \ + stack = 0x100 | (stack & 0xff); \ + } \ + } + +#define PULL16_UNSAFE(dest) \ + stack = (stack + 1) & 0xffff; \ + if(psr & 0x100) { \ + stack = 0x100 | (stack & 0xff); \ + } \ + GET_MEMORY16(stack, dest, 1); \ + stack = (stack + 1) & 0xffff; \ + if(psr & 0x100) { \ + stack = 0x100 | (stack & 0xff); \ + } + +#define PULL24(dest) \ + if((stack & 0xfc) == 0xfc) { /* page cross */ \ + PULL8(dest); \ + PULL8(pull_tmp); \ + pull_tmp = (pull_tmp << 8) + dest; \ + PULL8(dest); \ + dest = (dest << 16) + pull_tmp; \ + } else { \ + GET_MEMORY24(stack + 1, dest, 1); \ + stack = (stack + 3) & 0xffff; \ + if(psr & 0x100) { \ + stack = 0x100 | (stack & 0xff); \ + } \ + } + +#define SET_MEMORY8(addr, val) \ + CYCLES_PLUS_1; \ + stat = GET_PAGE_INFO_WR(((addr) >> 8) & 0xffff); \ + wstat = PTR2WORD(stat) & 0xff; \ + ptr = stat - wstat + ((addr) & 0xff); \ + if(wstat) { \ + fcycles_tmp1 = fcycles; \ + set_memory8_io_stub((addr), val, stat, &fcycles_tmp1, \ + fplus_x_m1); \ + fcycles = fcycles_tmp1; \ + } else { \ + *ptr = val; \ + } + + + + + +#define SET_MEMORY16(addr, val, in_bank) \ + stat = GET_PAGE_INFO_WR(((addr) >> 8) & 0xffff); \ + wstat = PTR2WORD(stat) & 0xff; \ + ptr = stat - wstat + ((addr) & 0xff); \ + if((wstat) || (((addr) & 0xff) == 0xff)) { \ + fcycles_tmp1 = fcycles; \ + set_memory16_pieces_stub((addr), (val), \ + &fcycles_tmp1, fplus_ptr, in_bank); \ + fcycles = fcycles_tmp1; \ + } else { \ + CYCLES_PLUS_2; \ + ptr[0] = (val); \ + ptr[1] = (val) >> 8; \ + } + +#define SET_MEMORY24(addr, val, in_bank) \ + stat = GET_PAGE_INFO_WR(((addr) >> 8) & 0xffff); \ + wstat = PTR2WORD(stat) & 0xff; \ + ptr = stat - wstat + ((addr) & 0xff); \ + if((wstat) || (((addr) & 0xfe) == 0xfe)) { \ + fcycles_tmp1 = fcycles; \ + set_memory24_pieces_stub((addr), (val), \ + &fcycles_tmp1, fplus_ptr, in_bank); \ + fcycles = fcycles_tmp1; \ + } else { \ + CYCLES_PLUS_3; \ + ptr[0] = (val); \ + ptr[1] = (val) >> 8; \ + ptr[2] = (val) >> 16; \ + } + +void +check_breakpoints(word32 addr) +{ + int count; + int i; + + count = g_num_breakpoints; + for(i = 0; i < count; i++) { + if(g_breakpts[i] == addr) { + halt2_printf("Hit breakpoint at %06x\n", addr); + } + } +} + +word32 +get_memory8_io_stub(word32 addr, byte *stat, double *fcycs_ptr, + double fplus_x_m1) +{ + double fcycles; + word32 wstat; + byte *ptr; + + wstat = PTR2WORD(stat) & 0xff; + if(wstat & BANK_BREAK) { + check_breakpoints(addr); + } + fcycles = *fcycs_ptr; + if(wstat & BANK_IO2_TMP) { + FCYCLES_ROUND; + *fcycs_ptr = fcycles; + return get_memory_io((addr), fcycs_ptr); + } else { + ptr = stat - wstat + (addr & 0xff); + return *ptr; + } +} + +word32 +get_memory16_pieces_stub(word32 addr, byte *stat, double *fcycs_ptr, + Fplus *fplus_ptr, int in_bank) +{ + byte *ptr; + double fcycles, fcycles_tmp1; + double fplus_1; + double fplus_x_m1; + word32 addrp1; + word32 wstat; + word32 addr_latch; + word32 ret; + word32 tmp1; + + fcycles = *fcycs_ptr; + fplus_1 = fplus_ptr->plus_1; + fplus_x_m1 = fplus_ptr->plus_x_minus_1; + GET_MEMORY8(addr, tmp1); + addrp1 = addr + 1; + if(in_bank) { + addrp1 = (addr & 0xff0000) + (addrp1 & 0xffff); + } + GET_MEMORY8(addrp1, ret); + *fcycs_ptr = fcycles; + return (ret << 8) + (tmp1); +} + +word32 +get_memory24_pieces_stub(word32 addr, byte *stat, double *fcycs_ptr, + Fplus *fplus_ptr, int in_bank) +{ + byte *ptr; + double fcycles, fcycles_tmp1; + double fplus_1; + double fplus_x_m1; + word32 addrp1, addrp2; + word32 wstat; + word32 addr_latch; + word32 ret; + word32 tmp1; + word32 tmp2; + + fcycles = *fcycs_ptr; + fplus_1 = fplus_ptr->plus_1; + fplus_x_m1 = fplus_ptr->plus_x_minus_1; + GET_MEMORY8(addr, tmp1); + addrp1 = addr + 1; + if(in_bank) { + addrp1 = (addr & 0xff0000) + (addrp1 & 0xffff); + } + GET_MEMORY8(addrp1, tmp2); + addrp2 = addr + 2; + if(in_bank) { + addrp2 = (addr & 0xff0000) + (addrp2 & 0xffff); + } + GET_MEMORY8(addrp2, ret); + *fcycs_ptr = fcycles; + return (ret << 16) + (tmp2 << 8) + tmp1; +} + +void +set_memory8_io_stub(word32 addr, word32 val, byte *stat, double *fcycs_ptr, + double fplus_x_m1) +{ + double fcycles; + word32 setmem_tmp1; + word32 tmp1, tmp2; + byte *ptr; + word32 wstat; + + wstat = PTR2WORD(stat) & 0xff; + if(wstat & (1 << (31 - BANK_BREAK_BIT))) { + check_breakpoints(addr); + } + ptr = stat - wstat + ((addr) & 0xff); \ + fcycles = *fcycs_ptr; + if(wstat & (1 << (31 - BANK_IO2_BIT))) { + FCYCLES_ROUND; + *fcycs_ptr = fcycles; + set_memory_io((addr), val, fcycs_ptr); + } else if(wstat & (1 << (31 - BANK_SHADOW_BIT))) { + FCYCS_PTR_FCYCLES_ROUND_SLOW; + tmp1 = (addr & 0xffff); + setmem_tmp1 = g_slow_memory_ptr[tmp1]; + *ptr = val; + if(setmem_tmp1 != ((val) & 0xff)) { + g_slow_memory_ptr[tmp1] = val; + slow_mem_changed[tmp1 >> CHANGE_SHIFT] |= + (1 << (31-((tmp1 >> SHIFT_PER_CHANGE) & 0x1f))); + } + } else if(wstat & (1 << (31 - BANK_SHADOW2_BIT))) { + FCYCS_PTR_FCYCLES_ROUND_SLOW; + tmp2 = (addr & 0xffff); + tmp1 = 0x10000 + tmp2; + setmem_tmp1 = g_slow_memory_ptr[tmp1]; + *ptr = val; + if(setmem_tmp1 != ((val) & 0xff)) { + g_slow_memory_ptr[tmp1] = val; + slow_mem_changed[tmp2 >>CHANGE_SHIFT] |= + (1 <<(31-((tmp2 >> SHIFT_PER_CHANGE) & 0x1f))); + } + } else { + /* breakpoint only */ + *ptr = val; + } +} + +void +set_memory16_pieces_stub(word32 addr, word32 val, double *fcycs_ptr, + Fplus *fplus_ptr, int in_bank) +{ + byte *ptr; + byte *stat; + double fcycles, fcycles_tmp1; + double fplus_1; + double fplus_x_m1; + word32 addrp1; + word32 wstat; + + fcycles = *fcycs_ptr; + fplus_1 = fplus_ptr->plus_1; + fplus_x_m1 = fplus_ptr->plus_x_minus_1; + SET_MEMORY8(addr, val); + addrp1 = addr + 1; + if(in_bank) { + addrp1 = (addr & 0xff0000) + (addrp1 & 0xffff); + } + SET_MEMORY8(addrp1, val >> 8); + + *fcycs_ptr = fcycles; +} + +void +set_memory24_pieces_stub(word32 addr, word32 val, double *fcycs_ptr, + Fplus *fplus_ptr, int in_bank) +{ + byte *ptr; + byte *stat; + double fcycles, fcycles_tmp1; + double fplus_1; + double fplus_x_m1; + word32 addrp1, addrp2; + word32 wstat; + + fcycles = *fcycs_ptr; + fplus_1 = fplus_ptr->plus_1; + fplus_x_m1 = fplus_ptr->plus_x_minus_1; + SET_MEMORY8(addr, val); + addrp1 = addr + 1; + if(in_bank) { + addrp1 = (addr & 0xff0000) + (addrp1 & 0xffff); + } + SET_MEMORY8(addrp1, val >> 8); + addrp2 = addr + 2; + if(in_bank) { + addrp2 = (addr & 0xff0000) + (addrp2 & 0xffff); + } + SET_MEMORY8(addrp2, val >> 16); + + *fcycs_ptr = fcycles; +} + + +word32 +get_memory_c(word32 addr, int cycs) +{ + byte *stat; + byte *ptr; + double fcycles, fcycles_tmp1; + double fplus_1; + double fplus_x_m1; + word32 addr_latch; + word32 wstat; + word32 ret; + + fcycles = 0; + fplus_1 = 0; + fplus_x_m1 = 0; + GET_MEMORY8(addr, ret); + return ret; +} +#if 0 + stat = page_info[(addr>>8) & 0xffff].rd; + ptr = (byte *)((stat & 0xffffff00) | (addr & 0xff)); + if(stat & (1 << (31 -BANK_IO_BIT))) { + return get_memory_io(addr, &in_fcycles); + } + return *ptr; +#endif + +word32 +get_memory16_c(word32 addr, int cycs) +{ + double fcycs; + + fcycs = 0; + return get_memory_c(addr, fcycs) + + (get_memory_c(addr+1, fcycs) << 8); +} + +word32 +get_memory24_c(word32 addr, int cycs) +{ + double fcycs; + + fcycs = 0; + return get_memory_c(addr, fcycs) + + (get_memory_c(addr+1, fcycs) << 8) + + (get_memory_c(addr+2, fcycs) << 16); +} + +void +set_memory_c(word32 addr, word32 val, int cycs) +{ + byte *stat; + byte *ptr; + double fcycles, fcycles_tmp1; + double fplus_1; + double fplus_x_m1; + word32 wstat; + + fcycles = 0; + fplus_1 = 0; + fplus_x_m1 = 0; + SET_MEMORY8(addr, val); +} +void +set_memory16_c(word32 addr, word32 val, int cycs) +{ + set_memory_c(addr, val, 0); + set_memory_c(addr + 1, val >> 8, 0); +} + +void +set_memory24_c(word32 addr, word32 val, int cycs) +{ + set_memory_c(addr, val, 0); + set_memory_c(addr + 1, val >> 8, 0); + set_memory_c(addr + 2, val >> 16, 0); +} + +word32 +do_adc_sbc8(word32 in1, word32 in2, word32 psr, int sub) +{ + word32 sum, carry, overflow; + word32 zero; + int decimal; + + overflow = 0; + decimal = psr & 8; + if(sub) { + in2 = (in2 ^ 0xff); + } + if(!decimal) { + sum = (in1 & 0xff) + in2 + (psr & 1); + overflow = ((sum ^ in2) >> 1) & 0x40; + } else { + /* decimal */ + sum = (in1 & 0xf) + (in2 & 0xf) + (psr & 1); + if(sub) { + if(sum < 0x10) { + sum = (sum - 0x6) & 0xf; + } + } else { + if(sum >= 0xa) { + sum = (sum - 0xa) | 0x10; + } + } + + sum = (in1 & 0xf0) + (in2 & 0xf0) + sum; + overflow = ((sum >> 2) ^ (sum >> 1)) & 0x40; + if(sub) { + if(sum < 0x100) { + sum = (sum + 0xa0) & 0xff; + } + } else { + if(sum >= 0xa0) { + sum += 0x60; + } + } + } + + zero = ((sum & 0xff) == 0); + carry = (sum >= 0x100); + if((in1 ^ in2) & 0x80) { + overflow = 0; + } + + psr = psr & (~0xc3); + psr = psr + (sum & 0x80) + overflow + (zero << 1) + carry; + + return (psr << 16) + (sum & 0xff); +} + +word32 +do_adc_sbc16(word32 in1, word32 in2, word32 psr, int sub) +{ + word32 sum, carry, overflow; + word32 tmp1, tmp2; + word32 zero; + int decimal; + + overflow = 0; + decimal = psr & 8; + if(!decimal) { + if(sub) { + in2 = (in2 ^ 0xffff); + } + sum = in1 + in2 + (psr & 1); + overflow = ((sum ^ in2) >> 9) & 0x40; + } else { + /* decimal */ + if(sub) { + tmp1 = do_adc_sbc8(in1 & 0xff, in2 & 0xff, psr, sub); + psr = (tmp1 >> 16); + tmp2 = do_adc_sbc8((in1 >> 8) & 0xff, + (in2 >> 8) & 0xff, psr, sub); + in2 = (in2 ^ 0xfffff); + } else { + tmp1 = do_adc_sbc8(in1 & 0xff, in2 & 0xff, psr, sub); + psr = (tmp1 >> 16); + tmp2 = do_adc_sbc8((in1 >> 8) & 0xff, + (in2 >> 8) &0xff, psr, sub); + } + sum = ((tmp2 & 0xff) << 8) + (tmp1 & 0xff) + + (((tmp2 >> 16) & 1) << 16); + overflow = (tmp2 >> 16) & 0x40; + } + + zero = ((sum & 0xffff) == 0); + carry = (sum >= 0x10000); + if((in1 ^ in2) & 0x8000) { + overflow = 0; + } + + psr = psr & (~0xc3); + psr = psr + ((sum & 0x8000) >> 8) + overflow + (zero << 1) + carry; + + return (psr << 16) + (sum & 0xffff); +} + +int g_ret1; +int g_ret2; + + +void +fixed_memory_ptrs_init() +{ + /* set g_slow_memory_ptr, g_rom_fc_ff_ptr, g_dummy_memory1_ptr, */ + /* and rom_cards_ptr */ + + g_slow_memory_ptr = memalloc_align(128*1024, 0); + g_dummy_memory1_ptr = memalloc_align(256, 1024); + g_rom_fc_ff_ptr = memalloc_align(256*1024, 1024); + g_rom_cards_ptr = memalloc_align(16*256, 1024); + +#if 0 + printf("g_memory_ptr: %08x, dummy_mem: %08x, slow_mem_ptr: %08x\n", + (word32)g_memory_ptr, (word32)g_dummy_memory1_ptr, + (word32)g_slow_memory_ptr); + printf("g_rom_fc_ff_ptr: %08x, g_rom_cards_ptr: %08x\n", + (word32)g_rom_fc_ff_ptr, (word32)g_rom_cards_ptr); + printf("page_info_rd = %08x, page_info_wr end = %08x\n", + (word32)&(page_info_rd_wr[0]), + (word32)&(page_info_rd_wr[PAGE_INFO_PAD_SIZE+0x1ffff].rd_wr)); +#endif +} + +word32 +get_itimer() +{ +#if defined(__i386) && defined(__GNUC__) + /* Here's my bad ia32 asm code to do rdtsc */ + /* Linux source uses: */ + /* asm volatile("rdtsc" : "=a"(ret) : : "edx"); */ + /* asm volatile("rdtsc" : "=%eax"(ret) : : "%edx"); */ + + /* GCC bug report 2001-03/msg00786.html used: */ + /*register word64 dtmp; */ + /*asm volatile ("rdtsc" : "=A" (dtmp)); */ + /*return (word32)dtmp; */ + + register word32 ret; + + asm volatile ("rdtsc;movl %%eax,%0" : "=r"(ret) : : "%eax","%edx"); + + return ret; +#else +# if defined(__POWERPC__) && defined(__GNUC__) + register word32 ret; + + asm volatile ("mftb %0" : "=r"(ret)); + return ret; +# else + return 0; +# endif +#endif +} + +void +set_halt_act(int val) +{ + if(val == 1 && g_ignore_halts) { + g_code_red++; + } else { + halt_sim |= val; + g_fcycles_stop = (double)0.0; + } +} + +void +clr_halt_act() +{ + halt_sim = 0; +} + +word32 +get_remaining_operands(word32 addr, word32 opcode, word32 psr, Fplus *fplus_ptr) +{ + byte *stat; + byte *ptr; + double fcycles, fcycles_tmp1; + double fplus_1, fplus_2, fplus_3; + double fplus_x_m1; + word32 addr_latch; + word32 wstat; + word32 save_addr; + word32 arg; + word32 addrp1; + int size; + + fcycles = 0; + fplus_1 = 0; + fplus_2 = 0; + fplus_3 = 0; + fplus_x_m1 = 0; + + size = size_tab[opcode]; + + addrp1 = (addr & 0xff0000) + ((addr + 1) & 0xffff); + switch(size) { + case 0: + arg = 0; /* no args */ + break; + case 1: + GET_MEMORY8(addrp1, arg); + break; /* 1 arg, already done */ + case 2: + GET_MEMORY16(addrp1, arg, 1); + break; + case 3: + GET_MEMORY24(addrp1, arg, 1); + break; + case 4: + if(psr & 0x20) { + GET_MEMORY8(addrp1, arg); + } else { + GET_MEMORY16(addrp1, arg, 1); + } + break; + case 5: + if(psr & 0x10) { + GET_MEMORY8(addrp1, arg); + } else { + GET_MEMORY16(addrp1, arg, 1); + } + break; + default: + printf("Unknown size: %d\n", size); + arg = 0; + exit(-2); + } + + return arg; +} + +#define FETCH_OPCODE \ + addr = kpc; \ + CYCLES_PLUS_2; \ + stat = GET_PAGE_INFO_RD(((addr) >> 8) & 0xffff); \ + wstat = PTR2WORD(stat) & 0xff; \ + ptr = stat - wstat + ((addr) & 0xff); \ + arg_ptr = ptr; \ + opcode = *ptr; \ + if((wstat & (1 << (31-BANK_IO_BIT))) || ((addr & 0xff) > 0xfc)) {\ + if(wstat & BANK_BREAK) { \ + check_breakpoints(addr); \ + } \ + if((addr & 0xfffff0) == 0x00c700) { \ + if(addr == 0xc700) { \ + FINISH(RET_C700, 0); \ + } else if(addr == 0xc70a) { \ + FINISH(RET_C70A, 0); \ + } else if(addr == 0xc70d) { \ + FINISH(RET_C70D, 0); \ + } \ + } \ + if(wstat & (1 << (31 - BANK_IO2_BIT))) { \ + FCYCLES_ROUND; \ + fcycles_tmp1 = fcycles; \ + opcode = get_memory_io((addr), &fcycles_tmp1); \ + fcycles = fcycles_tmp1; \ + } else { \ + opcode = *ptr; \ + } \ + arg = get_remaining_operands(addr, opcode, psr, fplus_ptr);\ + arg_ptr = (byte *)&tmp_bytes; \ + arg_ptr[1] = arg; \ + arg_ptr[2] = arg >> 8; \ + arg_ptr[3] = arg >> 16; \ + } + +int +enter_engine(Engine_reg *engine_ptr) +{ + register byte *ptr; + byte *arg_ptr; + Pc_log *tmp_pc_ptr; + byte *stat; + word32 wstat; + word32 arg; + register word32 kpc; + register word32 acc; + register word32 xreg; + register word32 yreg; + word32 stack; + word32 dbank; + register word32 direct; + register word32 psr; + register word32 zero; + register word32 neg; + word32 getmem_tmp; + word32 save_addr; + word32 pull_tmp; + word32 tmp_bytes; + double fcycles; + Fplus *fplus_ptr; + double fplus_1; + double fplus_2; + double fplus_3; + double fplus_x_m1; + double fcycles_tmp1; + + word32 opcode; + register word32 addr; + word32 addr_latch; + word32 tmp1, tmp2; + + + tmp_pc_ptr = 0; + + kpc = engine_ptr->kpc; + acc = engine_ptr->acc; + xreg = engine_ptr->xreg; + yreg = engine_ptr->yreg; + stack = engine_ptr->stack; + dbank = engine_ptr->dbank; + direct = engine_ptr->direct; + psr = engine_ptr->psr; + fcycles = engine_ptr->fcycles; + fplus_ptr = engine_ptr->fplus_ptr; + zero = !(psr & 2); + neg = (psr >> 7) & 1; + + fplus_1 = fplus_ptr->plus_1; + fplus_2 = fplus_ptr->plus_2; + fplus_3 = fplus_ptr->plus_3; + fplus_x_m1 = fplus_ptr->plus_x_minus_1; + + g_ret1 = 0; + g_ret2 = 0; + +recalc_accsize: + if(psr & 0x20) { + while(fcycles <= g_fcycles_stop) { +#if 0 + if((neg & ~1) || (psr & (~0x1ff))) { + halt_printf("psr = %04x\n", psr); + } +#endif + + FETCH_OPCODE; + +#ifdef LOG_PC + LOG_PC_MACRO(); +#endif + + switch(opcode) { + default: + halt_printf("acc8 unk op: %02x\n", opcode); + arg = 9 +#define ACC8 +#include "defs_instr.h" + * 2; + break; +#include "8inst_c.h" + break; + } + } + } else { + while(fcycles <= g_fcycles_stop) { + FETCH_OPCODE; +#ifdef LOG_PC + LOG_PC_MACRO(); +#endif + + switch(opcode) { + default: + halt_printf("acc16 unk op: %02x\n", opcode); + arg = 9 +#undef ACC8 +#include "defs_instr.h" + * 2; + break; +#include "16inst_c.h" + break; + } + } + } + +finish: + engine_ptr->kpc = kpc; + engine_ptr->acc = acc; + engine_ptr->xreg = xreg; + engine_ptr->yreg = yreg; + engine_ptr->stack = stack; + engine_ptr->dbank = dbank; + engine_ptr->direct = direct; + engine_ptr->fcycles = fcycles; + + psr = psr & (~0x82); + psr |= (neg << 7); + psr |= ((!zero) << 1); + + engine_ptr->psr = psr; + + return (g_ret1 << 28) + g_ret2; +} + + +int g_engine_c_mode = 1; + +int defs_instr_start_8 = 0; +int defs_instr_end_8 = 0; +int defs_instr_start_16 = 0; +int defs_instr_end_16 = 0; +int op_routs_start = 0; +int op_routs_end = 0; + + diff --git a/src/engine_s.s b/src/engine_s.s new file mode 100644 index 0000000..c98ff9f --- /dev/null +++ b/src/engine_s.s @@ -0,0 +1,2491 @@ +/************************************************************************/ +/* KEGS: Apple //gs Emulator */ +/* Copyright 2002 by Kent Dickey */ +/* */ +/* This code is covered by the GNU GPL */ +/* */ +/* The KEGS web page is kegs.sourceforge.net */ +/* You may contact the author at: kadickey@alumni.princeton.edu */ +/************************************************************************/ + + .data + .export rcsid_engine_s_s,data +rcsid_engine_s_s + .stringz "@(#)$KmKId: engine_s.s,v 1.155 2003-11-25 22:06:48-05 kentd Exp $" + + .code + + .level 1.1 + +#define INCLUDE_RCSID_S +#include "defs.h" +#undef INCLUDE_RCSID_S + +#define ASM + + +#if 0 +# define LOG_PC +# define ACCURATE_SLOW_MEM +# define DEBUG_TOOLBOX +#endif + + +/* +#define COUNT_GET_CALLS +*/ + +#if 0 +# define CHECK_SIZE_CONSISTENCY +#endif + + +#define STACK_ENGINE_SIZE 512 + +#define STACK_SAVE_CMP_INDEX_REG -64 +#define STACK_GET_MEM_B0_DIRECT_SAVELINK -68 +#define STACK_SAVE_ARG0 -72 +#define STACK_SAVE_INSTR -76 +#define STACK_SRC_BANK -80 +#define STACK_INST_TAB_PTR_DONT_USE_THIS -84 +#if 0 +#define STACK_BP_ARG0_SAVE -88 +#define STACK_BP_ARG1_SAVE -92 +#define STACK_BP_ARG2_SAVE -96 +#define STACK_BP_ARG3_SAVE -100 +#define STACK_BP_RP_SAVE -104 +#define STACK_BP_SCRATCH4_SAVE -108 +#endif + +#define STACK_GET_MEMORY_SAVE_LINK -112 +#define STACK_SET_MEMORY_SAVE_LINK -116 + +#define STACK_MEMORY16_SAVE1 -120 +#define STACK_MEMORY16_SAVE2 -124 +#define STACK_MEMORY16_SAVE3 -128 + +#define STACK_SAVE_CYCLES_WORD2 -132 /* Cycles = dword */ +#define STACK_SAVE_CYCLES -136 + +#define STACK_SET_MEMORY24_SAVE1 -140 +#define STACK_SET_MEMORY24_SAVE2 -144 +#define STACK_SET_MEMORY24_SAVE3 -148 + +#define STACK_GET_MEMORY24_SAVE1 -152 +#define STACK_GET_MEMORY24_SAVE2 -156 +#define STACK_GET_MEMORY24_SAVE3 -160 + +/* #define STACK_SAVE_INIT_CYCLES -164 */ +#define STACK_SAVE_DECIMAL16_A -168 +#define STACK_SAVE_DECIMAL16_B -172 +#define STACK_SAVE_INSTR_TMP1 -176 + +#define STACK_SAVE_DISPATCH_LINK -180 +#define STACK_SAVE_OP_LINK -184 +#define STACK_GET_MEMORY16_ADDR_LATCH -188 +#define STACK_GET_MEMORY24_ADDR_LATCH -192 + +#define STACK_GET_MEM_B0_DIRECT_ARG0 -200 +#define STACK_GET_MEM_B0_DIRECT_RET0 -204 +#define STACK_SAVE_PUSH16_LINK -208 +#define STACK_SAVE_PUSH16_ARG1 -212 + +#define STACK_SAVE_PULL16_LINK -216 +#define STACK_SAVE_PULL16_RET0 -220 +#define STACK_SAVE_PULL24_LINK -224 +#define STACK_SAVE_PULL24_RET0 -228 + +#define STACK_SAVE_COP_ARG0 -232 +#define STACK_SAVE_TMP_INST0 -236 +#define STACK_SAVE_TMP_INST -240 +#define STACK_SAVE_TMP_INST1 -244 +#define STACK_SAVE_DISP_PIECES_LINK -248 +#define STACK_SAVE_DISPATCH_SCRATCH1 -252 + +#if 0 +#define STACK_BP_SCRATCH2_SAVE -256 +#define STACK_BP_SCRATCH3_SAVE -260 +#endif + + + +#define CYCLES_PLUS_1 fadd,dbl fr_plus_1,fcycles,fcycles +#define CYCLES_PLUS_2 fadd,dbl fr_plus_2,fcycles,fcycles +#define CYCLES_PLUS_3 fadd,dbl fr_plus_3,fcycles,fcycles +#define CYCLES_PLUS_5 fadd,dbl fr_plus_3,fcycles,fcycles ! \ + fadd,dbl fr_plus_2,fcycles,fcycles + +#define CYCLES_MINUS_1 fsub,dbl fcycles,fr_plus_1,fcycles +#define CYCLES_MINUS_2 fsub,dbl fcycles,fr_plus_2,fcycles + +#define CYCLES_FINISH fadd,dbl fcycles_stop,fr_plus_1,fcycles + +#define FCYCLES_ROUND_1 fadd,dbl fcycles,fr_plus_x_m1,ftmp1 +#define FCYCLES_ROUND_2 fcnvfxt,dbl,dbl ftmp1,ftmp1 +#define FCYCLES_ROUND_3 fcnvxf,dbl,dbl ftmp1,fcycles + +/* HACK: INC_KPC* and DEC_KPC2 should avoid overflow into kbank! */ +#define INC_KPC_1 addi 1,kpc,kpc +#define INC_KPC_2 addi 2,kpc,kpc +#define INC_KPC_3 addi 3,kpc,kpc +#define INC_KPC_4 addi 4,kpc,kpc +#define DEC_KPC2 addi -2,kpc,kpc + +#define get_mem_b0_8 get_memory_asm +#define get_mem_b0_16 get_memory16_asm +#define get_mem_b0_24 get_memory24_asm + +#define get_mem_long_8 get_memory_asm +#define get_mem_long_16 get_memory16_asm +#define get_mem_long_24 get_memory24_asm + +#define set_mem_long_8 set_memory_asm +#define set_mem_long_16 set_memory16_asm + +#define set_mem_b0_8 set_memory_asm +#define set_mem_b0_16 set_memory16_asm +#define set_mem_b0_24 set_memory24_asm + + .code + .import halt_sim,data + .import g_fcycles_stop,data + .import g_irq_pending,data + .import g_wait_pending,data + .import g_rom_version,data + .import g_num_brk,data + .import g_num_cop,data + .import g_testing,data + + .import log_pc,code + .import toolbox_debug_c,code + + .import get_memory_io,code + + .import set_memory_io,code + .import set_memory16_pieces,code + .import set_memory24_pieces,code + +#define INCLUDE_RCSID_S +#include "op_routs.h" +#undef INCLUDE_RCSID_S + + .import do_break,code + .import do_cop,code + .import page_info_rd_wr,data + .import get_memory_calls,data + .import slow_mem_changed,data + .import g_cur_dcycs,data + .import g_last_vbl_dcycs,data + .import g_slow_memory_ptr,data + .import g_memory_ptr,data + .import g_dummy_memory1_ptr,data + .import g_rom_fc_ff_ptr,data + .import g_rom_cards_ptr,data + + .export fixed_memory_ptrs_init,code +fixed_memory_ptrs_init + LDC(slow_memory,arg0) + LDC(g_slow_memory_ptr,arg1) + stw arg0,(arg1) + + LDC(dummy_memory1,arg0) + LDC(g_dummy_memory1_ptr,arg1) + stw arg0,(arg1) + + LDC(rom_fc_ff,arg0) + LDC(g_rom_fc_ff_ptr,arg1) + stw arg0,(arg1) + + LDC(rom_cards,arg0) + LDC(g_rom_cards_ptr,arg1) + stw arg0,(arg1) + + bv 0(link) + nop + + .export get_itimer,code +get_itimer + bv 0(link) + mfctl %cr16,ret0 + + .export enter_asm,data +enter_asm + stwm page_info_ptr,STACK_ENGINE_SIZE(sp) + stw link,-STACK_ENGINE_SIZE+4(sp) + ldo -STACK_ENGINE_SIZE+16(sp),scratch2 + stw addr_latch,-STACK_ENGINE_SIZE+8(sp) + fstds,ma fcycles,8(scratch2) + fstds,ma fr_plus_1,8(scratch2) + fcpy,dbl 0,fcycles + fstds,ma fr_plus_2,8(scratch2) + fstds,ma fr_plus_3,8(scratch2) + fcpy,dbl 0,fr_plus_1 + fstds,ma fr_plus_x_m1,8(scratch2) + fstds,ma fcycles_stop,8(scratch2) + fcpy,dbl 0,fr_plus_2 + fstds,ma fcycles_last_dcycs,8(scratch2) + ldil l%g_cur_dcycs,scratch2 + + ldil l%g_last_vbl_dcycs,scratch3 + fcpy,dbl 0,fr_plus_3 + ldo r%g_cur_dcycs(scratch2),scratch2 + fcpy,dbl 0,fr_plus_x_m1 + ldo r%g_last_vbl_dcycs(scratch3),scratch3 + fldds 0(scratch2),ftmp1 + ldil l%page_info_rd_wr,page_info_ptr + fldds 0(scratch3),fcycles_last_dcycs + fcpy,dbl 0,fcycles_stop + ldo r%page_info_rd_wr(page_info_ptr),page_info_ptr + bv 0(scratch1) + fsub,dbl ftmp1,fcycles_last_dcycs,fcycles + + + .export leave_asm,data +leave_asm + ldw -STACK_ENGINE_SIZE+4(sp),link + ldo -STACK_ENGINE_SIZE+16(sp),scratch2 + ldw -STACK_ENGINE_SIZE+8(sp),addr_latch + fldds,ma 8(scratch2),fcycles + fldds,ma 8(scratch2),fr_plus_1 + fldds,ma 8(scratch2),fr_plus_2 + fldds,ma 8(scratch2),fr_plus_3 + fldds,ma 8(scratch2),fr_plus_x_m1 + fldds,ma 8(scratch2),fcycles_stop + fldds,ma 8(scratch2),fcycles_last_dcycs + + bv (link) + ldwm -STACK_ENGINE_SIZE(sp),page_info_ptr + + + .align 8 + .export get_memory_c +get_memory_c +; arg0 = addr +; arg1 = cycles + bl enter_asm,scratch1 + nop + bl get_memory_asm,link + nop + b leave_asm + nop + + .export get_memory16_c +get_memory16_c +; arg0 = addr +; arg1 = cycles + bl enter_asm,scratch1 + nop + bl get_memory16_asm,link + nop + b leave_asm + nop + + .export get_memory24_c +get_memory24_c +; arg0 = addr +; arg1 = cycles + bl enter_asm,scratch1 + nop + bl get_memory24_asm,link + nop + b leave_asm + nop + +#define GET_MEM8(upper16,lower8,ret0) \ + extru arg0,23,16,arg3 ! \ + CYCLES_PLUS_1 ! \ + ldwx,s arg3(page_info_ptr),scratch3 ! \ + copy arg0,addr_latch ! \ + copy scratch3,scratch2 ! \ + dep arg0,31,8,scratch3 ! \ + extru,= scratch2,BANK_IO_BIT,1,0 ! \ + bl,n get_memory_iocheck_stub_asm,link ! \ + ldb (scratch3),ret0 + + .align 32 + + .export get_memory_asm +get_memory_asm +; arg0 = addr + extru arg0,23,16,arg3 + copy arg0,addr_latch + + ldwx,s arg3(page_info_ptr),scratch2 + CYCLES_PLUS_1 + bb,<,n scratch2,BANK_IO_BIT,get_memory_iocheck_stub_asm + dep arg0,31,8,scratch2 + bv 0(link) + ldb (scratch2),ret0 + + + .align 8 + + .export get_memory16_asm +get_memory16_asm +; arg0 = addr + ldi 0xff,scratch3 + extru arg0,23,16,arg3 + + and scratch3,arg0,scratch4 + ldwx,s arg3(page_info_ptr),scratch2 + copy arg0,addr_latch + comb,= scratch4,scratch3,get_memory16_pieces_stub_asm + and scratch2,scratch3,scratch3 + comb,<> 0,scratch3,get_memory16_pieces_stub_asm + dep arg0,31,8,scratch2 + ldb (scratch2),ret0 + CYCLES_PLUS_2 + ldb 1(scratch2),scratch1 + bv 0(link) + dep scratch1,23,8,ret0 + + .align 8 + + .export get_memory24_asm +get_memory24_asm +; arg0 = addr + ldi 0xfe,scratch3 + extru arg0,23,16,arg3 + + and scratch3,arg0,scratch4 + ldwx,s arg3(page_info_ptr),scratch2 + copy arg0,addr_latch + comb,= scratch4,scratch3,get_memory24_pieces_stub_asm + extru scratch2,31,8,scratch3 + comb,<> 0,scratch3,get_memory24_pieces_stub_asm + dep arg0,31,8,scratch2 + ldb (scratch2),ret0 + ldb 1(scratch2),scratch1 + CYCLES_PLUS_3 + ldb 2(scratch2),scratch2 + dep scratch1,23,8,ret0 + bv 0(link) + dep scratch2,15,8,ret0 + + + .align 0x20 + .export get_memory_iocheck_stub_asm,code +get_memory_iocheck_stub_asm + extru,= scratch2,BANK_BREAK_BIT,1,0 + bl check_breakpoints_asm,scratch4 + stw link,STACK_GET_MEMORY_SAVE_LINK(sp) + bb,< scratch2,BANK_IO2_BIT,get_memory_io_stub_asm + dep arg0,31,8,scratch2 + bv 0(link) + ldb (scratch2),ret0 + + .export get_memory_io_stub_asm +get_memory_io_stub_asm + FCYCLES_ROUND_1 + ldo STACK_SAVE_CYCLES(sp),arg1 + FCYCLES_ROUND_2 + FCYCLES_ROUND_3 + bl get_memory_io,link + fstds fcycles,(arg1) + + ldw STACK_GET_MEMORY_SAVE_LINK(sp),link + ldo STACK_SAVE_CYCLES(sp),arg1 + bv (link) + fldds (arg1),fcycles + + + + .export get_memory16_pieces_stub_asm,code +get_memory16_pieces_stub_asm + stw addr_latch,STACK_GET_MEMORY16_ADDR_LATCH(sp) + addi 1,arg0,scratch1 + stw link,STACK_MEMORY16_SAVE2(sp) + bl get_memory_asm,link + stw scratch1,STACK_MEMORY16_SAVE1(sp) + + stw ret0,STACK_MEMORY16_SAVE3(sp) + bl get_memory_asm,link + ldw STACK_MEMORY16_SAVE1(sp),arg0 + + ldw STACK_MEMORY16_SAVE2(sp),link + copy ret0,scratch1 + ldw STACK_MEMORY16_SAVE3(sp),ret0 + ldw STACK_GET_MEMORY16_ADDR_LATCH(sp),addr_latch + bv (link) + dep scratch1,23,8,ret0 + + + .export get_memory24_pieces_stub_asm,code +get_memory24_pieces_stub_asm + stw addr_latch,STACK_GET_MEMORY16_ADDR_LATCH(sp) + addi 1,arg0,scratch1 + stw link,STACK_GET_MEMORY24_SAVE2(sp) + bl get_memory_asm,link + stw scratch1,STACK_GET_MEMORY24_SAVE1(sp) + + stw ret0,STACK_GET_MEMORY24_SAVE3(sp) + bl get_memory_asm,link + ldw STACK_GET_MEMORY24_SAVE1(sp),arg0 + + ldw STACK_GET_MEMORY24_SAVE1(sp),arg0 + stb ret0,STACK_GET_MEMORY24_SAVE3+2(sp) + bl get_memory_asm,link + addi 1,arg0,arg0 + + ldw STACK_GET_MEMORY24_SAVE2(sp),link + copy ret0,scratch1 + ldw STACK_GET_MEMORY24_SAVE3(sp),ret0 + ldw STACK_GET_MEMORY16_ADDR_LATCH(sp),addr_latch + bv (link) + dep scratch1,15,8,ret0 + + + + +; C callable routine to wrap around set_memory_asm + .export set_memory_c +set_memory_c +;arg0 = addr +;arg1 = val +;arg2 = cycles + bl enter_asm,scratch1 + nop + bl set_memory_asm,link + nop + b leave_asm + nop + + + .export set_memory16_c +set_memory16_c +;arg0 = addr +;arg1 = val +;arg2 = cycles + bl enter_asm,scratch1 + nop + bl set_memory16_asm,link + nop + b leave_asm + nop + + .export set_memory24_c +set_memory24_c +;arg0 = addr +;arg1 = val +;arg2 = cycles + bl enter_asm,scratch1 + nop + bl set_memory24_asm,link + nop + b leave_asm + nop + + + .align 32 + + .export set_memory_asm +set_memory_asm +; arg0 = addr +; arg1 = val + extru arg0,23,16,arg3 + addil l%PAGE_INFO_WR_OFFSET,arg3 + CYCLES_PLUS_1 + ldwx,s r1(page_info_ptr),scratch2 + ldi 0xff,scratch3 + and scratch2,scratch3,scratch3 + dep arg0,31,8,scratch2 + comib,<>,n 0,scratch3,set_memory_special_case +set_memory_cont_asm + bv 0(link) + stb arg1,(scratch2) + + + .export set_memory_special_case +set_memory_special_case + extru,= scratch3,BANK_BREAK_BIT,1,0 + bl check_breakpoints_asm,scratch4 + extru arg1,31,8,arg1 + +set_memory_special_case2 + bb,< scratch3,BANK_IO2_BIT,set_memory_io_stub_asm + ldil l%slow_memory,scratch4 + bb,< scratch3,BANK_SHADOW_BIT,set_memory_shadow1_asm + extru arg0,31,16,arg3 + bb,< scratch3,BANK_SHADOW2_BIT,set_memory_shadow2_asm + nop + bb,< scratch3,BANK_BREAK_BIT,set_memory_cont_asm + nop + break + + +set_memory_shadow1_asm +#ifdef ACCURATE_SLOW_MEM + FCYCLES_ROUND_1 +#endif + add arg3,scratch4,scratch4 + extru arg3,31-SHIFT_PER_CHANGE,5,scratch1 + ldb r%slow_memory(scratch4),arg2 +#ifdef ACCURATE_SLOW_MEM + FCYCLES_ROUND_2 +#endif + mtctl scratch1,cr11 +#ifdef ACCURATE_SLOW_MEM + FCYCLES_ROUND_3 +#endif + comclr,<> arg2,arg1,0 + bv 0(link) + stb arg1,(scratch2) + zvdepi 1,1,arg2 + extru arg3,31-CHANGE_SHIFT,16-CHANGE_SHIFT,scratch2 + ldil l%slow_mem_changed,scratch1 + sh2add scratch2,scratch1,scratch1 + ldw r%slow_mem_changed(scratch1),scratch3 + stb arg1,r%slow_memory(scratch4) + or arg2,scratch3,scratch3 + bv 0(link) + stw scratch3,r%slow_mem_changed(scratch1) + +set_memory_shadow2_asm + depi 1,15,1,arg3 +#ifdef ACCURATE_SLOW_MEM + FCYCLES_ROUND_1 +#endif + add arg3,scratch4,scratch4 + extru arg3,31-SHIFT_PER_CHANGE,5,scratch1 + ldb r%slow_memory(scratch4),arg2 +#ifdef ACCURATE_SLOW_MEM + FCYCLES_ROUND_2 +#endif + mtctl scratch1,cr11 +#ifdef ACCURATE_SLOW_MEM + FCYCLES_ROUND_3 +#endif + comclr,<> arg2,arg1,0 + bv 0(link) + stb arg1,(scratch2) + zvdepi 1,1,arg2 + extru arg3,31-CHANGE_SHIFT,16-CHANGE_SHIFT,scratch2 + ldil l%slow_mem_changed,scratch1 + sh2add scratch2,scratch1,scratch1 + ldw r%slow_mem_changed(scratch1),scratch3 + stb arg1,r%slow_memory(scratch4) + or arg2,scratch3,scratch3 + bv 0(link) + stw scratch3,r%slow_mem_changed(scratch1) + + +set_memory_io_stub_asm + FCYCLES_ROUND_1 + ldo STACK_SAVE_CYCLES(sp),arg2 + FCYCLES_ROUND_2 + stw link,STACK_SET_MEMORY_SAVE_LINK(sp) + FCYCLES_ROUND_3 + bl set_memory_io,link + fstds fcycles,(arg2) + + ldw STACK_SET_MEMORY_SAVE_LINK(sp),link + ldo STACK_SAVE_CYCLES(sp),arg2 + bv (link) + fldds (arg2),fcycles + + .align 8 + .export set_memory16_asm +set_memory16_asm +; arg0 = addr +; arg1 = val + extru arg0,23,16,arg3 + + addil l%PAGE_INFO_WR_OFFSET,arg3 + extrs arg0,31,8,scratch4 + ldwx,s r1(page_info_ptr),scratch2 + ldi 0xff,scratch3 + and scratch3,scratch2,scratch3 + dep arg0,31,8,scratch2 + comib,=,n -1,scratch4,set_memory16_pieces_stub_asm + comib,<>,n 0,scratch3,set_memory16_special_case +set_memory16_cont_asm + stb arg1,0(scratch2) + CYCLES_PLUS_2 + extru arg1,23,8,arg3 + bv 0(link) + stb arg3,1(scratch2) + + + .align 8 +set_memory16_shadow1_asm + CYCLES_PLUS_2 + copy arg1,arg2 + extru arg1,23,8,arg1 +#ifdef ACCURATE_SLOW_MEM + FCYCLES_ROUND_1 +#endif + add arg3,scratch4,scratch4 + dep arg2,23,8,arg1 + extru arg3,31-SHIFT_PER_CHANGE,5,scratch1 + ldh r%slow_memory(scratch4),arg2 +#ifdef ACCURATE_SLOW_MEM + FCYCLES_ROUND_2 +#endif + mtctl scratch1,cr11 +#ifdef ACCURATE_SLOW_MEM + FCYCLES_ROUND_3 +#endif + comclr,<> arg2,arg1,0 ;return if arg2 == arg1 + bv 0(link) + sth arg1,(scratch2) + zvdepi 1,1,arg2 + extru arg3,31-CHANGE_SHIFT,16-CHANGE_SHIFT,scratch2 + ldil l%slow_mem_changed,scratch1 + sh2add scratch2,scratch1,scratch1 + ldw r%slow_mem_changed(scratch1),scratch3 + sth arg1,r%slow_memory(scratch4) + or arg2,scratch3,scratch3 + bv 0(link) + stw scratch3,r%slow_mem_changed(scratch1) + + .align 8 +set_memory16_shadow2_asm + CYCLES_PLUS_2 + copy arg1,arg2 + extru arg1,23,8,arg1 + depi 1,15,1,arg3 +#ifdef ACCURATE_SLOW_MEM + FCYCLES_ROUND_1 +#endif + dep arg2,23,8,arg1 + add arg3,scratch4,scratch4 + extru arg3,31-SHIFT_PER_CHANGE,5,scratch1 + ldh r%slow_memory(scratch4),arg2 +#ifdef ACCURATE_SLOW_MEM + FCYCLES_ROUND_2 +#endif + mtctl scratch1,cr11 +#ifdef ACCURATE_SLOW_MEM + FCYCLES_ROUND_3 +#endif + comclr,<> arg2,arg1,0 + bv 0(link) + sth arg1,(scratch2) + zvdepi 1,1,arg2 + extru arg3,31-CHANGE_SHIFT,16-CHANGE_SHIFT,scratch2 + ldil l%slow_mem_changed,scratch1 + sh2add scratch2,scratch1,scratch1 + ldw r%slow_mem_changed(scratch1),scratch3 + sth arg1,r%slow_memory(scratch4) + or arg2,scratch3,scratch3 + bv 0(link) + stw scratch3,r%slow_mem_changed(scratch1) + + + .align 8 +set_memory16_special_case + extru,= scratch3,BANK_BREAK_BIT,1,0 + bl check_breakpoints_asm,scratch4 + extru arg1,31,16,arg1 + +set_memory16_special_case2 + bb,< scratch3,BANK_IO2_BIT,set_memory16_pieces_stub_asm + ldil l%slow_memory,scratch4 + +; if not halfword aligned, go through pieces_stub_asm + bb,<,n arg0,31,set_memory16_pieces_stub_asm + bb,< scratch3,BANK_SHADOW2_BIT,set_memory16_shadow2_asm + extru arg0,31,16,arg3 + bb,< scratch3,BANK_SHADOW_BIT,set_memory16_shadow1_asm + nop + bb,< scratch3,BANK_BREAK_BIT,set_memory16_cont_asm + nop + break + + .align 8 +set_memory16_pieces_stub_asm + addi 1,arg0,scratch1 + stw link,STACK_MEMORY16_SAVE3(sp) + extru arg1,23,8,scratch2 + stw scratch1,STACK_MEMORY16_SAVE1(sp) + bl set_memory_asm,link + stw scratch2,STACK_MEMORY16_SAVE2(sp) + + ldw STACK_MEMORY16_SAVE1(sp),arg0 + ldw STACK_MEMORY16_SAVE2(sp),arg1 + b set_memory_asm + ldw STACK_MEMORY16_SAVE3(sp),link + + + .align 8 + .export set_memory24_asm +set_memory24_asm +; arg0 = addr +; arg1 = val + extru arg0,23,16,arg3 + + addil l%PAGE_INFO_WR_OFFSET,arg3 + extrs arg0,30,7,scratch4 + ldwx,s r1(page_info_ptr),scratch2 + ldi 0xff,scratch3 + and scratch3,scratch2,scratch3 + dep arg0,31,8,scratch2 + comib,=,n -1,scratch4,set_memory24_pieces_stub_asm + comib,<>,n 0,scratch3,set_memory24_pieces_stub_asm + stb arg1,0(scratch2) + extru arg1,23,8,arg3 + CYCLES_PLUS_3 + stb arg3,1(scratch2) + extru arg1,15,8,arg3 + bv 0(link) + stb arg3,2(scratch2) + +set_memory24_pieces_stub_asm + addi 1,arg0,scratch1 + stw link,STACK_SET_MEMORY24_SAVE3(sp) + extru arg1,23,16,scratch2 + stw scratch1,STACK_SET_MEMORY24_SAVE1(sp) + bl set_memory_asm,link + stw scratch2,STACK_SET_MEMORY24_SAVE2(sp) + + ldw STACK_SET_MEMORY24_SAVE1(sp),arg0 + bl set_memory_asm,link + ldw STACK_SET_MEMORY24_SAVE2(sp),arg1 + + ldw STACK_SET_MEMORY24_SAVE1(sp),arg0 + ldw STACK_SET_MEMORY24_SAVE3(sp),link + addi 1,arg0,arg0 + b set_memory_asm + ldb STACK_SET_MEMORY24_SAVE2+2(sp),arg1 + + + + + + .import g_num_breakpoints,data + .import g_breakpts,data + + .align 8 + .export check_breakpoints_asm,code +check_breakpoints_asm +; can't use arg0-arg3. don't use scratch2,scratch3 +; scratch4: return link +; + ldil l%g_num_breakpoints,scratch1 + ldil l%g_breakpts,ret0 + ldw r%g_num_breakpoints(scratch1),r1 + ldo r%g_breakpts(ret0),ret0 + addi,>= -1,r1,r1 + bv,n 0(scratch4) + ldwx,s r1(ret0),scratch1 +check_breakpoints_loop_asm + comb,=,n scratch1,arg0,check_breakpoints_hit + addib,>=,n -1,r1,check_breakpoints_loop_asm + ldwx,s r1(ret0),scratch1 + + bv 0(scratch4) + nop + + .export check_breakpoints_hit,code +check_breakpoints_hit + LDC(halt_sim,scratch1) + ldw (scratch1),r1 + ldil l%g_fcycles_stop,ret0 + depi 1,31,1,r1 + stw 0,r%g_fcycles_stop(ret0) + stw 0,r%g_fcycles_stop+4(ret0) + bv 0(scratch4) + stw r1,(scratch1) + nop + nop + nop + + + + .align 8 + .export set_mem_yreg +set_mem_yreg +; arg0 = addr to write + extru,= psr,27,1,0 ;null branch if 16 bit + b set_memory_asm + copy yreg,arg1 +;if get here, 16 bit yreg + b,n set_memory16_asm + nop + + + .align 8 + .export set_mem_xreg +set_mem_xreg +; arg0 = addr to write + extru,= psr,27,1,0 ;null branch if 16 bit + b set_memory_asm + copy xreg,arg1 +;if get here, 16 bit xreg + b,n set_memory16_asm + nop + + + + .export get_memory_outofrange,code +get_memory_outofrange + break + + +get_mem_b0_16_stub + b get_mem_b0_16 + nop + + .align 8 +get_mem_b0_direct_page_16 +; get 2 bytes for direct-page fetch. +; arg0 = addr; +; if emul and dl = 0, then stick dh in +; into high bytes. +; if emul, grab + 1 byte from dh page also. +; if not emul, just call get_mem_b0 + ldi 0xff,scratch2 + extru,<> psr,23,1,0 ;null next if emul bit set + b get_mem_b0_16 + extru direct,23,8,scratch1 + and arg0,scratch2,scratch3 + extru,<> direct,31,8,0 ;null if direct not page aligned + dep scratch1,23,24,arg0 ;..done only if direct is page aligned + comb,<> scratch3,scratch2,get_mem_b0_16_stub + stw link,STACK_GET_MEM_B0_DIRECT_SAVELINK(sp) +; we're at 0x??ff, so next byte needs to come from 0x??00. + bl get_mem_b0_8,link + stw arg0,STACK_GET_MEM_B0_DIRECT_ARG0(sp) +; now, get next byte + ldw STACK_GET_MEM_B0_DIRECT_ARG0(sp),arg0 + extru direct,23,8,scratch1 + stw ret0,STACK_GET_MEM_B0_DIRECT_RET0(sp) + addi 1,arg0,arg0 + extru,<> direct,31,8,0 ;null if direct not page aligned + dep scratch1,23,24,arg0 ;..done only if direct is page aligned + bl get_mem_b0_8,link + nop + +; and return + copy ret0,scratch2 + ldw STACK_GET_MEM_B0_DIRECT_SAVELINK(sp),scratch1 + ldb STACK_GET_MEM_B0_DIRECT_RET0+3(sp),ret0 + bv (scratch1) + dep scratch2,23,8,ret0 + + + +push_8 + copy arg0,arg1 + copy stack,arg0 + addi -1,stack,stack + extru,= psr,23,1,0 ;emul mode? + depi 1,23,24,stack + b set_mem_b0_8 + extru stack,31,16,stack + +pull_8 + addi 1,stack,stack + extru,= psr,23,1,0 + depi 1,23,24,stack + extru stack,31,16,stack + b get_mem_b0_8 + copy stack,arg0 + +push_16 + copy arg0,arg1 + bb,>= psr,23,push_16_native + extru stack,30,7,scratch1 + +; push_16_emul + addi -2,stack,stack + comib,= 0,scratch1,push_16_emul_page + addi 1,stack,arg0 ;we know we are not at end of page + b set_mem_b0_16 + depi 1,23,24,stack + + +push_16_emul_page + stw link,STACK_SAVE_PUSH16_LINK(sp) + addi 1,arg0,arg0 + stw arg1,STACK_SAVE_PUSH16_ARG1(sp) + depi 1,23,24,arg0 + bl set_mem_b0_8,link + extru arg1,23,8,arg1 +; and do next push + addi 1,stack,arg0 + depi 1,23,24,stack + ldw STACK_SAVE_PUSH16_LINK(sp),link + ldb STACK_SAVE_PUSH16_ARG1+3(sp),arg1 + b set_mem_b0_8 + depi 1,23,24,arg0 + +push_16_native +; here, we're a native push_16 + addi -2,stack,stack + comib,= 0,scratch1,push_16_nat_page + addi 1,stack,arg0 ;we know we are not at end of page + b set_mem_b0_16 + extru stack,31,16,stack + + +push_16_nat_page + stw link,STACK_SAVE_PUSH16_LINK(sp) + addi 1,arg0,arg0 + stw arg1,STACK_SAVE_PUSH16_ARG1(sp) + extru arg0,31,16,arg0 + bl set_mem_b0_8,link + extru arg1,23,8,arg1 +; and do next push + addi 1,stack,arg0 + extru stack,31,16,stack + ldw STACK_SAVE_PUSH16_LINK(sp),link + ldb STACK_SAVE_PUSH16_ARG1+3(sp),arg1 + b set_mem_b0_8 + extru arg0,31,16,arg0 + +push_16_unsafe + copy arg0,arg1 + addi -1,stack,arg0 + addi -2,stack,stack + extru,= psr,23,1,0 + depi 1,23,24,stack + extru arg0,31,16,arg0 + b set_mem_b0_16 + extru stack,31,16,stack + +push_24_unsafe + copy arg0,arg1 + addi -2,stack,arg0 + addi -3,stack,stack + extru,= psr,23,1,0 + depi 1,23,24,stack + extru arg0,31,16,arg0 + b set_mem_b0_24 + extru stack,31,16,stack + +pull_16_unsafe + addi 1,stack,stack + extru,= psr,23,1,0 + depi 1,23,24,stack + extru stack,31,16,arg0 + addi 1,stack,stack + extru,= psr,23,1,0 + depi 1,23,24,stack + b get_mem_b0_16 + extru stack,31,16,stack + + .align 8 +pull_16 + extrs stack,29,6,scratch1 + bb,< psr,23,pull_16_emul + addi 1,stack,arg0 + comib,= -1,scratch1,pull_16_nat_page + addi 2,stack,stack +; if we get here, native & not near page cross + extru arg0,31,16,arg0 + b get_mem_b0_16 + extru stack,31,16,stack + +pull_16_emul + comib,= -1,scratch1,pull_16_emul_page + addi 2,stack,stack +; if get here, emul & not near page cross + b get_mem_b0_16 + depi 1,23,24,stack + +pull_16_nat_page + stw link,STACK_SAVE_PULL16_LINK(sp) + bl get_mem_b0_8,link + extru arg0,31,16,arg0 +; got first byte + stw ret0,STACK_SAVE_PULL16_RET0(sp) + extru stack,31,16,stack + bl get_mem_b0_8,link + copy stack,arg0 +; got second byte + ldw STACK_SAVE_PULL16_LINK(sp),link + copy ret0,scratch1 + ldb STACK_SAVE_PULL16_RET0+3(sp),ret0 + bv 0(link) + dep scratch1,23,8,ret0 + +pull_16_emul_page + stw link,STACK_SAVE_PULL16_LINK(sp) + bl get_mem_b0_8,link + depi 1,23,24,arg0 +; got first byte + stw ret0,STACK_SAVE_PULL16_RET0(sp) + depi 1,23,24,stack + bl get_mem_b0_8,link + copy stack,arg0 +; got second byte + ldw STACK_SAVE_PULL16_LINK(sp),link + copy ret0,scratch1 + ldb STACK_SAVE_PULL16_RET0+3(sp),ret0 + bv 0(link) + dep scratch1,23,8,ret0 + + .export pull_24,code +pull_24 + extrs stack,29,6,scratch1 + bb,< psr,23,pull_24_emul + addi 1,stack,arg0 + comib,= -1,scratch1,pull_24_nat_page + addi 3,stack,stack +; if we get here, native & not near page cross, go for it + extru arg0,31,16,arg0 + b get_mem_b0_24 + extru stack,31,16,stack + +pull_24_emul + depi 1,23,24,arg0 + comib,= -1,scratch1,pull_24_emul_page + addi 3,stack,stack +; if we get here, emul & not near page cross + b get_mem_b0_24 + depi 1,23,24,stack + +pull_24_nat_page + stw link,STACK_SAVE_PULL24_LINK(sp) + bl get_mem_b0_8,link + extru arg0,31,16,arg0 +; got first byte + stw ret0,STACK_SAVE_PULL24_RET0(sp) + addi -1,stack,arg0 + extru stack,31,16,stack + bl get_mem_b0_8,link + extru arg0,31,16,arg0 +; got second byte + stb ret0,STACK_SAVE_PULL24_RET0+2(sp) + bl get_mem_b0_8,link + copy stack,arg0 +; got all bytes + ldw STACK_SAVE_PULL24_LINK(sp),link + copy ret0,scratch1 + ldw STACK_SAVE_PULL24_RET0(sp),ret0 + bv (link) + dep scratch1,15,8,ret0 + +pull_24_emul_page + stw link,STACK_SAVE_PULL24_LINK(sp) + bl get_mem_b0_8,link + nop +; got first byte + addi -1,stack,arg0 + stw ret0,STACK_SAVE_PULL24_RET0(sp) + depi 1,23,24,stack + bl get_mem_b0_8,link + depi 1,23,24,arg0 +; got second byte + stb ret0,STACK_SAVE_PULL24_RET0+2(sp) + bl get_mem_b0_8,link + copy stack,arg0 +; got all bytes + ldw STACK_SAVE_PULL24_LINK(sp),link + copy ret0,scratch1 + ldw STACK_SAVE_PULL24_RET0(sp),ret0 + bv (link) + dep scratch1,15,8,ret0 + +update_system_state_and_change_kbank +; kbank already changed..do nothing + +update_system_state +; psr is new psw state +; arg0 is old in bits 31 and 30 + ldi 0x30,scratch1 + extru,= psr,23,1,0 + depi 3,27,2,psr + and psr,scratch1,scratch1 + extru,= psr,23,1,0 + depi 1,23,24,stack + dep arg0,29,2,scratch1 + blr scratch1,0 + addit,>= -0x3d,scratch1,0 +; 0000: no change + b update_sys9 + nop ! nop ! nop + nop ! nop ! nop ! nop +; 0001: x from 1->0 + b update_sys9 + ldi 2,scratch1 + nop ! nop + nop ! nop ! nop ! nop +; 0010: m from 1->0 + b resize_acc_to16 + ldi 0,ret0 + nop ! nop + nop ! nop ! nop ! nop +; 0011: m,x from 1->0 + b resize_acc_to16 + ldi 0,ret0 + nop ! nop + nop ! nop ! nop ! nop +; 0100: x from 0->1 + depi 0,23,24,yreg + b update_sys9 + depi 0,23,24,xreg + nop + nop ! nop ! nop ! nop +; 0101: no change + b update_sys9 + nop ! nop ! nop + nop ! nop ! nop ! nop +; 0110: x from 0->1, m from 1->0 + depi 0,23,24,yreg + ldi 0,ret0 + b resize_acc_to16 + depi 0,23,24,xreg + nop ! nop ! nop ! nop +; 0111: m from 1->0 + b resize_acc_to16 + ldi 0,ret0 + nop ! nop + nop ! nop ! nop ! nop +; 1000: m from 0->1 + b resize_acc_to8 + ldi 0,ret0 + nop ! nop + nop ! nop ! nop ! nop +; 1001: m from 0->1, x from 1->0 + b resize_acc_to8 + ldi 0,ret0 + nop ! nop + nop ! nop ! nop ! nop +; 1010: no change + b update_sys9 + nop ! nop ! nop + nop ! nop ! nop ! nop +; 1011: x from 1->0 + b update_sys9 + nop ! nop ! nop + nop ! nop ! nop ! nop +; 1100: m,x from 0->1 + depi 0,23,24,yreg + ldi 0,ret0 + b resize_acc_to8 + depi 0,23,24,xreg + nop ! nop ! nop ! nop +; 1101: m from 0->1 + b resize_acc_to8 + ldi 0,ret0 + nop ! nop + nop ! nop ! nop ! nop +; 1110: x from 0->1 + depi 0,23,24,yreg + ldi 0,ret0 + b update_sys9 + depi 0,23,24,xreg + nop ! nop ! nop ! nop +; 1111: no change + b update_sys9 + nop ! nop ! nop + nop ! nop ! nop ! nop +; 10000 + break + + + .export get_yreg_from_mem,code +get_yreg_from_mem +; arg0 = addr to read from, write into yreg + bb,>=,n psr,27,get_yreg_from_mem16 + bl get_mem_b0_8,link + extru arg0,31,24,arg0 + + extru ret0,31,8,zero + extru ret0,24,1,neg + b dispatch + copy zero,yreg + + .export get_yreg_from_mem16,code +get_yreg_from_mem16 + bl get_mem_b0_16,link + extru arg0,31,24,arg0 + + extru ret0,31,16,zero + extru ret0,16,1,neg + b dispatch + copy zero,yreg + + + .export get_xreg_from_mem,code +get_xreg_from_mem +; arg0 = addr to read from, write into xreg + bb,>=,n psr,27,get_xreg_from_mem16 + bl get_mem_b0_8,link + extru arg0,31,24,arg0 + + extru ret0,31,8,zero + extru ret0,24,1,neg + b dispatch + copy zero,xreg + + .export get_xreg_from_mem16,code +get_xreg_from_mem16 + bl get_mem_b0_16,link + extru arg0,31,24,arg0 + + extru ret0,31,16,zero + extru ret0,16,1,neg + b dispatch + copy zero,xreg + + + + + .export enter_engine,code +enter_engine +; load up regs with struct vals + .proc + .callinfo frame=STACK_ENGINE_SIZE,caller,save_rp,entry_gr=18,entry_fr=19 + .enter + + ldw ENGINE_FPLUS_PTR(arg0),scratch1 ;fplus ptr + fldds ENGINE_FCYCLES(arg0),fcycles + + ldil l%g_fcycles_stop,fcycles_stop_ptr + ldw ENGINE_REG_ACC(arg0),acc + ldo r%g_fcycles_stop(fcycles_stop_ptr),fcycles_stop_ptr + fldds FPLUS_PLUS_1(scratch1),fr_plus_1 + ldo FPLUS_PLUS_3(scratch1),ret0 + fldds FPLUS_PLUS_2(scratch1),fr_plus_2 + ldil l%g_last_vbl_dcycs,ret1 + fldds FPLUS_PLUS_3-FPLUS_PLUS_3(ret0),fr_plus_3 + ldo r%g_last_vbl_dcycs(ret1),ret1 + fldds FPLUS_PLUS_X_M1-FPLUS_PLUS_3(ret0),fr_plus_x_m1 + fldds 0(ret1),fcycles_last_dcycs + ldil l%table8,ret0 + ldw ENGINE_REG_XREG(arg0),xreg + ldil l%table16,inst_tab_ptr + ldw ENGINE_REG_YREG(arg0),yreg + ldo r%table8(ret0),ret0 + ldw ENGINE_REG_STACK(arg0),stack + ldo r%table16(inst_tab_ptr),inst_tab_ptr + ldw ENGINE_REG_PSR(arg0),psr + ldi 0,zero + ldw ENGINE_REG_DBANK(arg0),dbank + ldil l%page_info_rd_wr,page_info_ptr + ldw ENGINE_REG_DIRECT(arg0),direct + extru,= psr,26,1,0 ;nullify if acc size = 0 == 16bit + copy ret0,inst_tab_ptr + ldw ENGINE_REG_KPC(arg0),kpc + + ldo r%page_info_rd_wr(page_info_ptr),page_info_ptr + extru,<> psr,30,1,0 + ldi 1,zero + extru psr,24,1,neg + stw arg0,STACK_SAVE_ARG0(sp) + ldi 0xfd,const_fd + b dispatch + ldi 0,scratch1 + + .export resize_acc_to8,code +resize_acc_to8 + ldil l%table8,inst_tab_ptr + extru psr,27,1,scratch1 ;size of x + b update_sys9 + ldo r%table8(inst_tab_ptr),inst_tab_ptr + + .export resize_acc_to16,code +resize_acc_to16 + ldil l%table16,inst_tab_ptr + extru psr,27,1,scratch1 + b update_sys9 + ldo r%table16(inst_tab_ptr),inst_tab_ptr + + + +dispatch_done_cycles_mismatch + ldi -1,ret0 + b dispatch_done + nop + + + + .export dispatch_done +dispatch_done + bl refresh_engine_struct,link + ldw STACK_SAVE_ARG0(sp),arg0 + .leave + .procend + +refresh_engine_struct +; warning--this routine must not change arg1, arg2, arg3, or ret0 +; can only change scratch1 + + comiclr,<> 0,zero,scratch1 + ldi 1,scratch1 + dep neg,24,1,psr + dep scratch1,30,1,psr + stw acc,ENGINE_REG_ACC(arg0) + stw xreg,ENGINE_REG_XREG(arg0) + stw yreg,ENGINE_REG_YREG(arg0) + stw stack,ENGINE_REG_STACK(arg0) + stw dbank,ENGINE_REG_DBANK(arg0) + stw direct,ENGINE_REG_DIRECT(arg0) + stw psr,ENGINE_REG_PSR(arg0) + stw kpc,ENGINE_REG_KPC(arg0) + bv 0(link) + fstds fcycles,ENGINE_FCYCLES(arg0) + + .export check_irqs_pending,code +update_sys9 +check_irqs_pending +; if any g_irq_pending, return RET_IRQ + ldil l%g_irq_pending,scratch1 + ldw r%g_irq_pending(scratch1),scratch2 + bb,<,n psr,29,dispatch + comib,= 0,scratch2,dispatch + zdepi RET_IRQ,3,4,ret0 + b,n dispatch_done + nop + + .export clr_halt_act + .export set_halt_act +clr_halt_act + LDC(halt_sim,scratch1) + bv 0(link) + stw 0,(scratch1) + +set_halt_act + LDC(halt_sim,scratch1) + ldw (scratch1),scratch2 + ldil l%g_fcycles_stop,scratch3 + stw 0,r%g_fcycles_stop(scratch3) + or scratch2,arg0,arg0 + stw 0,r%g_fcycles_stop+4(scratch3) + bv 0(link) + stw arg0,(scratch1) + + + .align 32 + .export dispatch_fast,code +dispatch_fast +; instr is the instr to fetch +#ifdef LOG_PC + b dispatch + nop +#endif + fldds 0(fcycles_stop_ptr),fcycles_stop + extru kpc,23,16,arg2 + + extru kpc,31,8,scratch4 + ldwx,s arg2(page_info_ptr),scratch2 + + ldwx,s instr(inst_tab_ptr),link + fcmp,>,dbl fcycles,fcycles_stop ;C=1 if must stop + + addl scratch4,scratch2,scratch1 + comclr,>= scratch4,const_fd,0 ;stop for pieces if near end of page + + ldi -1,scratch2 + bb,<,n scratch2,BANK_IO_BIT,dispatch_instr_io + + ftest ;null next if can cont + + bv 0(link) + CYCLES_PLUS_2 + + b dispatch_instr_io + CYCLES_MINUS_2 + + + .align 32 + .export dispatch,code +dispatch + +#ifdef CHECK_SIZE_CONSISTENCY + nop + bl check_size_consist,link + nop +#endif + +#ifdef DEBUG_TOOLBOX + ldil l%g_rom_version,scratch1 + ldw r%g_rom_version(scratch1),scratch1 + ldi 0x00db,scratch1 ;ROM 01 + comiclr,> 3,scratch1,0 + ldi 0x00e5,scratch1 ;ROM 03 + depi -2,15,8,scratch1 ;set bank to 0xfe + comb,<>,n scratch1,kpc,no_debug_toolbox + copy xreg,arg0 + copy stack,arg1 + bl toolbox_debug_c,link + copy cycles,arg2 + + extru kpc,23,16,scratch2 +no_debug_toolbox +#endif + fldds 0(fcycles_stop_ptr),fcycles_stop + extru kpc,23,16,arg2 + + ldi 0xfd,scratch3 + ldwx,s arg2(page_info_ptr),scratch2 + + fcmp,<=,dbl fcycles,fcycles_stop ;C=1 if can cont + extru kpc,31,8,scratch4 + + ldbx scratch4(scratch2),instr + comclr,>= scratch4,scratch3,0 ;stop for pieces if near end of page + + ftest ;null next if can cont + + ldi -1,scratch2 + ldwx,s instr(inst_tab_ptr),link + + addl scratch4,scratch2,scratch1 + bb,<,n scratch2,BANK_IO_BIT,dispatch_instr_io + + ; depi 0,31,3,link + +#ifndef LOG_PC + bv 0(link) + CYCLES_PLUS_2 +#else + CYCLES_PLUS_2 + + .import log_pc_ptr,data + .import log_pc_start_ptr,data + .import log_pc_end_ptr,data + .export log_pc_asm +log_pc_asm +; save regs into log_pc_ptr, wrap around to log_pc_start_ptr if +; log_pc_ptr gets > log_pc_end_ptr + ldb 1(scratch1),scratch3 + dep neg,24,1,psr ;set neg + ldb 2(scratch1),scratch2 + ldil l%log_pc_ptr,scratch4 + ldb 3(scratch1),ret0 + fsub,dbl fcycles_last_dcycs,fr_plus_2,ftmp1 + dep scratch2,23,8,scratch3 + ldo r%log_pc_ptr(scratch4),scratch4 + dep instr,7,8,scratch3 + ldw 0(scratch4),scratch2 + dep ret0,15,8,scratch3 + copy kpc,ret1 + depi 0,30,1,psr ;zero + comiclr,<> 0,zero,0 + depi 1,30,1,psr ;set zero + stw scratch3,LOG_PC_INSTR(scratch2) + dep dbank,7,8,ret1 + copy acc,scratch3 + dep psr,15,16,scratch3 + fadd,dbl fcycles,ftmp1,ftmp1 + stw ret1,LOG_PC_DBANK_KPC(scratch2) + copy yreg,ret1 + stw scratch3,LOG_PC_PSR_ACC(scratch2) + dep xreg,15,16,ret1 + copy direct,scratch3 + fstds ftmp1,LOG_PC_DCYCS(scratch2) + ldw rs%log_pc_end_ptr-log_pc_ptr(scratch4),ret0 + dep stack,15,16,scratch3 + stw ret1,LOG_PC_XREG_YREG(scratch2) + addi LOG_PC_SIZE,scratch2,r31 + stw scratch3,LOG_PC_STACK_DIRECT(scratch2) + +; comb,>= r31,ret0,log_pc_oflow +; nop + + comclr,< r31,ret0,0 +; reload log_pc with log_pc_start_ptr + ldw rs%log_pc_start_ptr-log_pc_ptr(scratch4),r31 + + bv 0(link) + stw r31,0(scratch4) + +log_pc_oflow + ldil l%g_fcycles_stop,scratch3 + ldil l%halt_sim,ret0 + stw 0,r%g_fcycles_stop(scratch3) + ldi 2,arg0 + stw 0,r%g_fcycles_stop+4(scratch3) + stw arg0,r%halt_sim(ret0) + + ldw rs%log_pc_start_ptr-log_pc_ptr(scratch4),r31 + bv 0(link) + stw r31,0(scratch4) +#endif + + + .export dispatch_instr_io,code +dispatch_instr_io +; check if we're here because of timeout or halt required + fcmp,<=,dbl fcycles,fcycles_stop ;C=1 if we can cont + ldwx,s arg2(page_info_ptr),scratch2 + + ftest ;do next instr if must stop + b,n dispatch_done_clr_ret0 + + bb,>=,n scratch2,BANK_IO_BIT,dispatch_instr_pieces + + ldil l%0xc700,scratch1 + ldo r%0xc700(scratch1),scratch1 + addi 0x0a,scratch1,scratch2 + comb,= scratch1,kpc,dispatch_done + zdepi RET_C700,3,4,ret0 + + addi 0xd,scratch1,scratch3 + comb,= scratch2,kpc,dispatch_done + zdepi RET_C70A,3,4,ret0 + + comb,= scratch3,kpc,dispatch_done + zdepi RET_C70D,3,4,ret0 + + .export dispatch_instr_pieces,code +dispatch_instr_pieces +; fetch pc, get size from inst_info_ptr + bl get_mem_long_8,link + copy kpc,arg0 +; ret is instr + ldwx,s ret0(inst_tab_ptr),link + ldil l%sizes_tab,scratch4 + copy ret0,instr + ldo r%sizes_tab(scratch4),scratch4 + addi 1,kpc,arg0 + ldbx instr(scratch4),scratch2 +#ifdef LOG_PC +; save "real" link so call_log_pc can restore it + + stw link,STACK_SAVE_DISPATCH_LINK(sp) + LDC(call_log_pc,link) + stw instr,STACK_SAVE_INSTR(sp) +#endif + stw link,STACK_SAVE_DISP_PIECES_LINK(sp) + + ldi 0x1bea,ret0 + sh3add scratch2,0,scratch2 + ldo STACK_SAVE_TMP_INST(sp),scratch1 + blr scratch2,0 + addit,>= -48,scratch2,0 + +/* must correct cycle count so all instrs are called with cycls += 2 */ +/* since get_mem will auto-inc cycles by the number of bytes, we */ +/* need to "patch" things here, by adding 1 for 1byte, and subbing */ +/* from 3 and 4 byte instrs */ +; 0 + bv 0(link) + CYCLES_PLUS_1 + nop + nop + nop ! nop ! nop ! nop + nop ! nop ! nop ! nop + nop ! nop ! nop ! nop +; 1 + bl get_mem_long_8,link + nop + ldw STACK_SAVE_DISP_PIECES_LINK(sp),link + dep ret0,15,8,ret0 + ldo STACK_SAVE_TMP_INST(sp),scratch1 + bv 0(link) + stw ret0,0(scratch1) + nop ! nop + nop ! nop ! nop ! nop + nop ! nop ! nop ! nop +; 2 + bl get_mem_long_16,link + CYCLES_MINUS_1 + ldo STACK_SAVE_TMP_INST(sp),scratch1 + ldw STACK_SAVE_DISP_PIECES_LINK(sp),link + dep ret0,15,8,ret0 + bv 0(link) + stw ret0,0(scratch1) + nop + nop ! nop ! nop ! nop + nop ! nop ! nop ! nop +; 3 + bl get_mem_long_24,link + CYCLES_MINUS_2 + shd ret0,ret0,16,scratch2 + ldw STACK_SAVE_DISP_PIECES_LINK(sp),link + extru ret0,23,8,ret0 + ldo STACK_SAVE_TMP_INST(sp),scratch1 + dep ret0,23,8,scratch2 + bv 0(link) + stw scratch2,0(scratch1) + nop ! nop ! nop + nop ! nop ! nop ! nop +; 4 variable acc size + extru,<> psr,26,1,0 + bl,n get_mem_long_16,link + bl,n get_mem_long_8,link + CYCLES_MINUS_1 + ldw STACK_SAVE_DISP_PIECES_LINK(sp),link + ldo STACK_SAVE_TMP_INST(sp),scratch1 + dep ret0,15,8,ret0 + bv 0(link) + stw ret0,0(scratch1) + nop ! nop ! nop + nop ! nop ! nop ! nop +; 5 variable x size + extru,<> psr,27,1,0 + bl,n get_mem_long_16,link + bl,n get_mem_long_8,link + CYCLES_MINUS_1 + ldw STACK_SAVE_DISP_PIECES_LINK(sp),link + ldo STACK_SAVE_TMP_INST(sp),scratch1 + dep ret0,15,8,ret0 + bv 0(link) + stw ret0,0(scratch1) + nop ! nop ! nop + nop ! nop ! nop ! nop +; 6 = evil + break + + +#ifdef LOG_PC + .export call_log_pc,code +call_log_pc +; ret0 = operands +; must get instr = instruction +; and link = correct dispatch loc + ldw STACK_SAVE_INSTR(sp),instr + b log_pc_asm + ldw STACK_SAVE_DISPATCH_LINK(sp),link +#endif + +dispatch_done_clr_ret0 + nop ;just in case of bad nullification + b dispatch_done + ldi 0,ret0 + + +#ifdef CHECK_SIZE_CONSISTENCY + .import size_fail,code + + .export check_size_consist +check_size_consist + ldil l%table16,scratch1 + ldil l%table8,scratch2 + ldo r%table16(scratch1),scratch1 + bb,< psr,26,check_tab_8_bit + ldo r%table8(scratch2),scratch2 +; else 16 + comb,= scratch1,inst_tab_ptr,acc_size_ok + nop + + .export acc_tab_fail1 +acc_tab_fail1 + copy inst_tab_ptr,arg1 + copy scratch1,arg2 + bl size_fail,link + ldi 0x100,arg0 + b,n dispatch_done_clr_ret0 +; 8 + .export check_tab_8_bit +check_tab_8_bit + comb,= scratch2,inst_tab_ptr,acc_size_ok + nop + + .export acc_tab_fail0 +acc_tab_fail0 + copy inst_tab_ptr,arg1 + copy scratch2,arg2 + bl size_fail,link + ldi 0x101,arg0 + b dispatch_done + ldi 0,ret0 + + .export acc_size_ok +acc_size_ok + bv 0(link) + nop +#endif + + .align 8 +adc_binary_8_entry2 + extru psr,31,1,scratch3 + add ret0,scratch3,ret0 + + dep ret0,31,8,acc + extru ret0,31,8,zero + +/* and calc overflow */ + xor arg0,ret0,arg2 /* cmp binary add res w/ src1 */ + xor arg0,scratch2,scratch3 /* cmp signs of two inputs */ + extru ret0,24,1,neg + andcm arg2,scratch3,scratch3 /* and that with ~res. */ + extru ret0,23,1,scratch4 + extru scratch3,24,1,scratch3 + dep scratch4,31,1,psr /* set carry */ + b dispatch + dep scratch3,25,1,psr /* set overflow */ + + .align 8 + .export adc_binary_8 +adc_binary_8 + extru ret0,31,8,scratch2 + bb,>= psr,28,adc_binary_8_entry2 + add arg0,scratch2,ret0 + + + ldil l%dispatch,link + b adc_decimal_8 + ldo r%dispatch(link),link + + .export adc_decimal_8 +/* adds arg0 to scratch2 */ +/* acc8 (in arg0) and ret0 have already been added into ret0. Ignore that */ +adc_decimal_8 + ldi 0xf,scratch1 + extru psr,31,1,ret0 + + and arg0,scratch1,scratch3 + and scratch2,scratch1,scratch4 + + add scratch3,scratch4,ret1 + ldi 0xf0,arg3 + + add ret0,ret1,ret0 + + and arg0,arg3,scratch3 + addi -0xa,ret0,ret1 + + and scratch2,arg3,scratch4 + depi 1,27,4,ret1 + comiclr,> 0xa,ret0,0 + copy ret1,ret0 + + add scratch3,scratch4,ret1 + add ret0,ret1,ret0 + + extru ret0,24,1,ret1 + extru ret0,23,1,arg1 + xor ret1,arg1,ret1 + dep ret1,25,1,psr /* ov=((sum>>2) ^ (sum>>1) & 0x40 */ + + comiclr,> 0xa0,ret0,0 + addi 0x60,ret0,ret0 + + xor arg0,scratch2,scratch4 + extru ret0,31,8,zero + + extru,= scratch4,24,1,0 + depi 0,25,1,psr /* no overflow! */ + + + depi 0,31,1,psr + comiclr,> 0x100,ret0,0 + addi 1,psr,psr + + extru ret0,24,1,neg + bv 0(link) + dep zero,31,8,acc + + + + .align 8 + .export sbc_binary_8,code +sbc_binary_8 + extru ret0,31,8,scratch2 + bb,>= psr,28,adc_binary_8_entry2 + add arg0,scratch2,ret0 + + ldil l%dispatch,link + b sbc_decimal_8 + ldo r%dispatch(link),link + + +/* else decimal */ + .export sbc_decimal_8,code +sbc_decimal_8 +/* do arg0 - scratch2 = acc */ + ldi 0xf,scratch1 + extru psr,31,1,ret0 + + and scratch2,scratch1,scratch3 + and arg0,scratch1,scratch4 + + add scratch3,ret0,ret0 + + add ret0,scratch4,ret0 + ldi 0xf0,arg3 + + addi -0x6,ret0,ret1 + and scratch2,arg3,scratch3 + + and ret1,scratch1,ret1 /* sum2 = (sum - 0x6) & 0xf */ + and arg0,arg3,scratch4 + comiclr,<= 0x10,ret0,0 + copy ret1,ret0 /* sum = sum2 */ + + add scratch3,scratch4,ret1 + ldi 0xff,arg2 + add ret0,ret1,ret0 + + extru ret0,24,1,ret1 + addi 0xa0,ret0,scratch3 + extru ret0,23,1,arg3 + and scratch3,arg2,scratch3 /* (sum = sum + 0xa0) & 0xff */ + xor ret1,arg3,ret1 + + dep ret1,25,1,psr /* overflow = ((sum >> 2) ^ */ + /* (sum >> 1)) & 0x40 */ + + depi 0,31,1,psr + comiclr,<= 0x100,ret0,arg3 + or,TR scratch3,0,ret0 + addi 1,psr,psr + + and ret0,arg2,zero + extru ret0,24,1,neg + + xor arg0,scratch2,ret1 + + extru,= ret1,24,1,0 + depi 0,25,1,psr /* clear overflow */ + + bv 0(link) + dep ret0,31,8,acc + + + + .align 8 + .export adc_binary_16 +adc_binary_16 + extru ret0,31,16,scratch2 + bb,< psr,28,adc_decimal_16 + add arg0,scratch2,ret0 + +adc_binary_16_entry2 + extru psr,31,1,scratch1 + add ret0,scratch1,ret0 + + dep ret0,31,16,acc + extru ret0,31,16,zero + +/* and calc overflow */ + xor arg0,ret0,arg2 /* cmp binary add res w/ src1 */ + xor arg0,scratch2,scratch3 + extru ret0,16,1,neg + andcm arg2,scratch3,scratch3 /* and that with ~res. */ + extru ret0,15,1,scratch4 + extru scratch3,16,1,scratch3 + dep scratch4,31,1,psr /* set carry */ + b dispatch + dep scratch3,25,1,psr /* set overflow */ + + + .export adc_decimal_16 +adc_decimal_16 +/* must save arg0, scratch2 */ + stw arg0,STACK_SAVE_DECIMAL16_A(sp) + extru arg0,31,8,arg0 + stw scratch2,STACK_SAVE_DECIMAL16_B(sp) + bl adc_decimal_8,link + extru scratch2,31,8,scratch2 + + ldb STACK_SAVE_DECIMAL16_A+2(sp),arg0 + ldb STACK_SAVE_DECIMAL16_B+2(sp),scratch2 + bl adc_decimal_8,link + stw acc,STACK_SAVE_DECIMAL16_A(sp) + + ldw STACK_SAVE_DECIMAL16_A(sp),scratch1 + zdep acc,23,8,acc + dep scratch1,31,8,acc + b dispatch + copy acc,zero + + + .align 8 + .export sbc_binary_16,code +sbc_binary_16 + extru ret0,31,16,scratch2 + bb,>= psr,28,adc_binary_16_entry2 + add arg0,scratch2,ret0 + +/* else decimal */ + .export sbc_decimal_16,code +sbc_decimal_16 + stw arg0,STACK_SAVE_DECIMAL16_A(sp) + extru arg0,31,8,arg0 + stw scratch2,STACK_SAVE_DECIMAL16_B(sp) + bl sbc_decimal_8,link + extru scratch2,31,8,scratch2 + + ldb STACK_SAVE_DECIMAL16_A+2(sp),arg0 + ldb STACK_SAVE_DECIMAL16_B+2(sp),scratch2 + bl sbc_decimal_8,link + stw acc,STACK_SAVE_DECIMAL16_A(sp) + + ldw STACK_SAVE_DECIMAL16_A(sp),scratch1 + zdep acc,23,8,acc + dep scratch1,31,8,acc + b dispatch + copy acc,zero + + + + +#define ACC8 + .code +#define INCLUDE_RCSID_S +#include "defs_instr.h" +#include "8inst_s.h" +#undef INCLUDE_RCSID_S + .code +#undef SYM +#undef ACC8 + + .code +#include "defs_instr.h" +#include "16inst_s.h" + .code +#undef SYM + + .export inst00_8 + .export inst01_8 + .export inst02_8 + .export inst03_8 + .export inst04_8 + .export inst05_8 + .export inst06_8 + .export inst07_8 + .export inst08_8 + .export inst09_8 + .export inst0a_8 + .export inst0b_8 + .export inst0c_8 + .export inst0d_8 + .export inst0e_8 + .export inst0f_8 + + .export inst10_8 + .export inst11_8 + .export inst12_8 + .export inst13_8 + .export inst14_8 + .export inst15_8 + .export inst16_8 + .export inst17_8 + .export inst18_8 + .export inst19_8 + .export inst1a_8 + .export inst1b_8 + .export inst1c_8 + .export inst1d_8 + .export inst1e_8 + .export inst1f_8 + + .export inst20_8 + .export inst21_8 + .export inst22_8 + .export inst23_8 + .export inst24_8 + .export inst25_8 + .export inst26_8 + .export inst27_8 + .export inst28_8 + .export inst29_8 + .export inst2a_8 + .export inst2b_8 + .export inst2c_8 + .export inst2d_8 + .export inst2e_8 + .export inst2f_8 + + .export inst30_8 + .export inst31_8 + .export inst32_8 + .export inst33_8 + .export inst34_8 + .export inst35_8 + .export inst36_8 + .export inst37_8 + .export inst38_8 + .export inst39_8 + .export inst3a_8 + .export inst3b_8 + .export inst3c_8 + .export inst3d_8 + .export inst3e_8 + .export inst3f_8 + + .export inst40_8 + .export inst41_8 + .export inst42_8 + .export inst43_8 + .export inst44_8 + .export inst45_8 + .export inst46_8 + .export inst47_8 + .export inst48_8 + .export inst49_8 + .export inst4a_8 + .export inst4b_8 + .export inst4c_8 + .export inst4d_8 + .export inst4e_8 + .export inst4f_8 + + .export inst50_8 + .export inst51_8 + .export inst52_8 + .export inst53_8 + .export inst54_8 + .export inst55_8 + .export inst56_8 + .export inst57_8 + .export inst58_8 + .export inst59_8 + .export inst5a_8 + .export inst5b_8 + .export inst5c_8 + .export inst5d_8 + .export inst5e_8 + .export inst5f_8 + + .export inst60_8 + .export inst61_8 + .export inst62_8 + .export inst63_8 + .export inst64_8 + .export inst65_8 + .export inst66_8 + .export inst67_8 + .export inst68_8 + .export inst69_8 + .export inst6a_8 + .export inst6b_8 + .export inst6c_8 + .export inst6d_8 + .export inst6e_8 + .export inst6f_8 + + .export inst70_8 + .export inst71_8 + .export inst72_8 + .export inst73_8 + .export inst74_8 + .export inst75_8 + .export inst76_8 + .export inst77_8 + .export inst78_8 + .export inst79_8 + .export inst7a_8 + .export inst7b_8 + .export inst7c_8 + .export inst7d_8 + .export inst7e_8 + .export inst7f_8 + + .export inst80_8 + .export inst81_8 + .export inst82_8 + .export inst83_8 + .export inst84_8 + .export inst85_8 + .export inst86_8 + .export inst87_8 + .export inst88_8 + .export inst89_8 + .export inst8a_8 + .export inst8b_8 + .export inst8c_8 + .export inst8d_8 + .export inst8e_8 + .export inst8f_8 + .export inst90_8 + .export inst91_8 + .export inst92_8 + .export inst93_8 + .export inst94_8 + .export inst95_8 + .export inst96_8 + .export inst97_8 + .export inst98_8 + .export inst99_8 + .export inst9a_8 + .export inst9b_8 + .export inst9c_8 + .export inst9d_8 + .export inst9e_8 + .export inst9f_8 + .export insta0_8 + .export insta1_8 + .export insta2_8 + .export insta3_8 + .export insta4_8 + .export insta5_8 + .export insta6_8 + .export insta7_8 + .export insta8_8 + .export insta9_8 + .export instaa_8 + .export instab_8 + .export instac_8 + .export instad_8 + .export instae_8 + .export instaf_8 + .export instb0_8 + .export instb1_8 + .export instb2_8 + .export instb3_8 + .export instb4_8 + .export instb5_8 + .export instb6_8 + .export instb7_8 + .export instb8_8 + .export instb9_8 + .export instba_8 + .export instbb_8 + .export instbc_8 + .export instbd_8 + .export instbe_8 + .export instbf_8 + .export instc0_8 + .export instc1_8 + .export instc2_8 + .export instc3_8 + .export instc4_8 + .export instc5_8 + .export instc6_8 + .export instc7_8 + .export instc8_8 + .export instc9_8 + .export instca_8 + .export instcb_8 + .export instcc_8 + .export instcd_8 + .export instce_8 + .export instcf_8 + .export instd0_8 + .export instd1_8 + .export instd2_8 + .export instd3_8 + .export instd4_8 + .export instd5_8 + .export instd6_8 + .export instd7_8 + .export instd8_8 + .export instd9_8 + .export instda_8 + .export instdb_8 + .export instdc_8 + .export instdd_8 + .export instde_8 + .export instdf_8 + .export inste0_8 + .export inste1_8 + .export inste2_8 + .export inste3_8 + .export inste4_8 + .export inste5_8 + .export inste6_8 + .export inste7_8 + .export inste8_8 + .export inste9_8 + .export instea_8 + .export insteb_8 + .export instec_8 + .export insted_8 + .export instee_8 + .export instef_8 + .export instf0_8 + .export instf1_8 + .export instf2_8 + .export instf3_8 + .export instf4_8 + .export instf5_8 + .export instf6_8 + .export instf7_8 + .export instf8_8 + .export instf9_8 + .export instfa_8 + .export instfb_8 + .export instfc_8 + .export instfd_8 + .export instfe_8 + .export instff_8 + + + .export inst00_16 + .export inst01_16 + .export inst02_16 + .export inst03_16 + .export inst04_16 + .export inst05_16 + .export inst06_16 + .export inst07_16 + .export inst08_16 + .export inst09_16 + .export inst0a_16 + .export inst0b_16 + .export inst0c_16 + .export inst0d_16 + .export inst0e_16 + .export inst0f_16 + + .export inst10_16 + .export inst11_16 + .export inst12_16 + .export inst13_16 + .export inst14_16 + .export inst15_16 + .export inst16_16 + .export inst17_16 + .export inst18_16 + .export inst19_16 + .export inst1a_16 + .export inst1b_16 + .export inst1c_16 + .export inst1d_16 + .export inst1e_16 + .export inst1f_16 + + .export inst20_16 + .export inst21_16 + .export inst22_16 + .export inst23_16 + .export inst24_16 + .export inst25_16 + .export inst26_16 + .export inst27_16 + .export inst28_16 + .export inst29_16 + .export inst2a_16 + .export inst2b_16 + .export inst2c_16 + .export inst2d_16 + .export inst2e_16 + .export inst2f_16 + + .export inst30_16 + .export inst31_16 + .export inst32_16 + .export inst33_16 + .export inst34_16 + .export inst35_16 + .export inst36_16 + .export inst37_16 + .export inst38_16 + .export inst39_16 + .export inst3a_16 + .export inst3b_16 + .export inst3c_16 + .export inst3d_16 + .export inst3e_16 + .export inst3f_16 + + .export inst40_16 + .export inst41_16 + .export inst42_16 + .export inst43_16 + .export inst44_16 + .export inst45_16 + .export inst46_16 + .export inst47_16 + .export inst48_16 + .export inst49_16 + .export inst4a_16 + .export inst4b_16 + .export inst4c_16 + .export inst4d_16 + .export inst4e_16 + .export inst4f_16 + + .export inst50_16 + .export inst51_16 + .export inst52_16 + .export inst53_16 + .export inst54_16 + .export inst55_16 + .export inst56_16 + .export inst57_16 + .export inst58_16 + .export inst59_16 + .export inst5a_16 + .export inst5b_16 + .export inst5c_16 + .export inst5d_16 + .export inst5e_16 + .export inst5f_16 + + .export inst60_16 + .export inst61_16 + .export inst62_16 + .export inst63_16 + .export inst64_16 + .export inst65_16 + .export inst66_16 + .export inst67_16 + .export inst68_16 + .export inst69_16 + .export inst6a_16 + .export inst6b_16 + .export inst6c_16 + .export inst6d_16 + .export inst6e_16 + .export inst6f_16 + + .export inst70_16 + .export inst71_16 + .export inst72_16 + .export inst73_16 + .export inst74_16 + .export inst75_16 + .export inst76_16 + .export inst77_16 + .export inst78_16 + .export inst79_16 + .export inst7a_16 + .export inst7b_16 + .export inst7c_16 + .export inst7d_16 + .export inst7e_16 + .export inst7f_16 + + .export inst80_16 + .export inst81_16 + .export inst82_16 + .export inst83_16 + .export inst84_16 + .export inst85_16 + .export inst86_16 + .export inst87_16 + .export inst88_16 + .export inst89_16 + .export inst8a_16 + .export inst8b_16 + .export inst8c_16 + .export inst8d_16 + .export inst8e_16 + .export inst8f_16 + .export inst90_16 + .export inst91_16 + .export inst92_16 + .export inst93_16 + .export inst94_16 + .export inst95_16 + .export inst96_16 + .export inst97_16 + .export inst98_16 + .export inst99_16 + .export inst9a_16 + .export inst9b_16 + .export inst9c_16 + .export inst9d_16 + .export inst9e_16 + .export inst9f_16 + .export insta0_16 + .export insta1_16 + .export insta2_16 + .export insta3_16 + .export insta4_16 + .export insta5_16 + .export insta6_16 + .export insta7_16 + .export insta8_16 + .export insta9_16 + .export instaa_16 + .export instab_16 + .export instac_16 + .export instad_16 + .export instae_16 + .export instaf_16 + .export instb0_16 + .export instb1_16 + .export instb2_16 + .export instb3_16 + .export instb4_16 + .export instb5_16 + .export instb6_16 + .export instb7_16 + .export instb8_16 + .export instb9_16 + .export instba_16 + .export instbb_16 + .export instbc_16 + .export instbd_16 + .export instbe_16 + .export instbf_16 + .export instc0_16 + .export instc1_16 + .export instc2_16 + .export instc3_16 + .export instc4_16 + .export instc5_16 + .export instc6_16 + .export instc7_16 + .export instc8_16 + .export instc9_16 + .export instca_16 + .export instcb_16 + .export instcc_16 + .export instcd_16 + .export instce_16 + .export instcf_16 + .export instd0_16 + .export instd1_16 + .export instd2_16 + .export instd3_16 + .export instd4_16 + .export instd5_16 + .export instd6_16 + .export instd7_16 + .export instd8_16 + .export instd9_16 + .export instda_16 + .export instdb_16 + .export instdc_16 + .export instdd_16 + .export instde_16 + .export instdf_16 + .export inste0_16 + .export inste1_16 + .export inste2_16 + .export inste3_16 + .export inste4_16 + .export inste5_16 + .export inste6_16 + .export inste7_16 + .export inste8_16 + .export inste9_16 + .export instea_16 + .export insteb_16 + .export instec_16 + .export insted_16 + .export instee_16 + .export instef_16 + .export instf0_16 + .export instf1_16 + .export instf2_16 + .export instf3_16 + .export instf4_16 + .export instf5_16 + .export instf6_16 + .export instf7_16 + .export instf8_16 + .export instf9_16 + .export instfa_16 + .export instfb_16 + .export instfc_16 + .export instfd_16 + .export instfe_16 + .export instff_16 + + + .data +#define INCLUDE_RCSID_S +#include "8size_s.h" +#undef INCLUDE_RCSID_S + + .export table8,data +table8 +#include "8size_s.h" + + .export table16,data +table16 +#include "16size_s.h" + + .export sizes_tab,data +sizes_tab +#include "size_s.h" + + + .export g_engine_c_mode,data +g_engine_c_mode + .word 0 + + .bss + + .export slow_memory,data + .export rom_fc_ff,data + .export rom_cards,data + .export dummy_memory1,data + .align 0x100 +slow_memory .block 128*1024 +dummy_memory1 .block 3*1024 +rom_fc_ff .block 256*1024 +rom_cards .block 256*16 + diff --git a/src/info.nib b/src/info.nib new file mode 100644 index 0000000..4fcb7b8 --- /dev/null +++ b/src/info.nib @@ -0,0 +1,19 @@ + + + + + IBDocumentLocation + 152 85 356 240 0 0 1280 832 + IBEditorPositions + + 29 + 69 252 182 44 0 0 1280 832 + + IBFramework Version + 291.0 + IBSystem Version + 6R73 + targetFramework + IBCarbonFramework + + diff --git a/src/instable.h b/src/instable.h new file mode 100644 index 0000000..d350611 --- /dev/null +++ b/src/instable.h @@ -0,0 +1,2703 @@ +/************************************************************************/ +/* KEGS: Apple //gs Emulator */ +/* Copyright 2002 by Kent Dickey */ +/* */ +/* This code is covered by the GNU GPL */ +/* */ +/* The KEGS web page is kegs.sourceforge.net */ +/* You may contact the author at: kadickey@alumni.princeton.edu */ +/************************************************************************/ + +#ifdef ASM +# ifdef INCLUDE_RCSID_S + .stringz "@(#)$KmKId: instable.h,v 1.103 2004-01-10 15:50:50-05 kentd Exp $" +# endif +#endif + +inst00_SYM /* brk */ +#ifdef ASM + ldb 1(scratch1),ret0 + ldil l%g_testing,arg3 + ldil l%g_num_brk,arg1 + ldw r%g_testing(arg3),arg3 + INC_KPC_2; + ldw r%g_num_brk(arg1),arg2 + comib,<> 0,arg3,brk_testing_SYM + extru kpc,31,16,arg0 + addi 1,arg2,arg2 + bb,>= psr,23,brk_native_SYM + stw arg2,r%g_num_brk(arg1) + + bl push_16,link + nop + + bl push_8,link + extru psr,31,8,arg0 ;B bit already on in PSR + + ldil l%0xfffe,arg0 + bl get_mem_long_16,link + ldo r%0xfffe(arg0),arg0 + + zdep ret0,31,16,kpc ;set kbank to 0 + +#if 0 + bl set_halt_act,link + ldi 3,arg0 +#endif + + + ldi 0,dbank ;clear dbank in emul mode + b dispatch + depi 1,29,2,psr ;ints masked, decimal off + + +brk_native_SYM + stw arg0,STACK_SAVE_COP_ARG0(sp) + bl push_8,link + extru kpc,15,8,arg0 + + bl push_16,link + ldw STACK_SAVE_COP_ARG0(sp),arg0 + + bl push_8,link + extru psr,31,8,arg0 + + ldil l%0xffe6,arg0 + bl get_mem_long_16,link + ldo r%0xffe6(arg0),arg0 + + zdep ret0,31,16,kpc ;zero kbank in kpc + +#if 0 +#endif + bl set_halt_act,link + ldi 3,arg0 + + b dispatch + depi 1,29,2,psr ;ints masked, decimal off + +brk_testing_SYM + DEC_KPC2; + CYCLES_PLUS_2 + b dispatch_done + depi RET_BREAK,3,4,ret0 + +#else + GET_1BYTE_ARG; + if(g_testing) { + CYCLES_PLUS_2; + FINISH(RET_BREAK, arg); + } + g_num_brk++; + INC_KPC_2; + if(psr & 0x100) { + PUSH16(kpc & 0xffff); + PUSH8(psr & 0xff); + GET_MEMORY16(0xfffe, kpc, 0); + dbank = 0; + } else { + PUSH8(kpc >> 16); + PUSH16(kpc); + PUSH8(psr & 0xff); + GET_MEMORY16(0xffe6, kpc, 0); + halt_printf("Halting for native break!\n"); + } + kpc = kpc & 0xffff; + psr |= 0x4; + psr &= ~(0x8); +#endif + +inst01_SYM /* ORA (Dloc,X) */ +/* called with arg = val to ORA in */ + GET_DLOC_X_IND_RD(); + ORA_INST(); + +inst02_SYM /* COP */ +#ifdef ASM + ldil l%g_num_cop,arg1 + INC_KPC_2; + ldw r%g_num_cop(arg1),arg2 + extru kpc,31,16,arg0 + addi 1,arg2,arg2 + bb,>= psr,23,cop_native_SYM + stw arg2,r%g_num_cop(arg1) + + bl push_16,link + nop + + bl push_8,link + extru psr,31,8,arg0 + + ldil l%0xfff4,arg0 + bl get_mem_long_16,link + ldo r%0xfff4(arg0),arg0 + + ldi 0,dbank ;clear dbank in emul mode + zdep ret0,31,16,kpc ;clear kbank + + bl set_halt_act,link + ldi 3,arg0 + + b dispatch + depi 1,29,2,psr ;ints masked, decimal off + +cop_native_SYM + stw arg0,STACK_SAVE_COP_ARG0(sp) + bl push_8,link + extru kpc,15,8,arg0 + + bl push_16,link + ldw STACK_SAVE_COP_ARG0(sp),arg0 + + bl push_8,link + extru psr,31,8,arg0 + + ldil l%0xffe4,arg0 + bl get_mem_long_16,link + ldo r%0xffe4(arg0),arg0 + + zdep ret0,31,16,kpc ;clear kbank + b dispatch + depi 1,29,2,psr ;ints masked, decimal off + + +#else + g_num_cop++; + INC_KPC_2; + if(psr & 0x100) { + halt_printf("Halting for emul COP at %04x\n", kpc); + PUSH16(kpc & 0xffff); + PUSH8(psr & 0xff); + GET_MEMORY16(0xfff4, kpc, 0); + dbank = 0; + } else { + PUSH8(kpc >> 16); + PUSH16(kpc & 0xffff); + PUSH8(psr & 0xff); + GET_MEMORY16(0xffe4, kpc, 0); + } + kpc = kpc & 0xffff; + psr |= 4; + psr &= ~(0x8); +#endif + +inst03_SYM /* ORA Disp8,S */ + GET_DISP8_S_RD(); + ORA_INST(); + +inst04_SYM /* TSB Dloc */ + GET_DLOC_RD(); + TSB_INST(1); + +inst05_SYM /* ORA Dloc */ + GET_DLOC_RD(); + ORA_INST(); + +inst06_SYM /* ASL Dloc */ + GET_DLOC_RD(); + ASL_INST(1); + +inst07_SYM /* ORA [Dloc] */ + GET_DLOC_L_IND_RD(); + ORA_INST(); + +inst08_SYM /* PHP */ +#ifdef ASM + dep neg,24,1,psr + ldil l%dispatch,link + INC_KPC_1 + depi 0,30,1,psr + comiclr,<> 0,zero,0 + depi 1,30,1,psr + ldo r%dispatch(link),link + b push_8 + extru psr,31,8,arg0 +#else + INC_KPC_1; + psr = (psr & ~0x82) | ((neg & 1) << 7) | ((!zero) << 1); + PUSH8(psr); +#endif + +inst09_SYM /* ORA #imm */ + GET_IMM_MEM(); + ORA_INST(); + +inst0a_SYM /* ASL a */ +#ifdef ASM +# ifdef ACC8 + ldi 0xff,scratch1 + sh1add acc,0,scratch3 + INC_KPC_1 + extru scratch3,24,1,neg + and scratch3,scratch1,zero + extru scratch3,23,1,scratch2 + dep zero,31,8,acc + b dispatch + dep scratch2,31,1,psr /* set carry */ +# else + zdepi -1,31,16,scratch1 + sh1add acc,0,scratch3 + INC_KPC_1 + extru scratch3,16,1,neg + and scratch3,scratch1,zero + extru scratch3,15,1,scratch2 + dep scratch2,31,1,psr /* set carry */ + b dispatch + dep zero,31,16,acc +# endif +#else + INC_KPC_1; + tmp1 = acc + acc; +# ifdef ACC8 + SET_CARRY8(tmp1); + acc = (acc & 0xff00) + (tmp1 & 0xff); + SET_NEG_ZERO8(acc & 0xff); +# else + SET_CARRY16(tmp1); + acc = tmp1 & 0xffff; + SET_NEG_ZERO16(acc); +# endif +#endif + +inst0b_SYM /* PHD */ +#ifdef ASM + ldil l%dispatch,link + extru direct,31,16,arg0 + INC_KPC_1 + b push_16_unsafe + ldo r%dispatch(link),link +#else + INC_KPC_1; + PUSH16_UNSAFE(direct); +#endif + +inst0c_SYM /* TSB abs */ + GET_ABS_RD(); + TSB_INST(0); + +inst0d_SYM /* ORA abs */ + GET_ABS_RD(); + ORA_INST(); + +inst0e_SYM /* ASL abs */ + GET_ABS_RD(); + ASL_INST(0); + +inst0f_SYM /* ORA long */ + GET_LONG_RD(); + ORA_INST(); + + +inst10_SYM /* BPL disp8 */ +#ifdef ASM + COND_BR1 + comib,<> 0,neg,inst10_2_SYM + COND_BR2 + +inst10_2_SYM + COND_BR_UNTAKEN +#else + BRANCH_DISP8(neg == 0); +#endif + +inst11_SYM /* ORA (Dloc),y */ + GET_DLOC_IND_Y_RD(); + ORA_INST(); + +inst12_SYM /* ORA (Dloc) */ + GET_DLOC_IND_RD(); + ORA_INST(); + +inst13_SYM /* ORA (Disp8,s),y */ + GET_DISP8_S_IND_Y_RD(); + ORA_INST(); + +inst14_SYM /* TRB Dloc */ + GET_DLOC_RD(); + TRB_INST(1); + +inst15_SYM /* ORA Dloc,x */ + GET_DLOC_X_RD(); + ORA_INST(); + +inst16_SYM /* ASL Dloc,X */ + GET_DLOC_X_RD(); + ASL_INST(1); + +inst17_SYM /* ORA [Dloc],Y */ + GET_DLOC_L_IND_Y_RD(); + ORA_INST(); + +inst18_SYM /* CLC */ +#ifdef ASM + INC_KPC_1 + b dispatch + depi 0,31,1,psr /* clear carry */ +#else + psr = psr & (~1); + INC_KPC_1; +#endif + +inst19_SYM /* ORA abs,y */ + GET_ABS_Y_RD(); + ORA_INST(); + + +inst1a_SYM /* INC a */ +#ifdef ASM +# ifdef ACC8 + ldi 0xff,scratch2 + addi 1,acc,scratch1 + extru scratch1,24,1,neg + INC_KPC_1 + extru scratch1,31,8,zero + b dispatch + dep zero,31,8,acc +# else + zdepi -1,31,16,scratch2 + addi 1,acc,scratch1 + extru scratch1,16,1,neg + INC_KPC_1 + extru scratch1,31,16,zero + b dispatch + dep zero,31,16,acc +# endif +#else + INC_KPC_1; +# ifdef ACC8 + acc = (acc & 0xff00) | ((acc + 1) & 0xff); + SET_NEG_ZERO8(acc & 0xff); +# else + acc = (acc + 1) & 0xffff; + SET_NEG_ZERO16(acc); +# endif +#endif + +inst1b_SYM /* TCS */ +#ifdef ASM + copy acc,stack + extru,= psr,23,1,0 /* in emulation mode, stack page 1 */ + depi 1,23,24,stack + INC_KPC_1 + b dispatch + nop +#else + stack = acc; + INC_KPC_1; + if(psr & 0x100) { + stack = (stack & 0xff) + 0x100; + } +#endif + +inst1c_SYM /* TRB Abs */ + GET_ABS_RD(); + TRB_INST(0); + +inst1d_SYM /* ORA Abs,X */ + GET_ABS_X_RD(); + ORA_INST(); + +inst1e_SYM /* ASL Abs,X */ + GET_ABS_X_RD_WR(); + ASL_INST(0); + +inst1f_SYM /* ORA Long,X */ + GET_LONG_X_RD(); + ORA_INST(); + + +inst20_SYM /* JSR abs */ +#ifdef ASM + addi 2,kpc,arg0 + ldb 1(scratch1),scratch2 + CYCLES_PLUS_2 + ldb 2(scratch1),scratch1 + ldil l%dispatch,link + extru arg0,31,16,arg0 + ldo r%dispatch(link),link + dep scratch2,31,8,kpc + b push_16 + dep scratch1,23,8,kpc +#else + GET_2BYTE_ARG; + INC_KPC_2; + PUSH16(kpc); + kpc = (kpc & 0xff0000) + arg; + CYCLES_PLUS_2; +#endif + +inst21_SYM /* AND (Dloc,X) */ +/* called with arg = val to AND in */ + GET_DLOC_X_IND_RD(); + AND_INST(); + +inst22_SYM /* JSL Long */ +#ifdef ASM + INC_KPC_3 + ldb 3(scratch1),scratch2 + copy kpc,arg0 + ldb 1(scratch1),kpc + ldb 2(scratch1),scratch1 + CYCLES_PLUS_3 + dep scratch2,15,8,kpc + stw scratch2,STACK_SAVE_INSTR_TMP1(sp) + bl push_24_unsafe,link + dep scratch1,23,8,kpc + + b dispatch + nop +#else + GET_3BYTE_ARG; + tmp1 = arg; + CYCLES_PLUS_3; + INC_KPC_3; + PUSH24_UNSAFE(kpc); + kpc = tmp1 & 0xffffff; +#endif + +inst23_SYM /* AND Disp8,S */ +/* called with arg = val to AND in */ + GET_DISP8_S_RD(); + AND_INST(); + +inst24_SYM /* BIT Dloc */ + GET_DLOC_RD(); + BIT_INST(); + +inst25_SYM /* AND Dloc */ +/* called with arg = val to AND in */ + GET_DLOC_RD(); + AND_INST(); + +inst26_SYM /* ROL Dloc */ + GET_DLOC_RD(); +/* save1 is now apple addr */ +/* ret0 is data */ + ROL_INST(1); + +inst27_SYM /* AND [Dloc] */ + GET_DLOC_L_IND_RD(); + AND_INST(); + +inst28_SYM /* PLP */ +#ifdef ASM + bl pull_8,link + ldi 0,zero + + extru psr,27,2,scratch2 /* save old x & m */ + dep ret0,31,8,psr + CYCLES_PLUS_1 + INC_KPC_1 + extru,<> ret0,30,1,0 + ldi 1,zero + copy scratch2,arg0 + b update_system_state + extru ret0,24,1,neg +#else + PULL8(tmp1); + tmp2 = psr; + CYCLES_PLUS_1; + INC_KPC_1; + psr = (psr & ~0xff) | (tmp1 & 0xff); + zero = !(psr & 2); + neg = (psr >> 7) & 1; + UPDATE_PSR(psr, tmp2); +#endif + + +inst29_SYM /* AND #imm */ + GET_IMM_MEM(); + AND_INST(); + +inst2a_SYM /* ROL a */ +#ifdef ASM +# ifdef ACC8 + extru psr,31,1,scratch2 + ldi 0xff,scratch1 + sh1add acc,scratch2,scratch3 + INC_KPC_1 + extru scratch3,24,1,neg + and scratch3,scratch1,zero + extru scratch3,23,1,scratch2 + dep zero,31,8,acc + b dispatch + dep scratch2,31,1,psr /* set carry */ +# else + extru psr,31,1,scratch2 + INC_KPC_1 + sh1add acc,scratch2,scratch3 + zdepi -1,31,16,scratch1 + extru scratch3,16,1,neg + and scratch3,scratch1,zero + extru scratch3,15,1,scratch2 + dep scratch2,31,1,psr /* set carry */ + b dispatch + dep zero,31,16,acc +# endif +#else + INC_KPC_1; +# ifdef ACC8 + tmp1 = ((acc & 0xff) << 1) + (psr & 1); + SET_CARRY8(tmp1); + acc = (acc & 0xff00) + (tmp1 & 0xff); + SET_NEG_ZERO8(tmp1 & 0xff); +# else + tmp1 = (acc << 1) + (psr & 1); + SET_CARRY16(tmp1); + acc = (tmp1 & 0xffff); + SET_NEG_ZERO16(acc); +# endif +#endif + +inst2b_SYM /* PLD */ +#ifdef ASM + INC_KPC_1 + bl pull_16_unsafe,link + CYCLES_PLUS_1 + extru ret0,31,16,direct + extru ret0,16,1,neg + b dispatch + copy direct,zero +#else + INC_KPC_1; + PULL16_UNSAFE(direct); + CYCLES_PLUS_1; + SET_NEG_ZERO16(direct); +#endif + +inst2c_SYM /* BIT abs */ + GET_ABS_RD(); + BIT_INST(); + +inst2d_SYM /* AND abs */ + GET_ABS_RD(); + AND_INST(); + +inst2e_SYM /* ROL abs */ + GET_ABS_RD(); + ROL_INST(0); + +inst2f_SYM /* AND long */ + GET_LONG_RD(); + AND_INST(); + + +inst30_SYM /* BMI disp8 */ +#ifdef ASM + COND_BR1 + comib,= 0,neg,inst30_2_SYM + COND_BR2 + +inst30_2_SYM + COND_BR_UNTAKEN +#else + BRANCH_DISP8(neg); +#endif + +inst31_SYM /* AND (Dloc),y */ + GET_DLOC_IND_Y_RD(); + AND_INST(); + +inst32_SYM /* AND (Dloc) */ + GET_DLOC_IND_RD(); + AND_INST(); + +inst33_SYM /* AND (Disp8,s),y */ + GET_DISP8_S_IND_Y_RD(); + AND_INST(); + +inst34_SYM /* BIT Dloc,x */ + GET_DLOC_X_RD(); + BIT_INST(); + +inst35_SYM /* AND Dloc,x */ + GET_DLOC_X_RD(); + AND_INST(); + +inst36_SYM /* ROL Dloc,X */ + GET_DLOC_X_RD(); + ROL_INST(1); + +inst37_SYM /* AND [Dloc],Y */ + GET_DLOC_L_IND_Y_RD(); + AND_INST(); + +inst38_SYM /* SEC */ +#ifdef ASM + INC_KPC_1 + b dispatch + depi 1,31,1,psr /* set carry */ +#else + psr = psr | 1; + INC_KPC_1; +#endif + +inst39_SYM /* AND abs,y */ + GET_ABS_Y_RD(); + AND_INST(); + +inst3a_SYM /* DEC a */ +#ifdef ASM +# ifdef ACC8 + addi -1,acc,scratch1 + extru scratch1,24,1,neg + INC_KPC_1 + extru scratch1,31,8,zero + b dispatch + dep zero,31,8,acc +# else + addi -1,acc,scratch1 + extru scratch1,16,1,neg + INC_KPC_1 + extru scratch1,31,16,zero + b dispatch + dep zero,31,16,acc +# endif +#else + INC_KPC_1; +# ifdef ACC8 + acc = (acc & 0xff00) | ((acc - 1) & 0xff); + SET_NEG_ZERO8(acc & 0xff); +# else + acc = (acc - 1) & 0xffff; + SET_NEG_ZERO16(acc); +# endif +#endif + +inst3b_SYM /* TSC */ +/* set N,Z according to 16 bit acc */ +#ifdef ASM + copy stack,acc + extru stack,16,1,neg + INC_KPC_1 + b dispatch + extru acc,31,16,zero +#else + INC_KPC_1; + acc = stack; + SET_NEG_ZERO16(acc); +#endif + +inst3c_SYM /* BIT Abs,x */ + GET_ABS_X_RD(); + BIT_INST(); + +inst3d_SYM /* AND Abs,X */ + GET_ABS_X_RD(); + AND_INST(); + +inst3e_SYM /* ROL Abs,X */ + GET_ABS_X_RD_WR(); + ROL_INST(0); + +inst3f_SYM /* AND Long,X */ + GET_LONG_X_RD(); + AND_INST(); + + +inst40_SYM /* RTI */ +#ifdef ASM + bb,>= psr,23,rti_native_SYM + CYCLES_PLUS_1 +/* emulation */ + bl pull_24,link + ldi 0,zero + + extru psr,27,2,scratch2 + extru ret0,23,16,scratch3 + copy scratch2,arg0 + extru,<> ret0,30,1,0 + ldi 1,zero + dep ret0,31,8,psr + + extru ret0,24,1,neg + b update_system_state + dep scratch3,31,16,kpc + +rti_native_SYM + bl pull_8,link + ldi 0,zero + + copy ret0,scratch1 + extru ret0,24,1,neg + dep ret0,31,8,scratch1 + bl pull_24,link + stw scratch1,STACK_SAVE_INSTR_TMP1(sp) + + extru psr,27,2,scratch2 + ldw STACK_SAVE_INSTR_TMP1(sp),psr + extru ret0,31,24,kpc + extru,<> psr,30,1,0 + ldi 1,zero + + b update_system_state_and_change_kbank + copy scratch2,arg0 +#else + CYCLES_PLUS_1 + if(psr & 0x100) { + PULL24(tmp1); + kpc = (kpc & 0xff0000) + ((tmp1 >> 8) & 0xffff); + tmp2 = psr; + psr = (psr & ~0xff) + (tmp1 & 0xff); + neg = (psr >> 7) & 1; + zero = !(psr & 2); + UPDATE_PSR(psr, tmp2); + } else { + PULL8(tmp1); + tmp2 = psr; + psr = (tmp1 & 0xff); + neg = (psr >> 7) & 1; + zero = !(psr & 2); + PULL24(kpc); + UPDATE_PSR(psr, tmp2); + } +#endif + + +inst41_SYM /* EOR (Dloc,X) */ +/* called with arg = val to EOR in */ + GET_DLOC_X_IND_RD(); + EOR_INST(); + +inst42_SYM /* WDM */ +#ifdef ASM + ldb 1(scratch1),ret0 + CYCLES_PLUS_5 + CYCLES_PLUS_2 + INC_KPC_2 + b dispatch_done + depi RET_WDM,3,4,ret0 +#else + GET_1BYTE_ARG; + INC_KPC_2; + CYCLES_PLUS_5; + CYCLES_PLUS_2; + FINISH(RET_WDM, arg & 0xff); +#endif + +inst43_SYM /* EOR Disp8,S */ +/* called with arg = val to EOR in */ + GET_DISP8_S_RD(); + EOR_INST(); + +inst44_SYM /* MVP */ +#ifdef ASM + ldb 2(scratch1),scratch2 /* src bank */ + bb,< psr,23,inst44_notnat_SYM + ldb 1(scratch1),dbank /* dest bank */ + bb,< psr,27,inst44_notnat_SYM + stw scratch2,STACK_SRC_BANK(sp) + +inst44_loop_SYM + CYCLES_PLUS_1 + ldw STACK_SRC_BANK(sp),scratch2 + copy xreg,arg0 + + bl get_mem_long_8,link + dep scratch2,15,8,arg0 +/* got byte */ + copy ret0,arg1 + copy yreg,arg0 + bl set_mem_long_8,link + dep dbank,15,8,arg0 +/* wrote byte, dec acc */ + CYCLES_PLUS_2 + fldds 0(fcycles_stop_ptr),fcycles_stop + addi -1,xreg,xreg + zdepi -1,31,16,scratch2 + addi -1,yreg,yreg + addi -1,acc,acc + fcmp,<,dbl fcycles,fcycles_stop + and xreg,scratch2,xreg + extrs acc,31,16,scratch1 + and yreg,scratch2,yreg + + comib,= -1,scratch1,inst44_done_SYM + and acc,scratch2,acc + + ftest + b inst44_out_of_time_SYM + + CYCLES_PLUS_2 + b inst44_loop_SYM + nop + +/* get here if done */ +inst44_done_SYM + INC_KPC_3 + b dispatch + nop + +inst44_notnat_SYM + copy dbank,ret0 + dep scratch2,23,8,ret0 + CYCLES_PLUS_3 + depi RET_MVP,3,4,ret0 + b dispatch_done + CYCLES_PLUS_2 + +inst44_out_of_time_SYM +/* cycle have gone positive, just get out, do not update kpc */ + b,n dispatch +#else + GET_2BYTE_ARG; + /* arg & 0xff = dest bank, arg & 0xff00 = src bank */ + if(psr & 0x110) { + halt_printf("MVP but not native m or x!\n"); + break; + } + CYCLES_MINUS_2 + dbank = arg & 0xff; + tmp1 = (arg >> 8) & 0xff; + while(1) { + CYCLES_PLUS_3; + GET_MEMORY8((tmp1 << 16) + xreg, arg); + SET_MEMORY8((dbank << 16) + yreg, arg); + CYCLES_PLUS_2; + xreg = (xreg - 1) & 0xffff; + yreg = (yreg - 1) & 0xffff; + acc = (acc - 1) & 0xffff; + if(acc == 0xffff) { + INC_KPC_3; + break; + } + if(fcycles >= g_fcycles_stop) { + break; + } + } +#endif + + +inst45_SYM /* EOR Dloc */ +/* called with arg = val to EOR in */ + GET_DLOC_RD(); + EOR_INST(); + +inst46_SYM /* LSR Dloc */ + GET_DLOC_RD(); +/* save1 is now apple addr */ +/* ret0 is data */ + LSR_INST(1); + +inst47_SYM /* EOR [Dloc] */ + GET_DLOC_L_IND_RD(); + EOR_INST(); + +inst48_SYM /* PHA */ +#ifdef ASM +# ifdef ACC8 + INC_KPC_1 + ldil l%dispatch,link + extru acc,31,8,arg0 + b push_8 + ldo r%dispatch(link),link +# else + INC_KPC_1 + ldil l%dispatch,link + extru acc,31,16,arg0 + b push_16 + ldo r%dispatch(link),link +# endif +#else + INC_KPC_1; +# ifdef ACC8 + PUSH8(acc); +# else + PUSH16(acc); +# endif +#endif + +inst49_SYM /* EOR #imm */ + GET_IMM_MEM(); + EOR_INST(); + +inst4a_SYM /* LSR a */ +#ifdef ASM +# ifdef ACC8 + extru acc,31,1,scratch2 + INC_KPC_1 + extru acc,30,7,zero + ldi 0,neg + dep scratch2,31,1,psr /* set carry */ + b dispatch + dep zero,31,8,acc +# else + extru acc,31,1,scratch2 + INC_KPC_1 + extru acc,30,15,zero + ldi 0,neg + dep scratch2,31,1,psr /* set carry */ + b dispatch + dep zero,31,16,acc +# endif +#else + INC_KPC_1; +# ifdef ACC8 + tmp1 = ((acc & 0xff) >> 1); + SET_CARRY8(acc << 8); + acc = (acc & 0xff00) + (tmp1 & 0xff); + SET_NEG_ZERO8(tmp1 & 0xff); +# else + tmp1 = (acc >> 1); + SET_CARRY8((acc << 8)); + acc = (tmp1 & 0xffff); + SET_NEG_ZERO16(acc); +# endif +#endif + +inst4b_SYM /* PHK */ +#ifdef ASM + ldil l%dispatch,link + extru kpc,15,8,arg0 + INC_KPC_1 + b push_8 + ldo r%dispatch(link),link +#else + PUSH8(kpc >> 16); + INC_KPC_1; +#endif + +inst4c_SYM /* JMP abs */ +#ifdef ASM + ldb 1(scratch1),scratch2 + CYCLES_PLUS_1 + ldb 2(scratch1),scratch1 + dep scratch2,31,8,kpc + b dispatch + dep scratch1,23,8,kpc +#else + GET_2BYTE_ARG; + CYCLES_PLUS_1; + kpc = (kpc & 0xff0000) + arg; +#endif + + +inst4d_SYM /* EOR abs */ + GET_ABS_RD(); + EOR_INST(); + +inst4e_SYM /* LSR abs */ + GET_ABS_RD(); + LSR_INST(0); + +inst4f_SYM /* EOR long */ + GET_LONG_RD(); + EOR_INST(); + + +inst50_SYM /* BVC disp8 */ +#ifdef ASM + COND_BR1 + bb,< psr,25,inst50_2_SYM + COND_BR2 + +inst50_2_SYM + COND_BR_UNTAKEN + +#else + BRANCH_DISP8((psr & 0x40) == 0); +#endif + +inst51_SYM /* EOR (Dloc),y */ + GET_DLOC_IND_Y_RD(); + EOR_INST(); + +inst52_SYM /* EOR (Dloc) */ + GET_DLOC_IND_RD(); + EOR_INST(); + +inst53_SYM /* EOR (Disp8,s),y */ + GET_DISP8_S_IND_Y_RD(); + EOR_INST(); + +inst54_SYM /* MVN */ +#ifdef ASM + ldb 2(scratch1),scratch2 /* src bank */ + bb,< psr,23,inst54_notnat_SYM + ldb 1(scratch1),dbank /* dest bank */ + bb,< psr,27,inst54_notnat_SYM + stw scratch2,STACK_SRC_BANK(sp) + +/* even in 8bit acc mode, use 16-bit accumulator! */ + +inst54_loop_SYM + CYCLES_PLUS_1 + ldw STACK_SRC_BANK(sp),scratch2 + copy xreg,arg0 + + bl get_mem_long_8,link + dep scratch2,15,8,arg0 +/* got byte */ + copy ret0,arg1 + copy yreg,arg0 + bl set_mem_long_8,link + dep dbank,15,8,arg0 +/* wrote byte, dec acc */ + CYCLES_PLUS_2 + fldds 0(fcycles_stop_ptr),fcycles_stop + addi 1,xreg,xreg + zdepi -1,31,16,scratch2 + addi 1,yreg,yreg + addi -1,acc,acc + fcmp,<,dbl fcycles,fcycles_stop + and xreg,scratch2,xreg + extrs acc,31,16,scratch1 + and yreg,scratch2,yreg + + comib,= -1,scratch1,inst54_done_SYM + and acc,scratch2,acc + ftest + b,n inst54_out_of_time_SYM + + CYCLES_PLUS_2 + b inst54_loop_SYM + nop + +/* get here if done */ +inst54_done_SYM + INC_KPC_3 + b dispatch + nop + +inst54_out_of_time_SYM +/* cycle have gone positive, just get out, don't update kpc */ + b,n dispatch + +inst54_notnat_SYM + copy dbank,ret0 + dep scratch2,23,8,ret0 + CYCLES_PLUS_3 + depi RET_MVN,3,4,ret0 + b dispatch_done + CYCLES_PLUS_3 +#else + GET_2BYTE_ARG; + /* arg & 0xff = dest bank, arg & 0xff00 = src bank */ + if(psr & 0x110) { + halt_printf("MVN but not native m or x!\n"); + break; + } + CYCLES_MINUS_2; + dbank = arg & 0xff; + tmp1 = (arg >> 8) & 0xff; + while(1) { + CYCLES_PLUS_3; + GET_MEMORY8((tmp1 << 16) + xreg, arg); + SET_MEMORY8((dbank << 16) + yreg, arg); + CYCLES_PLUS_2; + xreg = (xreg + 1) & 0xffff; + yreg = (yreg + 1) & 0xffff; + acc = (acc - 1) & 0xffff; + if(acc == 0xffff) { + INC_KPC_3; + break; + } + if(fcycles >= g_fcycles_stop) { + break; + } + } +#endif + +inst55_SYM /* EOR Dloc,x */ + GET_DLOC_X_RD(); + EOR_INST(); + +inst56_SYM /* LSR Dloc,X */ + GET_DLOC_X_RD(); + LSR_INST(1); + +inst57_SYM /* EOR [Dloc],Y */ + GET_DLOC_L_IND_Y_RD(); + EOR_INST(); + +inst58_SYM /* CLI */ +#ifdef ASM + INC_KPC_1 + b check_irqs_pending /* check for ints pending! */ + depi 0,29,1,psr /* clear int disable */ +#else + psr = psr & (~4); + INC_KPC_1; + if(((psr & 0x4) == 0) && g_irq_pending) { + FINISH(RET_IRQ, 0); + } +#endif + +inst59_SYM /* EOR abs,y */ + GET_ABS_Y_RD(); + EOR_INST(); + +inst5a_SYM /* PHY */ +#ifdef ASM + INC_KPC_1 + ldil l%dispatch,link + bb,>= psr,27,phy_16_SYM + ldo r%dispatch(link),link + + b push_8 + copy yreg,arg0 + +phy_16_SYM + b push_16 + copy yreg,arg0 +#else + INC_KPC_1; + if(psr & 0x10) { + PUSH8(yreg); + } else { + PUSH16(yreg); + } +#endif + +inst5b_SYM /* TCD */ +#ifdef ASM + extru acc,31,16,direct + INC_KPC_1 + copy acc,zero + b dispatch + extru acc,16,1,neg +#else + INC_KPC_1; + direct = acc; + SET_NEG_ZERO16(acc); +#endif + +inst5c_SYM /* JMP Long */ +#ifdef ASM + ldb 1(scratch1),kpc + ldb 2(scratch1),scratch2 + CYCLES_PLUS_1 + ldb 3(scratch1),arg0 /* new bank */ + dep scratch2,23,8,kpc + b dispatch + dep arg0,15,8,kpc +#else + GET_3BYTE_ARG; + CYCLES_PLUS_1; + kpc = arg; +#endif + +inst5d_SYM /* EOR Abs,X */ + GET_ABS_X_RD(); + EOR_INST(); + +inst5e_SYM /* LSR Abs,X */ + GET_ABS_X_RD_WR(); + LSR_INST(0); + +inst5f_SYM /* EOR Long,X */ + GET_LONG_X_RD(); + EOR_INST(); + + +inst60_SYM /* RTS */ +#ifdef ASM + bl pull_16,link + CYCLES_PLUS_2 +/* ret0 is new kpc-1 */ + addi 1,ret0,ret0 + b dispatch + dep ret0,31,16,kpc +#else + CYCLES_PLUS_2 + PULL16(tmp1); + kpc = (kpc & 0xff0000) + ((tmp1 + 1) & 0xffff); +#endif + + +inst61_SYM /* ADC (Dloc,X) */ +/* called with arg = val to ADC in */ + GET_DLOC_X_IND_RD(); + ADC_INST(); + +inst62_SYM /* PER */ +#ifdef ASM + ldb 1(scratch1),ret0 + INC_KPC_3 + ldb 2(scratch1),scratch1 + CYCLES_PLUS_2 + ldil l%dispatch,link + dep scratch1,23,8,ret0 + ldo r%dispatch(link),link + add kpc,ret0,arg0 + b push_16_unsafe + extru arg0,31,16,arg0 +#else + GET_2BYTE_ARG; + CYCLES_PLUS_2; + INC_KPC_3; + PUSH16_UNSAFE(kpc + arg); +#endif + +inst63_SYM /* ADC Disp8,S */ +/* called with arg = val to ADC in */ + GET_DISP8_S_RD(); + ADC_INST(); + +inst64_SYM /* STZ Dloc */ + GET_DLOC_ADDR(); + STZ_INST(1); + +inst65_SYM /* ADC Dloc */ +/* called with arg = val to ADC in */ + GET_DLOC_RD(); + ADC_INST(); + +inst66_SYM /* ROR Dloc */ + GET_DLOC_RD(); +/* save1 is now apple addr */ +/* ret0 is data */ + ROR_INST(1); + +inst67_SYM /* ADC [Dloc] */ + GET_DLOC_L_IND_RD(); + ADC_INST(); + +inst68_SYM /* PLA */ +#ifdef ASM +# ifdef ACC8 + INC_KPC_1 + bl pull_8,link + CYCLES_PLUS_1 + extru ret0,31,8,zero + extru ret0,24,1,neg + b dispatch + dep ret0,31,8,acc +# else + INC_KPC_1 + bl pull_16,link + CYCLES_PLUS_1 + + extru ret0,31,16,zero + extru ret0,16,1,neg + b dispatch + extru ret0,31,16,acc +# endif +#else + INC_KPC_1; + CYCLES_PLUS_1; +# ifdef ACC8 + PULL8(tmp1); + acc = (acc & 0xff00) + tmp1; + SET_NEG_ZERO8(tmp1); +# else + PULL16(tmp1); + acc = tmp1; + SET_NEG_ZERO16(tmp1); +# endif +#endif + + +inst69_SYM /* ADC #imm */ + GET_IMM_MEM(); + ADC_INST(); + +inst6a_SYM /* ROR a */ +#ifdef ASM +# ifdef ACC8 + extru psr,31,1,neg + INC_KPC_1 + extru acc,30,7,zero + dep neg,24,1,zero + dep acc,31,1,psr /* set carry */ + b dispatch + dep zero,31,8,acc +# else + extru psr,31,1,neg + INC_KPC_1 + extru acc,30,15,zero + dep neg,16,1,zero + dep acc,31,1,psr /* set carry */ + b dispatch + dep zero,31,16,acc +# endif +#else + INC_KPC_1; +# ifdef ACC8 + tmp1 = ((acc & 0xff) >> 1) + ((psr & 1) << 7); + SET_CARRY8((acc << 8)); + acc = (acc & 0xff00) + (tmp1 & 0xff); + SET_NEG_ZERO8(tmp1 & 0xff); +# else + tmp1 = (acc >> 1) + ((psr & 1) << 15); + SET_CARRY16((acc << 16)); + acc = (tmp1 & 0xffff); + SET_NEG_ZERO16(acc); +# endif +#endif + +inst6b_SYM /* RTL */ +#ifdef ASM + bl pull_24,link + CYCLES_PLUS_1 +/* ret0 is new kpc-1 */ + copy ret0,kpc + addi 1,ret0,scratch1 + b dispatch + dep scratch1,31,16,kpc + +#else + CYCLES_PLUS_1; + PULL24(tmp1); + kpc = (tmp1 & 0xff0000) + ((tmp1 + 1) & 0xffff); +#endif + +inst6c_SYM /* JMP (abs) */ +#ifdef ASM + ldb 1(scratch1),arg0 + CYCLES_PLUS_1 + ldb 2(scratch1),scratch1 + bl get_mem_long_16,link + dep scratch1,23,8,arg0 +/* ret0 is addr to jump to */ + b dispatch + dep ret0,31,16,kpc +#else + GET_2BYTE_ARG; + CYCLES_PLUS_1; + GET_MEMORY16(arg, tmp1, 1); + kpc = (kpc & 0xff0000) + tmp1; +#endif + +inst6d_SYM /* ADC abs */ + GET_ABS_RD(); + ADC_INST(); + +inst6e_SYM /* ROR abs */ + GET_ABS_RD(); + ROR_INST(0); + +inst6f_SYM /* ADC long */ + GET_LONG_RD(); + ADC_INST(); + + +inst70_SYM /* BVS disp8 */ +#ifdef ASM + COND_BR1 + bb,>= psr,25,inst70_2_SYM + COND_BR2 + +inst70_2_SYM + COND_BR_UNTAKEN +#else + BRANCH_DISP8((psr & 0x40)); +#endif + +inst71_SYM /* ADC (Dloc),y */ + GET_DLOC_IND_Y_RD(); + ADC_INST(); + +inst72_SYM /* ADC (Dloc) */ + GET_DLOC_IND_RD(); + ADC_INST(); + +inst73_SYM /* ADC (Disp8,s),y */ + GET_DISP8_S_IND_Y_RD(); + ADC_INST(); + +inst74_SYM /* STZ Dloc,x */ +#ifdef ASM + ldb 1(scratch1),arg0 + GET_DLOC_X_WR(); + STZ_INST(1); +#else + GET_1BYTE_ARG; + GET_DLOC_X_WR(); + STZ_INST(1); +#endif + +inst75_SYM /* ADC Dloc,x */ + GET_DLOC_X_RD(); + ADC_INST(); + +inst76_SYM /* ROR Dloc,X */ + GET_DLOC_X_RD(); + ROR_INST(1); + +inst77_SYM /* ADC [Dloc],Y */ + GET_DLOC_L_IND_Y_RD(); + ADC_INST(); + +inst78_SYM /* SEI */ +#ifdef ASM + INC_KPC_1 + b dispatch + depi 1,29,1,psr /* set int disable */ +#else + psr = psr | 4; + INC_KPC_1; +#endif + +inst79_SYM /* ADC abs,y */ + GET_ABS_Y_RD(); + ADC_INST(); + +inst7a_SYM /* PLY */ +#ifdef ASM + INC_KPC_1 + bb,>= psr,27,inst7a_16bit_SYM + nop + + bl pull_8,link + CYCLES_PLUS_1 + + extru ret0,31,8,zero + extru ret0,24,1,neg + b dispatch + copy zero,yreg + +inst7a_16bit_SYM + bl pull_16,link + CYCLES_PLUS_1 + + extru ret0,31,16,zero + extru ret0,16,1,neg + b dispatch + copy zero,yreg + +#else + INC_KPC_1; + CYCLES_PLUS_1 + if(psr & 0x10) { + PULL8(yreg); + SET_NEG_ZERO8(yreg); + } else { + PULL16(yreg); + SET_NEG_ZERO16(yreg); + } +#endif + +inst7b_SYM /* TDC */ +#ifdef ASM + extru direct,31,16,zero + copy direct,acc + INC_KPC_1 + b dispatch + extru direct,16,1,neg +#else + INC_KPC_1; + acc = direct; + SET_NEG_ZERO16(direct); +#endif + +inst7c_SYM /* JMP (Abs,x) */ +/* always access kbank, xreg cannot wrap into next bank */ +#ifdef ASM + ldb 1(scratch1),ret0 + copy kpc,scratch2 + ldb 2(scratch1),scratch1 + dep xreg,31,16,scratch2 + CYCLES_PLUS_2 + dep scratch1,23,8,ret0 + add ret0,scratch2,arg0 + bl get_mem_long_16,link + extru arg0,31,24,arg0 + b dispatch + dep ret0,31,16,kpc +#else + GET_2BYTE_ARG; + arg = (kpc & 0xff0000) + ((xreg + arg) & 0xffff); + CYCLES_PLUS_2; + GET_MEMORY16(arg, tmp1, 1); + kpc = (kpc & 0xff0000) + tmp1; +#endif + +inst7d_SYM /* ADC Abs,X */ + GET_ABS_X_RD(); + ADC_INST(); + +inst7e_SYM /* ROR Abs,X */ + GET_ABS_X_RD_WR(); + ROR_INST(0); + +inst7f_SYM /* ADC Long,X */ + GET_LONG_X_RD(); + ADC_INST(); + + +inst80_SYM /* BRA */ +#ifdef ASM + COND_BR1 + COND_BR2 +#else + BRANCH_DISP8(1); +#endif + + +inst81_SYM /* STA (Dloc,X) */ + GET_DLOC_X_IND_ADDR(); + STA_INST(0); + +inst82_SYM /* BRL disp16 */ +#ifdef ASM + ldb 1(scratch1),ret0 + CYCLES_PLUS_1 + ldb 2(scratch1),scratch1 + addi 3,kpc,scratch2 + dep scratch1,23,8,ret0 + add ret0,scratch2,scratch2 + b dispatch + dep scratch2,31,16,kpc +#else + GET_2BYTE_ARG; + CYCLES_PLUS_1; + kpc = (kpc & 0xff0000) + ((kpc + 3 + arg) & 0xffff); +#endif + +inst83_SYM /* STA Disp8,S */ + GET_DISP8_S_ADDR(); + STA_INST(1); + +inst84_SYM /* STY Dloc */ + GET_DLOC_ADDR(); + STY_INST(1); + + +inst85_SYM /* STA Dloc */ + GET_DLOC_ADDR(); + STA_INST(1); + +inst86_SYM /* STX Dloc */ + GET_DLOC_ADDR(); + STX_INST(1); + + +inst87_SYM /* STA [Dloc] */ + GET_DLOC_L_IND_ADDR(); + STA_INST(0); + +inst88_SYM /* DEY */ +#ifdef ASM + INC_KPC_1 + bb,< psr,27,inst88_8bit_SYM + addi -1,yreg,yreg +/* 16 bit */ + extru yreg,31,16,zero + extru yreg,16,1,neg + b dispatch + copy zero,yreg + +inst88_8bit_SYM + extru yreg,31,8,zero + extru yreg,24,1,neg + b dispatch + copy zero,yreg +#else + INC_KPC_1; + SET_INDEX_REG(yreg - 1, yreg); +#endif + +inst89_SYM /* BIT #imm */ +#ifdef ASM + GET_IMM_MEM(); +# ifdef ACC8 +/* Immediate BIT does not set condition flags */ + and acc,ret0,zero + b dispatch + extru zero,31,8,zero +# else + and acc,ret0,zero + b dispatch + extru zero,31,16,zero +# endif +#else + GET_IMM_MEM(); +# ifdef ACC8 + zero = (acc & arg) & 0xff; +# else + zero = (acc & arg) & 0xffff; +# endif +#endif + +inst8a_SYM /* TXA */ +#ifdef ASM +# ifdef ACC8 + extru xreg,31,8,zero + INC_KPC_1 + extru xreg,24,1,neg + b dispatch + dep zero,31,8,acc +# else + extru xreg,31,16,zero + INC_KPC_1 + extru xreg,16,1,neg + b dispatch + zdep zero,31,16,acc +# endif +#else + INC_KPC_1; + arg = xreg; + LDA_INST(); +#endif + +inst8b_SYM /* PHB */ +#ifdef ASM + ldil l%dispatch,link + extru dbank,31,8,arg0 + INC_KPC_1 + b push_8 + ldo r%dispatch(link),link +#else + INC_KPC_1; + PUSH8(dbank); +#endif + +inst8c_SYM /* STY abs */ + GET_ABS_ADDR(); + STY_INST(0); + +inst8d_SYM /* STA abs */ + GET_ABS_ADDR(); + STA_INST(0); + +inst8e_SYM /* STX abs */ + GET_ABS_ADDR(); + STX_INST(0); + + +inst8f_SYM /* STA long */ + GET_LONG_ADDR(); + STA_INST(0); + + +inst90_SYM /* BCC disp8 */ +#ifdef ASM + COND_BR1 + bb,< psr,31,inst90_2_SYM + COND_BR2 + +inst90_2_SYM + COND_BR_UNTAKEN +#else + BRANCH_DISP8((psr & 0x01) == 0); +#endif + + +inst91_SYM /* STA (Dloc),y */ + GET_DLOC_IND_Y_ADDR_FOR_WR(); + STA_INST(0); + +inst92_SYM /* STA (Dloc) */ + GET_DLOC_IND_ADDR(); + STA_INST(0); + +inst93_SYM /* STA (Disp8,s),y */ + GET_DISP8_S_IND_Y_ADDR(); + STA_INST(0); + +inst94_SYM /* STY Dloc,x */ + GET_DLOC_X_ADDR(); + STY_INST(1); + +inst95_SYM /* STA Dloc,x */ + GET_DLOC_X_ADDR(); + STA_INST(1); + +inst96_SYM /* STX Dloc,Y */ + GET_DLOC_Y_ADDR(); + STX_INST(1); + +inst97_SYM /* STA [Dloc],Y */ + GET_DLOC_L_IND_Y_ADDR(); + STA_INST(0); + +inst98_SYM /* TYA */ +#ifdef ASM +# ifdef ACC8 + extru yreg,31,8,zero + INC_KPC_1 + extru yreg,24,1,neg + b dispatch + dep zero,31,8,acc +# else + extru yreg,31,16,zero + INC_KPC_1 + extru yreg,16,1,neg + b dispatch + zdep zero,31,16,acc +# endif +#else + INC_KPC_1; + arg = yreg; + LDA_INST(); +#endif + +inst99_SYM /* STA abs,y */ + GET_ABS_INDEX_ADDR_FOR_WR(yreg) + STA_INST(0); + +inst9a_SYM /* TXS */ +#ifdef ASM + copy xreg,stack + extru,= psr,23,1,0 + depi 1,23,24,stack + INC_KPC_1 + b dispatch + nop +#else + stack = xreg; + if(psr & 0x100) { + stack = 0x100 | (stack & 0xff); + } + INC_KPC_1; +#endif + + +inst9b_SYM /* TXY */ +#ifdef ASM + extru xreg,24,1,neg + INC_KPC_1 + extru,<> psr,27,1,0 ;skip next if 8bit + extru xreg,16,1,neg + copy xreg,yreg + b dispatch + copy xreg,zero +#else + SET_INDEX_REG(xreg, yreg); + INC_KPC_1; +#endif + + +inst9c_SYM /* STZ Abs */ + GET_ABS_ADDR(); + STZ_INST(0); + +inst9d_SYM /* STA Abs,X */ + GET_ABS_INDEX_ADDR_FOR_WR(xreg); + STA_INST(0); + +inst9e_SYM /* STZ Abs,X */ + GET_ABS_INDEX_ADDR_FOR_WR(xreg); + STZ_INST(0); + +inst9f_SYM /* STA Long,X */ + GET_LONG_X_ADDR_FOR_WR(); + STA_INST(0); + + +insta0_SYM /* LDY #imm */ +#ifdef ASM + INC_KPC_2 + bb,>= psr,27,insta0_16bit_SYM + ldb 1(scratch1),zero + + extru zero,24,1,neg + b dispatch + copy zero,yreg +insta0_16bit_SYM + ldb 2(scratch1),scratch1 + INC_KPC_1 + CYCLES_PLUS_1 + extru scratch1,24,1,neg + dep scratch1,23,8,zero + b dispatch + copy zero,yreg +#else + INC_KPC_2; + if((psr & 0x10) == 0) { + GET_2BYTE_ARG; + CYCLES_PLUS_1 + INC_KPC_1; + } else { + GET_1BYTE_ARG; + } + SET_INDEX_REG(arg, yreg); +#endif + + +insta1_SYM /* LDA (Dloc,X) */ +/* called with arg = val to LDA in */ + GET_DLOC_X_IND_RD(); + LDA_INST(); + +insta2_SYM /* LDX #imm */ +#ifdef ASM + ldb 1(scratch1),zero + bb,>= psr,27,insta2_16bit_SYM + INC_KPC_2; + + extru zero,24,1,neg + b dispatch + copy zero,xreg +insta2_16bit_SYM + ldb 2(scratch1),scratch1 + INC_KPC_1 + CYCLES_PLUS_1 + extru scratch1,24,1,neg + dep scratch1,23,8,zero + b dispatch + copy zero,xreg +#else + INC_KPC_2; + if((psr & 0x10) == 0) { + GET_2BYTE_ARG; + CYCLES_PLUS_1 + INC_KPC_1; + } else { + GET_1BYTE_ARG; + } + SET_INDEX_REG(arg, xreg); +#endif + +insta3_SYM /* LDA Disp8,S */ +/* called with arg = val to LDA in */ + GET_DISP8_S_RD(); + LDA_INST(); + +insta4_SYM /* LDY Dloc */ +#ifdef ASM + ldb 1(scratch1),arg0 + GET_DLOC_WR() + b get_yreg_from_mem + nop +#else + C_LDY_DLOC(); +#endif + +insta5_SYM /* LDA Dloc */ +/* called with arg = val to LDA in */ + GET_DLOC_RD(); + LDA_INST(); + +insta6_SYM /* LDX Dloc */ +#ifdef ASM + ldb 1(scratch1),arg0 + GET_DLOC_WR() + b get_xreg_from_mem + nop +#else + C_LDX_DLOC(); +#endif + +insta7_SYM /* LDA [Dloc] */ + GET_DLOC_L_IND_RD(); + LDA_INST(); + +insta8_SYM /* TAY */ +#ifdef ASM + INC_KPC_1 + bb,>= psr,27,insta8_16bit_SYM + extru acc,31,8,zero + + extru acc,24,1,neg + b dispatch + copy zero,yreg + +insta8_16bit_SYM + extru acc,31,16,zero + extru acc,16,1,neg + b dispatch + copy zero,yreg +#else + INC_KPC_1; + SET_INDEX_REG(acc, yreg); +#endif + +insta9_SYM /* LDA #imm */ + GET_IMM_MEM(); + LDA_INST(); + +instaa_SYM /* TAX */ +#ifdef ASM + INC_KPC_1 + bb,>= psr,27,instaa_16bit_SYM + extru acc,31,8,zero + + extru acc,24,1,neg + b dispatch + copy zero,xreg + +instaa_16bit_SYM + extru acc,31,16,zero + extru acc,16,1,neg + b dispatch + copy zero,xreg +#else + INC_KPC_1; + SET_INDEX_REG(acc, xreg); +#endif + +instab_SYM /* PLB */ +#ifdef ASM + INC_KPC_1 + bl pull_8,link + CYCLES_PLUS_1 + + extru ret0,31,8,zero + extru ret0,24,1,neg + b dispatch + copy zero,dbank +#else + INC_KPC_1; + CYCLES_PLUS_1 + PULL8(dbank); + SET_NEG_ZERO8(dbank); +#endif + +instac_SYM /* LDY abs */ +#ifdef ASM + GET_ABS_ADDR() + b get_yreg_from_mem + nop +#else + C_LDY_ABS(); +#endif + + +instad_SYM /* LDA abs */ + GET_ABS_RD(); + LDA_INST(); + +instae_SYM /* LDX abs */ +#ifdef ASM + GET_ABS_ADDR() + b get_xreg_from_mem + nop +#else + C_LDX_ABS(); +#endif + +instaf_SYM /* LDA long */ + GET_LONG_RD(); + LDA_INST(); + + +instb0_SYM /* BCS disp8 */ +#ifdef ASM + COND_BR1 + bb,>= psr,31,instb0_2_SYM + COND_BR2 + +instb0_2_SYM + COND_BR_UNTAKEN +#else + BRANCH_DISP8((psr & 0x01)); +#endif + +instb1_SYM /* LDA (Dloc),y */ + GET_DLOC_IND_Y_RD(); + LDA_INST(); + +instb2_SYM /* LDA (Dloc) */ + GET_DLOC_IND_RD(); + LDA_INST(); + +instb3_SYM /* LDA (Disp8,s),y */ + GET_DISP8_S_IND_Y_RD(); + LDA_INST(); + +instb4_SYM /* LDY Dloc,x */ +#ifdef ASM + ldb 1(scratch1),arg0 + GET_DLOC_X_WR(); + b get_yreg_from_mem + nop +#else + C_LDY_DLOC_X(); +#endif + +instb5_SYM /* LDA Dloc,x */ + GET_DLOC_X_RD(); + LDA_INST(); + +instb6_SYM /* LDX Dloc,y */ +#ifdef ASM + ldb 1(scratch1),arg0 + GET_DLOC_Y_WR(); + b get_xreg_from_mem + nop +#else + C_LDX_DLOC_Y(); +#endif + +instb7_SYM /* LDA [Dloc],Y */ + GET_DLOC_L_IND_Y_RD(); + LDA_INST(); + +instb8_SYM /* CLV */ +#ifdef ASM + INC_KPC_1 + b dispatch + depi 0,25,1,psr /* clear overflow */ +#else + psr = psr & ~0x40; + INC_KPC_1; +#endif + +instb9_SYM /* LDA abs,y */ + GET_ABS_Y_RD(); + LDA_INST(); + +instba_SYM /* TSX */ +#ifdef ASM + INC_KPC_1 + bb,>= psr,27,instba_16bit_SYM + extru stack,31,8,zero + + extru stack,24,1,neg + b dispatch + copy zero,xreg +instba_16bit_SYM + copy stack,zero + extru stack,16,1,neg + b dispatch + copy zero,xreg +#else + INC_KPC_1; + SET_INDEX_REG(stack, xreg); +#endif + +instbb_SYM /* TYX */ +#ifdef ASM + INC_KPC_1 + bb,>= psr,27,instbb_16bit_SYM + copy yreg,xreg + +/* 8 bit */ + extru yreg,24,1,neg + b dispatch + copy yreg,zero +instbb_16bit_SYM + extru yreg,16,1,neg + b dispatch + copy yreg,zero +#else + INC_KPC_1; + SET_INDEX_REG(yreg, xreg); +#endif + +instbc_SYM /* LDY Abs,X */ +#ifdef ASM + GET_ABS_INDEX_ADDR_FOR_RD(xreg) + b get_yreg_from_mem + nop +#else + C_LDY_ABS_X(); +#endif + +instbd_SYM /* LDA Abs,X */ + GET_ABS_X_RD(); + LDA_INST(); + +instbe_SYM /* LDX Abs,y */ +#ifdef ASM + GET_ABS_INDEX_ADDR_FOR_RD(yreg) + b get_xreg_from_mem + nop +#else + C_LDX_ABS_Y(); +#endif + +instbf_SYM /* LDA Long,X */ + GET_LONG_X_RD(); + LDA_INST(); + + +instc0_SYM /* CPY #imm */ +#ifdef ASM + ldb 1(scratch1),ret0 + bb,>= psr,27,instc0_16bit_SYM + INC_KPC_2; + CMP_INDEX_REG_MEAT8(yreg) +instc0_16bit_SYM + ldb 2(scratch1),scratch1 + CYCLES_PLUS_1 + INC_KPC_1 + dep scratch1,23,8,ret0 + CMP_INDEX_REG_MEAT16(yreg) +#else + C_CPY_IMM(); +#endif + + +instc1_SYM /* CMP (Dloc,X) */ +/* called with arg = val to CMP in */ + GET_DLOC_X_IND_RD(); + CMP_INST(); + +instc2_SYM /* REP #imm */ +#ifdef ASM + ldb 1(scratch1),ret0 + extru psr,27,2,arg0 /* save old x & m */ + INC_KPC_2; + dep neg,24,1,psr + CYCLES_PLUS_1 + depi 0,30,1,psr + comiclr,<> 0,zero,0 + depi 1,30,1,psr + andcm psr,ret0,ret0 + ldi 0,zero + extru,<> ret0,30,1,0 + ldi 1,zero + dep ret0,31,8,psr + b update_system_state + extru ret0,24,1,neg +#else + GET_1BYTE_ARG; + tmp2 = psr; + CYCLES_PLUS_1; + INC_KPC_2; + psr = (psr & ~0x82) | ((neg & 1) << 7) | ((!zero) << 1); + psr = psr & ~(arg & 0xff); + zero = !(psr & 2); + neg = (psr >> 7) & 1; + UPDATE_PSR(psr, tmp2); +#endif + + +instc3_SYM /* CMP Disp8,S */ +/* called with arg = val to CMP in */ + GET_DISP8_S_RD(); + CMP_INST(); + +instc4_SYM /* CPY Dloc */ +#ifdef ASM + GET_DLOC_ADDR() + CMP_INDEX_REG_LOAD(instc4_16bit_SYM, yreg) +#else + C_CPY_DLOC(); +#endif + + +instc5_SYM /* CMP Dloc */ + GET_DLOC_RD(); + CMP_INST(); + +instc6_SYM /* DEC Dloc */ + GET_DLOC_RD(); + DEC_INST(1); + +instc7_SYM /* CMP [Dloc] */ + GET_DLOC_L_IND_RD(); + CMP_INST(); + +instc8_SYM /* INY */ +#ifdef ASM + INC_KPC_1 + addi 1,yreg,yreg + bb,>= psr,27,instc8_16bit_SYM + extru yreg,31,8,zero + + extru yreg,24,1,neg + b dispatch + copy zero,yreg + +instc8_16bit_SYM + extru yreg,31,16,zero + extru yreg,16,1,neg + b dispatch + copy zero,yreg +#else + INC_KPC_1; + SET_INDEX_REG(yreg + 1, yreg); +#endif + +instc9_SYM /* CMP #imm */ + GET_IMM_MEM(); + CMP_INST(); + +instca_SYM /* DEX */ +#ifdef ASM + INC_KPC_1 + addi -1,xreg,xreg + bb,>= psr,27,instca_16bit_SYM + extru xreg,31,8,zero + + extru xreg,24,1,neg + b dispatch + copy zero,xreg + +instca_16bit_SYM + extru xreg,31,16,zero + extru xreg,16,1,neg + b dispatch + copy zero,xreg +#else + INC_KPC_1; + SET_INDEX_REG(xreg - 1, xreg); +#endif + +instcb_SYM /* WAI */ +#ifdef ASM + ldil l%g_wait_pending,scratch1 + CYCLES_FINISH + ldi 1,scratch2 + b dispatch + stw scratch2,r%g_wait_pending(scratch1) +#else + g_wait_pending = 1; + CYCLES_FINISH +#endif + +instcc_SYM /* CPY abs */ +#ifdef ASM + GET_ABS_ADDR() + CMP_INDEX_REG_LOAD(instcc_16bit_SYM, yreg) +#else + C_CPY_ABS(); +#endif + + + + +instcd_SYM /* CMP abs */ + GET_ABS_RD(); + CMP_INST(); + +instce_SYM /* DEC abs */ + GET_ABS_RD(); + DEC_INST(0); + + +instcf_SYM /* CMP long */ + GET_LONG_RD(); + CMP_INST(); + + +instd0_SYM /* BNE disp8 */ +#ifdef ASM + COND_BR1 + comib,= 0,zero,instd0_2_SYM + COND_BR2 + +instd0_2_SYM + COND_BR_UNTAKEN +#else + BRANCH_DISP8(zero != 0); +#endif + +instd1_SYM /* CMP (Dloc),y */ + GET_DLOC_IND_Y_RD(); + CMP_INST(); + +instd2_SYM /* CMP (Dloc) */ + GET_DLOC_IND_RD(); + CMP_INST(); + +instd3_SYM /* CMP (Disp8,s),y */ + GET_DISP8_S_IND_Y_RD(); + CMP_INST(); + +instd4_SYM /* PEI Dloc */ +#ifdef ASM + GET_DLOC_ADDR() + bl get_mem_long_16,link + CYCLES_PLUS_1 + +/* push ret0 */ + extru ret0,31,16,arg0 + ldil l%dispatch,link + b push_16_unsafe + ldo r%dispatch(link),link +#else + GET_DLOC_ADDR() + GET_MEMORY16(arg, arg, 1); + CYCLES_PLUS_1; + PUSH16_UNSAFE(arg); +#endif + +instd5_SYM /* CMP Dloc,x */ + GET_DLOC_X_RD(); + CMP_INST(); + +instd6_SYM /* DEC Dloc,x */ + GET_DLOC_X_RD(); + DEC_INST(1); + +instd7_SYM /* CMP [Dloc],Y */ + GET_DLOC_L_IND_Y_RD(); + CMP_INST(); + +instd8_SYM /* CLD */ +#ifdef ASM + INC_KPC_1 + b dispatch + depi 0,28,1,psr /* clear decimal */ +#else + psr = psr & (~0x8); + INC_KPC_1; +#endif + +instd9_SYM /* CMP abs,y */ + GET_ABS_Y_RD(); + CMP_INST(); + +instda_SYM /* PHX */ +#ifdef ASM + INC_KPC_1 + bb,>= psr,27,instda_16bit_SYM + ldil l%dispatch,link + + extru xreg,31,8,arg0 + b push_8 + ldo r%dispatch(link),link + +instda_16bit_SYM + extru xreg,31,16,arg0 + b push_16 + ldo r%dispatch(link),link +#else + INC_KPC_1; + if(psr & 0x10) { + PUSH8(xreg); + } else { + PUSH16(xreg); + } +#endif + +instdb_SYM /* STP */ +#ifdef ASM + ldb 1(scratch1),ret0 + CYCLES_PLUS_1 + b dispatch_done + depi RET_STP,3,4,ret0 +#else + GET_1BYTE_ARG; + CYCLES_PLUS_1 + FINISH(RET_STP, arg); +#endif + +instdc_SYM /* JML (Abs) */ +#ifdef ASM + ldb 1(scratch1),arg0 + ldb 2(scratch1),scratch1 + CYCLES_PLUS_1 + bl get_mem_long_24,link + dep scratch1,23,8,arg0 + + b dispatch + copy ret0,kpc +#else + GET_2BYTE_ARG; + CYCLES_PLUS_1; + GET_MEMORY24(arg, kpc, 1); +#endif + +instdd_SYM /* CMP Abs,X */ + GET_ABS_X_RD(); + CMP_INST(); + +instde_SYM /* DEC Abs,X */ + GET_ABS_X_RD_WR(); + DEC_INST(0); + +instdf_SYM /* CMP Long,X */ + GET_LONG_X_RD(); + CMP_INST(); + + +inste0_SYM /* CPX #imm */ +#ifdef ASM + ldb 1(scratch1),ret0 + bb,>= psr,27,inste0_16bit_SYM + INC_KPC_2; + CMP_INDEX_REG_MEAT8(xreg) +inste0_16bit_SYM + ldb 2(scratch1),scratch1 + CYCLES_PLUS_1 + INC_KPC_1 + dep scratch1,23,8,ret0 + CMP_INDEX_REG_MEAT16(xreg) +#else + C_CPX_IMM(); +#endif + + +inste1_SYM /* SBC (Dloc,X) */ +/* called with arg = val to SBC in */ + GET_DLOC_X_IND_RD(); + SBC_INST(); + +inste2_SYM /* SEP #imm */ +#ifdef ASM + ldb 1(scratch1),ret0 + extru psr,27,2,arg0 /* save old x & m */ + INC_KPC_2; + dep neg,24,1,psr + CYCLES_PLUS_1 + depi 0,30,1,psr + comiclr,<> 0,zero,0 + depi 1,30,1,psr + or psr,ret0,ret0 + ldi 0,zero + extru,<> ret0,30,1,0 + ldi 1,zero + dep ret0,31,8,psr + b update_system_state + extru ret0,24,1,neg +#else + GET_1BYTE_ARG; + tmp2 = psr; + CYCLES_PLUS_1; + INC_KPC_2; + psr = (psr & ~0x82) | ((neg & 1) << 7) | ((!zero) << 1); + psr = psr | (arg & 0xff); + zero = !(psr & 2); + neg = (psr >> 7) & 1; + UPDATE_PSR(psr, tmp2); +#endif + + +inste3_SYM /* SBC Disp8,S */ +/* called with arg = val to SBC in */ + GET_DISP8_S_RD(); + SBC_INST(); + +inste4_SYM /* CPX Dloc */ +#ifdef ASM + GET_DLOC_ADDR() + CMP_INDEX_REG_LOAD(inste4_16bit_SYM, xreg) +#else + C_CPX_DLOC(); +#endif + + +inste5_SYM /* SBC Dloc */ +/* called with arg = val to SBC in */ + GET_DLOC_RD(); + SBC_INST(); + +inste6_SYM /* INC Dloc */ + GET_DLOC_RD(); + INC_INST(1); + +inste7_SYM /* SBC [Dloc] */ + GET_DLOC_L_IND_RD(); + SBC_INST(); + +inste8_SYM /* INX */ +#ifdef ASM + INC_KPC_1 + addi 1,xreg,xreg + bb,>= psr,27,inste8_16bit_SYM + extru xreg,31,8,zero + + extru xreg,24,1,neg + b dispatch + copy zero,xreg + +inste8_16bit_SYM + extru xreg,31,16,zero + extru xreg,16,1,neg + b dispatch + copy zero,xreg +#else + INC_KPC_1; + SET_INDEX_REG(xreg + 1, xreg); +#endif + +inste9_SYM /* SBC #imm */ + GET_IMM_MEM(); + SBC_INST(); + +instea_SYM /* NOP */ +#ifdef ASM + INC_KPC_1 + b dispatch + nop +#else + INC_KPC_1; +#endif + +insteb_SYM /* XBA */ +#ifdef ASM + extru acc,16,1,neg /* Z and N reflect status of low 8 */ + CYCLES_PLUS_1 /* bits of final acc value! */ + copy acc,scratch1 /* regardlessof ACC 8 or 16 bit */ + extru acc,23,8,acc + INC_KPC_1 + copy acc,zero + b dispatch + dep scratch1,23,8,acc +#else + tmp1 = acc & 0xff; + CYCLES_PLUS_1 + acc = (tmp1 << 8) + (acc >> 8); + INC_KPC_1; + SET_NEG_ZERO8(acc & 0xff); +#endif + +instec_SYM /* CPX abs */ +#ifdef ASM + GET_ABS_ADDR() + CMP_INDEX_REG_LOAD(instec_16bit_SYM, xreg) +#else + C_CPX_ABS(); +#endif + + + + +insted_SYM /* SBC abs */ + GET_ABS_RD(); + SBC_INST(); + +instee_SYM /* INC abs */ + GET_ABS_RD(); + INC_INST(0); + + +instef_SYM /* SBC long */ + GET_LONG_RD(); + SBC_INST(); + + +instf0_SYM /* BEQ disp8 */ +#ifdef ASM + COND_BR1 + comib,<> 0,zero,instf0_2_SYM + COND_BR2 + +instf0_2_SYM + COND_BR_UNTAKEN +#else + BRANCH_DISP8(zero == 0); +#endif + +instf1_SYM /* SBC (Dloc),y */ + GET_DLOC_IND_Y_RD(); + SBC_INST(); + +instf2_SYM /* SBC (Dloc) */ + GET_DLOC_IND_RD(); + SBC_INST(); + +instf3_SYM /* SBC (Disp8,s),y */ + GET_DISP8_S_IND_Y_RD(); + SBC_INST(); + +instf4_SYM /* PEA Abs */ +#ifdef ASM + ldb 1(scratch1),arg0 + ldil l%dispatch,link + ldb 2(scratch1),scratch1 + INC_KPC_3 + CYCLES_PLUS_1 + ldo r%dispatch(link),link + b push_16_unsafe + dep scratch1,23,8,arg0 +#else + GET_2BYTE_ARG; + CYCLES_PLUS_1; + INC_KPC_3; + PUSH16_UNSAFE(arg); +#endif + +instf5_SYM /* SBC Dloc,x */ + GET_DLOC_X_RD(); + SBC_INST(); + +instf6_SYM /* INC Dloc,x */ + GET_DLOC_X_RD(); + INC_INST(1); + +instf7_SYM /* SBC [Dloc],Y */ + GET_DLOC_L_IND_Y_RD(); + SBC_INST(); + +instf8_SYM /* SED */ +#ifdef ASM + INC_KPC_1 + b dispatch + depi 1,28,1,psr /* set decimal */ +#else + INC_KPC_1; + psr |= 0x8; +#endif + +instf9_SYM /* SBC abs,y */ + GET_ABS_Y_RD(); + SBC_INST(); + +instfa_SYM /* PLX */ +#ifdef ASM + bb,< psr,27,instfa_8bit_SYM + CYCLES_PLUS_1 + + INC_KPC_1 + bl pull_16,link + nop + + extru ret0,31,16,zero + extru ret0,16,1,neg + b dispatch + copy zero,xreg + +instfa_8bit_SYM + INC_KPC_1 + bl pull_8,link + nop + + extru ret0,31,8,zero + extru ret0,24,1,neg + b dispatch + copy zero,xreg +#else + INC_KPC_1; + CYCLES_PLUS_1; + if(psr & 0x10) { + PULL8(xreg); + SET_NEG_ZERO8(xreg); + } else { + PULL16(xreg); + SET_NEG_ZERO16(xreg); + } +#endif + +instfb_SYM /* XCE */ +#ifdef ASM + extru psr,27,2,arg0 /* save old x & m */ + INC_KPC_1 + extru psr,23,1,scratch1 /* e bit */ + dep psr,23,1,psr /* copy carry to e bit */ + b update_system_state + dep scratch1,31,1,psr /* copy e bit to carry */ +#else + tmp2 = psr; + INC_KPC_1; + psr = (tmp2 & 0xfe) | ((tmp2 & 1) << 8) | ((tmp2 >> 8) & 1); + UPDATE_PSR(psr, tmp2); +#endif + +instfc_SYM /* JSR (Abs,X) */ +#ifdef ASM + ldb 1(scratch1),ret0 + extru kpc,15,8,scratch2 + ldb 2(scratch1),scratch1 + dep scratch2,15,16,ret0 + INC_KPC_2; + dep scratch1,23,8,ret0 + add xreg,ret0,arg0 + bl get_mem_long_16,link + extru arg0,31,24,arg0 + + CYCLES_PLUS_2 + extru kpc,31,16,arg0 + ldil l%dispatch,link + dep ret0,31,16,kpc + b push_16_unsafe + ldo r%dispatch(link),link +#else + GET_2BYTE_ARG; + INC_KPC_2; + tmp1 = kpc; + arg = (kpc & 0xff0000) + ((arg + xreg) & 0xffff); + GET_MEMORY16(arg, tmp2, 1); + kpc = (kpc & 0xff0000) + tmp2; + CYCLES_PLUS_2 + PUSH16_UNSAFE(tmp1); +#endif + +instfd_SYM /* SBC Abs,X */ + GET_ABS_X_RD(); + SBC_INST(); + +instfe_SYM /* INC Abs,X */ + GET_ABS_X_RD_WR(); + INC_INST(0); + +instff_SYM /* SBC Long,X */ + GET_LONG_X_RD(); + SBC_INST(); + diff --git a/src/iwm.c b/src/iwm.c new file mode 100644 index 0000000..0625d8a --- /dev/null +++ b/src/iwm.c @@ -0,0 +1,2329 @@ +/************************************************************************/ +/* KEGS: Apple //gs Emulator */ +/* Copyright 2002 by Kent Dickey */ +/* */ +/* This code is covered by the GNU GPL */ +/* */ +/* The KEGS web page is kegs.sourceforge.net */ +/* You may contact the author at: kadickey@alumni.princeton.edu */ +/************************************************************************/ + +const char rcsid_iwm_c[] = "@(#)$KmKId: iwm.c,v 1.111 2003-11-03 22:14:10-05 kentd Exp $"; + +#include "defc.h" + +extern int Verbose; +extern int g_vbl_count; +extern int speed_fast; +extern word32 g_slot_motor_detect; + +const byte phys_to_dos_sec[] = { + 0x00, 0x07, 0x0e, 0x06, 0x0d, 0x05, 0x0c, 0x04, + 0x0b, 0x03, 0x0a, 0x02, 0x09, 0x01, 0x08, 0x0f +}; + +const byte phys_to_prodos_sec[] = { + 0x00, 0x08, 0x01, 0x09, 0x02, 0x0a, 0x03, 0x0b, + 0x04, 0x0c, 0x05, 0x0d, 0x06, 0x0e, 0x07, 0x0f +}; + + +const byte to_disk_byte[] = { + 0x96, 0x97, 0x9a, 0x9b, 0x9d, 0x9e, 0x9f, 0xa6, + 0xa7, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb2, 0xb3, +/* 0x10 */ + 0xb4, 0xb5, 0xb6, 0xb7, 0xb9, 0xba, 0xbb, 0xbc, + 0xbd, 0xbe, 0xbf, 0xcb, 0xcd, 0xce, 0xcf, 0xd3, +/* 0x20 */ + 0xd6, 0xd7, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, + 0xdf, 0xe5, 0xe6, 0xe7, 0xe9, 0xea, 0xeb, 0xec, +/* 0x30 */ + 0xed, 0xee, 0xef, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, + 0xf7, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff +}; + +int g_track_bytes_35[] = { + 0x200*12, + 0x200*11, + 0x200*10, + 0x200*9, + 0x200*8 +}; + +int g_track_nibs_35[] = { + 816*12, + 816*11, + 816*10, + 816*9, + 816*8 +}; + + + +int g_fast_disk_emul = 1; +int g_slow_525_emul_wr = 0; +double g_dcycs_end_emul_wr = 0.0; +int g_fast_disk_unnib = 0; +int g_iwm_fake_fast = 0; + + +int from_disk_byte[256]; +int from_disk_byte_valid = 0; + +Iwm iwm; + +int g_apple35_sel = 0; +int head_35 = 0; +int g_iwm_motor_on = 0; + +int g_check_nibblization = 0; + +/* prototypes for IWM special routs */ +int iwm_read_data_35(Disk *dsk, int fast_disk_emul, double dcycs); +int iwm_read_data_525(Disk *dsk, int fast_disk_emul, double dcycs); +void iwm_write_data_35(Disk *dsk, word32 val, int fast_disk_emul, double dcycs); +void iwm_write_data_525(Disk *dsk, word32 val, int fast_disk_emul,double dcycs); + +void +iwm_init_drive(Disk *dsk, int smartport, int drive, int disk_525) +{ + int num_tracks; + int i; + + num_tracks = MAX_TRACKS; + + dsk->dcycs_last_read = 0.0; + dsk->name_ptr = 0; + dsk->partition_name = 0; + dsk->partition_num = -1; + dsk->fd = -1; + dsk->force_size = 0; + dsk->image_start = 0; + dsk->image_size = 0; + dsk->smartport = smartport; + dsk->disk_525 = disk_525; + dsk->drive = drive; + dsk->cur_qtr_track = 0; + dsk->image_type = 0; + dsk->vol_num = 254; + dsk->write_prot = 1; + dsk->write_through_to_unix = 0; + dsk->disk_dirty = 0; + dsk->just_ejected = 0; + dsk->last_phase = 0; + dsk->nib_pos = 0; + dsk->num_tracks = 0; + + for(i = 0; i < num_tracks; i++) { + dsk->tracks[i].dsk = dsk; + dsk->tracks[i].nib_area = 0; + dsk->tracks[i].track_dirty = 0; + dsk->tracks[i].overflow_size = 0; + dsk->tracks[i].track_len = 0; + dsk->tracks[i].unix_pos = -1; + dsk->tracks[i].unix_len = -1; + } +} + +void +iwm_init() +{ + int val; + int i; + + for(i = 0; i < 2; i++) { + iwm_init_drive(&(iwm.drive525[i]), 0, i, 1); + iwm_init_drive(&(iwm.drive35[i]), 0, i, 0); + } + + for(i = 0; i < MAX_C7_DISKS; i++) { + iwm_init_drive(&(iwm.smartport[i]), 1, i, 0); + } + + if(from_disk_byte_valid == 0) { + for(i = 0; i < 256; i++) { + from_disk_byte[i] = -1; + } + for(i = 0; i < 64; i++) { + val = to_disk_byte[i]; + from_disk_byte[val] = i; + } + from_disk_byte_valid = 1; + } else { + halt_printf("iwm_init called twice!\n"); + } + + iwm_reset(); +} + +void +iwm_reset() +{ + iwm.q6 = 0; + iwm.q7 = 0; + iwm.motor_on = 0; + iwm.motor_on35 = 0; + iwm.motor_off = 0; + iwm.motor_off_vbl_count = 0; + iwm.step_direction35 = 0; + iwm.head35 = 0; + iwm.drive_select = 0; + iwm.iwm_mode = 0; + iwm.enable2 = 0; + iwm.reset = 0; + iwm.iwm_phase[0] = 0; + iwm.iwm_phase[1] = 0; + iwm.iwm_phase[2] = 0; + iwm.iwm_phase[3] = 0; + iwm.previous_write_val = 0; + iwm.previous_write_bits = 0; + + g_iwm_motor_on = 0; + g_apple35_sel = 0; +} + +void +draw_iwm_status(int line, char *buf) +{ + char *flag[2][2]; + + flag[0][0] = " "; + flag[0][1] = " "; + flag[1][0] = " "; + flag[1][1] = " "; + + if(g_iwm_motor_on) { + flag[g_apple35_sel][iwm.drive_select] = "*"; + } + + sprintf(buf, "s6d1:%2d%s s6d2:%2d%s s5d1:%2d/%d%s " + "s5d2:%2d/%d%s fast_disk_emul:%d,%d c036:%02x", + iwm.drive525[0].cur_qtr_track >> 2, flag[0][0], + iwm.drive525[1].cur_qtr_track >> 2, flag[0][1], + iwm.drive35[0].cur_qtr_track >> 1, + iwm.drive35[0].cur_qtr_track & 1, flag[1][0], + iwm.drive35[1].cur_qtr_track >> 1, + iwm.drive35[1].cur_qtr_track & 1, flag[1][1], + g_fast_disk_emul, g_slow_525_emul_wr, + (speed_fast << 7) + g_slot_motor_detect); + + video_update_status_line(line, buf); +} + +void +iwm_flush_disk_to_unix(Disk *dsk) +{ + byte buffer[0x4000]; + int num_dirty; + int j; + int ret; + int unix_pos; + int unix_len; + + if(dsk->disk_dirty == 0 || dsk->write_through_to_unix == 0) { + return; + } + + printf("Writing disk %s to Unix\n", dsk->name_ptr); + dsk->disk_dirty = 0; + num_dirty = 0; + + /* Dirty data! */ + for(j = 0; j < dsk->num_tracks; j++) { + + ret = disk_track_to_unix(dsk, j, &(buffer[0])); + + if(ret != 1 && ret != 0) { + printf("iwm_flush_disk_to_unix ret: %d, cannot write " + "image to unix\n", ret); + halt_printf("Adjusting image not to write through!\n"); + dsk->write_through_to_unix = 0; + break; + } + + if(ret != 1) { + /* not at an even track, or not dirty */ + continue; + } + if((j & 3) != 0 && dsk->disk_525) { + halt_printf("Valid data on a non-whole trk: %03x\n", j); + continue; + } + + num_dirty++; + + /* Write it out */ + unix_pos = dsk->tracks[j].unix_pos; + unix_len = dsk->tracks[j].unix_len; + if(unix_pos < 0 || unix_len < 0x1000) { + halt_printf("Disk:%s trk:%d, unix_pos:%08x, len:%08x\n", + dsk->name_ptr, j, unix_pos, unix_len); + break; + } + + ret = lseek(dsk->fd, unix_pos, SEEK_SET); + if(ret != unix_pos) { + halt_printf("lseek 525: %08x, errno: %d\n", ret, errno); + } + + ret = write(dsk->fd, &(buffer[0]), unix_len); + if(ret != unix_len) { + printf("write: %08x, errno:%d, qtrk: %02x, disk: %s\n", + ret, errno, j, dsk->name_ptr); + } + } + + if(num_dirty == 0) { + halt_printf("Drive %s was dirty, but no track was dirty!\n", + dsk->name_ptr); + } + +} + +/* Check for dirty disk 3 times a second */ + +void +iwm_vbl_update(int doit_3_persec) +{ + Disk *dsk; + int motor_on; + int i; + + if(iwm.motor_on && iwm.motor_off) { + if(iwm.motor_off_vbl_count <= g_vbl_count) { + printf("Disk timer expired, drive off: %08x\n", + g_vbl_count); + iwm.motor_on = 0; + iwm.motor_off = 0; + } + } + + if(!doit_3_persec) { + return; + } + + motor_on = iwm.motor_on; + if(g_apple35_sel) { + motor_on = iwm.motor_on35; + } + + if(motor_on == 0 || iwm.motor_off) { + /* Disk not spinning, see if any dirty tracks to flush */ + /* out to Unix */ + for(i = 0; i < 2; i++) { + dsk = &(iwm.drive525[i]); + iwm_flush_disk_to_unix(dsk); + } + for(i = 0; i < 2; i++) { + dsk = &(iwm.drive35[i]); + iwm_flush_disk_to_unix(dsk); + } + } +} + + +void +iwm_show_stats() +{ + printf("IWM stats: q7,q6: %d, %d, reset,enable2: %d,%d, mode: %02x\n", + iwm.q7, iwm.q6, iwm.reset, iwm.enable2, iwm.iwm_mode); + printf("motor: %d,%d, motor35:%d drive: %d, on: %d, head35: %d " + "phs: %d %d %d %d\n", + iwm.motor_on, iwm.motor_off, g_iwm_motor_on, + iwm.drive_select, g_apple35_sel, + head_35, iwm.iwm_phase[0], iwm.iwm_phase[1], iwm.iwm_phase[2], + iwm.iwm_phase[3]); + printf("iwm.drive525[0].fd: %d, [1].fd: %d\n", + iwm.drive525[0].fd, iwm.drive525[1].fd); + printf("iwm.drive525[0].last_phase: %d, [1].last_phase: %d\n", + iwm.drive525[0].last_phase, iwm.drive525[1].last_phase); +} + +void +iwm_touch_switches(int loc, double dcycs) +{ + Disk *dsk; + int phase; + int on; + int drive; + + if(iwm.reset) { + iwm_printf("IWM under reset: %d, enable2: %d\n", iwm.reset, + iwm.enable2); + } + + on = loc & 1; + drive = iwm.drive_select; + phase = loc >> 1; + if(g_apple35_sel) { + dsk = &(iwm.drive35[drive]); + } else { + dsk = &(iwm.drive525[drive]); + } + + + if(loc < 8) { + /* phase adjustments. See if motor is on */ + + iwm.iwm_phase[phase] = on; + + if(iwm.motor_on) { + if(g_apple35_sel) { + if(phase == 3 && on) { + iwm_do_action35(dcycs); + } + } else if(on) { + /* Move apple525 head */ + iwm525_phase_change(drive, phase); + } + } + /* See if enable or reset is asserted */ + if(iwm.iwm_phase[0] && iwm.iwm_phase[2]) { + iwm.reset = 1; + iwm_printf("IWM reset active\n"); + } else { + iwm.reset = 0; + } + if(iwm.iwm_phase[1] && iwm.iwm_phase[3]) { + iwm.enable2 = 1; + iwm_printf("IWM ENABLE2 active\n"); + } else { + iwm.enable2 = 0; + } + } else { + /* loc >= 8 */ + switch(loc) { + case 0x8: + iwm_printf("Turning IWM motor off!\n"); + if(iwm.iwm_mode & 0x04) { + /* Turn off immediately */ + iwm.motor_off = 0; + iwm.motor_on = 0; + } else { + /* 1 second delay */ + if(iwm.motor_on && !iwm.motor_off) { + iwm.motor_off = 1; + iwm.motor_off_vbl_count = g_vbl_count + + 60; + } + } + + if(g_iwm_motor_on || g_slow_525_emul_wr) { + /* recalc current speed */ + set_halt(HALT_EVENT); + } + + g_iwm_motor_on = 0; + g_slow_525_emul_wr = 0; + break; + case 0x9: + iwm_printf("Turning IWM motor on!\n"); + iwm.motor_on = 1; + iwm.motor_off = 0; + + if(g_iwm_motor_on == 0) { + /* recalc current speed */ + set_halt(HALT_EVENT); + } + g_iwm_motor_on = 1; + + break; + case 0xa: + case 0xb: + iwm.drive_select = on; + break; + case 0xc: + case 0xd: + iwm.q6 = on; + break; + case 0xe: + case 0xf: + iwm.q7 = on; + break; + default: + printf("iwm_touch_switches: loc: %02x unknown!\n", loc); + exit(2); + } + } + + if(!iwm.q7) { + iwm.previous_write_bits = 0; + } + + if((dcycs > g_dcycs_end_emul_wr) && g_slow_525_emul_wr) { + set_halt(HALT_EVENT); + g_slow_525_emul_wr = 0; + } +} + +void +iwm_move_to_track(Disk *dsk, int new_track) +{ + int disk_525; + int dr; + + disk_525 = dsk->disk_525; + + if(new_track < 0) { + new_track = 0; + } + if(new_track >= dsk->num_tracks) { + if(disk_525) { + new_track = dsk->num_tracks - 4; + } else { + new_track = dsk->num_tracks - 2 + iwm.head35; + } + + if(new_track <= 0) { + new_track = 0; + } + } + + if(dsk->cur_qtr_track != new_track) { + dr = dsk->drive + 1; + if(disk_525) { + iwm_printf("s6d%d Track: %d.%02d\n", dr, + new_track >> 2, 25* (new_track & 3)); + } else { + iwm_printf("s5d%d Track: %d Side: %d\n", dr, + new_track >> 1, new_track & 1); + } + + dsk->cur_qtr_track = new_track; + } +} + +void +iwm525_phase_change(int drive, int phase) +{ + Disk *dsk; + int qtr_track; + int last_phase; + int phase_up; + int phase_down; + int delta; + + phase_up = (phase - 1) & 3; + phase_down = (phase + 1) & 3; + + dsk = &(iwm.drive525[drive]); + last_phase = dsk->last_phase; + + qtr_track = dsk->cur_qtr_track; + + delta = 0; + if(last_phase == phase_up) { + delta = 2; + last_phase = phase; + } else if(last_phase == phase_down) { + delta = -2; + last_phase = phase; + } + + qtr_track += delta; + if(qtr_track < 0) { + printf("GRIND...GRIND...GRIND\n"); + qtr_track = 0; + last_phase = 0; + } + if(qtr_track > 4*34) { + printf("Disk arm moved past track 0x21, moving it back\n"); + qtr_track = 4*34; + last_phase = 0; + } + + iwm_move_to_track(dsk, qtr_track); + + dsk->last_phase = last_phase; + + iwm_printf("Moving drive to qtr track: %04x, %d, %d, %d, " + "%d %d %d %d\n", + qtr_track, phase, delta, last_phase, iwm.iwm_phase[0], + iwm.iwm_phase[1], iwm.iwm_phase[2], iwm.iwm_phase[3]); + + /* sanity check stepping algorithm */ + if((qtr_track & 7) == 0) { + /* check for just access phase 0 */ + if(last_phase != 0 ) { + halt_printf("last_phase: %d!\n", last_phase); + } + } +} + +int +iwm_read_status35(double dcycs) +{ + Disk *dsk; + int drive; + int state; + int tmp; + + drive = iwm.drive_select; + dsk = &(iwm.drive35[drive]); + + if(iwm.motor_on) { + /* Read status */ + state = (iwm.iwm_phase[1] << 3) + (iwm.iwm_phase[0] << 2) + + (head_35 << 1) + iwm.iwm_phase[2]; + + iwm_printf("Iwm status read state: %02x\n", state); + + switch(state) { + case 0x00: /* step direction */ + return iwm.step_direction35; + break; + case 0x01: /* lower head activate */ + /* also return instantaneous data from head */ + iwm.head35 = 0; + iwm_move_to_track(dsk, (dsk->cur_qtr_track & (-2))); + return (((int)dcycs) & 1); + break; + case 0x02: /* disk in place */ + /* 1 = no disk, 0 = disk */ + iwm_printf("read disk in place, num_tracks: %d\n", + dsk->num_tracks); + tmp = (dsk->num_tracks <= 0); + return tmp; + break; + case 0x03: /* upper head activate */ + /* also return instantaneous data from head */ + iwm.head35 = 1; + iwm_move_to_track(dsk, (dsk->cur_qtr_track | 1)); + return (((int)dcycs) & 1); + break; + case 0x04: /* disk is stepping? */ + /* 1 = not stepping, 0 = stepping */ + return 1; + break; + case 0x05: /* Unknown function of ROM 03? */ + /* 1 = or $20 into 0xe1/f24+drive, 0 = don't */ + return 1; + break; + case 0x06: /* disk is locked */ + /* 0 = locked, 1 = unlocked */ + return (!dsk->write_prot); + break; + case 0x08: /* motor on */ + /* 0 = on, 1 = off */ + return !iwm.motor_on35; + break; + case 0x09: /* number of sides */ + /* 1 = 2 sides, 0 = 1 side */ + return 1; + break; + case 0x0a: /* at track 0 */ + /* 1 = not at track 0, 0 = there */ + tmp = (dsk->cur_qtr_track != 0); + iwm_printf("Read at track0_35: %d\n", tmp); + return tmp; + break; + case 0x0b: /* disk ready??? */ + /* 0 = ready, 1 = not ready? */ + tmp = !iwm.motor_on35; + iwm_printf("Read disk ready, ret: %d\n", tmp); + return tmp; + break; + case 0x0c: /* disk switched?? */ + /* 0 = not switched, 1 = switched? */ + tmp = (dsk->just_ejected != 0); + iwm_printf("Read disk switched: %d\n", tmp); + return tmp; + break; + case 0x0d: /* false read when ejecting disk */ + return 1; + case 0x0e: /* tachometer */ + halt_printf("Reading tachometer!\n"); + return (((int)dcycs) & 1); + break; + case 0x0f: /* drive installed? */ + /* 0 = drive exists, 1 = no drive */ + if(drive) { + /* pretend no drive 1 */ + return 1; + } + return 0; + break; + default: + halt_printf("Read 3.5 status, state: %02x\n", state); + return 1; + } + } else { + iwm_printf("Read 3.5 status with drive off!\n"); + return 1; + } +} + +void +iwm_do_action35(double dcycs) +{ + Disk *dsk; + int drive; + int state; + + drive = iwm.drive_select; + dsk = &(iwm.drive35[drive]); + + if(iwm.motor_on) { + /* Perform action */ + state = (iwm.iwm_phase[1] << 3) + (iwm.iwm_phase[0] << 2) + + (head_35 << 1) + iwm.iwm_phase[2]; + switch(state) { + case 0x00: /* Set step direction inward */ + /* towards higher tracks */ + iwm.step_direction35 = 0; + iwm_printf("Iwm set step dir35 = 0\n"); + break; + case 0x01: /* Set step direction outward */ + /* towards lower tracks */ + iwm.step_direction35 = 1; + iwm_printf("Iwm set step dir35 = 1\n"); + break; + case 0x03: /* reset disk-switched flag? */ + iwm_printf("Iwm reset disk switch\n"); + dsk->just_ejected = 0; + /* set_halt(1); */ + break; + case 0x04: /* step disk */ + if(iwm.step_direction35) { + iwm_move_to_track(dsk, dsk->cur_qtr_track - 2); + } else { + iwm_move_to_track(dsk, dsk->cur_qtr_track + 2); + } + break; + case 0x08: /* turn motor on */ + iwm_printf("Iwm set motor_on35 = 1\n"); + iwm.motor_on35 = 1; + break; + case 0x09: /* turn motor off */ + iwm_printf("Iwm set motor_on35 = 0\n"); + iwm.motor_on35 = 0; + break; + case 0x0d: /* eject disk */ + eject_disk(dsk); + break; + case 0x02: + case 0x07: + case 0x0b: /* hacks to allow AE 1.6MB driver to not crash me */ + break; + default: + halt_printf("Do 3.5 action, state: %02x\n", state); + return; + } + } else { + halt_printf("Set 3.5 status with drive off!\n"); + return; + } +} + +void +iwm_set_apple35_sel(int newval) +{ + if(g_apple35_sel != newval) { + /* Handle speed changes */ + set_halt(HALT_EVENT); + } + + g_apple35_sel = newval; +} + +int +iwm_read_c0ec(double dcycs) +{ + Disk *dsk; + int drive; + + iwm.q6 = 0; + + if(iwm.q7 == 0 && iwm.enable2 == 0 && iwm.motor_on) { + drive = iwm.drive_select; + if(g_apple35_sel) { + dsk = &(iwm.drive35[drive]); + return iwm_read_data_35(dsk, g_fast_disk_emul, dcycs); + } else { + dsk = &(iwm.drive525[drive]); + return iwm_read_data_525(dsk, g_fast_disk_emul, dcycs); + } + + } + + return read_iwm(0xc, dcycs); +} + + +int +read_iwm(int loc, double dcycs) +{ + Disk *dsk; + word32 status; + double diff_dcycs; + double dcmp; + int on; + int state; + int drive; + int val; + + loc = loc & 0xf; + on = loc & 1; + + if(loc == 0xc) { + iwm.q6 = 0; + } else { + iwm_touch_switches(loc, dcycs); + } + + state = (iwm.q7 << 1) + iwm.q6; + drive = iwm.drive_select; + if(g_apple35_sel) { + dsk = &(iwm.drive35[drive]); + } else { + dsk = &(iwm.drive525[drive]); + } + + if(on) { + /* odd address, return 0 */ + return 0; + } else { + /* even address */ + switch(state) { + case 0x00: /* q7 = 0, q6 = 0 */ + if(iwm.enable2) { + return iwm_read_enable2(dcycs); + } else { + if(iwm.motor_on) { + return iwm_read_data(dsk, + g_fast_disk_emul, dcycs); + } else { + iwm_printf("read iwm st 0, m off!\n"); +/* HACK!!!! */ + return 0xff; + //return (((int)dcycs) & 0x7f) + 0x80; + } + } + break; + case 0x01: /* q7 = 0, q6 = 1 */ + /* read IWM status reg */ + if(iwm.enable2) { + iwm_printf("Read status under enable2: 1\n"); + status = 1; + } else { + if(g_apple35_sel) { + status = iwm_read_status35(dcycs); + } else { + status = dsk->write_prot; + } + } + + val = (status << 7) + (iwm.motor_on << 5) + + iwm.iwm_mode; + iwm_printf("Read status: %02x\n", val); + + return val; + break; + case 0x02: /* q7 = 1, q6 = 0 */ + /* read handshake register */ + if(iwm.enable2) { + return iwm_read_enable2_handshake(dcycs); + } else { + status = 0xc0; + diff_dcycs = dcycs - dsk->dcycs_last_read; + dcmp = 16.0; + if(dsk->disk_525 == 0) { + dcmp = 32.0; + } + if(diff_dcycs > dcmp) { + iwm_printf("Write underrun!\n"); + iwm_printf("cur: %f, dc_last: %f\n", + dcycs, dsk->dcycs_last_read); + status = status & 0xbf; + } + return status; + } + break; + case 0x03: /* q7 = 1, q6 = 1 */ + halt_printf("read iwm state 3!\n"); + return 0; + break; + } + + } + halt_printf("Got to end of read_iwm, loc: %02x!\n", loc); + + return 0; +} + +void +write_iwm(int loc, int val, double dcycs) +{ + Disk *dsk; + int on; + int state; + int drive; + int fast_writes; + + loc = loc & 0xf; + on = loc & 1; + + iwm_touch_switches(loc, dcycs); + + state = (iwm.q7 << 1) + iwm.q6; + drive = iwm.drive_select; + fast_writes = g_fast_disk_emul; + if(g_apple35_sel) { + dsk = &(iwm.drive35[drive]); + } else { + dsk = &(iwm.drive525[drive]); + fast_writes = !g_slow_525_emul_wr && fast_writes; + } + + if(on) { + /* odd address, write something */ + if(state == 0x03) { + /* q7, q6 = 1,1 */ + if(iwm.motor_on) { + if(iwm.enable2) { + iwm_write_enable2(val, dcycs); + } else { + iwm_write_data(dsk, val, + fast_writes, dcycs); + } + } else { + /* write mode register */ + val = val & 0x1f; + iwm.iwm_mode = val; + if(val != 0 && val != 0x0f && val != 0x07 && + val != 0x04 && val != 0x0b) { + halt_printf("set iwm_mode:%02x!\n",val); + } + } + } else { + if(iwm.enable2) { + iwm_write_enable2(val, dcycs); + } else { + printf("Write iwm1, st: %02x, loc: %x: %02x\n", + state, loc, val); + } + } + return; + } else { + /* even address */ + if(iwm.enable2) { + iwm_write_enable2(val, dcycs); + } else { + iwm_printf("Write iwm2, st: %02x, loc: %x: %02x\n", + state, loc, val); + } + return; + } + + halt_printf("Got to end of write_iwm, loc:%02x, val: %02x\n", loc, val); + + return; + +} + + + +int +iwm_read_enable2(double dcycs) +{ + iwm_printf("Read under enable2!\n"); + return 0xff; +} + +int g_cnt_enable2_handshake = 0; + +int +iwm_read_enable2_handshake(double dcycs) +{ + int val; + + iwm_printf("Read handshake under enable2!\n"); + + val = 0xc0; + g_cnt_enable2_handshake++; + if(g_cnt_enable2_handshake > 3) { + g_cnt_enable2_handshake = 0; + val = 0x80; + } + + return val; +} + +void +iwm_write_enable2(int val, double dcycs) +{ + iwm_printf("Write under enable2: %02x!\n", val); + + return; +} + +int +iwm_read_data(Disk *dsk, int fast_disk_emul, double dcycs) +{ + if(dsk->disk_525) { + return iwm_read_data_525(dsk, fast_disk_emul, dcycs); + } else { + return iwm_read_data_35(dsk, fast_disk_emul, dcycs); + } +} + +void +iwm_write_data(Disk *dsk, word32 val, int fast_disk_emul, double dcycs) +{ + if(dsk->disk_525) { + iwm_write_data_525(dsk, val, fast_disk_emul, dcycs); + } else { + iwm_write_data_35(dsk, val, fast_disk_emul, dcycs); + } +} + +#undef IWM_READ_ROUT +#undef IWM_WRITE_ROUT +#undef IWM_CYC_MULT +#undef IWM_DISK_525 + +#define IWM_READ_ROUT iwm_read_data_35 +#define IWM_WRITE_ROUT iwm_write_data_35 +#define IWM_CYC_MULT 1 +#define IWM_DISK_525 0 + +#define INCLUDE_IWM_RCSID_C +#include "iwm_35_525.h" +#undef INCLUDE_IWM_RCSID_C + +#undef IWM_READ_ROUT +#undef IWM_WRITE_ROUT +#undef IWM_CYC_MULT +#undef IWM_DISK_525 + +#define IWM_READ_ROUT iwm_read_data_525 +#define IWM_WRITE_ROUT iwm_write_data_525 +#define IWM_CYC_MULT 2 +#define IWM_DISK_525 1 +#include "iwm_35_525.h" + +#undef IWM_READ_ROUT +#undef IWM_WRITE_ROUT +#undef IWM_CYC_MULT +#undef IWM_DISK_525 + + + + + +/* c600 */ +void +sector_to_partial_nib(byte *in, byte *nib_ptr) +{ + byte *aux_buf; + byte *nib_out; + int val; + int val2; + int x; + int i; + + /* Convert 256(+1) data bytes to 342+1 disk nibbles */ + + aux_buf = nib_ptr; + nib_out = nib_ptr + 0x56; + + for(i = 0; i < 0x56; i++) { + aux_buf[i] = 0; + } + + x = 0x55; + for(i = 0x101; i >= 0; i--) { + val = in[i]; + if(i >= 0x100) { + val = 0; + } + val2 = (aux_buf[x] << 1) + (val & 1); + val = val >> 1; + val2 = (val2 << 1) + (val & 1); + val = val >> 1; + nib_out[i] = val; + aux_buf[x] = val2; + x--; + if(x < 0) { + x = 0x55; + } + } +} + + +int +disk_unnib_4x4(Disk *dsk) +{ + int val1; + int val2; + + val1 = iwm_read_data(dsk, 1, 0); + val2 = iwm_read_data(dsk, 1, 0); + + return ((val1 << 1) + 1) & val2; +} + +int +iwm_denib_track525(Disk *dsk, Track *trk, int qtr_track, byte *outbuf) +{ + byte aux_buf[0x80]; + byte *buf; + int sector_done[16]; + int num_sectors_done; + int track_len; + int vol, track, phys_sec, log_sec, cksum; + int val; + int val2; + int prev_val; + int x; + int my_nib_cnt; + int save_qtr_track; + int save_nib_pos; + int tmp_nib_pos; + int status; + int i; + + save_qtr_track = dsk->cur_qtr_track; + save_nib_pos = dsk->nib_pos; + + iwm_move_to_track(dsk, qtr_track); + + dsk->nib_pos = 0; + g_fast_disk_unnib = 1; + + track_len = trk->track_len; + + for(i = 0; i < 16; i++) { + sector_done[i] = 0; + } + + num_sectors_done = 0; + + val = 0; + status = -1; + my_nib_cnt = 0; + while(my_nib_cnt++ < 2*track_len) { + /* look for start of a sector */ + if(val != 0xd5) { + val = iwm_read_data(dsk, 1, 0); + continue; + } + + val = iwm_read_data(dsk, 1, 0); + if(val != 0xaa) { + continue; + } + + val = iwm_read_data(dsk, 1, 0); + if(val != 0x96) { + continue; + } + + /* It's a sector start */ + vol = disk_unnib_4x4(dsk); + track = disk_unnib_4x4(dsk); + phys_sec = disk_unnib_4x4(dsk); + if(phys_sec < 0 || phys_sec > 15) { + printf("Track %02x, read sec as %02x\n", qtr_track>>2, + phys_sec); + break; + } + if(dsk->image_type == DSK_TYPE_DOS33) { + log_sec = phys_to_dos_sec[phys_sec]; + } else { + log_sec = phys_to_prodos_sec[phys_sec]; + } + cksum = disk_unnib_4x4(dsk); + if((vol ^ track ^ phys_sec ^ cksum) != 0) { + /* not correct format */ + printf("Track %02x not DOS 3.3 since hdr cksum, %02x " + "%02x %02x %02x\n", + qtr_track>>2, vol, track, phys_sec, cksum); + break; + } + + /* see what sector it is */ + if(track != (qtr_track>>2) || (phys_sec < 0)||(phys_sec > 15)) { + printf("Track %02x bad since track: %02x, sec: %02x\n", + qtr_track>>2, track, phys_sec); + break; + } + + if(sector_done[phys_sec]) { + printf("Already done sector %02x on track %02x!\n", + phys_sec, qtr_track>>2); + break; + } + + /* So far so good, let's do it! */ + val = 0; + i = 0; + while(i < NIBS_FROM_ADDR_TO_DATA) { + i++; + if(val != 0xd5) { + val = iwm_read_data(dsk, 1, 0); + continue; + } + + val = iwm_read_data(dsk, 1, 0); + if(val != 0xaa) { + continue; + } + + val = iwm_read_data(dsk, 1, 0); + if(val != 0xad) { + continue; + } + + /* got it, just break */ + break; + } + + if(i >= NIBS_FROM_ADDR_TO_DATA) { + printf("No data header, track %02x, sec %02x\n", + qtr_track>>2, phys_sec); + printf("nib_pos: %08x\n", dsk->nib_pos); + break; + } + + buf = outbuf + 0x100*log_sec; + + /* Data start! */ + prev_val = 0; + for(i = 0x55; i >= 0; i--) { + val = iwm_read_data(dsk, 1, 0); + val2 = from_disk_byte[val]; + if(val2 < 0) { + printf("Bad data area1, val:%02x,val2:%02x\n", + val, val2); + printf(" i:%03x,n_pos:%04x\n", i, dsk->nib_pos); + break; + } + prev_val = val2 ^ prev_val; + aux_buf[i] = prev_val; + } + + /* rest of data area */ + for(i = 0; i < 0x100; i++) { + val = iwm_read_data(dsk, 1, 0); + val2 = from_disk_byte[val]; + if(val2 < 0) { + printf("Bad data area2, read: %02x\n", val); + printf(" nib_pos: %04x\n", dsk->nib_pos); + break; + } + prev_val = val2 ^ prev_val; + buf[i] = prev_val; + } + + /* checksum */ + val = iwm_read_data(dsk, 1, 0); + val2 = from_disk_byte[val]; + if(val2 < 0) { + printf("Bad data area3, read: %02x\n", val); + printf(" nib_pos: %04x\n", dsk->nib_pos); + break; + } + if(val2 != prev_val) { + printf("Bad data cksum, got %02x, wanted: %02x\n", + val2, prev_val); + printf(" nib_pos: %04x\n", dsk->nib_pos); + break; + } + + /* Got this far, data is good, merge aux_buf into buf */ + x = 0x55; + for(i = 0; i < 0x100; i++) { + val = aux_buf[x]; + val2 = (buf[i] << 1) + (val & 1); + val = val >> 1; + val2 = (val2 << 1) + (val & 1); + buf[i] = val2; + val = val >> 1; + aux_buf[x] = val; + x--; + if(x < 0) { + x = 0x55; + } + } + sector_done[phys_sec] = 1; + num_sectors_done++; + if(num_sectors_done >= 16) { + status = 0; + break; + } + } + + tmp_nib_pos = dsk->nib_pos; + iwm_move_to_track(dsk, save_qtr_track); + dsk->nib_pos = save_nib_pos; + g_fast_disk_unnib = 0; + + if(status == 0) { + return 1; + } + + printf("Nibblization not done, %02x sectors found on track %02x\n", + num_sectors_done, qtr_track>>2); + printf("my_nib_cnt: %04x, nib_pos: %04x, trk_len: %04x\n", my_nib_cnt, + tmp_nib_pos, track_len); + for(i = 0; i < 16; i++) { + printf("sector_done[%d] = %d\n", i, sector_done[i]); + } + return -1; +} + +int +iwm_denib_track35(Disk *dsk, Track *trk, int qtr_track, byte *outbuf) +{ + word32 buf_c00[0x100]; + word32 buf_d00[0x100]; + word32 buf_e00[0x100]; + byte *buf; + word32 tmp_5c, tmp_5d, tmp_5e; + word32 tmp_66, tmp_67; + int sector_done[16]; + int num_sectors_done; + int track_len; + int phys_track, phys_sec, phys_side, phys_capacity, cksum; + int tmp; + int track, side; + int num_sectors; + int val; + int val2; + int x, y; + int carry; + int my_nib_cnt; + int save_qtr_track; + int save_nib_pos; + int status; + int i; + + save_qtr_track = dsk->cur_qtr_track; + save_nib_pos = dsk->nib_pos; + + iwm_move_to_track(dsk, qtr_track); + + dsk->nib_pos = 0; + g_fast_disk_unnib = 1; + + track_len = trk->track_len; + + num_sectors = g_track_bytes_35[qtr_track >> 5] >> 9; + + for(i = 0; i < num_sectors; i++) { + sector_done[i] = 0; + } + + num_sectors_done = 0; + + val = 0; + status = -1; + my_nib_cnt = 0; + + track = qtr_track >> 1; + side = qtr_track & 1; + + while(my_nib_cnt++ < 2*track_len) { + /* look for start of a sector */ + if(val != 0xd5) { + val = iwm_read_data(dsk, 1, 0); + continue; + } + + val = iwm_read_data(dsk, 1, 0); + if(val != 0xaa) { + continue; + } + + val = iwm_read_data(dsk, 1, 0); + if(val != 0x96) { + continue; + } + + /* It's a sector start */ + val = iwm_read_data(dsk, 1, 0); + phys_track = from_disk_byte[val]; + if(phys_track != (track & 0x3f)) { + printf("Track %02x.%d, read track %02x, %02x\n", + track, side, phys_track, val); + break; + } + + phys_sec = from_disk_byte[iwm_read_data(dsk, 1, 0)]; + if(phys_sec < 0 || phys_sec >= num_sectors) { + printf("Track %02x.%d, read sector %02x??\n", + track, side, phys_sec); + break; + } + phys_side = from_disk_byte[iwm_read_data(dsk, 1, 0)]; + + if(phys_side != ((side << 5) + (track >> 6))) { + printf("Track %02x.%d, read side %02x??\n", + track, side, phys_side); + break; + } + phys_capacity = from_disk_byte[iwm_read_data(dsk, 1, 0)]; + if(phys_capacity != 0x24 && phys_capacity != 0x22) { + printf("Track %02x.%x capacity: %02x != 0x24/22\n", + track, side, phys_capacity); + } + cksum = from_disk_byte[iwm_read_data(dsk, 1, 0)]; + + tmp = phys_track ^ phys_sec ^ phys_side ^ phys_capacity; + if(cksum != tmp) { + printf("Track %02x.%d, sector %02x, cksum: %02x.%02x\n", + track, side, phys_sec, cksum, tmp); + break; + } + + + if(sector_done[phys_sec]) { + printf("Already done sector %02x on track %02x.%x!\n", + phys_sec, track, side); + break; + } + + /* So far so good, let's do it! */ + val = 0; + for(i = 0; i < 38; i++) { + val = iwm_read_data(dsk, 1, 0); + if(val == 0xd5) { + break; + } + } + if(val != 0xd5) { + printf("No data header, track %02x.%x, sec %02x\n", + track, side, phys_sec); + break; + } + + val = iwm_read_data(dsk, 1, 0); + if(val != 0xaa) { + printf("Bad data hdr1,val:%02x trk %02x.%x, sec %02x\n", + val, track, side, phys_sec); + printf("nib_pos: %08x\n", dsk->nib_pos); + break; + } + + val = iwm_read_data(dsk, 1, 0); + if(val != 0xad) { + printf("Bad data hdr2,val:%02x trk %02x.%x, sec %02x\n", + val, track, side, phys_sec); + break; + } + + buf = outbuf + (phys_sec << 9); + + /* check sector again */ + val = from_disk_byte[iwm_read_data(dsk, 1, 0)]; + if(val != phys_sec) { + printf("Bad data hdr3,val:%02x trk %02x.%x, sec %02x\n", + val, track, side, phys_sec); + break; + } + + /* Data start! */ + tmp_5c = 0; + tmp_5d = 0; + tmp_5e = 0; + y = 0xaf; + carry = 0; + + while(y > 0) { +/* 626f */ + val = iwm_read_data(dsk, 1, 0); + val2 = from_disk_byte[val]; + if(val2 < 0) { + printf("Bad data area1b, read: %02x\n", val); + printf(" i:%03x,n_pos:%04x\n", i, dsk->nib_pos); + break; + } + tmp_66 = val2; + + tmp_5c = tmp_5c << 1; + carry = (tmp_5c >> 8); + tmp_5c = (tmp_5c + carry) & 0xff; + + val = iwm_read_data(dsk, 1, 0); + val2 = from_disk_byte[val]; + if(val2 < 0) { + printf("Bad data area2, read: %02x\n", val); + break; + } + + val2 = val2 + ((tmp_66 << 2) & 0xc0); + + val2 = val2 ^ tmp_5c; + buf_c00[y] = val2; + + tmp_5e = val2 + tmp_5e + carry; + carry = (tmp_5e >> 8); + tmp_5e = tmp_5e & 0xff; +/* 62b8 */ + val = iwm_read_data(dsk, 1, 0); + val2 = from_disk_byte[val]; + val2 = val2 + ((tmp_66 << 4) & 0xc0); + val2 = val2 ^ tmp_5e; + buf_d00[y] = val2; + tmp_5d = val2 + tmp_5d + carry; + + carry = (tmp_5d >> 8); + tmp_5d = tmp_5d & 0xff; + + y--; + if(y <= 0) { + break; + } + +/* 6274 */ + val = iwm_read_data(dsk, 1, 0); + val2 = from_disk_byte[val]; + val2 = val2 + ((tmp_66 << 6) & 0xc0); + val2 = val2 ^ tmp_5d; + buf_e00[y+1] = val2; + + tmp_5c = val2 + tmp_5c + carry; + carry = (tmp_5c >> 8); + tmp_5c = tmp_5c & 0xff; + } + +/* 62d0 */ + val = iwm_read_data(dsk, 1, 0); + val2 = from_disk_byte[val]; + + tmp_66 = (val2 << 6) & 0xc0; + tmp_67 = (val2 << 4) & 0xc0; + val2 = (val2 << 2) & 0xc0; + + val = iwm_read_data(dsk, 1, 0); + val2 = from_disk_byte[val] + val2; + if(tmp_5e != (word32)val2) { + printf("Checksum 5e bad: %02x vs %02x\n", tmp_5e, val2); + printf("val:%02x trk %02x.%x, sec %02x\n", + val, track, side, phys_sec); + break; + } + + val = iwm_read_data(dsk, 1, 0); + val2 = from_disk_byte[val] + tmp_67; + if(tmp_5d != (word32)val2) { + printf("Checksum 5d bad: %02x vs %02x\n", tmp_5e, val2); + printf("val:%02x trk %02x.%x, sec %02x\n", + val, track, side, phys_sec); + break; + } + + val = iwm_read_data(dsk, 1, 0); + val2 = from_disk_byte[val] + tmp_66; + if(tmp_5c != (word32)val2) { + printf("Checksum 5c bad: %02x vs %02x\n", tmp_5e, val2); + printf("val:%02x trk %02x.%x, sec %02x\n", + val, track, side, phys_sec); + break; + } + + /* Whew, got it!...check for DE AA */ + val = iwm_read_data(dsk, 1, 0); + if(val != 0xde) { + printf("Bad data epi1,val:%02x trk %02x.%x, sec %02x\n", + val, track, side, phys_sec); + printf("nib_pos: %08x\n", dsk->nib_pos); + break; + } + + val = iwm_read_data(dsk, 1, 0); + if(val != 0xaa) { + printf("Bad data epi2,val:%02x trk %02x.%x, sec %02x\n", + val, track, side, phys_sec); + break; + } + + /* Now, convert buf_c/d/e to output */ +/* 6459 */ + y = 0; + for(x = 0xab; x >= 0; x--) { + *buf++ = buf_c00[x]; + y++; + if(y >= 0x200) { + break; + } + + *buf++ = buf_d00[x]; + y++; + if(y >= 0x200) { + break; + } + + *buf++ = buf_e00[x]; + y++; + if(y >= 0x200) { + break; + } + } + + sector_done[phys_sec] = 1; + num_sectors_done++; + if(num_sectors_done >= num_sectors) { + status = 0; + break; + } + val = 0; + } + + if(status < 0) { + printf("dsk->nib_pos: %04x, status: %d\n", dsk->nib_pos, + status); + for(i = 0; i < num_sectors; i++) { + printf("sector done[%d] = %d\n", i, sector_done[i]); + } + } + + iwm_move_to_track(dsk, save_qtr_track); + dsk->nib_pos = save_nib_pos; + g_fast_disk_unnib = 0; + + if(status == 0) { + return 1; + } + + printf("Nibblization not done, %02x sectors found on track %02x\n", + num_sectors_done, qtr_track>>2); + return -1; + + + +} + +/* ret = 1 -> dirty data written out */ +/* ret = 0 -> not dirty, no error */ +/* ret < 0 -> error */ +int +disk_track_to_unix(Disk *dsk, int qtr_track, byte *outbuf) +{ + Track *trk; + int disk_525; + + disk_525 = dsk->disk_525; + + trk = &(dsk->tracks[qtr_track]); + + if(trk->track_len == 0 || trk->track_dirty == 0) { +#if 0 + printf("disk_track_to_unix: dirty: %d\n", trk->track_dirty); +#endif + return 0; + } + + trk->track_dirty = 0; + + if((qtr_track & 3) && disk_525) { + halt_printf("You wrote to phase %02x! Can't wr bk to unix!\n", + qtr_track); + dsk->write_through_to_unix = 0; + return -1; + } + + if(disk_525) { + return iwm_denib_track525(dsk, trk, qtr_track, outbuf); + } else { + return iwm_denib_track35(dsk, trk, qtr_track, outbuf); + } +} + + +void +show_hex_data(byte *buf, int count) +{ + int i; + + for(i = 0; i < count; i += 16) { + printf("%04x: %02x %02x %02x %02x %02x %02x %02x %02x " + "%02x %02x %02x %02x %02x %02x %02x %02x\n", i, + buf[i+0], buf[i+1], buf[i+2], buf[i+3], + buf[i+4], buf[i+5], buf[i+6], buf[i+7], + buf[i+8], buf[i+9], buf[i+10], buf[i+11], + buf[i+12], buf[i+13], buf[i+14], buf[i+15]); + } + +} + +void +disk_check_nibblization(Disk *dsk, int qtr_track, byte *buf, int size) +{ + byte buffer[0x3000]; + Track *trk; + int ret, ret2; + int i; + + if(size > 0x3000) { + printf("size %08x is > 0x3000, disk_check_nibblization\n",size); + exit(3); + } + + for(i = 0; i < size; i++) { + buffer[i] = 0; + } + + trk = &(dsk->tracks[qtr_track]); + + if(dsk->disk_525) { + ret = iwm_denib_track525(dsk, trk, qtr_track, &(buffer[0])); + } else { + ret = iwm_denib_track35(dsk, trk, qtr_track, &(buffer[0])); + } + + ret2 = -1; + for(i = 0; i < size; i++) { + if(buffer[i] != buf[i]) { + printf("buffer[%04x]: %02x != %02x\n", i, buffer[i], + buf[i]); + ret2 = i; + break; + } + } + + if(ret != 1 || ret2 >= 0) { + printf("disk_check_nib ret:%d, ret2:%d for q_track %03x\n", + ret, ret2, qtr_track); + show_hex_data(buf, 0x1000); + show_hex_data(buffer, 0x1000); + iwm_show_a_track(&(dsk->tracks[qtr_track])); + + exit(2); + } +} + + +#define TRACK_BUF_LEN 0x2000 + +void +disk_unix_to_nib(Disk *dsk, int qtr_track, int unix_pos, int unix_len, + int nib_len) +{ + byte track_buf[TRACK_BUF_LEN]; + Track *trk; + int must_clear_track; + int ret; + int len; + int i; + + /* Read track from dsk int track_buf */ + + must_clear_track = 0; + + if(unix_len > TRACK_BUF_LEN) { + printf("diks_unix_to_nib: requested len of image %s = %05x\n", + dsk->name_ptr, unix_len); + } + + if(unix_pos >= 0) { + ret = lseek(dsk->fd, unix_pos, SEEK_SET); + if(ret != unix_pos) { + printf("lseek of disk %s len 0x%x ret: %d, errno: %d\n", + dsk->name_ptr, unix_pos, ret, errno); + must_clear_track = 1; + } + + len = read(dsk->fd, track_buf, unix_len); + if(len != unix_len) { + printf("read of disk %s q_trk %d ret: %d, errno: %d\n", + dsk->name_ptr, qtr_track, ret, errno); + must_clear_track = 1; + } + } + + if(must_clear_track) { + for(i = 0; i < TRACK_BUF_LEN; i++) { + track_buf[i] = 0; + } + } + +#if 0 + printf("Q_track %02x dumped out\n", qtr_track); + + for(i = 0; i < 4096; i += 32) { + printf("%04x: %02x%02x%02x%02x%02x%02x%02x%02x " + "%02x%02x%02x%02x%02x%02x%02x%02x " + "%02x%02x%02x%02x%02x%02x%02x%02x " + "%02x%02x%02x%02x%02x%02x%02x%02x\n", i, + track_buf[i+0], track_buf[i+1], track_buf[i+2], + track_buf[i+3], track_buf[i+4], track_buf[i+5], + track_buf[i+6], track_buf[i+7], track_buf[i+8], + track_buf[i+9], track_buf[i+10], track_buf[i+11], + track_buf[i+12], track_buf[i+13], track_buf[i+14], + track_buf[i+15], track_buf[i+16], track_buf[i+17], + track_buf[i+18], track_buf[i+19], track_buf[i+20], + track_buf[i+21], track_buf[i+22], track_buf[i+23], + track_buf[i+24], track_buf[i+25], track_buf[i+26], + track_buf[i+27], track_buf[i+28], track_buf[i+29], + track_buf[i+30], track_buf[i+31]); + } +#endif + + dsk->nib_pos = 0; /* for consistency */ + + trk = &(dsk->tracks[qtr_track]); + trk->track_dirty = 0; + trk->overflow_size = 0; + trk->track_len = 2*nib_len; + trk->unix_pos = unix_pos; + trk->unix_len = unix_len; + trk->dsk = dsk; + trk->nib_area = (byte *)malloc(trk->track_len); + + /* create nibblized image */ + + if(dsk->disk_525 && dsk->image_type == DSK_TYPE_NIB) { + iwm_nibblize_track_nib525(dsk, trk, track_buf, qtr_track); + } else if(dsk->disk_525) { + iwm_nibblize_track_525(dsk, trk, track_buf, qtr_track); + } else { + iwm_nibblize_track_35(dsk, trk, track_buf, qtr_track); + } +} + +void +iwm_nibblize_track_nib525(Disk *dsk, Track *trk, byte *track_buf, int qtr_track) +{ + byte *nib_ptr; + byte *trk_ptr; + int len; + int i; + + len = trk->track_len; + trk_ptr = track_buf; + nib_ptr = &(trk->nib_area[0]); + for(i = 0; i < len; i += 2) { + nib_ptr[i] = 8; + nib_ptr[i+1] = *trk_ptr++;; + } + + iwm_printf("Nibblized q_track %02x\n", qtr_track); +} + +void +iwm_nibblize_track_525(Disk *dsk, Track *trk, byte *track_buf, int qtr_track) +{ + byte partial_nib_buf[0x300]; + word32 *word_ptr; + word32 val; + word32 last_val; + int phys_sec; + int log_sec; + int num_sync; + int i; + + + word_ptr = (word32 *)&(trk->nib_area[0]); +#ifdef KEGS_LITTLE_ENDIAN + val = 0xff08ff08; +#else + val = 0x08ff08ff; +#endif + for(i = 0; i < trk->track_len; i += 4) { + *word_ptr++ = val; + } + + + for(phys_sec = 0; phys_sec < 16; phys_sec++) { + if(dsk->image_type == DSK_TYPE_DOS33) { + log_sec = phys_to_dos_sec[phys_sec]; + } else { + log_sec = phys_to_prodos_sec[phys_sec]; + } + + /* Create sync headers */ + if(phys_sec == 0) { + num_sync = 70; + } else { + num_sync = 14; + } + + for(i = 0; i < num_sync; i++) { + disk_nib_out(dsk, 0xff, 10); + } + disk_nib_out(dsk, 0xd5, 10); /* prolog */ + disk_nib_out(dsk, 0xaa, 8); /* prolog */ + disk_nib_out(dsk, 0x96, 8); /* prolog */ + disk_4x4_nib_out(dsk, dsk->vol_num); + disk_4x4_nib_out(dsk, qtr_track >> 2); + disk_4x4_nib_out(dsk, phys_sec); + disk_4x4_nib_out(dsk, dsk->vol_num ^ (qtr_track>>2) ^ phys_sec); + disk_nib_out(dsk, 0xde, 8); /* epi */ + disk_nib_out(dsk, 0xaa, 8); /* epi */ + disk_nib_out(dsk, 0xeb, 8); /* epi */ + + /* Inter sync */ + disk_nib_out(dsk, 0xff, 8); + for(i = 0; i < 5; i++) { + disk_nib_out(dsk, 0xff, 10); + } + disk_nib_out(dsk, 0xd5, 10); /* data prolog */ + disk_nib_out(dsk, 0xaa, 8); /* data prolog */ + disk_nib_out(dsk, 0xad, 8); /* data prolog */ + + sector_to_partial_nib( &(track_buf[log_sec*256]), + &(partial_nib_buf[0])); + + last_val = 0; + for(i = 0; i < 0x156; i++) { + val = partial_nib_buf[i]; + disk_nib_out(dsk, to_disk_byte[last_val ^ val], 8); + last_val = val; + } + disk_nib_out(dsk, to_disk_byte[last_val], 8); + + /* data epilog */ + disk_nib_out(dsk, 0xde, 8); /* epi */ + disk_nib_out(dsk, 0xaa, 8); /* epi */ + disk_nib_out(dsk, 0xeb, 8); /* epi */ + disk_nib_out(dsk, 0xff, 8); + for(i = 0; i < 6; i++) { + disk_nib_out(dsk, 0xff, 10); + } + } + + /* finish nibblization */ + disk_nib_end_track(dsk); + + iwm_printf("Nibblized q_track %02x\n", qtr_track); + + if(g_check_nibblization) { + disk_check_nibblization(dsk, qtr_track, &(track_buf[0]),0x1000); + } +} + +void +iwm_nibblize_track_35(Disk *dsk, Track *trk, byte *track_buf, int qtr_track) +{ + int phys_to_log_sec[16]; + word32 buf_c00[0x100]; + word32 buf_d00[0x100]; + word32 buf_e00[0x100]; + byte *buf; + word32 *word_ptr; + word32 val; + int num_sectors; + int unix_len; + int log_sec; + int phys_sec; + int track; + int side; + int interleave; + int num_sync; + word32 phys_track, phys_side, capacity, cksum; + word32 tmp_5c, tmp_5d, tmp_5e, tmp_5f; + word32 tmp_63, tmp_64, tmp_65; + word32 acc_hi; + int carry; + int x, y; + int i; + + word_ptr = (word32 *)&(trk->nib_area[0]); +#ifdef KEGS_LITTLE_ENDIAN + val = 0xff08ff08; +#else + val = 0x08ff08ff; +#endif + if(trk->track_len & 3) { + halt_printf("track_len: %08x is not a multiple of 4\n", + trk->track_len); + } + + for(i = 0; i < trk->track_len; i += 4) { + *word_ptr++ = val; + } + + unix_len = trk->unix_len; + + num_sectors = (unix_len >> 9); + + for(i = 0; i < num_sectors; i++) { + phys_to_log_sec[i] = -1; + } + + phys_sec = 0; + interleave = 2; + for(log_sec = 0; log_sec < num_sectors; log_sec++) { + while(phys_to_log_sec[phys_sec] >= 0) { + phys_sec++; + if(phys_sec >= num_sectors) { + phys_sec = 0; + } + } + phys_to_log_sec[phys_sec] = log_sec; + phys_sec += interleave; + if(phys_sec >= num_sectors) { + phys_sec -= num_sectors; + } + } + + track = qtr_track >> 1; + side = qtr_track & 1; + for(phys_sec = 0; phys_sec < num_sectors; phys_sec++) { + + log_sec = phys_to_log_sec[phys_sec]; + if(log_sec < 0) { + printf("Track: %02x.%x phys_sec: %02x = %d!\n", + track, side, phys_sec, log_sec); + exit(2); + } + + /* Create sync headers */ + if(phys_sec == 0) { + num_sync = 400; + } else { + num_sync = 54; + } + + for(i = 0; i < num_sync; i++) { + disk_nib_out(dsk, 0xff, 10); + } + + disk_nib_out(dsk, 0xd5, 10); /* prolog */ + disk_nib_out(dsk, 0xaa, 8); /* prolog */ + disk_nib_out(dsk, 0x96, 8); /* prolog */ + + phys_track = track & 0x3f; + phys_side = (side << 5) + (track >> 6); + capacity = 0x22; + disk_nib_out(dsk, to_disk_byte[phys_track], 8); /* trk */ + disk_nib_out(dsk, to_disk_byte[log_sec], 8); /* sec */ + disk_nib_out(dsk, to_disk_byte[phys_side], 8); /* sides+trk */ + disk_nib_out(dsk, to_disk_byte[capacity], 8); /* capacity*/ + + cksum = (phys_track ^ log_sec ^ phys_side ^ capacity) & 0x3f; + disk_nib_out(dsk, to_disk_byte[cksum], 8); /* cksum*/ + + disk_nib_out(dsk, 0xde, 8); /* epi */ + disk_nib_out(dsk, 0xaa, 8); /* epi */ + + /* Inter sync */ + for(i = 0; i < 5; i++) { + disk_nib_out(dsk, 0xff, 10); + } + disk_nib_out(dsk, 0xd5, 10); /* data prolog */ + disk_nib_out(dsk, 0xaa, 8); /* data prolog */ + disk_nib_out(dsk, 0xad, 8); /* data prolog */ + disk_nib_out(dsk, to_disk_byte[log_sec], 8); /* sec again */ + + /* do nibblizing! */ + buf = track_buf + (log_sec << 9); + +/* 6320 */ + tmp_5e = 0; + tmp_5d = 0; + tmp_5c = 0; + y = 0; + x = 0xaf; + buf_c00[0] = 0; + buf_d00[0] = 0; + buf_e00[0] = 0; + buf_e00[1] = 0; + for(y = 0x4; y > 0; y--) { + buf_c00[x] = 0; + buf_d00[x] = 0; + buf_e00[x] = 0; + x--; + } + + while(x >= 0) { +/* 6338 */ + tmp_5c = tmp_5c << 1; + carry = (tmp_5c >> 8); + tmp_5c = (tmp_5c + carry) & 0xff; + + val = buf[y]; + tmp_5e = val + tmp_5e + carry; + carry = (tmp_5e >> 8); + tmp_5e = tmp_5e & 0xff; + + val = val ^ tmp_5c; + buf_c00[x] = val; + y++; +/* 634c */ + val = buf[y]; + tmp_5d = tmp_5d + val + carry; + carry = (tmp_5d >> 8); + tmp_5d = tmp_5d & 0xff; + val = val ^ tmp_5e; + buf_d00[x] = val; + y++; + x--; + if(x <= 0) { + break; + } + +/* 632a */ + val = buf[y]; + tmp_5c = tmp_5c + val + carry; + carry = (tmp_5c >> 8); + tmp_5c = tmp_5c & 0xff; + + val = val ^ tmp_5d; + buf_e00[x+1] = val; + y++; + } + +/* 635f */ + val = ((tmp_5c >> 2) ^ tmp_5d) & 0x3f; +/* 6367 */ + val = (val ^ tmp_5d) >> 2; +/* 636b */ + val = (val ^ tmp_5e) & 0x3f; +/* 636f */ + val = (val ^ tmp_5e) >> 2; +/* 6373 */ + tmp_5f = val; +/* 6375 */ + tmp_63 = 0; + tmp_64 = 0; + tmp_65 = 0; + acc_hi = 0; + + + y = 0xae; + while(y >= 0) { +/* 63e4 */ + /* write out acc_hi */ + val = to_disk_byte[acc_hi & 0x3f]; + disk_nib_out(dsk, val, 8); + +/* 63f2 */ + val = to_disk_byte[tmp_63 & 0x3f]; + tmp_63 = buf_c00[y]; + acc_hi = tmp_63 >> 6; + disk_nib_out(dsk, val, 8); +/* 640b */ + val = to_disk_byte[tmp_64 & 0x3f]; + tmp_64 = buf_d00[y]; + acc_hi = (acc_hi << 2) + (tmp_64 >> 6); + disk_nib_out(dsk, val, 8); + y--; + if(y < 0) { + break; + } + +/* 63cb */ + val = to_disk_byte[tmp_65 & 0x3f]; + tmp_65 = buf_e00[y+1]; + acc_hi = (acc_hi << 2) + (tmp_65 >> 6); + disk_nib_out(dsk, val, 8); + } +/* 6429 */ + val = to_disk_byte[tmp_5f & 0x3f]; + disk_nib_out(dsk, val, 8); + + val = to_disk_byte[tmp_5e & 0x3f]; + disk_nib_out(dsk, val, 8); + + val = to_disk_byte[tmp_5d & 0x3f]; + disk_nib_out(dsk, val, 8); + + val = to_disk_byte[tmp_5c & 0x3f]; + disk_nib_out(dsk, val, 8); + +/* 6440 */ + /* data epilog */ + disk_nib_out(dsk, 0xde, 8); /* epi */ + disk_nib_out(dsk, 0xaa, 8); /* epi */ + disk_nib_out(dsk, 0xff, 8); + } + + + disk_nib_end_track(dsk); + + if(g_check_nibblization) { + disk_check_nibblization(dsk, qtr_track, &(track_buf[0]), + unix_len); + } +} + +void +disk_4x4_nib_out(Disk *dsk, word32 val) +{ + disk_nib_out(dsk, 0xaa | (val >> 1), 8); + disk_nib_out(dsk, 0xaa | val, 8); +} + +void +disk_nib_out(Disk *dsk, byte val, int size) +{ + Track *trk; + int pos; + int old_size; + int track_len; + int overflow_size; + int qtr_track; + + + qtr_track = dsk->cur_qtr_track; + + trk = &(dsk->tracks[qtr_track]); + + track_len = trk->track_len; + + if(track_len <= 10) { + printf("Writing to an invalid qtr track: %02x!\n", qtr_track); + printf("name: %s, track_len: %08x, val: %08x, size: %d\n", + dsk->name_ptr, track_len, val, size); + exit(1); + return; + } + + trk->track_dirty = 1; + dsk->disk_dirty = 1; + + pos = trk->dsk->nib_pos; + overflow_size = trk->overflow_size; + if(pos >= track_len) { + pos = 0; + } + + old_size = trk->nib_area[pos]; + + + while(size >= (10 + old_size)) { + size = size - old_size; + pos += 2; + if(pos >= track_len) { + pos = 0; + } + old_size = trk->nib_area[pos]; + } + + if(size > 10) { + size = 10; + } + + if((val & 0x80) == 0) { + val |= 0x80; + } + + trk->nib_area[pos++] = size; + trk->nib_area[pos++] = val; + if(pos >= track_len) { + pos = 0; + } + + overflow_size += (size - old_size); + if((overflow_size > 8) && (size > 8)) { + overflow_size -= trk->nib_area[pos]; + trk->nib_area[pos++] = 0; + trk->nib_area[pos++] = 0; + if(pos >= track_len) { + pos = 0; + } + } else if(overflow_size < -64) { + halt_printf("overflow_sz:%03x, pos:%02x\n",overflow_size,pos); + } + + trk->dsk->nib_pos = pos; + trk->overflow_size = overflow_size; + + if((val & 0x80) == 0 || size < 8) { + halt_printf("disk_nib_out, wrote %02x, size: %d\n", val, size); + } +} + +void +disk_nib_end_track(Disk *dsk) +{ + int qtr_track; + + dsk->nib_pos = 0; + qtr_track = dsk->cur_qtr_track; + dsk->tracks[qtr_track].track_dirty = 0; + + dsk->disk_dirty = 0; +} + +void +iwm_show_track(int slot_drive, int track) +{ + Disk *dsk; + Track *trk; + int drive; + int sel35; + int qtr_track; + + if(slot_drive < 0) { + drive = iwm.drive_select; + sel35 = g_apple35_sel; + } else { + drive = slot_drive & 1; + sel35 = !((slot_drive >> 1) & 1); + } + + if(sel35) { + dsk = &(iwm.drive35[drive]); + } else { + dsk = &(iwm.drive525[drive]); + } + + if(track < 0) { + qtr_track = dsk->cur_qtr_track; + } else { + qtr_track = track; + } + trk = &(dsk->tracks[qtr_track]); + + if(trk->track_len <= 0) { + printf("Track_len: %d\n", trk->track_len); + printf("No track for type: %d, drive: %d, qtrk: %02x\n", + g_apple35_sel, drive, qtr_track); + return; + } + + printf("Current drive: %d, q_track: %02x\n", drive, qtr_track); + + iwm_show_a_track(trk); +} + +void +iwm_show_a_track(Track *trk) +{ + int sum; + int len; + int pos; + int i; + + printf(" Showtrack:dirty: %d, pos: %04x, ovfl: %04x, len: %04x\n", + trk->track_dirty, trk->dsk->nib_pos, + trk->overflow_size, trk->track_len); + + len = trk->track_len; + printf("Track len in bytes: %04x\n", len); + if(len >= 2*15000) { + len = 2*15000; + printf("len too big, using %04x\n", len); + } + + pos = 0; + for(i = 0; i < len; i += 16) { + printf("%04x: %2d,%02x %2d,%02x %2d,%02x %2d,%02x " + "%2d,%02x %2d,%02x %2d,%02x %2d,%02x\n", pos, + trk->nib_area[pos], trk->nib_area[pos+1], + trk->nib_area[pos+2], trk->nib_area[pos+3], + trk->nib_area[pos+4], trk->nib_area[pos+5], + trk->nib_area[pos+6], trk->nib_area[pos+7], + trk->nib_area[pos+8], trk->nib_area[pos+9], + trk->nib_area[pos+10], trk->nib_area[pos+11], + trk->nib_area[pos+12], trk->nib_area[pos+13], + trk->nib_area[pos+14], trk->nib_area[pos+15]); + pos += 16; + if(pos >= len) { + pos -= len; + } + } + + sum = 0; + for(i = 0; i < len; i += 2) { + sum += trk->nib_area[i]; + } + + printf("bit_sum: %d, expected: %d, overflow_size: %d\n", + sum, len*8/2, trk->overflow_size); +} + diff --git a/src/iwm.h b/src/iwm.h new file mode 100644 index 0000000..e9a6587 --- /dev/null +++ b/src/iwm.h @@ -0,0 +1,116 @@ +/************************************************************************/ +/* KEGS: Apple //gs Emulator */ +/* Copyright 2002 by Kent Dickey */ +/* */ +/* This code is covered by the GNU GPL */ +/* */ +/* The KEGS web page is kegs.sourceforge.net */ +/* You may contact the author at: kadickey@alumni.princeton.edu */ +/************************************************************************/ + +#ifdef INCLUDE_RCSID_C +const char rcsid_iwm_h[] = "@(#)$KmKId: iwm.h,v 1.13 2003-07-08 23:29:48-04 kentd Exp $"; +#endif + +#define MAX_TRACKS (2*80) +#define MAX_C7_DISKS 32 + +#define NIB_LEN_525 0x1900 /* 51072 bits per track */ +#define NIBS_FROM_ADDR_TO_DATA 20 + +#define DSK_TYPE_PRODOS 0 +#define DSK_TYPE_DOS33 1 +#define DSK_TYPE_NIB 2 + +typedef struct _Disk Disk; + +STRUCT(Track) { + Disk *dsk; + byte *nib_area; + int track_dirty; + int overflow_size; + int track_len; + int unix_pos; + int unix_len; +}; + +struct _Disk { + double dcycs_last_read; + char *name_ptr; + char *partition_name; + int partition_num; + int fd; + int force_size; + int image_start; + int image_size; + int smartport; + int disk_525; + int drive; + int cur_qtr_track; + int image_type; + int vol_num; + int write_prot; + int write_through_to_unix; + int disk_dirty; + int just_ejected; + int last_phase; + int nib_pos; + int num_tracks; + Track tracks[MAX_TRACKS]; +}; + + +STRUCT(Iwm) { + Disk drive525[2]; + Disk drive35[2]; + Disk smartport[MAX_C7_DISKS]; + int motor_on; + int motor_off; + int motor_off_vbl_count; + int motor_on35; + int head35; + int step_direction35; + int iwm_phase[4]; + int iwm_mode; + int drive_select; + int q6; + int q7; + int enable2; + int reset; + + word32 previous_write_val; + int previous_write_bits; +}; + + +STRUCT(Driver_desc) { + word16 sig; + word16 blk_size; + word32 blk_count; + word16 dev_type; + word16 dev_id; + word32 data; + word16 drvr_count; +}; + +STRUCT(Part_map) { + word16 sig; + word16 sigpad; + word32 map_blk_cnt; + word32 phys_part_start; + word32 part_blk_cnt; + char part_name[32]; + char part_type[32]; + word32 data_start; + word32 data_cnt; + word32 part_status; + word32 log_boot_start; + word32 boot_size; + word32 boot_load; + word32 boot_load2; + word32 boot_entry; + word32 boot_entry2; + word32 boot_cksum; + char processor[16]; + char junk[128]; +}; diff --git a/src/iwm_35_525.h b/src/iwm_35_525.h new file mode 100644 index 0000000..2604c42 --- /dev/null +++ b/src/iwm_35_525.h @@ -0,0 +1,310 @@ + +#ifdef INCLUDE_IWM_RCSID_C +const char rcsdif_iwm_35_525_h[] = "@(#)$KmKId: iwm_35_525.h,v 1.9 2002-11-14 01:03:16-05 kadickey Exp $"; +#endif + +int +IWM_READ_ROUT (Disk *dsk, int fast_disk_emul, double dcycs) +{ + Track *trk; + double dcycs_last_read; + int pos; + int pos2; + int size; + int next_size; + int qtr_track; + int skip_nibs; + int track_len; + byte ret; + int shift; + int skip; + int cycs_this_nib; + int cycs_passed; + double dcycs_this_nib; + double dcycs_next_nib; + double dcycs_passed; + double track_dcycs; + double dtmp; + + iwm.previous_write_bits = 0; + + qtr_track = dsk->cur_qtr_track; + + trk = &(dsk->tracks[qtr_track]); + track_len = trk->track_len; + + dcycs_last_read = dsk->dcycs_last_read; + dcycs_passed = dcycs - dcycs_last_read; + + pos = dsk->nib_pos; + if(pos >= track_len) { + /* Arm may have moved from inner 3.5 track to outer one, */ + /* and so must make pos fit on smaller sized track */ + pos = 0; + } + + cycs_passed = (int)dcycs_passed; + + if(track_len == 0) { + return (cycs_passed & 0x7f) + 0x80; + } + size = trk->nib_area[pos]; + + while(size == 0) { + pos += 2; + if(pos >= track_len) { + pos = 0; + } + size = trk->nib_area[pos]; + } + + cycs_this_nib = size * (2 * IWM_CYC_MULT); + dcycs_this_nib = (double)cycs_this_nib; + + if(fast_disk_emul) { + cycs_passed = cycs_this_nib; + dcycs_passed = dcycs_this_nib; + + /* pull a trick to make disk motor-on test pass ($bd34 RWTS) */ + /* if this would be a sync byte, and we didn't just do this */ + /* then don't return whole byte */ + /* BUT, don't do this if g_fast_disk_unnib, since it will */ + /* cause the dsk->unix routines to break */ + if(size > 8 && !g_fast_disk_unnib && (g_iwm_fake_fast == 0)) { + cycs_passed = cycs_passed >> 1; + dcycs_passed = dcycs_passed * 0.5; + g_iwm_fake_fast = 1; + } else { + g_iwm_fake_fast = 0; + } + } + + skip = 0; + if(cycs_passed >= (cycs_this_nib + 11)) { + /* skip some bits? */ + skip = 1; + if(iwm.iwm_mode & 1) { + /* latch mode */ + + pos2 = pos + 2; + if(pos2 >= track_len) { + pos2 = 0; + } + next_size = trk->nib_area[pos2]; + while(next_size == 0) { + pos2 += 2; + if(pos2 >= track_len) { + pos2 = 0; + } + next_size = trk->nib_area[pos2]; + } + + dcycs_next_nib = next_size * (2 * IWM_CYC_MULT); + + if(dcycs_passed < (dcycs_this_nib + dcycs_next_nib)) { + skip = 0; + } + } + } + + if(skip) { + iwm_printf("skip since cycs_passed: %f, cycs_this_nib: %f\n", + dcycs_passed, dcycs_this_nib); + + track_dcycs = IWM_CYC_MULT * (track_len * 8); + + if(dcycs_passed >= track_dcycs) { + dtmp = (int)(dcycs_passed / track_dcycs); + dcycs_passed = dcycs_passed - + (dtmp * track_dcycs); + dcycs_last_read += (dtmp * track_dcycs); + } + + if(dcycs_passed >= track_dcycs || dcycs_passed < 0.0) { + dcycs_passed = 0.0; + } + + cycs_passed = (int)dcycs_passed; + + skip_nibs = ((word32)cycs_passed) >> (4 + IWM_DISK_525); + + pos += skip_nibs * 2; + while(pos >= track_len) { + pos -= track_len; + } + + dcycs_last_read += (skip_nibs * 16 * IWM_CYC_MULT); + + dsk->dcycs_last_read = dcycs_last_read; + + size = trk->nib_area[pos]; + dcycs_passed = dcycs - dcycs_last_read; + if(dcycs_passed < 0.0 || dcycs_passed > 64.0) { + halt_printf("skip, last_read:%f, dcycs:%f, dcyc_p:%f\n", + dcycs_last_read, dcycs, dcycs_passed); + } + + while(size == 0) { + pos += 2; + if(pos >= track_len) { + pos = 0; + } + size = trk->nib_area[pos]; + } + + cycs_this_nib = size * (2 * IWM_CYC_MULT); + cycs_passed = (int)dcycs_passed; + dcycs_this_nib = cycs_this_nib; + } + + if(cycs_passed < cycs_this_nib) { + /* partial */ +#if 0 + iwm_printf("Disk partial, %f < %f, size: %d\n", + dcycs_passed, dcycs_this_nib, size); +#endif + shift = (cycs_passed) >> (1 + IWM_DISK_525); + ret = trk->nib_area[pos+1] >> (size - shift); + if(ret & 0x80) { + halt_printf("Bad shift in partial read: %02x, but " + "c_pass:%f, this_nib:%f, shift: %d, size: %d\n", + ret, dcycs_passed, dcycs_this_nib, shift, size); + } + } else { + /* whole thing */ + ret = trk->nib_area[pos+1]; + pos += 2; + if(pos >= track_len) { + pos = 0; + } + if(!fast_disk_emul) { + dsk->dcycs_last_read = dcycs_last_read + dcycs_this_nib; + } + } + + dsk->nib_pos = pos; + if(pos < 0 || pos > track_len) { + halt_printf("I just set nib_pos: %d!\n", pos); + } + +#if 0 + iwm_printf("Disk read, returning: %02x\n", ret); +#endif + + return ret; +} + + +void +IWM_WRITE_ROUT (Disk *dsk, word32 val, int fast_disk_emul, double dcycs) +{ + double dcycs_last_read; + word32 bits_read; + word32 mask; + word32 prev_val; + double dcycs_this_nib; + double dcycs_passed; + int sdiff; + int prev_bits; + + if(dsk->fd < 0) { + halt_printf("Tried to write to type: %d, drive: %d, fd: %d!\n", + IWM_DISK_525, dsk->drive, dsk->fd); + return; + } + + dcycs_last_read = dsk->dcycs_last_read; + + dcycs_passed = dcycs - dcycs_last_read; + + prev_val = iwm.previous_write_val; + prev_bits = iwm.previous_write_bits; + mask = 0x100; + iwm_printf("Iwm write: prev: %x,%d, new:%02x\n", prev_val, prev_bits, + val); + + if(IWM_DISK_525) { + /* Activate slow write emulation mode */ + g_dcycs_end_emul_wr = dcycs + 64.0; + if(!g_slow_525_emul_wr) { + set_halt(HALT_EVENT); + g_slow_525_emul_wr = 1; + } + } else { + /* disable slow writes on 3.5" drives */ + if(g_slow_525_emul_wr) { + set_halt(HALT_EVENT); + printf("HACK3: g_slow_525_emul_wr set to 0\n"); + g_slow_525_emul_wr = 0; + } + } + + if(iwm.iwm_mode & 2) { + /* async mode = 3.5" default */ + bits_read = 8; + } else { + /* sync mode, 5.25" drives */ + bits_read = ((int)dcycs_passed) >> (1 + IWM_DISK_525); + if(bits_read < 8) { + bits_read = 8; + } + } + + if(fast_disk_emul) { + bits_read = 8; + } + + dcycs_this_nib = bits_read * (2 * IWM_CYC_MULT); + + if(fast_disk_emul) { + dcycs_passed = dcycs_this_nib; + } + + if(prev_bits > 0) { + while((prev_val & 0x80) == 0 && bits_read > 0) { + /* previous byte needs some bits */ + mask = mask >> 1; + prev_val = (prev_val << 1) + ((val & mask) !=0); + prev_bits++; + bits_read--; + } + } + + val = val & (mask - 1); + if(prev_bits) { + /* force out prev_val if it had many bits before */ + /* this prevents writes of 0 from messing us up */ + if(((prev_val & 0x80) == 0) && (prev_bits < 10)) { + /* special case: we still don't have enough to go */ + iwm_printf("iwm_write: zip2: %02x, %d, left:%02x,%d\n", + prev_val, prev_bits, val,bits_read); + val = prev_val; + bits_read = prev_bits; + } else { + iwm_printf("iwm_write: prev: %02x, %d, left:%02x, %d\n", + prev_val, prev_bits, val, bits_read); + disk_nib_out(dsk, prev_val, prev_bits); + } + } else if(val & 0x80) { + iwm_printf("iwm_write: new: %02x, %d\n", val,bits_read); + disk_nib_out(dsk, val, bits_read); + bits_read = 0; + } else { + iwm_printf("iwm_write: zip: %02x, %d, left:%02x,%d\n", + prev_val, prev_bits, val,bits_read); + } + + iwm.previous_write_val = val; + iwm.previous_write_bits = bits_read; + if(bits_read < 0) { + halt_printf("iwm, bits_rd:%d, val:%08x, prev:%02x, prevb:%d\n", + bits_read, val, prev_val, prev_bits); + } + + sdiff = dcycs - dcycs_last_read; + if(sdiff < (dcycs_this_nib) || (sdiff > (2*dcycs_this_nib)) ) { + dsk->dcycs_last_read = dcycs; + } else { + dsk->dcycs_last_read = dcycs_last_read + dcycs_this_nib; + } +} diff --git a/src/joystick_driver.c b/src/joystick_driver.c new file mode 100644 index 0000000..35c106f --- /dev/null +++ b/src/joystick_driver.c @@ -0,0 +1,227 @@ +/************************************************************************/ +/* KEGS: Apple //gs Emulator */ +/* Copyright 2002 by Kent Dickey */ +/* */ +/* This code is covered by the GNU GPL */ +/* */ +/* The KEGS web page is kegs.sourceforge.net */ +/* You may contact the author at: kadickey@alumni.princeton.edu */ +/************************************************************************/ + +const char rcsid_joystick_driver_c[] = "@(#)$KmKId: joystick_driver.c,v 1.7 2002-11-19 03:09:59-05 kadickey Exp $"; + +#include "defc.h" +#include + +#ifdef __linux__ +# include +#endif + +#ifdef _WIN32 +# include +# include +#endif + +extern int g_joystick_type; /* in paddles.c */ +extern int g_paddle_button[]; +extern int g_paddle_val[]; + + +const char *g_joystick_dev = "/dev/js0"; /* default joystick dev file */ +#define MAX_JOY_NAME 128 + +int g_joystick_fd = -1; +int g_joystick_num_axes = 0; +int g_joystick_num_buttons = 0; + + +#ifdef __linux__ +void +joystick_init() +{ + char joy_name[MAX_JOY_NAME]; + int version; + int fd; + int i; + + fd = open(g_joystick_dev, O_RDONLY | O_NONBLOCK); + if(fd < 0) { + printf("Unable to open joystick dev file: %s, errno: %d\n", + g_joystick_dev, errno); + printf("Defaulting to mouse joystick\n"); + return; + } + + strcpy(&joy_name[0], "Unknown Joystick"); + version = 0x800; + + ioctl(fd, JSIOCGNAME(MAX_JOY_NAME), &joy_name[0]); + ioctl(fd, JSIOCGAXES, &g_joystick_num_axes); + ioctl(fd, JSIOCGBUTTONS, &g_joystick_num_buttons); + ioctl(fd, JSIOCGVERSION, &version); + + printf("Detected joystick: %s [%d axes, %d buttons vers: %08x]\n", + joy_name, g_joystick_num_axes, g_joystick_num_buttons, + version); + + g_joystick_type = JOYSTICK_LINUX; + g_joystick_fd = fd; + for(i = 0; i < 4; i++) { + g_paddle_val[i] = 280; + g_paddle_button[i] = 1; + } + + joystick_update(); +} + +/* joystick_update_linux() called from paddles.c. Update g_paddle_val[] */ +/* and g_paddle_button[] arrays with current information */ +void +joystick_update() +{ + struct js_event js; /* the linux joystick event record */ + int val; + int num; + int type; + int ret; + int len; + int i; + + /* suck up to 20 events, then give up */ + len = sizeof(struct js_event); + for(i = 0; i < 20; i++) { + ret = read(g_joystick_fd, &js, len); + if(ret != len) { + /* just get out */ + return; + } + type = js.type & ~JS_EVENT_INIT; + val = js.value; + num = js.number & 3; /* clamp to 0-3 */ + switch(type) { + case JS_EVENT_BUTTON: + g_paddle_button[num] = val; + break; + case JS_EVENT_AXIS: + /* val is -32767 to +32767, convert to 0->280 */ + /* want just 255, but go a little over for robustness*/ + g_paddle_val[num] = ((val + 32767) * 9) >> 11; + break; + } + } +} + +void +joystick_update_button() +{ +} + +#else /* !__linux__ */ + +# ifdef _WIN32 +void +joystick_init() +{ + JOYINFO info; + JOYCAPS joycap; + MMRESULT ret1, ret2; + int i; + + // Check that there is a joystick device + if(joyGetNumDevs() <= 0) { + printf("--No joystick hardware detected\n"); + return; + } + + // Check that at least joystick 1 or joystick 2 is available + ret1 = joyGetPos(JOYSTICKID1, &info); + ret2 = joyGetDevCaps(JOYSTICKID1, &joycap, sizeof(joycap)); + if(ret1 == JOYERR_NOERROR && ret2 == JOYERR_NOERROR) { + g_joystick_type = JOYSTICK_WIN32_1; + printf("--Joystick #1 = %s\n", joycap.szPname); + } else { + ret1 = joyGetPos(JOYSTICKID2, &info); + ret2 = joyGetDevCaps(JOYSTICKID2, &joycap, sizeof(joycap)); + if(ret1 == JOYERR_NOERROR && ret2 == JOYERR_NOERROR) { + g_joystick_type = JOYSTICK_WIN32_2; + printf("--Joystick #2 = %s\n", joycap.szPname); + } else { + printf("No joysticks found...\n"); + g_joystick_type = JOYSTICK_MOUSE; + return; + } + + } + + for(i = 0; i < 4; i++) { + g_paddle_val[i] = 280; + g_paddle_button[i] = 1; + } + + joystick_update(); +} + +void +joystick_update() +{ + JOYCAPS joycap; + JOYINFO info; + UINT id; + MMRESULT ret1, ret2; + + id = JOYSTICKID1; + if(g_joystick_type == JOYSTICK_WIN32_2) { + id = JOYSTICKID2; + } + + ret1 = joyGetDevCaps(id, &joycap, sizeof(joycap)); + ret2 = joyGetPos(id, &info); + if(ret1 == JOYERR_NOERROR && ret2 == JOYERR_NOERROR) { + g_paddle_val[0] = (info.wXpos - joycap.wXmin) * 280 / + (joycap.wXmax - joycap.wXmin); + g_paddle_val[1] = (info.wYpos - joycap.wYmin) * 280 / + (joycap.wYmax - joycap.wYmin); + g_paddle_button[0] = ((info.wButtons & JOY_BUTTON1) ? 1 : 0); + g_paddle_button[1] = ((info.wButtons & JOY_BUTTON2) ? 1 : 0); + } +} + +void +joystick_update_button() +{ + JOYINFOEX info; + UINT id; + + id = JOYSTICKID1; + if(g_joystick_type == JOYSTICK_WIN32_2) { + id = JOYSTICKID2; + } + + info.dwSize = sizeof(JOYINFOEX); + info.dwFlags = JOY_RETURNBUTTONS; + if(joyGetPosEx(id, &info) == JOYERR_NOERROR) { + g_paddle_button[0] = ((info.dwButtons & JOY_BUTTON1) ? 1 : 0); + g_paddle_button[1] = ((info.dwButtons & JOY_BUTTON2) ? 1 : 0); + } +} +# else + +/* stubs for the routines */ +void +joystick_init() +{ + printf("No joy with joystick\n"); +} + +void +joystick_update() +{ +} + +void +joystick_update_button() +{ +} + +# endif /* !WIN32 */ +#endif diff --git a/src/kegsfont.h b/src/kegsfont.h new file mode 100644 index 0000000..d4394a7 --- /dev/null +++ b/src/kegsfont.h @@ -0,0 +1,514 @@ +/* $KmKId: kegsfont.h,v 1.1 2002-11-10 03:31:51-05 kadickey Exp $ */ + +/* char 0x00 (raw 0x40) */ + { 0xc7, 0xbb, 0xab, 0xa3, 0xa7, 0xbf, 0xc3, 0xff }, +/* char 0x01 (raw 0x41) */ + { 0xef, 0xd7, 0xbb, 0xbb, 0x83, 0xbb, 0xbb, 0xff }, +/* char 0x02 (raw 0x42) */ + { 0x87, 0xbb, 0xbb, 0x87, 0xbb, 0xbb, 0x87, 0xff }, +/* char 0x03 (raw 0x43) */ + { 0xc7, 0xbb, 0xbf, 0xbf, 0xbf, 0xbb, 0xc7, 0xff }, +/* char 0x04 (raw 0x44) */ + { 0x87, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x87, 0xff }, +/* char 0x05 (raw 0x45) */ + { 0x83, 0xbf, 0xbf, 0x87, 0xbf, 0xbf, 0x83, 0xff }, +/* char 0x06 (raw 0x46) */ + { 0x83, 0xbf, 0xbf, 0x87, 0xbf, 0xbf, 0xbf, 0xff }, +/* char 0x07 (raw 0x47) */ + { 0xc3, 0xbf, 0xbf, 0xbf, 0xb3, 0xbb, 0xc3, 0xff }, +/* char 0x08 (raw 0x48) */ + { 0xbb, 0xbb, 0xbb, 0x83, 0xbb, 0xbb, 0xbb, 0xff }, +/* char 0x09 (raw 0x49) */ + { 0xc7, 0xef, 0xef, 0xef, 0xef, 0xef, 0xc7, 0xff }, +/* char 0x0a (raw 0x4a) */ + { 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xbb, 0xc7, 0xff }, +/* char 0x0b (raw 0x4b) */ + { 0xbb, 0xb7, 0xaf, 0x9f, 0xaf, 0xb7, 0xbb, 0xff }, +/* char 0x0c (raw 0x4c) */ + { 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0x83, 0xff }, +/* char 0x0d (raw 0x4d) */ + { 0xbb, 0x93, 0xab, 0xab, 0xbb, 0xbb, 0xbb, 0xff }, +/* char 0x0e (raw 0x4e) */ + { 0xbb, 0xbb, 0x9b, 0xab, 0xb3, 0xbb, 0xbb, 0xff }, +/* char 0x0f (raw 0x4f) */ + { 0xc7, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xc7, 0xff }, +/* char 0x10 (raw 0x50) */ + { 0x87, 0xbb, 0xbb, 0x87, 0xbf, 0xbf, 0xbf, 0xff }, +/* char 0x11 (raw 0x51) */ + { 0xc7, 0xbb, 0xbb, 0xbb, 0xab, 0xb7, 0xcb, 0xff }, +/* char 0x12 (raw 0x52) */ + { 0x87, 0xbb, 0xbb, 0x87, 0xaf, 0xb7, 0xbb, 0xff }, +/* char 0x13 (raw 0x53) */ + { 0xc7, 0xbb, 0xbf, 0xc7, 0xfb, 0xbb, 0xc7, 0xff }, +/* char 0x14 (raw 0x54) */ + { 0x83, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xff }, +/* char 0x15 (raw 0x55) */ + { 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xc7, 0xff }, +/* char 0x16 (raw 0x56) */ + { 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xd7, 0xef, 0xff }, +/* char 0x17 (raw 0x57) */ + { 0xbb, 0xbb, 0xbb, 0xab, 0xab, 0x93, 0xbb, 0xff }, +/* char 0x18 (raw 0x58) */ + { 0xbb, 0xbb, 0xd7, 0xef, 0xd7, 0xbb, 0xbb, 0xff }, +/* char 0x19 (raw 0x59) */ + { 0xbb, 0xbb, 0xd7, 0xef, 0xef, 0xef, 0xef, 0xff }, +/* char 0x1a (raw 0x5a) */ + { 0x83, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x83, 0xff }, +/* char 0x1b (raw 0x5b) */ + { 0x83, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x83, 0xff }, +/* char 0x1c (raw 0x5c) */ + { 0xff, 0xbf, 0xdf, 0xef, 0xf7, 0xfb, 0xff, 0xff }, +/* char 0x1d (raw 0x5d) */ + { 0x83, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0x83, 0xff }, +/* char 0x1e (raw 0x5e) */ + { 0xff, 0xff, 0xef, 0xd7, 0xbb, 0xff, 0xff, 0xff }, +/* char 0x1f (raw 0x5f) */ + { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01 }, +/* char 0x20 (raw 0x20) */ + { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, +/* char 0x21 (raw 0x21) */ + { 0xef, 0xef, 0xef, 0xef, 0xef, 0xff, 0xef, 0xff }, +/* char 0x22 (raw 0x22) */ + { 0xd7, 0xd7, 0xd7, 0xff, 0xff, 0xff, 0xff, 0xff }, +/* char 0x23 (raw 0x23) */ + { 0xd7, 0xd7, 0x83, 0xd7, 0x83, 0xd7, 0xd7, 0xff }, +/* char 0x24 (raw 0x24) */ + { 0xef, 0xc3, 0xaf, 0xc7, 0xeb, 0x87, 0xef, 0xff }, +/* char 0x25 (raw 0x25) */ + { 0x9f, 0x9b, 0xf7, 0xef, 0xdf, 0xb3, 0xf3, 0xff }, +/* char 0x26 (raw 0x26) */ + { 0xdf, 0xaf, 0xaf, 0xdf, 0xab, 0xb7, 0xcb, 0xff }, +/* char 0x27 (raw 0x27) */ + { 0xef, 0xef, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff }, +/* char 0x28 (raw 0x28) */ + { 0xef, 0xdf, 0xbf, 0xbf, 0xbf, 0xdf, 0xef, 0xff }, +/* char 0x29 (raw 0x29) */ + { 0xef, 0xf7, 0xfb, 0xfb, 0xfb, 0xf7, 0xef, 0xff }, +/* char 0x2a (raw 0x2a) */ + { 0xef, 0xab, 0xc7, 0xef, 0xc7, 0xab, 0xef, 0xff }, +/* char 0x2b (raw 0x2b) */ + { 0xff, 0xef, 0xef, 0x83, 0xef, 0xef, 0xff, 0xff }, +/* char 0x2c (raw 0x2c) */ + { 0xff, 0xff, 0xff, 0xff, 0xef, 0xef, 0xdf, 0xff }, +/* char 0x2d (raw 0x2d) */ + { 0xff, 0xff, 0xff, 0x83, 0xff, 0xff, 0xff, 0xff }, +/* char 0x2e (raw 0x2e) */ + { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff }, +/* char 0x2f (raw 0x2f) */ + { 0xff, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0xff, 0xff }, +/* char 0x30 (raw 0x30) */ + { 0xc7, 0xbb, 0xb3, 0xab, 0x9b, 0xbb, 0xc7, 0xff }, +/* char 0x31 (raw 0x31) */ + { 0xef, 0xcf, 0xef, 0xef, 0xef, 0xef, 0xc7, 0xff }, +/* char 0x32 (raw 0x32) */ + { 0xc7, 0xbb, 0xfb, 0xe7, 0xdf, 0xbf, 0x83, 0xff }, +/* char 0x33 (raw 0x33) */ + { 0x83, 0xfb, 0xf7, 0xe7, 0xfb, 0xbb, 0xc7, 0xff }, +/* char 0x34 (raw 0x34) */ + { 0xf7, 0xe7, 0xd7, 0xb7, 0x83, 0xf7, 0xf7, 0xff }, +/* char 0x35 (raw 0x35) */ + { 0x83, 0xbf, 0x87, 0xfb, 0xfb, 0xbb, 0xc7, 0xff }, +/* char 0x36 (raw 0x36) */ + { 0xe3, 0xdf, 0xbf, 0x87, 0xbb, 0xbb, 0xc7, 0xff }, +/* char 0x37 (raw 0x37) */ + { 0x83, 0xfb, 0xf7, 0xef, 0xdf, 0xdf, 0xdf, 0xff }, +/* char 0x38 (raw 0x38) */ + { 0xc7, 0xbb, 0xbb, 0xc7, 0xbb, 0xbb, 0xc7, 0xff }, +/* char 0x39 (raw 0x39) */ + { 0xc7, 0xbb, 0xbb, 0xc3, 0xfb, 0xf7, 0x8f, 0xff }, +/* char 0x3a (raw 0x3a) */ + { 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xff, 0xff }, +/* char 0x3b (raw 0x3b) */ + { 0xff, 0xff, 0xef, 0xff, 0xef, 0xef, 0xdf, 0xff }, +/* char 0x3c (raw 0x3c) */ + { 0xf7, 0xef, 0xdf, 0xbf, 0xdf, 0xef, 0xf7, 0xff }, +/* char 0x3d (raw 0x3d) */ + { 0xff, 0xff, 0x83, 0xff, 0x83, 0xff, 0xff, 0xff }, +/* char 0x3e (raw 0x3e) */ + { 0xdf, 0xef, 0xf7, 0xfb, 0xf7, 0xef, 0xdf, 0xff }, +/* char 0x3f (raw 0x3f) */ + { 0xc7, 0xbb, 0xf7, 0xef, 0xef, 0xff, 0xef, 0xff }, +/* char 0x40 (raw 0x14) */ + { 0x08, 0x10, 0x6c, 0xfe, 0xfc, 0xfc, 0x7e, 0x6c }, +/* char 0x41 (raw 0x11) */ + { 0x08, 0x10, 0x6c, 0x82, 0x84, 0x84, 0x52, 0x6c }, +/* char 0x42 (raw 0xf5) */ + { 0x00, 0x00, 0x40, 0x60, 0x70, 0x78, 0x6c, 0x42 }, +/* char 0x43 (raw 0x82) */ + { 0xfe, 0x44, 0x28, 0x10, 0x10, 0x28, 0x54, 0xfe }, +/* char 0x44 (raw 0xeb) */ + { 0x00, 0x02, 0x04, 0x88, 0x50, 0x20, 0x20, 0x00 }, +/* char 0x45 (raw 0xe4) */ + { 0xfe, 0xfc, 0xfa, 0x36, 0xae, 0xde, 0xde, 0xfe }, +/* char 0x46 (raw 0xec) */ + { 0xfc, 0xfc, 0xfc, 0xdc, 0x9c, 0x00, 0x9e, 0xde }, +/* char 0x47 (raw 0xed) */ + { 0xfe, 0x00, 0xfe, 0x00, 0xfe, 0x00, 0x00, 0xfe }, +/* char 0x48 (raw 0xee) */ + { 0x10, 0x20, 0x40, 0xfe, 0x40, 0x20, 0x10, 0x00 }, +/* char 0x49 (raw 0xe9) */ + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54 }, +/* char 0x4a (raw 0xef) */ + { 0x10, 0x10, 0x10, 0x10, 0x92, 0x54, 0x38, 0x10 }, +/* char 0x4b (raw 0xf0) */ + { 0x10, 0x38, 0x54, 0x92, 0x10, 0x10, 0x10, 0x10 }, +/* char 0x4c (raw 0xf1) */ + { 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, +/* char 0x4d (raw 0xf7) */ + { 0x02, 0x02, 0x02, 0x22, 0x62, 0xfe, 0x60, 0x20 }, +/* char 0x4e (raw 0xf6) */ + { 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc }, +/* char 0x4f (raw 0xaf) */ + { 0xc8, 0x18, 0x38, 0x7e, 0x38, 0x18, 0x08, 0xf6 }, +/* char 0x50 (raw 0xb8) */ + { 0x26, 0x30, 0x38, 0xfc, 0x38, 0x30, 0x20, 0xde }, +/* char 0x51 (raw 0xce) */ + { 0x02, 0x12, 0x10, 0xfe, 0x7c, 0x38, 0x12, 0x02 }, +/* char 0x52 (raw 0xe5) */ + { 0x02, 0x12, 0x38, 0x7c, 0xfe, 0x10, 0x12, 0x02 }, +/* char 0x53 (raw 0xea) */ + { 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00 }, +/* char 0x54 (raw 0xe6) */ + { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xfe }, +/* char 0x55 (raw 0xe8) */ + { 0x10, 0x08, 0x04, 0xfe, 0x04, 0x08, 0x10, 0x00 }, +/* char 0x56 (raw 0xd7) */ + { 0x54, 0xaa, 0x54, 0xaa, 0x54, 0xaa, 0x54, 0xaa }, +/* char 0x57 (raw 0xe3) */ + { 0xaa, 0x54, 0xaa, 0x54, 0xaa, 0x54, 0xaa, 0x54 }, +/* char 0x58 (raw 0xf4) */ + { 0x00, 0x7c, 0x82, 0x80, 0x80, 0x80, 0xfe, 0x00 }, +/* char 0x59 (raw 0xe7) */ + { 0x00, 0x00, 0xfc, 0x02, 0x02, 0x02, 0xfe, 0x00 }, +/* char 0x5a (raw 0xf3) */ + { 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02 }, +/* char 0x5b (raw 0xd2) */ + { 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00 }, +/* char 0x5c (raw 0xc7) */ + { 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe }, +/* char 0x5d (raw 0xd4) */ + { 0x28, 0x28, 0xee, 0x00, 0xee, 0x28, 0x28, 0x00 }, +/* char 0x5e (raw 0xdf) */ + { 0xfe, 0x02, 0x02, 0x32, 0x32, 0x02, 0x02, 0xfe }, +/* char 0x5f (raw 0xd1) */ + { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 }, +/* char 0x60 (raw 0x60) */ + { 0xdf, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff }, +/* char 0x61 (raw 0x61) */ + { 0xff, 0xff, 0xc7, 0xfb, 0xc3, 0xbb, 0xc3, 0xff }, +/* char 0x62 (raw 0x62) */ + { 0xbf, 0xbf, 0x87, 0xbb, 0xbb, 0xbb, 0x87, 0xff }, +/* char 0x63 (raw 0x63) */ + { 0xff, 0xff, 0xc3, 0xbf, 0xbf, 0xbf, 0xc3, 0xff }, +/* char 0x64 (raw 0x64) */ + { 0xfb, 0xfb, 0xc3, 0xbb, 0xbb, 0xbb, 0xc3, 0xff }, +/* char 0x65 (raw 0x65) */ + { 0xff, 0xff, 0xc7, 0xbb, 0x83, 0xbf, 0xc3, 0xff }, +/* char 0x66 (raw 0x66) */ + { 0xe7, 0xdb, 0xdf, 0x87, 0xdf, 0xdf, 0xdf, 0xff }, +/* char 0x67 (raw 0x67) */ + { 0xff, 0xff, 0xc7, 0xbb, 0xbb, 0xc3, 0xfb, 0xc7 }, +/* char 0x68 (raw 0x68) */ + { 0xbf, 0xbf, 0x87, 0xbb, 0xbb, 0xbb, 0xbb, 0xff }, +/* char 0x69 (raw 0x69) */ + { 0xef, 0xff, 0xcf, 0xef, 0xef, 0xef, 0xc7, 0xff }, +/* char 0x6a (raw 0x6a) */ + { 0xf7, 0xff, 0xe7, 0xf7, 0xf7, 0xf7, 0xb7, 0xcf }, +/* char 0x6b (raw 0x6b) */ + { 0xbf, 0xbf, 0xbb, 0xb7, 0x8f, 0xb7, 0xbb, 0xff }, +/* char 0x6c (raw 0x6c) */ + { 0xcf, 0xef, 0xef, 0xef, 0xef, 0xef, 0xc7, 0xff }, +/* char 0x6d (raw 0x6d) */ + { 0xff, 0xff, 0x93, 0xab, 0xab, 0xab, 0xbb, 0xff }, +/* char 0x6e (raw 0x6e) */ + { 0xff, 0xff, 0x87, 0xbb, 0xbb, 0xbb, 0xbb, 0xff }, +/* char 0x6f (raw 0x6f) */ + { 0xff, 0xff, 0xc7, 0xbb, 0xbb, 0xbb, 0xc7, 0xff }, +/* char 0x70 (raw 0x70) */ + { 0xff, 0xff, 0x87, 0xbb, 0xbb, 0x87, 0xbf, 0xbf }, +/* char 0x71 (raw 0x71) */ + { 0xff, 0xff, 0xc3, 0xbb, 0xbb, 0xc3, 0xfb, 0xfb }, +/* char 0x72 (raw 0x72) */ + { 0xff, 0xff, 0xa3, 0x9f, 0xbf, 0xbf, 0xbf, 0xff }, +/* char 0x73 (raw 0x73) */ + { 0xff, 0xff, 0xc3, 0xbf, 0xc7, 0xfb, 0x87, 0xff }, +/* char 0x74 (raw 0x74) */ + { 0xdf, 0xdf, 0x87, 0xdf, 0xdf, 0xdb, 0xe7, 0xff }, +/* char 0x75 (raw 0x75) */ + { 0xff, 0xff, 0xbb, 0xbb, 0xbb, 0xb3, 0xcb, 0xff }, +/* char 0x76 (raw 0x76) */ + { 0xff, 0xff, 0xbb, 0xbb, 0xbb, 0xd7, 0xef, 0xff }, +/* char 0x77 (raw 0x77) */ + { 0xff, 0xff, 0xbb, 0xbb, 0xab, 0xab, 0x93, 0xff }, +/* char 0x78 (raw 0x78) */ + { 0xff, 0xff, 0xbb, 0xd7, 0xef, 0xd7, 0xbb, 0xff }, +/* char 0x79 (raw 0x79) */ + { 0xff, 0xff, 0xbb, 0xbb, 0xbb, 0xc3, 0xfb, 0xc7 }, +/* char 0x7a (raw 0x7a) */ + { 0xff, 0xff, 0x83, 0xf7, 0xef, 0xdf, 0x83, 0xff }, +/* char 0x7b (raw 0x7b) */ + { 0xe3, 0xcf, 0xcf, 0x9f, 0xcf, 0xcf, 0xe3, 0xff }, +/* char 0x7c (raw 0x7c) */ + { 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef }, +/* char 0x7d (raw 0x7d) */ + { 0x8f, 0xe7, 0xe7, 0xf3, 0xe7, 0xe7, 0x8f, 0xff }, +/* char 0x7e (raw 0x7e) */ + { 0xcb, 0xa7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, +/* char 0x7f (raw 0x7f) */ + { 0xff, 0xab, 0xd7, 0xab, 0xd7, 0xab, 0xff, 0xff }, +/* char 0x80 (raw 0x40) */ + { 0x38, 0x44, 0x54, 0x5c, 0x58, 0x40, 0x3c, 0x00 }, +/* char 0x81 (raw 0x41) */ + { 0x10, 0x28, 0x44, 0x44, 0x7c, 0x44, 0x44, 0x00 }, +/* char 0x82 (raw 0x42) */ + { 0x78, 0x44, 0x44, 0x78, 0x44, 0x44, 0x78, 0x00 }, +/* char 0x83 (raw 0x43) */ + { 0x38, 0x44, 0x40, 0x40, 0x40, 0x44, 0x38, 0x00 }, +/* char 0x84 (raw 0x44) */ + { 0x78, 0x44, 0x44, 0x44, 0x44, 0x44, 0x78, 0x00 }, +/* char 0x85 (raw 0x45) */ + { 0x7c, 0x40, 0x40, 0x78, 0x40, 0x40, 0x7c, 0x00 }, +/* char 0x86 (raw 0x46) */ + { 0x7c, 0x40, 0x40, 0x78, 0x40, 0x40, 0x40, 0x00 }, +/* char 0x87 (raw 0x47) */ + { 0x3c, 0x40, 0x40, 0x40, 0x4c, 0x44, 0x3c, 0x00 }, +/* char 0x88 (raw 0x48) */ + { 0x44, 0x44, 0x44, 0x7c, 0x44, 0x44, 0x44, 0x00 }, +/* char 0x89 (raw 0x49) */ + { 0x38, 0x10, 0x10, 0x10, 0x10, 0x10, 0x38, 0x00 }, +/* char 0x8a (raw 0x4a) */ + { 0x04, 0x04, 0x04, 0x04, 0x04, 0x44, 0x38, 0x00 }, +/* char 0x8b (raw 0x4b) */ + { 0x44, 0x48, 0x50, 0x60, 0x50, 0x48, 0x44, 0x00 }, +/* char 0x8c (raw 0x4c) */ + { 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x7c, 0x00 }, +/* char 0x8d (raw 0x4d) */ + { 0x44, 0x6c, 0x54, 0x54, 0x44, 0x44, 0x44, 0x00 }, +/* char 0x8e (raw 0x4e) */ + { 0x44, 0x44, 0x64, 0x54, 0x4c, 0x44, 0x44, 0x00 }, +/* char 0x8f (raw 0x4f) */ + { 0x38, 0x44, 0x44, 0x44, 0x44, 0x44, 0x38, 0x00 }, +/* char 0x90 (raw 0x50) */ + { 0x78, 0x44, 0x44, 0x78, 0x40, 0x40, 0x40, 0x00 }, +/* char 0x91 (raw 0x51) */ + { 0x38, 0x44, 0x44, 0x44, 0x54, 0x48, 0x34, 0x00 }, +/* char 0x92 (raw 0x52) */ + { 0x78, 0x44, 0x44, 0x78, 0x50, 0x48, 0x44, 0x00 }, +/* char 0x93 (raw 0x53) */ + { 0x38, 0x44, 0x40, 0x38, 0x04, 0x44, 0x38, 0x00 }, +/* char 0x94 (raw 0x54) */ + { 0x7c, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00 }, +/* char 0x95 (raw 0x55) */ + { 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x38, 0x00 }, +/* char 0x96 (raw 0x56) */ + { 0x44, 0x44, 0x44, 0x44, 0x44, 0x28, 0x10, 0x00 }, +/* char 0x97 (raw 0x57) */ + { 0x44, 0x44, 0x44, 0x54, 0x54, 0x6c, 0x44, 0x00 }, +/* char 0x98 (raw 0x58) */ + { 0x44, 0x44, 0x28, 0x10, 0x28, 0x44, 0x44, 0x00 }, +/* char 0x99 (raw 0x59) */ + { 0x44, 0x44, 0x28, 0x10, 0x10, 0x10, 0x10, 0x00 }, +/* char 0x9a (raw 0x5a) */ + { 0x7c, 0x04, 0x08, 0x10, 0x20, 0x40, 0x7c, 0x00 }, +/* char 0x9b (raw 0x5b) */ + { 0x7c, 0x60, 0x60, 0x60, 0x60, 0x60, 0x7c, 0x00 }, +/* char 0x9c (raw 0x5c) */ + { 0x00, 0x40, 0x20, 0x10, 0x08, 0x04, 0x00, 0x00 }, +/* char 0x9d (raw 0x5d) */ + { 0x7c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x7c, 0x00 }, +/* char 0x9e (raw 0x5e) */ + { 0x00, 0x00, 0x10, 0x28, 0x44, 0x00, 0x00, 0x00 }, +/* char 0x9f (raw 0x5f) */ + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe }, +/* char 0xa0 (raw 0x20) */ + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, +/* char 0xa1 (raw 0x21) */ + { 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x10, 0x00 }, +/* char 0xa2 (raw 0x22) */ + { 0x28, 0x28, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00 }, +/* char 0xa3 (raw 0x23) */ + { 0x28, 0x28, 0x7c, 0x28, 0x7c, 0x28, 0x28, 0x00 }, +/* char 0xa4 (raw 0x24) */ + { 0x10, 0x3c, 0x50, 0x38, 0x14, 0x78, 0x10, 0x00 }, +/* char 0xa5 (raw 0x25) */ + { 0x60, 0x64, 0x08, 0x10, 0x20, 0x4c, 0x0c, 0x00 }, +/* char 0xa6 (raw 0x26) */ + { 0x20, 0x50, 0x50, 0x20, 0x54, 0x48, 0x34, 0x00 }, +/* char 0xa7 (raw 0x27) */ + { 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00 }, +/* char 0xa8 (raw 0x28) */ + { 0x10, 0x20, 0x40, 0x40, 0x40, 0x20, 0x10, 0x00 }, +/* char 0xa9 (raw 0x29) */ + { 0x10, 0x08, 0x04, 0x04, 0x04, 0x08, 0x10, 0x00 }, +/* char 0xaa (raw 0x2a) */ + { 0x10, 0x54, 0x38, 0x10, 0x38, 0x54, 0x10, 0x00 }, +/* char 0xab (raw 0x2b) */ + { 0x00, 0x10, 0x10, 0x7c, 0x10, 0x10, 0x00, 0x00 }, +/* char 0xac (raw 0x2c) */ + { 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x20, 0x00 }, +/* char 0xad (raw 0x2d) */ + { 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00 }, +/* char 0xae (raw 0x2e) */ + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00 }, +/* char 0xaf (raw 0x2f) */ + { 0x00, 0x04, 0x08, 0x10, 0x20, 0x40, 0x00, 0x00 }, +/* char 0xb0 (raw 0x30) */ + { 0x38, 0x44, 0x4c, 0x54, 0x64, 0x44, 0x38, 0x00 }, +/* char 0xb1 (raw 0x31) */ + { 0x10, 0x30, 0x10, 0x10, 0x10, 0x10, 0x38, 0x00 }, +/* char 0xb2 (raw 0x32) */ + { 0x38, 0x44, 0x04, 0x18, 0x20, 0x40, 0x7c, 0x00 }, +/* char 0xb3 (raw 0x33) */ + { 0x7c, 0x04, 0x08, 0x18, 0x04, 0x44, 0x38, 0x00 }, +/* char 0xb4 (raw 0x34) */ + { 0x08, 0x18, 0x28, 0x48, 0x7c, 0x08, 0x08, 0x00 }, +/* char 0xb5 (raw 0x35) */ + { 0x7c, 0x40, 0x78, 0x04, 0x04, 0x44, 0x38, 0x00 }, +/* char 0xb6 (raw 0x36) */ + { 0x1c, 0x20, 0x40, 0x78, 0x44, 0x44, 0x38, 0x00 }, +/* char 0xb7 (raw 0x37) */ + { 0x7c, 0x04, 0x08, 0x10, 0x20, 0x20, 0x20, 0x00 }, +/* char 0xb8 (raw 0x38) */ + { 0x38, 0x44, 0x44, 0x38, 0x44, 0x44, 0x38, 0x00 }, +/* char 0xb9 (raw 0x39) */ + { 0x38, 0x44, 0x44, 0x3c, 0x04, 0x08, 0x70, 0x00 }, +/* char 0xba (raw 0x3a) */ + { 0x00, 0x00, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00 }, +/* char 0xbb (raw 0x3b) */ + { 0x00, 0x00, 0x10, 0x00, 0x10, 0x10, 0x20, 0x00 }, +/* char 0xbc (raw 0x3c) */ + { 0x08, 0x10, 0x20, 0x40, 0x20, 0x10, 0x08, 0x00 }, +/* char 0xbd (raw 0x3d) */ + { 0x00, 0x00, 0x7c, 0x00, 0x7c, 0x00, 0x00, 0x00 }, +/* char 0xbe (raw 0x3e) */ + { 0x20, 0x10, 0x08, 0x04, 0x08, 0x10, 0x20, 0x00 }, +/* char 0xbf (raw 0x3f) */ + { 0x38, 0x44, 0x08, 0x10, 0x10, 0x00, 0x10, 0x00 }, +/* char 0xc0 (raw 0x40) */ + { 0x38, 0x44, 0x54, 0x5c, 0x58, 0x40, 0x3c, 0x00 }, +/* char 0xc1 (raw 0x41) */ + { 0x10, 0x28, 0x44, 0x44, 0x7c, 0x44, 0x44, 0x00 }, +/* char 0xc2 (raw 0x42) */ + { 0x78, 0x44, 0x44, 0x78, 0x44, 0x44, 0x78, 0x00 }, +/* char 0xc3 (raw 0x43) */ + { 0x38, 0x44, 0x40, 0x40, 0x40, 0x44, 0x38, 0x00 }, +/* char 0xc4 (raw 0x44) */ + { 0x78, 0x44, 0x44, 0x44, 0x44, 0x44, 0x78, 0x00 }, +/* char 0xc5 (raw 0x45) */ + { 0x7c, 0x40, 0x40, 0x78, 0x40, 0x40, 0x7c, 0x00 }, +/* char 0xc6 (raw 0x46) */ + { 0x7c, 0x40, 0x40, 0x78, 0x40, 0x40, 0x40, 0x00 }, +/* char 0xc7 (raw 0x47) */ + { 0x3c, 0x40, 0x40, 0x40, 0x4c, 0x44, 0x3c, 0x00 }, +/* char 0xc8 (raw 0x48) */ + { 0x44, 0x44, 0x44, 0x7c, 0x44, 0x44, 0x44, 0x00 }, +/* char 0xc9 (raw 0x49) */ + { 0x38, 0x10, 0x10, 0x10, 0x10, 0x10, 0x38, 0x00 }, +/* char 0xca (raw 0x4a) */ + { 0x04, 0x04, 0x04, 0x04, 0x04, 0x44, 0x38, 0x00 }, +/* char 0xcb (raw 0x4b) */ + { 0x44, 0x48, 0x50, 0x60, 0x50, 0x48, 0x44, 0x00 }, +/* char 0xcc (raw 0x4c) */ + { 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x7c, 0x00 }, +/* char 0xcd (raw 0x4d) */ + { 0x44, 0x6c, 0x54, 0x54, 0x44, 0x44, 0x44, 0x00 }, +/* char 0xce (raw 0x4e) */ + { 0x44, 0x44, 0x64, 0x54, 0x4c, 0x44, 0x44, 0x00 }, +/* char 0xcf (raw 0x4f) */ + { 0x38, 0x44, 0x44, 0x44, 0x44, 0x44, 0x38, 0x00 }, +/* char 0xd0 (raw 0x50) */ + { 0x78, 0x44, 0x44, 0x78, 0x40, 0x40, 0x40, 0x00 }, +/* char 0xd1 (raw 0x51) */ + { 0x38, 0x44, 0x44, 0x44, 0x54, 0x48, 0x34, 0x00 }, +/* char 0xd2 (raw 0x52) */ + { 0x78, 0x44, 0x44, 0x78, 0x50, 0x48, 0x44, 0x00 }, +/* char 0xd3 (raw 0x53) */ + { 0x38, 0x44, 0x40, 0x38, 0x04, 0x44, 0x38, 0x00 }, +/* char 0xd4 (raw 0x54) */ + { 0x7c, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00 }, +/* char 0xd5 (raw 0x55) */ + { 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x38, 0x00 }, +/* char 0xd6 (raw 0x56) */ + { 0x44, 0x44, 0x44, 0x44, 0x44, 0x28, 0x10, 0x00 }, +/* char 0xd7 (raw 0x57) */ + { 0x44, 0x44, 0x44, 0x54, 0x54, 0x6c, 0x44, 0x00 }, +/* char 0xd8 (raw 0x58) */ + { 0x44, 0x44, 0x28, 0x10, 0x28, 0x44, 0x44, 0x00 }, +/* char 0xd9 (raw 0x59) */ + { 0x44, 0x44, 0x28, 0x10, 0x10, 0x10, 0x10, 0x00 }, +/* char 0xda (raw 0x5a) */ + { 0x7c, 0x04, 0x08, 0x10, 0x20, 0x40, 0x7c, 0x00 }, +/* char 0xdb (raw 0x5b) */ + { 0x7c, 0x60, 0x60, 0x60, 0x60, 0x60, 0x7c, 0x00 }, +/* char 0xdc (raw 0x5c) */ + { 0x00, 0x40, 0x20, 0x10, 0x08, 0x04, 0x00, 0x00 }, +/* char 0xdd (raw 0x5d) */ + { 0x7c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x7c, 0x00 }, +/* char 0xde (raw 0x5e) */ + { 0x00, 0x00, 0x10, 0x28, 0x44, 0x00, 0x00, 0x00 }, +/* char 0xdf (raw 0x5f) */ + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe }, +/* char 0xe0 (raw 0x60) */ + { 0x20, 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 }, +/* char 0xe1 (raw 0x61) */ + { 0x00, 0x00, 0x38, 0x04, 0x3c, 0x44, 0x3c, 0x00 }, +/* char 0xe2 (raw 0x62) */ + { 0x40, 0x40, 0x78, 0x44, 0x44, 0x44, 0x78, 0x00 }, +/* char 0xe3 (raw 0x63) */ + { 0x00, 0x00, 0x3c, 0x40, 0x40, 0x40, 0x3c, 0x00 }, +/* char 0xe4 (raw 0x64) */ + { 0x04, 0x04, 0x3c, 0x44, 0x44, 0x44, 0x3c, 0x00 }, +/* char 0xe5 (raw 0x65) */ + { 0x00, 0x00, 0x38, 0x44, 0x7c, 0x40, 0x3c, 0x00 }, +/* char 0xe6 (raw 0x66) */ + { 0x18, 0x24, 0x20, 0x78, 0x20, 0x20, 0x20, 0x00 }, +/* char 0xe7 (raw 0x67) */ + { 0x00, 0x00, 0x38, 0x44, 0x44, 0x3c, 0x04, 0x38 }, +/* char 0xe8 (raw 0x68) */ + { 0x40, 0x40, 0x78, 0x44, 0x44, 0x44, 0x44, 0x00 }, +/* char 0xe9 (raw 0x69) */ + { 0x10, 0x00, 0x30, 0x10, 0x10, 0x10, 0x38, 0x00 }, +/* char 0xea (raw 0x6a) */ + { 0x08, 0x00, 0x18, 0x08, 0x08, 0x08, 0x48, 0x30 }, +/* char 0xeb (raw 0x6b) */ + { 0x40, 0x40, 0x44, 0x48, 0x70, 0x48, 0x44, 0x00 }, +/* char 0xec (raw 0x6c) */ + { 0x30, 0x10, 0x10, 0x10, 0x10, 0x10, 0x38, 0x00 }, +/* char 0xed (raw 0x6d) */ + { 0x00, 0x00, 0x6c, 0x54, 0x54, 0x54, 0x44, 0x00 }, +/* char 0xee (raw 0x6e) */ + { 0x00, 0x00, 0x78, 0x44, 0x44, 0x44, 0x44, 0x00 }, +/* char 0xef (raw 0x6f) */ + { 0x00, 0x00, 0x38, 0x44, 0x44, 0x44, 0x38, 0x00 }, +/* char 0xf0 (raw 0x70) */ + { 0x00, 0x00, 0x78, 0x44, 0x44, 0x78, 0x40, 0x40 }, +/* char 0xf1 (raw 0x71) */ + { 0x00, 0x00, 0x3c, 0x44, 0x44, 0x3c, 0x04, 0x04 }, +/* char 0xf2 (raw 0x72) */ + { 0x00, 0x00, 0x5c, 0x60, 0x40, 0x40, 0x40, 0x00 }, +/* char 0xf3 (raw 0x73) */ + { 0x00, 0x00, 0x3c, 0x40, 0x38, 0x04, 0x78, 0x00 }, +/* char 0xf4 (raw 0x74) */ + { 0x20, 0x20, 0x78, 0x20, 0x20, 0x24, 0x18, 0x00 }, +/* char 0xf5 (raw 0x75) */ + { 0x00, 0x00, 0x44, 0x44, 0x44, 0x4c, 0x34, 0x00 }, +/* char 0xf6 (raw 0x76) */ + { 0x00, 0x00, 0x44, 0x44, 0x44, 0x28, 0x10, 0x00 }, +/* char 0xf7 (raw 0x77) */ + { 0x00, 0x00, 0x44, 0x44, 0x54, 0x54, 0x6c, 0x00 }, +/* char 0xf8 (raw 0x78) */ + { 0x00, 0x00, 0x44, 0x28, 0x10, 0x28, 0x44, 0x00 }, +/* char 0xf9 (raw 0x79) */ + { 0x00, 0x00, 0x44, 0x44, 0x44, 0x3c, 0x04, 0x38 }, +/* char 0xfa (raw 0x7a) */ + { 0x00, 0x00, 0x7c, 0x08, 0x10, 0x20, 0x7c, 0x00 }, +/* char 0xfb (raw 0x7b) */ + { 0x1c, 0x30, 0x30, 0x60, 0x30, 0x30, 0x1c, 0x00 }, +/* char 0xfc (raw 0x7c) */ + { 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10 }, +/* char 0xfd (raw 0x7d) */ + { 0x70, 0x18, 0x18, 0x0c, 0x18, 0x18, 0x70, 0x00 }, +/* char 0xfe (raw 0x7e) */ + { 0x34, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, +/* char 0xff (raw 0x7f) */ + { 0x00, 0x54, 0x28, 0x54, 0x28, 0x54, 0x00, 0x00 }, diff --git a/src/kegsicon.icns b/src/kegsicon.icns new file mode 100644 index 0000000000000000000000000000000000000000..72ea1720399aefb0e22a255031b9c00afd333727 GIT binary patch literal 70755 zcmeFZcT`l_@+f>zM;)`0p~;P)DCR)Uxd{zT&RKF&Py{_q&Ph;I5CsG!NRCZtpxX}Q zB%mTu5z|C<92LGg%>3TF-u0aC-nG89-ajAMEvKu_u2Z#Z?~1#V234lY+O*ANK= zL3A&#!0+n!epN8&Kf=92jvYmi^*b&jN1HmkI=VW$sWjTdkrN0K*IZFfA1F#FsLU>& zA|ptAJujD%k)3C8)UJRRiEvHh!go5GyC>b>9<-Q@U2`98GH`W`zOnt>GF->fcpItO zX7u+x5rNqCWg7pfW>Y&_Se?B$Ee$~;Y22yJdTQ3x=XsVNX^1f5OB2v|7;aDNtlV~yV@gR@x$g6CL0p(cjrCg%=FF0Mpl z5_sUMr9;3U-e=eo>Im0;L!+sddPM8Q%^tw^D{H80LKlD^gVv|Y<3=g zTdfd8pt_>6qWtdNI|X@pXODvf!eVkt>nPnc2D48JlYlyFV6cC%e|TVUc=(A5g5*7H zZSJS%L?qS~HO%HC$jzQZ`;Ms_*yqOzkY5%eNIw6;V?x^MR>`$Iu}gOWpYPxaVI@6c z)D51bUrG_=2H)XIX=Nk9-}ui3yuODZnS94&MN~9!fjT4N&Q-9J2> zN>U|QpMN;ZzKBU+^wHy|Gc$AZv$OPYkihVx!6(ztX69z+=ICJvBGFRc+}K)E``|%Y zSuznM5SLR_-$`Tl_YaOJAV|n*KOY3)*u0&#`Y%7{H*A0TFPE#U3fq^zxi0&r@ny){lNI<+7Hov{jVQbASVBMxf+F-eRG|};rq>X%DRky zz5W`3Y*_mE<$peUnYdd|!w7a4j?F5otAD`j3%pkTzj%qjIDfxZ;s0-5DscVxYYk`9 zUv&Sk()aSkcE_!|b|c81bvp!)$>|!px+kZm zI;m8&XG<52)OTJW8iTjCqSDiZMhk# z58@-zV*G9O)K!#~sui_u@<*1_F=!K|oSTGv@$LpC1)B#&(bP1_x@1pC3!B1 zyoDDT5!le}G;barf)1_I$k@ce>tcOR2$55=@cHx180@jyXfC|G2J$qDu7#D0Z(>em zlnTnMr@8RqND8zzJ%9$3cQr;u?2)-U$!fx&O%#AuM;xQIscPukJVd*h zw5~f+jT5h~(Eq-d|DZ__t=-Zkk^b<>U_H&6Tk7G=o2ypW zh<16`ek$$p)mVVGQDNl0Ms75Uo~2nUJtV1=W-BClk4~c(M7z@0H%u*3eK?(b6ep&}e;;^5G4X z0#&aw033kLZ;bA%r$e;%a0&@IuWuXEHT`VX&)pJ$LxaO(qmLMsIq{K!XFLMQ1=O+G z`T2!FS1SOH43CbDJ(`+&JUKDk+cz>bw*VGmb|Ki=9Du{a<6{#Oj~_pIHaj=FxUje| zKM&jIe1J1)?as-@vbM)hpUym+o0|tqJNJBUW`4ofo)C@fdNejZ_4w(tnP*^v76CYm zF8nYzx9DbJ2x8ckm6M%YP*`}kwDkV{it6f`>ZX>C&W?6REtM#QpV>b&G%`9eIyOEw zHaZUfj}8s>_YFKC9*;)&diw{4z$Oh24G)bB4UGgnlb4fI99l0}<0wKOy|)jz1NN1L`a*Vom;nr&kaOvUd3KAKzGZf0fF#GOwi zds`c5sVXTqsu?=n8h(+q8=#c7auH!+Z7nqe_b~snS!J!@E{V$Cc(j-z3(%Oj^P(a` zCr`=4e43KHZ_=NTSq8-iN#BZG)Bnd_o`~>8H*<}4&h}^o>0$! zm9-b3?eTaCvBSn8cxh|5RH>6w!v%OX{-femud*)yv@;Rz-KTGW-)nAdB_ZYP6OPyA zJ|%-s%)y?jBMF}BfSwJ0ub!EqG(Ie|7H=nhqymo@2Yv;q02TE#G{8$)>Kj{1%4F(X zN(z#={k}>ZzX?_(H3e=yLC^!1%p9(rTKxQblaZ)^+56Ae#YF(z9frq?NUNLT@%s8^ z`xI=v+)Pwy6kz+iOMR94Cx zo02T8jg;)qgSIXCRBW^F3>=Erm`z|9QpbFsu#*`}?K$wA3_|)Ugm&Gv$J#L`Au^( zGqbb79@sJ;8W?J==z9F*>GU+L{W;7DnuSH}=WG&<>=+pye>5=(AaD=?LZ00?KRZ3^ zWo-;x?o7?fx_0Z%ox8C6+SCZCh5W#Vy=8O^9p@OEK*zUYYjtFJ zaIn9^{8TjZBZy~sXc!QN(BZA4BO}8jV}LMTEemUvW5cGcy9I?M_Z?T#BM?n(9NiDC ztySC)d-kbmncAE?TU1z7@t~ox0jy+wLtSf!0+?6WJ=yHu-oE~Ufgu?i^A>Ozfb8DPFPi0 z!^k#}AfSkeLclcV&5;h^~@`fe`+RT133>iHZ*u0=OSw zHL_B1GW!mxE8~Pz^{jJ`EPnc>T2p+V@gHC71b6|=K9AdX@Yo3r98O75L*$TwjjjGk zE_vRS)q2_8uvmR~`1tng$O;Jx?pIaYAC23Hx91mZ4+ZTI7#L!=Ha5wsXlm%_svjl# z1)jH+C3b;7-dqIG!TzBeqhlvds;DTd9~ZYclV*S3+;{4il`qxU*3~zJSqddp9VI1| zy*Q$apNWEK%+uvxS0mM7b!Z2-G`E~SA$RPgyn>XV0N+u2rRW}Jjb~gUwjjZ_j?z3; zm2^yf4e#VWlKRosUNL^bZVLtaGq90!==^2~RAy%9(k`Ec6*)ZU|FDz& z`0@0!X$V;6(Ya0Yb2GE^2@%0CESUO7lasKNpFNwNorN_HYj$>KE+#Yp80EQs>&ET! z>e|M}y1M#S%&>KK)0y<1OJ43#$j0HZv2nN;ors#;0w({_qw&$v_5iTv=;-?KM~`47 zBkpv}eb?fn4VyWnQX4vG14Cou z^9#TI_S^gSAO6?RuMzy~5sm|?;LU&MCsbm?Kf=3gcmE@NC`|Al;WH6J90$_ALmWQ- z953~c_kYbk`j7B0xhMY-zH`3Ev-owL*boZ`?n?e^EjtWBvjDND0CJ)BSl$h)CE!+@Gh2>`?ta{BibighTA> zcM)F1OCcQigxQbZ!$^mi4*w4f6zKvy`g?b;uHH965Xso@!^_S88D43Ptdso*1pxSV z)8c;^-wNUOYWhC>z8SJj{k!1LO&RjYx^3Ts|I(1Aw+Awd8bX0&*5>?|*P$fBz+u|6e}em5FO78jlm z_BYgmRF0~=qH`Osi1^-=vfy-~-FXo-Jp_#3IrO&6I{hE*>r}Zhd8CEkh$a7duB6&+`Sv*DIs^M0WFu1yoMG zc$1an9D_(dpmtMRuU*d}JGzKb`QeXO1s9yRaf=1j%q>38Nr(pAgoaict&NL^7q6k* zL1VZWo10rWy88wtl{elGwBE)e5mYz7uy8dA1>D5OW*Y524-c=jiFyl-Wl1p8wy?7I z4^O3a<)882$|HHMWocGF&oeJBPYxti}>-+(v9%KHl7LxnoTq9Dyt))jKxn6t7SbYEaCs4|Z zN5li^bIWh@lW7=kYYPo<`SF@YMj!_lH;?dZ3|dcnN#5md8XCM+@*J)A?b}D4e!iXNhvBxh0WKd8|7kN@z%@0o zwKHean7(1>qHAf_?04dQIw2cbp9bSfTx#pUaQSeG1WyW$r9&VXo95DJ0a4kdZ8V3_ z&0-!sER@Fa@O}&;8A-v$Gd_FD$M?M`4H1k+ z_CyFah2lq55U0NIb)4H+_i0bUJF8_?l!Yg>DdUK*pjvy)Pt=fJm9 zB!Ko}Wu-iq4pAj?pG@1x7_67`ZyP|_8+%zL76eCDENx4mnCdNh7h|r2RRQAlrS)_- z7Tzf+^j0Uj+1feSTU(o1((FT`lKgTLTNvhr?Qg*msZ0Si`yrIZpmlemT_T}s`j!N1 z6H9YzGgh#LOVH)G1KviBp@WOBzpkv5MT^BCQjuNV4{7h1ee81vdPJg`fuV^FgXMbW zLR^YX6!~2H<>AGjK7C%PB*!8;Xi>VahyN?umTE@CYmmxB~pPHK2!iY48tMBOUyAXWH`g|O+ z*Y#X-_LZzF=hd}z^^{c?$eh(iQMp!Nk}&LhBk z3_;Q?mW71e<>K?jMM#P)%+9s@xYz;W4`V}v6JulOBXmjtqA)Cqf+XhB!t?o=+2?Sz zIN#w94o0J?d;|8+rN!!Y&BcAl13B2*t#O`9+A$AnV!YXJ-R)-#a=wUNq6z z#eRszb<+^g&CJiu0~#b`AbUCs*;UFJdvih@vhK;qI6AfQ$&+b_>t@hd9t;kHE-cK# zpz{lhv-6#-ufcxEy~2OPqr+I*6%sn5!{g%+ zF0LONHIhw0B$C3yE|6oBbFSyzEvu=ohk8aEEW?KnX-r6Zv-<`hkd`b!wsCCQzJ2#@ zPF_*zqbE6Ik-rSS;Le?#4o+5an z(OCn`!RTUhBvdr`=SFab>4NT4_M}WCcq_nkAZ)T+?#t(Xg zCf&Ug7~pN7t)Zr*Bq#6OEi855Pejs#^L4GaGYT2lw-Lif`8^_=|Ik z$^<`{oPU*(;0m}WboKQ##3aOpL=PO4IVgMR$PqIl$CUq0@7>RADp$?C44Bf9u`h^JcgS@X%oY6g%25O?Zr!&=o56z zNOl2{S^caMAD^ALBVkR?7v`@frenArJs7UA@DYNV6kb9{*I3`g+B@t{b7jWW=u10s zhb~Z_FD+h8x(=IbQhOKRq9P)vG}QrD&%o5y#nr(hG~s4NLF9$qLPsyOFTH+sJrT2? z$!#>i6%`dz)mM?mOX%ww5)5n{-K6+&hi+t_J0W~3ywjCaj891o!)?XJ6&1b}sh|Mk zs;g;g>Kh%Fh%0Qy;qk)4Ck~(AdvfW`HE%rjye(7=SH!&e*N2Dk654vE79M_h*`@b? zPK)8Y!;c+w!8g8meKYL<;Kp~-FylDgSmz) zDsN&8ycrpi%#6hG;$C6rQ;hIB)||2dRv=QVRPfUz&~^-0R9Heu%W6MfLRa5Fhj0Rq zKOb|wQWJkBWcvYMh8&9L5P`XnBvUb55eXAj6H_U?xEaaB(8&AJ;zY&DO{%K41HT)Ji_XjcFJ!c z&>>|F19)OBLmStyo?Jh$_rK76`qqG}peC|*F@f@Ezcj+cMa9Wt8;+Xl8;Hl^w+Kn<#z_gtq?_Oz zVh|(%rQ=&liwcXqR1EDctt>4qNJh329${qE>b%8X35UY=cVO?UFncc@%4D)0Lg3LE zs;X(CPckGDElkCY&0NpLMIH3flMEkPTn2kz5-kq)K8DF;vu^hFvCrzL6O0J@1_Vn9 zF=w}Pv6BiyOkb84uZgy@P!b1TAwDf`PH2g*aZ_!#^U)-N{Hy zKXvH&%Xfc%Ndp@J_FgTxqO$UVE7Tjbw6%?N4K*b2k|$u{@0au<#5MHvu)~5*I$elE zwtIx8XJ==nUeM6g)>BbMRk*cu@nT{k0tz==B-H~muU)&M?Gfk|1cs-tx370-aCD@> zQAlATmZAbzjqKT+VxeYoprJxn&_Jto7(zY znYx|NeDH8`7D^2rXT4k8{#u@UnjWf@vA!qC@-A(PIlT&~OT_ZD5b313}ux1i0-#XV4=wwffL-vdej|Gpk z(EAxrz|)upn?Ey;&hElI32a1gJ)X7rxL6s(Lp~aTy3ORoPQ03PBllL`?SlNfrDb&u?JcccU`WxP?Y)Ejqm$Smr5*-)TA+4( zb=5UhmC(mi39epQMaBJ+yLU?O78TsSbEn`={>}W{{2MtTB>iJx?)UN!_6_#;4MH~$ zX1<|_GzzwRdNs~Uz(4GAR6<62MtWL0{Da?=%&XaeCC#BueaK`(6>Mm55KQ{OAoL-D z>o)>Zf<4#p$iT=LNV-@h7U9jKGnl<>@GW4Ku)sZmf)l9M2$Y6)Ub_K0h6VsVkbw}-Zh z{OMDtkDhX&9aYpe&ToFa^tS94aF)kp^)$<$Ja+V`$_d#cDi$H}F_8($RqTbKvMU+f z6329-nx>z>E4$4BQwzA=)h7?{-?v}+*f9kaH60@YW<2xrZnVXo*(-2B18tsN{;9k$ z6mXAeXltp8iwR3g!gh_{E4f!%SIfZ4wP5I%&x7%9-22p{I_6)!Dlg28LAK_zq3|Mj zKw9d^;S(qhx-ZP^eUmF$Wxu~)x*KB7vsWvs{rSt+6@@u4Za%F=04H=%df&0*$AoZ# z+N$arn#NYvXOiowwf<(@(%P}@OG~dF6k`EZt~BY>-{tfpr~B-#Z;<(AwG_2J&DmqdU2^Dp%!MSz>f#Kt{* z;J|5x!yfmKVhds;C(#s0!fl`WB8}CvY;BJeubOaQogjm4X46&R~rJ?opWohm=pj z^Qx+E>4xir*hdGhvRd&djpJp#@e`tW%i#qt_0)CD=RA~?Z@FlqVmgC zaA&;tC`Jq&LvbA3g<$N{)^HCUGCr!R2IFdKt7xd`N#pop3-fu%e(q+6gP8CCIEvyB z4lv1?z1Tw>I^YnkxHfU}r{%QONQyGjhdm#?evxY~?MQk5@k5iv1w{C+tx zoSF}=2$0hSH%22I(!40wMs98az0)H6sK7>k0ckCH6@ggX7NNtAsUmyuDQ28!K)x3T zhx>YJt12J3A5|de=<4WbYO4_hZGs}t8~d9I3%FJGzg<~f?Yj>$la7WkXt1}xmmPHU zw2GpJqPm)nmVl0~W#DC0#8pN3^2jol3Fyk-3)Vgjw!#s$vC+{$#p6mUN{UKKx`O=X z*4_~b)?t@D1e3?!{`Pg{ODDFIB(VGYhM{6O9;U9KucfZ6rbZCJ$=SMv#yXp-3m9Zh zyn6G~*VS@wu=a)qdeK-K)Ai^nc?AWi(P}CP;)M1~NlS|hxtnIthleLp#)M z#MsDiFQbdvLmwD>G6!}UjQtENJu3Vh*io){u(&h(^+i@|XSFE}nE<8+vPEUy5iS!kEQimzbuw_=#!=Fk%&L;ZpF z7*-GkV?X(18hTE_f50*U81f91ae?}?Sr$1sBmgjTiwo}-S3qAyb$v559W#39Y$hFg z9QsGVJU@o$exe2S_5pu?gG2|!q~v;zrUtZ8psBUKuBN=cwz{&iw&uYD$n=zzmz5+3 zc$w~uK{iQ1vjy1iv8eG4kHFa(kDA%B3l0U?W^{yO zLLLJ9b(?nX;^Gz%l0BuWFCL4WS*sBLxEa|hd%mEmx~8rkI;mP(A3kI*y#%}Q>h+tq z|N8Uh)%-cx9qTv{Wc%@G8Z5T|;OG3IJsSZ3WX!isx!=Fj;n?o=82+2@qkIFo;ViS| zzZt*!qT;Uq2ERF0gY&<^?@Z9){%`PqNhR90Qk}Wss60q_C1JfJ0A64;cq=g{%`RYzduFa zKvuBlf$_g>BL4>E+lxY<1OFX9$Y<*@^gkZI$X6EGdi3(lw^YA>RpoyO*>dDR$6xU~ zjBGg+@gI-B>VE|JQTF2W_n+Y3AOS~^&9dQB|Muy3FWZ6u|KNpRzKig0fD#OUU+~z! zef-_ayMVt@*qZrWgl_?3MSil#`kluczWw;SS3UWz(s1TSFzdS*-vYX>kc8nh0H1E{ z)7O7~3;I7_U4i`s;SeNV{rP_#D)>Jm{{3m5y9|OLg2eQH#QNXB`{D9y`2Wyk^uLke zuLn`8Yxw`r6!Z;#Rg4DUuTzmM__q$b4x&Xf`2D!%#j`xiCI`k+4cS8KkH@(rXaK?hmSNBri{$u|h! zT&yt5B9su09mhPr`S`mR9d%jGpg$XS%6%8*UjScQDl+U2!+{QD-TJ!mm+wCO@#klV zGQRh-+LxNY(jSmY}>hOH>akZHPjL ziwYi+J$#a2?r3Fg<>>Dpcs@QhE+OH1;oZB1w+bsOp%J2^9jk;;dl(E>AM`Kx^`Qgn zp}Nyg?n8Rf{@vJT*vF8;=^wzdDggg+Xl!y~YWneXi*q)@&G7@m8(8q50-E-rSE2(e zM8Tm3l(~ULW3p&y*S_v}E{qzqTfi~8Zp@(v&k>R?9Z^SFqn`p8iVr*kX@Xh z#>IPWxq@sx(cawL)Z7RMbedbyHno?x?^9 z##DC?l!dz?lhjUyt^l9}FDT71Q5p{r?df4a{0BADQ7D~Gb|*w9`fz0-TQ%Y6Ph(R< z1I8Gc@?%R2utn*hwzr`jXeXZ1)Y=I(uC@*;R_DSRT7anz=w$|4KoYuEpooRiwqX1~ zgEMx*2<_p68W;@)!@Z zb-?6xcDA*`DL71^P%i6$W>HK*+S|!gq&=#W2g+tZz6Ztz&ahd;*rUP3?V&N)?0z6N z&^t1@#7K&c4>*#ADCxI1Ha9k7Z8MnIV7j1b2C9Iq(0l_7wF1jnWeviZw)T!z3Oog+ zsihs8Q;am6!1$p9MBLGVQrE-$QOR9=FrRBwAt4C)KnSCsyy!`ej}Mg2LUxfT4N&Q8 z#Yjc}6{T>rQP6fSm@_)7cVM6oPLxhOe#J|)(F(q zLz%G|(|}I)Ajoli;_38Tc5-}NutXNZq1}cyax}M)o7cC%v!Zp%)o@G^BE;IdIxIy| zQwwcKkDe{R`}wDz-z~pf!MxPiJ5CW+Ww^7t2DpblMu$qyuj?W%usiD=986x{DT>-Yvdc(%8uy znwpq-JUQCeT5~V|T6+A&ATLjQ8*>topbv!$f|Z+}zi)8t)$GKa_U^)rn7G8ml$7+W zE4erF3hxzE!g3y&oL+eOj+veqe?bK1Oovhry;ZF(jcr{GB?ULGUqiFWIg(ee<#$dD zH0EU|UARn+4vV-LhDHPgd%Jo0x|`dW8xZugp<7A|Ro~com;K=y&i{+o#87WDZM6(cYosMQ81#?TGbkyC+y_S)hk`x~cZDes7 zz0YbroP*B>C#FL9onM-FK8zfEF4V)-(8N#+I#$%wHJ~|5O+`^&UO`dK!ndL=doQn$ zu&9{0Bz~_-Tvl2c3&c3R@cixj-ptfQ6toej!Dn z&C4e!j1|jVQj+eo`o|`pEzU3f+@F@15G8|A>%ZEvWrxK|Xw8O3YK!^O?Z z!!56)s->r+sjg|LZ)m8ct)oveaf0p$JsopLXDiYL@9+qR?Bs}ws|ESjZr{l+&khSb zdur3Rot!*COI*S(CcX@^`;VT?Exvl+pP3Y&U@x4B`0BJbHh~!1>gy|uZh3O@0AF0( zAVzL3Ic;Sn6%|!wZGAnWxsj=c|le8Zk63GEzb*& z%8m#z+4bYjJ=}aa5pi+r*o4YKQ2hDj<+r~LWhTTYCp&RufEYX1^bt0XvOD=MTztH| zC^rWeH_BxyZ>*@KsGuw_uckrJ(lgZ7H#ar5AvswY*?T%V*;<@&yXcltbf@@QRzk`B z2Wc_!J`QI*WH;>qdY~i{RgSQH#Dvrsmmf0_LA4Ip4qIEA>g!AM zZ&+{Ny=%vgo!fT+ivow$Q8~j?3a8{%72rUrp|ZM>rlyIWwY9OWyO)Kf?AsrFln8gGW z2^%(*+TPl7|JwEIxi|8Qvcz}ur$A|{2sV}?v{?II>HX4@(u%UG5?f1qOB)YIlDUnG zv!`F=^~UC@#djY*{rPFOHkW+yf~Ux4m_R{cQE|KYq}oT5GYij`UN65MxRMy3loy$S zY}%LwIXgO?My7Kz7;tWTWP(+9FH?LMUvha1b9gYim!AziE(62#=9)rVf`O?8$=)*{ zEcRw4z2SNEs?^Hr$K|ET_Ut5Y(akV*0wQ8~`-FsQSZXu#FP4{I4QHn$rIM{SW*|Rk zbik(G6-V3ETV{V$Y|nO(q1b*y*Nc_VCdS9AB?tIgTe-TsxcOa7$|-Jo^vmkX)cEQu zx+1;$b@l7V`HtJ+{??pZcJATAh}b2>*G)Z}n}4zNV&V1V)s*DK3^EYeq)WxjV;4P| zwufCVxn&ooki1;`<>ciQG>h5MG%Z%OF`SIDvv+ZIa`KM66n*v1-FC*4h028wAO85` zw~w#7D>Gs)ID0u`l(_kY#PBu=2{mw{YiV(D>G{jCoTP-r8__=iC7pK2#nn@Q(xw5| zZP1a)!;SK6I;5?9QsKA08?Av+7m_lfkBhsjnUc4 z%fm0LrG8relzuCdOw*#%Ld@aZG0B2NGPklYw{dWC_VDud@bnFfNzW~*Z>?=AzmEEO zdlRLBEgpVh@x4~@@pX@%&c9e#e6{#|G$$nm*vdpUX?HX>G_|&N11(m)3?~n76wi;i z1A1C|dRn?wY%*;{AHC7>-lSo!JmiCU$o*tf_{(-?^mt!)EYU;Zv4Yid?A#Pqy zN4MV!a)JHxL9@jIC*(^;A}Rx zkc^==)bow2SY(+-sYd+%UTctzXZV7M34fO-f3Rwpp7kY9s8v%@|+3_r*XbFd1@-9WpXe z(ok2w2b^iKYHiI-2)YJlx|(_f3v&xATU*C7{$U~EvGG}@w6TG%s^Y8X&o~G8pZIb6 zE*=39$-NG#sV%dh7B5~dExsAgO-_oxb2Ss$tWBxIe5Wotna0&yg5%=l;pOAOh#6Zc zsVbi=>Lt@ov8qT!BGgDsbksDB&26o0Y#p3jy@EpnE?z=&8U_aHDr;&g&wB@5RQhq- zZXSM-y^?mxNlml!tO-@S6xd4o<*Xd|%L0dWgCMFDY!0Le*PuZAg>u9Rp z1!AXJRc0ncJuPixJ#Ae>k}dFNYwcj?c;-UFjYi7E%U26y6P-mD-Fz<^Z`-z;TTl!y zWuFMd7G9!2?d9_5wUmU^MCVNz$Yz~R@ZDOPsDH601XhIy`f7PZPgxkKt0HkKI61*4@6j9-{`yda##&^9I!TK$N^ZQpXLIRaBKu7XvkI)&m_aT>}FX zD|;IUXD26@GoJ3Q9?s5QE*>6k!55MX+XlKSu0;5_M4E2h4%9>>q#cry8yA*eF1=b_ zT3UW`Eio-6<-#_grrp`n0-iNS?T6kHVQwBi(0wU+Yt_@Ijvpz+Xt68x4Gm0;tejl@ zLV`liUpOD+?&yBTKQx@2Sy)h3S$I1Gjl2}*<#ow$3(&%eO6+q;Nuj)0T3Uo{YWd~k z>uG68aREEmQG4}rerg0A*vx)#BRxJL2*;5Qu069KZm5IKdwzeseK(w+lBUw2(Il6nE&1<2dY#AnlMs2CB zD$k1}p9>}vwr=GT#>*UVNJ{B^IXgs$pcmpN7PGDTW=V}Gkb`+^1snZ0cQ|f7J{}%H zG3gUF#&AGYNev^X!>TqnGBh-E0NuB>w6=x=cy8`7&1@EnibbJVCpwc_m7gASDeA1{ z2~k0*!!q{CX>Cs!-QDb|`Nf5iIx36F>}BAAoL1*rxbi>3$QU)>O|r1Iw7bq?(z|XXX5K7mg4mD7OJ}ken3ZSrT5^i*m9bAq zYi9QJ49ug;Kfa#2akY*~V^GD{xPwTrWld9HGZE(Ht@=Mru4D`+orBSzJ4>6mzJDHVRd0b z)NF5RYG|zepM3;~@!{otE9W5v{L_=##5%BP0>G7~j<%kWp02JAoNl&oZ)8E{qq%ut z<}JFq{_Dz{HDXNKgM=HzT>@fKrb!UXch9_f`3i!%CwZx<2{#%22QXqCup2iw{6(x( zl!uR>4^Facc-|g-{_f@DjtBu@zN3?a_-xF3Uf>Gn! z3)MVTO}pUO+`Lu>nYIhmk!WZFw%W-h@N(j{N;*p0(LMOv`!8QVuYUdd=Z9aPfr^OH z>8?s90WzDp1SF&hw}AQXr?20O^YGzi z6$wNmeS(o@Da_g~R-LPpgPWVDM?mPM_-pwM@B}-0x;ok$?ibz9yLsjEDtJ&un`mln zYG#aCQDDu|!pag>nUxhRG}8<=O54f4Z)Ia;V@)zQGbQS4-Gh&|_ue2yxN>jh6p}t@ zSOBLac6N`yd-MA3?@I-#X(>qw^+(c?^@loIL6peftbv^YE#cp%tY&O#WlS_J!>F+< z)s@siOEfjrA0hi_!Cq|cfSBmMLxzbxpj_L!`{sUr|LS&fdTN{> zSkX;~s1SBiFk0B0fr7w}%zs!-N!P&C&P4xzXjxjjIC%R)+rE#F^Bqi#jFu2@-yq-M z0RI60pmVv99^1jLFb=ccvx5^SDMLi-m{GK?Jv}3@mJ8rJby72TW28Ev(Y>V|^Bvbn zVSW$4f~u^Mv7L>D0j3@sSv9t%rWR(X?M4SMqQ+SmDLOsW#Va`Q+=a6jz(>9X`mvK; zWaeYewUbj&Vn3051M{$d?4}Qwq@<=~6$)aMpvSI*LII{Qi^j`O=H1GFLiwnXrJbF# z?O&=8YriP64XKt`5A>1^xl{2!&@hX9bDU-p!;Daqd6)uj@=#@(zc0(_fOkxBKc8)AxTCH*(W$NJ1+G~T5cd-p3TsuGP!kdDF# zakA>1Y;3K}O-ZIiSk7jamZ@x*k-ev65GBg*KgXQST4D)cY571N=fUk+sj)-gf z<)`0%U+nJ!>8)e89fPGL1G(ikYTI8d3Gwjp2<`<>Mp@I$(vW1Qhj|;|@H<%B+mK92 zW~LTk{>?1xb6CKX6stHqGBWeZohmAw-d&W8dY^L+u{BeH5MBL3H07t?K7RQ4>s)Ua z7_@bL4T5RNrh^@DGy#&yYvL0ESDRnV`KIRy=(;vG&?npnu2fmo)|Sq82Kw4s+J?Xx zkz{EVOM%Us&TP7qS6bNtr;I4~^Kvt;7ZxVmI#JM7=7+cQ@SX}d-|*&_j~{>gEI9) zQ_1S>rgTkBE&cZ4^QS-1_j}$=*OH0HwRA*(+{MXtIE_hp^B(>9<0p*V-PBv%VCgsp zGExzagI!Tn1hysE0Yxyco{L}RxS|&LyQYSkm=7huu29p^*Eck=Ftv8|KATufdptMH zoc#I2rXcp=4R%$_I8#w&dyGDu7wPCjGc{* zm6@r&4tDNDTSr3+T19nF9ox1U5`NsgCo@^}m!KtNp!U8rBQv>z3DjbNIgy4=SZ^3} zT&#wJJOY?FMfac3)X*V1*xT0vIeBJ_@iAp}HDx6QOf#*F5_@UvSR+d-qLmHF+R@(5 z-qOYn=Z^VhE!cfXfpq$QLAy&>yVWFveN8djHzrg71G zJ^k94P&q10xDu4)(D( zH72OQ`I}R!n;}$!{FKr)7LESX${KgS-^)mkt!C0#?fgKEry48RbjQ&43^?u<6c7~T z@Bb{eOgSn43x3RJU7u&(v(b?$&cmNyeY&NaEtr=|)gu}%p*R$jNZL~Dx z>mK~HN?zHr`msDS?FyAn8}Ud(HtsI%>44G&P-2%zaq{sC3h+v6 zX=rIdz*mY38#cy+|4W9N7%#269nNUOH}17H#zlu*FE6XC zD7|~{UKv`>4GnXZ6%`Na?s>X6S(_T0nmIvC#UQiRF`0}QECn}s^N50ve%YI7SIG)ie=@%{{C-LxccjIJBG8SXNY6RCpVD{NZ#;lD>kvwl>kh)!ok4 z(b3M{!O_;<0aVJ;#@dWzVHwZtI&1Ih6B0&_j!Qx>`4zRD3r&p8ZDG@TQY zSZ}|6`MMIdy6Ml#jEuynWYG-7VNV%+RUMQWq4?L?4nn(OG~VhP7n^pjZZID1EX*C4-h!QReJUYD*CWt}e=>-rV3*!kXR6+PoK zi_hPF{PZLax^I&p?9p z)z8021F_F#iSbe1%HY8|aaO`sN5@`REfaPF$RNXa0WgYn6g6zjpu60{+`!by(GCb% z*_vBg+u7S&g;qTAT%U0IU_a6+AvSeBF)O$PA_XH2dItE#$Ua9`_s?p7miOK z(bWx=(d3JVp)ABzfd%H>T?{%%a^~Fty|)ia`0RmFb$J6T__{Gu6ZlFvGcyxIJ$*=U znGo%)!muc~!qM~08Na{_m!p!>Q!XahkLv31L2N{-I_X`u=y>y^+L#N~N8NT@7&F`PTtge1(wUm=S zqG_$aJ`)kc`9(%tjJz0mA-Uid{1z78DJm+=M^DL~REDI5vA&)js=ZxRUE9#qNLO7; zQ`h)*KLlHh-mdCfNuj9Cerp>Czqq_wMqh7x%4ua&Px7_9wGhff=KypnbVJ8R;%R|> z@)mxV&TPJc@RK^~VGC|Bu zy1To(yStIJ>%D*?f;0#ck_Om?MZ+WAV1OWL$Ba5X+wTNkruEiRC-Ec^PXURrz2@mO_JkJytHm+ufLLRatj z<1}3<8A*OolfcNxhy=T0Hu)7RR|btV-EV4ddeC$qK#CGn{0;T=bhL1*29AQ3u8zL3 zDHyXhR#vvoIURkEkndw!sV*^ShO}Qe5_Z_u0A7^e>KK}SF*7y$_T#%OH8F8n1K*g) zkg(vyNcMcT2zJw;grq&YGr;sI%+D*x%go8oNe?pvP9HZ6X1HX~)7974)z#M4(bL7Z zD9J?Ev?6XEk^KedL|gmN?9AC{|3FPqMI8(8xFhKWMaAW1B~?{L(Wctw{!!84sQY?H z`>o1nliqrHjI1S8pg>jvL8%v!-g_y?#@xsV1tn((J1c8D8|+lJcGx#47rHlgso1oN zD|hkM*VQ}n?B%DK#uG6ywpwcXR_+l;3hJ)iOL?&6ArL#M@o}+nkwJmJo-E=<&+Zn6hXlD98d}(UM(oR|Y3v>YKORW<>F3igelCoOjR^~)0#N7z6qa|7ypwC{Go9zO?M?IM5;j4^8P2u1#Yps2Vo|KNx?Dm4{X%DJZ~kp1k< zjqx7j9UOP$RK<<9!O7_vVCo_3Ju~~dC^n1?pP#przX!1-+%QB9tb$&_2>5GT0j7s< z6Jvv2ck43u1iIMU*)pwH**Up*cvHUGeu0tu!+l^ZILOP+**h*ZImX7w1TXu6k%>pr zPuJXNA9zdv|Fh3$z!F?|U6dHLD<(9+&%@EhdTl=2Dv|opfg!5@2e8k;pBM(=VT>Ag z8tH4jRaca@FDA&#)ydY{+S0<>*3R0-!QIEtN>@Wu&&eG+ z>}|Vy35@jABfI0G@RIB0?re{@Dx95xq0v#{p_DJ5D}n`lq{#UFDVar8^|zWk`$wK) z1VNmbo5Q}e@bW|9?wHWPpx{7H2M;?Kix9amIt+z7C}P7iLgbhvbO8vm_CIR6eW|Xz zI5*>1@{t4k_9P|5M1~=#kdAwMdboRf`v-+Z#qB+EEVHoU!nM1tkNU@+f}Jr3W2i|( z5qAFNyP~-8=;-hOZ&xQzdopDr)x*@#S_noGrAUmR_QdEY#OfZ8jSTibYI}J5>ZQ8M z^3vjhysXUhw3H(U_w7zhK$$;r&;Em;5@zO>Rb9N<*!*a4Y+@2uk<<$=*a6jJeqmvD z@m;~51OQ5WyfKXN+w$4gh*XUL^)^I-n?tOz_zw`ho0xb!IWaOk*xT9i;LfevH;BFu zNR?+!7v$z-XJ_T)6`d}rsH&?652CHRe-uCo5ITrn<`RVXX6C7d&GXCe3-%;N2M31) z5Zw=YKHEl-nh`=~gk$65BXCT80>elI_8_tM^aXxH!Pc-TVL=vw#T^PA4H& z0^8Kq)jbF$bHv2VJacyCix=}t)EwvY={bbr;;X`4p%GCL!N^wJ>^QNUg{q;u0sbR9 z0-ibk!vq#ItPx|(XKNtn_w>p5C}^Bx<5(WTy~q-3R(TS&6QFBCkV(|rn zV*bSfyaT@m;rL?dZDHK5*cc3#RS3BI+%c5FYHhg?2rqlo~(cR?Aa51 z*7K)NsOgoU<4#l0*zpw9IL8=hxI4m&%mWw2%0WHil`tl zDTd@g-Y%~GMhK1_4Y;a7aFCD~A7@Uig+$@RQ;<}sXV&1VfZOs63lz^eKQ}}8x}e=W zn}!SX3(E)t7+PLjSb}5+XhI|a!TE(K2R5p?xtB`?@sT0H!9jjr4sO;;u*G$GG-Y@t ziHYQK<|O+Qc=0TGifx+td?hT*Q?s1l??9}BeA40)J{lI57GF?{>lTO&LPnzIRxQH6 zh2$5T<`+*V#>9jpNp`Vz@x{PxlzBLam;>nu0|)t$i4~Jin2%S(3+Qf8Q(V|-5LVBp zadSUIs7Ux6OvuvGGIL3K5h5Y*VsUW^VGZ+eXKsELzsJyl7rk5*9~&DH?C<0342G`C|Sh>U{FPwTUcKDJ}3BwG;H|yH*jkKhVduif1&!!N5GqW>@W;`>DAS~buwZM*e zpyoL-X4n?UKw|cw3PXMmueXDs^&BXZ#pTn{Q4wJQ?mk|QcD91~Y-$`ACm?__I^1;W z>VvT-Z7p3rw_C^RI!4>ark_CodukfT9bCgO&0<~R!^wVt8KM?tA?JdP9KtUM7+73f z#+o8gNC;>cP5jTo=$Pl?@H~gn^8SzWSRSks2^o5OSvnHjL#@Ed$IU<7bv0faLw3;d6{ndOD$f~csF zKrcUEXDcfknS3^Jk$Xg)cWAu5fBePs(bmc6>k5X*$KsD*>5@Q1$joAkLbT3fbu8tFM}+u#`+GQ{#EeZaV#|4QGLB5% zPE0JCoSf?KYy&S3JhK6W1%d-q`R6c+&;ULSWIlu;R?;k)CWwu}Zl#~BDnYs+D>+kG{YOt*t4&(J(u1rl#PGFOmX3qRT1_Sg%vOrQ6*5U6X1n?_!juQb5 z7|zo2(h>yOS@DfeV=k;i_>quY#NNb(KY)0|@F5_JgMy*T4vA|MQ*$`GGIXg+>S*p^sY@b3v}sX`QIv7oT6Vlb8f4R{G`(aXh`z@fgv+s3QqrPpuX;Ja7L z%$IzxU*Y}~4}s{wQ@vR#@C))HS&4;_k;RYsY`WaXvWrd^L*A(POfj%x6~NI$yr8D4 zqO792`rP^RDA-)SehcDzO|1ZL+;47egSK;9E1=7${B(9wT^oCP`iK${#0e$6v1rS_SDz;jVVJ)~0%@ z+$#W+7i}Gdt^!fp7)N$N#}Q1vo8n zhKP1It`oYta7PWvJF0CfBplm1+JUOiiSTiNu8XPmjzTsr(Pj`3@YdZo*he@B{R2aT zBjY&yN2uXVqu58rz^-Mr1{fJj8Q(ODr0vOLWFB}3lIT3%_H>FIIiTO-FhJZAb&Ds| z(+y9aP3HT%H~^NVv#p4YU8BE`>R&ZD2m{oRG+wv~zZWuBq`koC2>E7o2rSoO`~v&f zlR-Vkp@SWBkcroq&Col+*Z3&uL;x);@L)INZ{lmL?U0MTjis47=V>-}-QEFapAZIs zh-l#zfkn;5Fpdrm;@2^JA}cwB05j=lKn<@O0}UHPjL#brh29yGgwbJ==aVRBr9b4q z_=~VTblld)(n6i1m~EA5?;vqw1I+Y8H3O^zD5MXq?Z>zHcZeEh8%iEt3qP>qkKvt& ziyYM?b0eRF>4~w)WF%~B#xiW|%w;!}u&prcp?ZZuFc<=*0mAOk zg~5{!6ICyw)B$^#AGinqnIi(M1c%6oz#2(EFn<_Q_=T1YVA=h!P(Ula!?UbadC{v!t7PP$1l$B!j)zqG+F0QP{gHShC0Wf*zE{idM-3Y7(RD(HDrf5Y?7WyH; zp>{ud)YqRXwBice`jxBK{;+o4`VBvBrnao)*t&yE#;`6ds$9xbMKeqf{z0CGA-1iL{ec#;UBWoSZzWaMkIeygkxWY(GFOf8BE4yyMkLD6JMVIe=o{?vzuE$cvN&$ zT+;5n`;R22BvUDClbI9~-=$E=Jj`VLAUXATN^&wYh5Yc?u@fgwp2|28%K>T;9n>PV z%>p}ddm*Qw1@$F&FAu-yU9r1UjvYUqcBZE8TusH<`YYg&G&euQeI{<=P|zkNGnBQd ze)a)U##u?Gjfrn|l7b}aTj;FN-AmMHuzNgv{In6wqD^2Ham80&y8?#9-G{AhRQpO& zpiS=Lz<=6V75`IWa6V*aBuLh3@2Ljc)v8|1;eGnpqp3{=t%G-u5rM<1alOUwP z=OC8)28Rd7?xYb`(dr*gwKv@ddno1p3PSu?2|^Kmw-&d|;KAGj3uHwzEU@F&jcQtj zk7#LOwr#{jlY<90o2U!o;RIk64~1uI!()K^)YA(#3>Ns{*u&(saFB{ts5GVC<-rX( zP~Q&;5E~bHATj|;NPZ;|1VacTT~@uj33MO0g9mQ!AoQtb3_%+lM@1Y#)&Xxium&LI z{s@wEcz4Arn0Sy(9gp6DN~~!!U}VXS>>%+lAFzYw!)$_Ac>WeKf~JSJ;Ml-&6HMX9 zgaJmlKM>9(-?b5f5!EIP$`&eBFx5eZ4OhQVnj9SH?(QFY+JwMJ{FhF&*18463gD8! zx50?P;%a<8E*zQ7Tbdr?rT`2Nf=6MEC-Wi3IHg%0EFV(Uhm%;#Ef_dV6>$y!fm>}| zy**^t>gyXCn{PSBObM6EW7}>6ygcx6__(|GFls>R-veAf`Qa)ozk7|;9XU{W8u4RT zW6d1I1#s>WyZ9BfBbX!q!cQ_5EiFw{Gv~vH?WpI1-_ws>V{)$LL~3fJNFLidR}jEh z3;DpfK*_lQG&R-81qK=6 zJ>iezx`dEHKxQJrhfV5-fsx6lFFVtZrN)4BV{G5@U0)CeKuc;Qmn*k!g0FP*792ve znJdtAy3lax$_-QiZlj|VyutJ#32K4oIL|L%yLRI$b(wzU@{Ma(ucLnsc?Phn?%r#` ziHWiXUgY2Qq#r*P4<5`~ixzlt{~-}Gy4MH;Xkv5y`i<*X;lZ^V*REW-cB8p-;PLDm z>XXRFcdwV`pFbTR#_ApDY461uG5c)bQO{7{t;-kA=7aBe`gCy#b!JCtDYByr7aIu) zZ2Z}?*|$9>(^3<`@L5GDV0Wqefe-H9y?*t|r3)9%pF4NHuI~J~y1H|>+Xs*=Og)(# z?!MDdS)7-dvL`wy7l_TOr(_M?Nh^dN;9eW-3OC* z@86q1Q3T~k1X@{`n_8L}7z0;G0+C0SKVaSwYi1q zk+wVab*)#f*VdleeKc*?o}-5kC+q>)eWQ=3Ud$}Ne{|}^@xx-c@Gx&8dYuThd)Ka9tuo~0=i{aLocZ|$_zg`A z9AZ*WpSyIqxd*vT@8EFv^vv?dufKl&<@aAdfBx_mFEB4Zy#DF;Z-0G$`>d)oDPJb5;|xcv4LoI93&A`nc3Xv=o&eK2?Tc765PNSK3;C)5r3`1y5g4Gi^l z4Rv&kOstGd?MzIODmq!a*&DkBd-}M$ga#c7&abPeDJjgpeycJ6_{m*DE$_sOlV&cWW;@TF*#wPlP zdd8L}7RC;)W|od-W{#$)6}kt7IypPX`5*9)JxS%}l%2c#sQuo9@;x!(o)#Qiz`GF^ zla%pK%eYQ>XQ)Cge(cLhPdjlef*l+IQ4wD4fbjDVRB+IOON$?CKQ+By4JR%Y&!$pT2(m<&XK>WycOt5wwllxCG#x zOu&hZn`7h8=a-gVExqo`O;1ZJJ)Fz7TFg+1zMYGS@L;1+!W%_|8BkE=;}a2)6s6H9 z20NV&``c+kG*L+fdpk=zM|;o6q@(D?H*tTN`X>3$KY#!I4>(0rt;Ja}j1Aj%@-rl) zWdqY;3v8)5LeCm3NKeb8Ja7-a#`@9q>Qn>4Md zkp2p*HtH^WTc$v&@M=2rUitZ${A*QA^|cKQRbj`3u5E5=Zf@fMkiG@EatVozN!XK| zo0$on;D$SQ&tEuOn6<|{!d>V`3>jTa26j%~d^!b+)I913L-{99X0g~r>#W;=g}z@e zPfHPANf#6pp!hck@CzxKnPORKh{BYSqnov@t)qjTJy5m|t{$H7EjTbZa$j0u#igc> z`wy?3J(&;@YcBzh1Q_D7O8)8TH=j+tSeThbHD;(VI%AbztXhzN0IcBD|U= zfF%V}>qQi-P0TD!^duNeVH1Y9y&EpYfYr0LwX=2h@bdQ$2n>mc-Ax@oeeuq{N3Hko z+$h=;xGO-3#2j5hUO6x$^VV}xczm%u_i8*3F;}u5rYzg;-)m}Nnc7MdB7+BeA_{h9 zz*Cq?!jvgP+{(q)!or%M0zoe428-$Q`zq=)6ZEt5~ zZjQQ%31~ztqy;YA_Qt2Aq*go_9cx6lkvmj$!hXGt96R{wQVJ>|ncytFScLR8>RY2A znWg9LCvL$OUdcKwVQU3VkSXvZjEQQw14(UWCPOD9C;CiErNx)aU`T-om~krE7mIeCc^fnV z9(E8@>=?mhfgkwAwA`&p5+Ti+D@pK&%*`z9fUvN&haLZL2wERLS#|Gz_6>~;PM~t?TF3itl~7UsdmJ`%?Bo}f zl2;AO%)0+#>GdM@a{bc67$hyzPwgZ0A#;Gb0gbm{O_q=#IYa~nWOQ5&b@g@hq*#7Q zn1IJ-WAEY`5E>X35grok=i>*~)#2p4y2?v8FJ354OHSS&8MfPM(^lA$kyj^vLGgd# z0#r&w(pZA#1#BdKtNbt4;R2@W=7vlNts(>1Z`}wxCluv zz{fKH79(Sm682LkD;uu2wB9Ob#)Tg8-cH<-R@MqTb*lN*^2>McK79P}(^PrRsbdv& ztjTI8`VIdZLbxTsC!%2CVrynpCprW~#fdF{6>&)6o7W&;UBu;RPAt|Ts zZiOUM-+~x3V@Q*_kd3n&(9m|yt{^b@1euEo3JR%$>m$a;pz#Z<+l1_j3Jh`A;o}ui z(AEga$m*_>;pWoIZSCkd=PJo3C`c1oO^kKowXx}cgD5CKR||*%2OEOJ+APnuF(gck z4L||618c($AY~VKA47`1Sq-r~t3?#79io z^RmN6g2KXdV$2-ikq1rR1CdTVlkusj%8sr596 zh+?ewKAu&2@ygxa!+a?a`^HKU5LCfNs2P-bL=>I4N8T;Geg{@r&M8yD?}kiU$Xj^} ziFh3KLYwH40bL^lbAuI;wpz;KFtnB-sjp|KZ(u|j?l3m7w6k_FW-x?>w0Be44ZY(F zAOG;!#BwVH#iJ zNNas+f#Y_DjEaAP7(b`HfU> zZ_n-AG-+iYPs|VR{ZFre$No4|o{@PfMT1U^A?T5MvVtDQkXj4L$g2V{;tRG=osj^H z(WRYS?43X<4+!6za;p5i2Kgk%&wwQT{`X&h{r=^ba^7TNL0tpeIJu25rQ&TXgg;RB z`5iz~KfNf+Je6J?BS=hXx1)T}-1VGds%`%3sMs8yGTLhBmgiT(XzL>e_ms$<5Kl5tIf;4djYz zY06&Cj=8CRCb2eh*fyEi0S5i7jN!*ycCTlnk`NY;m@*COgy?hNP9fvyZ!9 zfQOTvudnxaQ&PSmAyJ{bBBNpx_opNhFK9|0@ow9f-2X3tq1pJz038LvZk} z==eQJ`>C|T!vgSr4taA7FIR}@NyH)5D?BIN12C@q~}%~fuSuz4u0PDd|P=K zvRYm#MJL3NVXmX|iaDj`Cx?S0MC@ReSOd3$E<_1#g#3Yuw!XcMqrI!IB8`buUebby zub>wt0dhLpkqKbX<%435q@7C5I8}1~b~?YXFhj!ABRZ4;a-p=kXG-Qsy)a*@@EW>+ zlvj2I2?jZ$xbxi((oS-Q2r<-jfbDg1a`#riv%`*=xd~2UE8Gj(Slc_R!1xNfJ+)6T zr?liu-Q}iem=UxLcMnQ2-N-E@t>luD`RMmM8eEK2;Z<~g%WOe1J*=AFzwwQb{AXJBoQB*L5=PMaEuE0UVYxzd2g_75O6a}cgBxDa#mtUf z%ndX%&@(h-EmuQ^l$D*Wl`**MhGyW!+uOVQ274KaUAgFJ>MH!#2W3EZD=zkdGm_1(;kLyk(u7Q!1jx%l<%c*^m! zufP5N+n?V)RTadGW20Ol$^~=MC~UX3^;xryUyLL_KV42q!w?WkY^h}9V@##A3Jo4)=zkL0j zh|_(j%qj}R&bUGp1e0}=xST+#-V85v|Gp&fE2x7HV2DG}TpH#q$XNy62SEA(VR4DM z=WgC~*`a>3t@+`dyNy@QQ zJFbYBVwsbG>REI913he7SP$5peiV>bP%|_(b#QmIB+<`ch#6QTK0Lg=9PHfOJ={DM z_-NKX4o;4?w&uph=4M8whPsr|Rzp(_)lJ|%?c}GKx(P}Q{{1J5J@dIXH#f@|8Gz_c zI7h#adx(w)Iin6(fBkD>rw0lD=y)#B??FbWvE<(FJ?l)>eZiOxJS+ zS07YYAnASmU7Z2x0$N)|apR91oIErUa}OHVk#B!stU(R>xeAazGh%KN%<0L_r&QcurtmxI7D;VojKqGDpgG({xu z#&$kl3UpXv$T;|Vx;VMoxVt<12b#k;y2YR?3dwxvG*MYCRr&@FaCqbtoI~;K++J_K z5oXZWx`KRjL17WDY>>Xb8H@l;Oj;3#pe<#&(n^ue$QS0LbMcADX`2U^Byfw#^73rw z(%Y4tchX*hM?lgS2gNFGLvu@O%0>yVVAfV91|)cJ8Xd}rlNFayP-N8-6%`Z}6_k_} z6%^!^ok5ug(azP=N{YrvrLog#G$xKWUNL1Om&n7}=MF36PTXcXs_fi?YgsN4HOGXD z;0CSSa$|UqIYfi@R?q!&WhF(0h57h8eZW+k==XU0yCKE;P7U(b^9%9|^l-2;v$1zD z7n8NKGu2U3QdE#vQjwyGNx)4yf{dYR;hm6@4>(Hi-x&Rc-m49l&m~jY*~}Ok)QPr% z2-VZW>~ikBcLmoiHPr-1Qd@V@Tpg{lZBXHJar5#-WO;x->g4L|1Z@W}C5-4I&Y=+y zzd4$mLM6qzrX`vxYzy?Zy`!gtCkhhYK7LNt<`!0Jbeg%phgU#s+@AQD zi13*7dS*R`| zVjSoh;cOu;q`9}?=HT?gEA-9znb3lMtxG$e9H~c&1>3K}P%or1sGgPNKA-Bc0XVj! z^PGv6zPp`~nU$@*nKhUt_NXtq0Hf~e?%|?=$*}>_+TADk;K4&VXBs+VWi;e%lcF5; z>|}(r50^X`9eVoi&6|&(|N4hT3;N^y@l*=5pv^mO;Cj3V(j6Ge&c0FHjJx&W2D!i9 zRKwKS*Z{dJ6gZq*0KI{SPIk~M^3uWPrr_`G=j#(3m9&S-E6&aIuqxJd&2|T(MOx4_ zx%}?X@Z9US?_uq)KfnI+_qTt}r=%POEr@-`&0c2r4`2{<_p}z4HV@!l-lea1=z_kw znTw$z#4rrW%Z(L^aaN={;pS$EcU_uNKtK?m9CDB ztDB{{i<`AIii{A)u(Wb;S0Qbll!E<2BBFQgJAC9QMWs+Dd=7P11v%Kq=n2S29sef$Tl(kLW^+xjrB;AYR&w)(kaX#wu!R8m9K{ zPFD7=4#>xxkp0=%Ir%7%WK7I6EEF0GkuiG?9zK?HwCwWClVD>%9|IwVb?nKymcdCt z))p7u{rU&>&)PqK{j=uK!Dy)NDXlE)2U-cx1*l)*Zk!+)h6p^VhUg)hm{>TuA|odQ zX=!T*h$YIxO2`b9kb8!PLTo4Qz|rF;_wNeXm0)LN>#9YgYk0<;y3jm418c8dz5Vd{ z*T290^~a~IC|}(z`D|NMuj7^)=oa8>zC(JbApu;GR~zdXS>m&OJRKb!tc>-H4UJ8V zkpWt}8REcKp-Gx~g@uJjc?CtfIv@)`nM6xmKv>m^kb>HVrhow?+WY7_@$K7hSDm#b zl`TE2K?>p!h&~Kb(1F7TGFbpLpy8;hAIq5<@)|lurq)(yi2+g&Dvs86q+$zF5P2I} z%McP%)V46!pmaEOb@dI@WkiGokZhF{C7>syH z>?k!eG&9iAqqKME=xC~|YwPIf8yVxG(Z zmK2|^sK}4BG_ns(Op1$&4EKxoBkZH?S4JQqJJ9^p`Rf%8*4N72D#u(16hHUojrsDJaK^eMeYp&Mk_2R$Oqy&-)TX{R!;7I z;R%OM6jtA8=^F=38u0;0`}4V1x$$vHiBX^txcS%uuls{UJ!Xtx)rJPCAq(LBS@^x- zp4OYSMQQs%3vwYUsut#;JfVtj@9bu#sivl5=H|QW*pXOQBERS69~QemHLIlN>b=fE zaybep_w(n}?54#xxx05KMuh~02D%4E5M$!?15mAnW0G{f_eZFs4U7% zJ+wPEBE%oUDNeW+0Ta&IFC>aZ3z7r&*TKyvC?akj^JHPg#T)n9`bHiDEDb&j{sD4% z{#{N|LR3UF@Z1ibPJjrn7P&~^Gt5B?j3L235=0>op@Y304{l$sJ6oKSaf~`hNI`Lc z(FX_kczbz6;TmU{Ur>HkV3KRc!aRHBO=}m52OhQ6z2yUpo zoZ#3>@j9FzLjgeRkltV5R}o+-fh-R7bhJLWbL~Q1Wob!Keh%+|>*I5oi_E40tcVe9v;e0Gu4RV{dc7i4FxQ-rLE;l>rur=s5rviB~MJDK#nt zj3>~LFvfx`g7pMa5W%svwmi6Z=jQb*4a|DM1_J$oeltLJ1HN2#$DVEs(4C6u410OV3rE2pQPO);mrfXf9V1qjViKmf-l9zUG|iGt7y zsClqZ$VXGND~PdegaI`grJSV`7fhtDp-6=vVn1Wg^F@?uxkr!s?sfG3PK-PneF}JWl z&2J(63=(-H6o^$k%x2iii;0bojS5DKVNZVy9fxcaPJ04oB@2p0N~6X&VGW}<`3$&! zjNEf@O@P3sX4b!8bt0U_LlasHhy`;i5N`9rEbqRX1382c1jquLU&@V3OpJ{P@^f?b zaTLS4;;bLRwzQEnRc0|30oop)U_NGNA*HEjE1pkNQydt@=it(jiJyl-gc>!!8pMLd zd3>`Oqll3N9RSgX5qLq(a^O$SFXhK4CG83e!pY$3EPzNCxQSMd2=(ESUiiiw+d&k_0ydGU$y z(P4hxK92S-SXDxdyTEP3)<9=77>i`CKz4dEi8e|sAovvcVlo&*Y}wDTnc|a(G0=qI z8a^Lpz&Am-;kWobF@_}tx)+8J6t(==_?W0*FF#LQbAi*M%5f3F0b4`U(=SHH+S|K& z8rw!Mc8_%o!x-j^b>hJmq8T5=GDf^e##GIN4Kj!Aftq6{LH{BJ8wFekKwv&MCN4VC z-xu^j2PX-vD8U;zhWUp3FJHR@u-$_T*RP+h>PfqMb$36)590?r$e@z@J5 zUNGm@k`+t*Va_sGKV!`l88yNhnVSYhC>xOuj-9_7PCR)S5_gCVl-P%nNYBSn}vtRFDv)_~E1Aw`4E?T9;2 zdY;cO=7dK^1q1|pxq)^olF!D;QBU@o(ea1nCAA~tkNdkH0hmucSv`Poc!JI02TTn* zJQ7~jbLQ+8R+zyGaR1vog`R8Q^zMFe303gnaN?<*HE2ZunZB5b>v7uUHc!p1 z!f?*cF<)$(#Wn>?*%8rTCnCb_?5%K2{m9WUNr3HOJ|N_%C)=?-fOGT&+#`5`QDAKl zIJ)`B#>omigf@l>3+q2(6BZaXyAI(`R@E$tJJ=$>d6gd-78n945l0Y+VN7i6CBnF1 zQI1$E2V85yog&eSKx1vy7#S>9JUVmXNAQ^N1?~?3LU3g*J``47AnF7VFCbikphR{` z1k1vFL2yX0haFf)*0vTn>(_0%0Lj$uzJcN7Q465!;Uvk#OE7=HyIfj^0P)iDG6+@JImr52T%?vZEx*VK4E43MwF4T{$`Wj#mE0-m zc~sFBg7yT;w7jCUysW&kvf?aNZd+D+4$V+6)n5Sy_clN*Xf6(b4sgVvx;$(KZW@rZ zj<$Bl1Ar?sG6dT9_&AFZh6y{`|%D{!@ zsqDNWFnvHiV%CX**i&0qi!#c&nlpt+?$4YqEGRw;bZJ>-RV8Fs%Fd#f`k6CS2}f~B zDR@L@%gZayR@I%WJ6BspRc@*%&58^3wu6j<36g(aV0^a3ozBTCC^~(bS-j$OadP4M zqT(|k6oL73Hn~y--8rDOUsVmAc*u z-4X82jz9-#@!*YwU924}jtxlr$m;i!|cr3`W zN%lgGpw}Ll^UJkwu|HAxW_DMUArIZ*Bu|n5ET#?0aPBI0PYVH9!Jd zZH03diTUsr*hU6M9swT=>Jomo6?-m7O01Nfq+NJLKx1FFv6NAE{Fph$j{(Jp;$z8R zlJ7{ajYK5V-qBVO1fVS@JuG<_ykh*Dr7%a=k=>Q#dU&XvBxwNyh-De$!E%aB5_2>a zxip4~8rwk{3bX7WVI!YEK9*%~E4!wYZHrIM=`(}|S6y@d{KfjqSFTVuI7q)h0;6y1 z=mg0Iue_ijtwM=saDWWsD&hr3WHlKhkTPoWv|P^HEGZ~rZU^N@ zR*quZ%q1=)EFx@VVjmK{7fAL!6m#ndvNu)S0OsR95L})8I9jO@K8!rr*GLe^+9F3b znDg`wX;N_s?E15Zk}4s03Zmtkl%kwO7kJ7nE+;YVl+i&(~Si$nsO z?951QSq+mLU4;AwQTYU!5{6JeUQGyBgmJZ^w~yH`)Hi?*odYO95T+cqO^5(sr@$r# zem)`o4JVJVVaMTMxk=vP!P0~8*o~-3jwe%3C&nK?nH#SyEz7b0-b9E!c&Ll{XccSJ zs2*_)AE=qA*m9X*SiAe2*_5KDz z0TwE9B_w4tuP5rts!kf@u?dEZ-~=Ow0*cmT4DreM^qwA8XR5y5Zf4hdR@wJoVW#yx zUETfPN1N)AMs1c@$5?~HiATc#U>xv0EW#Ko3a95@kJMFEWojc8h#lyF#ts=t6xQG% zq=BH>^QfD8L?gk{Njh3#wL|I+2P$}2onS4Y{S)|0*aoOJwhrbasOfdHVSx;j`SHL6_|pG!WN)$a=Au%;$l z$)igbc`Ez|^V!|k)ztGa><`f^X>A)6`+9l@#;L~}p5g^!;lty)>gp_9 zGpq_2?7)Ra*BYb(-FSYgRfo`^2p0>#fT7j&uzP6y*}{j9XeIP|>BVD+MRcP#SUVv; zJ?xtrYinvB9JtkR`BHVo+1hjGFHj9^m#E9zuHLx&pc}mUXU|bg{`9oIuC`DKmH)tj zj;`cK0oWItz}h16DK~GDuj@CPdq$^UJbgMb*57=&x+wGbk)((qcXtLhgbe9^ABL#R0Vdzm8l)7MhhRM*y0 z)6i6-6h#!&Ok8)}>nzhzRM&*yIdB}IC6$nJd;Dx>8Mih+P1e^{mm)+W1NvE^kJ?Xb zXIo?Ck@zqlS7*RGP~Z)%8+$f6*mC<~U30_LsxG%zdQzbmPe6G#DgH@4h@aUtL*g2$CJF;p(IpN>eRO z_b&OVt3sMxQIS%1P%&4QGxkkP$t|h5-O@8S^ayKsW@_pEPhX&3_T}@}k1z3>w(xHG zXb*D>OUrM6pT1aKRc)Tf78Njn<$-&l zjt($Zaxn?YN=gdyxNB9k&{I)UQIVCEP*By<)3^4IPCR-r4eCOvhmWS6V4ln@x!QmY z;N{HZaO;Koa|i3wazZr09TAmL(l9W$Ov))}9Dj^yTYmfD%T#@B^;sKmf3^ko6Kxv^ zL$*J>b1hvyymMP;TEn}#ys-mH(rLF}yfsrQU$Skcqee7(_O`&XVZN1BHSG-Hs ziM?mmDT$h=qH4s1Us{* ztE2P5^#-Q8s-m2fFR!d9uPg^Fh`yS(F&czJpxe;KRNKzW(b>kr z$2-wKuePMLu;^4{Q%io@@esEFALT6s`L3jCXc?Cab2D?xuim`*^y%rP+N#=e(qtk! zbdscfLj%1%O}DNbfH_zb7ZVc|QPek9S5;S3RZ`c}(9<+E)6@p*$WYD9Qs3I$8??R% zj|BIK)cqNml{NPU`yMpa?TZTXu-eYa&yZ45*SCnvDQ1tgCVX3EW8Q0{kAS%ott`^mk z<=wcM$w_Tz<6`pu2(pZzh?p2%kS@T@ox;oHG2-UpL0=4GGqew|c8^FpT6FQ@_??As z-~RsNk6(ZL^Ov`879O2B9wfGfTTl$3TbrbUl7~-Gxda^e)%&T2y6W09yFj5@6FT{z zK3#X`alXy?d~s0>pqPe|j=qL4?=e1OUb>20%*_tPLFM>K$$~+L6My znpcp-1tqp#LR?c-URe`OB2xHFc!e}ce-0g9}``W<%wvM)e?Cr#ogggQs zmc%8rwG?EPR3u?(C9i_E4rQQgqL14g3mZpAukf(QxPvE;ohYqsxOC_4h5DMZ%xJeT z7dm_*mh9qli<(hWc>Qwu&CAy_@TsmWj$F|P4&ngry)Mq3!najUL0VE=0{=*9;*v{2 zQBDA+j7=G~VrGH+69A9ByxhI~gW{9(&t7ZkY-+hxn;sn!Z7sExTTq{_Wg2pAb(~4q{Eq)L~yyNz*Lax4e4! z?(Mr5S8MBPu4J*S4dD*Ew?mURh3m~u(pwxZVaH7rnZ5$rjDUCWvOE8 z;2sc}7=I`$tLWe2LX}udUfbMde@R*U0@_z#(^!57YqjU@)e{;^ zKtGX37&hnpZcR=~5@RVTp`xN=te_~bNN4#*$2*yxrl!7*hPH*BGwy!voIL}h6XH_R zj+Zw-o@~4UE#$pHVY_s;Zs!-1)3J2gTU^=(^Z^OQH*a5D0obJ>8xg{e9hmB2A2oxu z)$mPDT7r_=AR(ckWs0L%Ra5x8HMCvOQCHE{(a?dKAUJ8ZHZG1X!DtM2@8QJq+vUek zACxC}`$buC!kU7vx%2+w()LB1(U`S2Z=YYQt-V;ef8FVIXHVkV9+}v>16f&!S?GV2SeQ>ZMJRa7gd692SJox zeD&_#`*+mawe#0eh&q>p76R)628l+?z`xd%WTj<*zLJnsQ!!VRlarPsQL=`YrlqHC zXh0ch+oBf^utiP|_Fn#ByZ0Z;s4PE!sk$mZQW*8^wDI>DZSC@I?f zytr0(;e6FembD=m`~TMDq@>{;$YsjL>Po6=%EX!mFI^Y?7%ac;@?OLFn#Kp4;G5q7 zsWq|YjjRB9a7ZO3#1xdY&Gb~1l~n$XNpPREk*65|d~Zrn0=6#RL6M=pPzVl+%BZ`3 z_w2E~dk)1X#7S=x5LeN)@j7zmY|pDtp9cy;tZ_VqGt2AGXA-2f-w;XQ|Ffo|APd^6 zq_mQ(p}v-my1Y7xNi+DSr>&)~qooEtemzqw2Q>Wg@-~+f5do}V81(>JW&#=UdnrtPve;l?}Jfl&4xBB|=|e_gzN^q5BZW}KR z+nA=VfxfmbkSeC;!1HQQJo17rX=m%N*Lb5=!VAfMZtx!Y==|VXSi0>$eIbp1$2R-t zzx?#`m)8%AO}6v#@xUBA!jgK#{y%dHveHs&p%?4(LyZ*`|d^FC)C*4=6lgfMk zQI%FzQB?QLe)#O=!sAYIHqd#6v#tw_`s+7jSHFDzrD$6+pP-V8 zairo_At`O^I6E%cho_2(|;@~(tA!$G{xJR17$oscp%W^5)>lFEwOI)<8BhN3XHj#o%tQCUSrU71o@ zrLL|j3|BVuGSCk=7@%68djTbJ~m8NWkF{2^K@Be2^ zm36>LNy@1zXyIi-L-+eY(li18Hnz4kH`2AVFq0t0cLiyHnyYZkxEJ z9m5=XxP;{N9piFMIFtD{aPIW({M?N4K;1cXu32XUO_T2Kv467?RV8wmN=j*{h(Itw zU72+PVA9~0kshQwjCD+m^`v-``8JxkyMvP-84-Vo%G!@1+sv)%?qko#O_SAih|4=6 zxHFmW2i|SG;T6~FYcAekz>?tr5mz1mcfM3qUl~FrmdoBL?VNOmkup#9H5q_$PkdW3L-QZJmNkP zhmL2ark^USznzMmmY3${8tf;=Nte-e*p*%J>^vW5D&HC&PSLRX`(*SC2XLbG{x?&~ z3gk6cN>WxuSw&e#UB{5j6bH$BG<0!VYoYiJ`E5!3P?*OoF=Rc#~r0Dys3PvxVD*B%d(~kHNYNLI){BjmE?1h3K9y+3hHWb!-yE# z%1Z}`8Vaz$X!I@r3AIS*J5|3-W4pckt~{NCUSt3$W=d@Xi_T{v9PMuE6QPN@;AxlanWNFiL z+DNn_Bud)%kg}Cxj6{@H*}{BgFk=`qcKzP>DaL31_`)J~HlVX=$v> zT4iWp$>Eq9afnNBF*eyaIapwZwzS6VY-i`>&YjEvn7sbFU8yMnGo)or(@y1N9L*|> zPxH4`)izlemDup=k2i1Me(d`xdaA5qq>`wL4A3~pN!8fcIxxx`8)|E4X>fOzr!Gd= z#gN4&1FwzZ6u3Ia%m|S;YbP9#+RmBpIp0#5AulhB;zhEOGFFKlZ(cmO|Mb<%qpO`v zm>Mj%9SOC+{{GirZ{EK9+}GwV#{it-aOgF-w-6L=xdfw zuG>=fnb&^^l|&!Nj95NoZd>x|D z!S-y+?%XRqZS$mj-auUYrLXVf+m|nTua`zGb1=|VGj))XLhV0o*LmYY{y-doP@5&* zoy)EC$Jo-3XLTLa_r@DGnEbJMVqs%Q%^YoEVPI-uguW~^5P)R4xNylbq%-#4vcKq5 ziStXeVLNHG%mgJhy}1jdU5T9NBT64hzQ2%PoTZ1E zlo&XY-HcHC^{@M0mZ3IQ{hAggx@P94Tt1gg*5PJ~DlE2*4G)5OCoq{21D!E}!JF#< zW+>O(%+!K|-IXcn`BvKcvJgv6(llPYXyWANFCS3QMDl%YVP2`zM8J&)obFW|{xn@S z>)&P@8R?N>%g|WU*vQz@fzKsy=87u%T8KvK=;|358nd}0d)z1_?0K^w^#r3gbhd@cAt0ll)LukYD6X@W$Z%X*5txVz+BF6(W^nm04y+ zx?0$1m}u&;jI3>VR+G?gOqDry&@piB9TAqB?P9BnL^{eEh!kKp^YRPuTDKHD3@#hS zaiyX1(J6dWw(vgmMkBp+8r76o0p-xK{q$F5h zm?1PWj%hT@hKuRU#?i^%mTv^_kpKgE>eT7fOeM`3TIw@d=H`}cYXeJE0h*#?;o!SQ zUP+dZO4fqDQ6K6H%lA)XDyc|8Po!Aae5f@{vRGcw~^7?2RzIQS+4Abh9Uw$^;Ut(rozk{oV7 zX^I@Qu4a69YQsbrCSzLgyP$9Q^|aZjDk+cXo`SK4{OEDf6J+GD_$M{!r_Wm8jKlus!W+-=YXuLY_`=*rs6&&MHMwYE7#>Axm(7os>{kqNt^oYNsOC2ZTy63 z>$ITqk;m>ZhCdM}%50QRH8r-5P1~e~WJWWl61^OyE;j=kW&~u_HSCdz)(ZQr+168; z%xK0iCW9fsJHgy##$Of`d$5AiJkWkBY_|5e5!A@xQpzlkxH=O=K$#8gIEL%!#5rcm z<Bnr&o+4ekAdcxM8R3dSO&>j*>Gb?MJrOGs09*3okU9Gy7_Ed)I zWTpa>NwPGVxULCLI#ya%d5eyo8B-TY38@f{mE`)a`OlAHOPoIs!VLQUM`>%Hq?BhGQ zo2jcXl^8~=!_!JGpdiJs5HCje4R0zfIdSCpOkgQr6y{lN#_1f+ws6Kq(i=ium|>%# z3&uK5GPpJ{!e$l>1#QP=F1Y&~t`3u7y#V6wwd+^s~(eKv^BZ( zO6OA){(SR}sFps~<>VeaRHcU)rOHK|-{Jt2YMzUeL7XFk<%-bF^(?M|J{QXd8y?r% z#)e~QX^P^^2%qrn9c=Bim=@4edF&v!@aeoeWFCQIB`I@(W>Hwiy@rsd(! zsQL5zpI-p>sqRGf;YbGrp;jo>;z$%=pdpcyV;nmohiv1GmOOJkL%tQu%-qrnBz(R- zrgy9>c@BJR3-s`IY~5XE!L-1w;Ys^)YJDf`PP6e}&1X4l$eRj|U2N@s^4sf|FaLrn zQ_v^zr9La;FfT>KOXVUA!dWLWLf*Prk$nYh9GoQIYAv)teA7_h#LU79D+xO&B%wjD z<<4@P<*3J0V$PT+jQuY6*{~xuB|g^K=Aen|Zg+d0>kK*DnBx~(9^gRg)gOO;`TX(2 zd!(SaoN?$7j%9}_*WIR?Mv=U@H(Rbdb8D$P3jx^LS!kkTIm?KJ#ibFJ%w*MPfmj}! z%ja9d>lm5~kln}A3)JtZ_+24!i}yxsh>37=TDwG<;SqnL_RfPBFG+nwOj@6a#@+r*N?bJD~r ziJ<{`9Wtx>Z{D&kG-OA_&IOw<9A3ufuC0&%XQ%l>#if_lUb>!JuLk?qWg_3bl6{1I} z%<=G8yae?w0)m30c5P29>~8fmcb#LYz;q7Un^)i7`}~)mpCbtM4)WgNA3mN-ONq6H z0Bm$|D@Xx22}hDa!kD32Cn0TZ+nsEXxvbH8eB>(PAu}P_>~lC=q*oz3D?_H*>?Ix^ z%U7=p3XX``?6cBmjg^s&gD#U{;u(}&bh+c{i=W|3AV)`pWNDzfG*-@dQx${mul2$6-~{`^eFlB)p=`1AZW%!ia{tkw>&@#Ew6#HCkM zoI-BQsxj54t19C4rs=ZGP=uLcOIui40tiB7HYRW@TMdRlX@nvJHEu20dX%A@k)g4f z?sOG}iE0aZyoLU8$I35)Q9)gkz7B3%!=(%R-OQc6!jg_qqYsOS`L1!LX3{9{lpe)9ckU7r|$*dry(_~c23$VY`~Ltbtw&wI1g?3KZ3 zrL|3{C;^_uPmgmwo!nfkXZuI*j^CZ)EqXv?6sk0i-F6qJ>^G1E8Rw-pGTFu!ESy{# z7~mvKM@Qe#fMsHe-4D-(ZEd5g#8BL=q%JQbr|Y`HbE^w)o=?p2^Nk%OGyCITU;Ucr zxnSO6pQ!j~L2T^)7zv1pB$sa5os^Q6Q;JjeJTM8fj$!cfGBeUQFvd>X(u!?rY+`C+ zVgwj$?M+NAY%R4^qfnn&L4Ja)%mmi@j0E4s%dE7_?B;rh?>(GXM3u;tR+i=M3RoQw zowz3^Dmo&3PY^UPGbYvFLbBD{*Rh(tL0A>wN!++q&%tgJy6_$R}2>O%fh`0dr(9yVn;|^ z9P;gsw7&wn2f7r>3oRw3c;M#kEsdv=0~Ycv%}mWLaLjAPLr4pGgGd%SACCo|tNp{H zVv>@QcSmjY1>t%YazW1XSQQW&f291SA`sX!Pe%^@>N zGfB3J+|z{pN3}||wcNN|bs|1!nX`i}&x+#MLi6S924=(j#p|{&pSx(8*W!h4_71K- zl)xXEE}h&LukhI%zH5J0#igb@T|`L)t03g~C4bMZg!pJu;3jxI4D;ch1AzrZr^f3T zDgcIo5Z{}x)t<^DN+RS?aYh0+WGlkaGA2$tdox6tSsd3ze$h}8*+VHyltdv!NmN?h zaHH++Jwk0J#W5Z{dX}4zk`P0Mhld7*$BYJNno|QO2W^IMcWya=djvGpl)3R^-I=1y z1AC)GgEoLQ?zv=vs}pj$@f@5Nd#!_@Yni9#0+d5rxYTQ%P)T$wzw%st(`~RD(1xHQ z-$d}>B+=^4})y4`-46j^wLM2lcD<52IH zl7yI@&=Lo4+7!A8SmK=1paN0Nqre~oZ4k2w-kA-u#zQ65^kZGksfxlA$BrCIOHEFQ z-x))sL_tJJ1St_Hf|0TDNry7?%BwCkG&Z*q-3=ky3+p<7s6F=4zw(p!B<`fbLPEBN zr9krlFmSu5=COp;MdTbX3W#k~hi+RdxL?=mFPyJBRapiKZvF{Ki6CG~OaKcREX0H9 znI|A6sz#BKn|Io~dZ_!uAya`U0YEZ@w*BOJUSdKVg(7_0HwJIjO%*ZZoCc|i(BII4 zgf&WRYe5Qk$R4^oA+I5a_E%BL4pO4?L`qarTvS9AGKv6t$1^(1qmob9~l}J zymiY)Qk+D-oRI7YjT8vN76j+Lm8^%sDF$JT$VMRSfuf^F29z$0EMy*qyOM%4cOkJ6 zc|<*?9!-K7gcL7yc;JEW{`lwo#KgErDpC-#c?;!EszIGc?;>PF2B08IY5`Fhq(-v# zCK3x^fxp;eLsTwsXHf0$1DuROIHrahkPlQ|0jr*92k>vG#sOIH@gF{T+?$sW8xWflF!__aMC7@ioJ&JfCdKX*+^?%x-f$9c|EYSC~7WVcQ>_z&(*og4W{#(K{;e@t?c1IDSUyCRyG&CN7gt(7_ z2&j|;g~h#YLC++jND!(L&`*WW50gOXheZ%}Kz#EkvR4TbA0n8+BauJ7kMk1}_QXYn zhHeVjwiz<1F{(9W8A?{YL`8H*6qSZLhJpSDAyIofh1yBfT}j9nh3$u1;KA|UAITFW z0KlLpXg8t8gA_}7BzOq*2n-@>qTIcE;&%x`w`|?IIY6M!poNhKZR;E{>m_VzbZA^P7gjj$fx-Yz{i3AD*X%D&#+72%W z_k(x?3O4c~cq9eo5|NS+^$?zgbYRkYPx9iBX*7IuNHB&MbYL+?H3}HqVl;<4dn96W z4Q+WtW82A_ZC6_0e)y*+`u=dt3}n=y9!Wh$;T8h;^fA;SXhs>lE%L7L9W}^=UW31t zhN$UD-p<{z(P7)eLN;yMY?LaZs#zx_UN*1NGdT@#EFR?3qM#UbRV9Qm%F7^}J&7`;XU|G<4+}nBP~C>_Zo^ z>n4a_iGUjOG#b3El@t>bYPcX7!?4+s89{CI^Ywvl+Q&mWS%f*=p7l<oHYY2}^N(le2(m4*a*9ie3X3W#$}2GOSDdUYDK0H3 zC@d_-JdD39tE@PE=1euFXYxN4=j;s+@k8ZC@3pI!tAU=Tz=+O2l8(wK&>7`rLl%{B z44R-5IjCk`KovTXV%C_>Q9z))^kjKaaZy3;@e_q5g@xs1#bw3$dHF>p5d2``FGfHI z(|=x0US3gNQF#fftdx}IrSIPz>L1|eyLRPLHZt8SEWJ;rQ;ZeZ5)%@86L`@?oO6}B zB!)@-Qe>?Nswr6a7h9;QMoolM=c>+BS7S+oN(rcCfIk&wC(BC9OG{9EsG_1QA2K7z zjq-C(r0=DI0{z#oTeXCR{fLCgP0ZaLf_7=FSBQUiVI(J$F4V$ZBHeRe3XoE7L4Q_v@ zJ*sUYph(S0{0;=s!pWJ`=Ywc^0x7J7XTr?W)_F4(2^!Y>ELBSrk(-M?L)MbSaH62; zAWJ;Vw~_=6V+9%qYp~(C84+U%+D757olscADym~7#>M^x0M)@D>nIfUx=TKY>KvvY z+YlJww@3-1l4TH7NdN@5jJzX)2O0)IL8~YV7z6DTC}uv$sWd$N;$q1^^#+bmndFa~zW{A`y5IN(kQ$S--Tmq3|OF>Ox5T)Y$qB4@lCsdgutiF|2P8TpotiF?DqWQ6C^MiBh~H$)4y|OF9d}qEQeQjYRbyVJ#u6 z335!NEmzmlH8jBqsHF{(p3QchGjHi~FYf^4%nU=y1LSm0OhskkwEc$;9YHa#Y!Ju@ zExw>QFVW(z$mn6X-|u?!8)0>6mFbQEO6i9=ywQE_o`3AtcYIWBZ=e4?aCNKvZf$PkFc2%7! zFF2YIx@NAGrsDXKVxpoV1DEJMbANKVLhK(4nYB0+y;doX_ z)fEyTyxw-F`^nGG0Q2nE7r*}Y>NPH8Kl<|d)0e*g{%T*&HJZ*CCpkiV-~on>keSSy zy)ODt&gI__W*hYDmKT?$#*~rz`T(LMWTzO-@r^oA@#mmp3jywW@l0o`P8>V3zXb@s z@f_di{XfzJ-`969(9u$Niu^b{0DKVmlZ{<~f157+zIsnrzTuRKQX|Ch0N?CyqYb}n z>zcV--AS^eC58t7!S3KSZfp%j>5*dJ+22K*{k?~GZ(ZrgnleFB{5$*(+VG#`rbYQL zV9#VoNqmP-pa1<-nzeV^ayuPm>EYkux6o$))027ocC2wUm^6OGkl;T#Q+P1aXO7Vn zSu+299e?Pv|9N$BdW@f|$yC{)vH#`SlJr=AH`8eohQ|Ka=Sz>o2DlFy{-3p_8M`*j zHJ>gwH1>aPDm@w(INw5TqU4bHU*EIx%=pcVI5Xsj2><=r%!I8TTutVf@9d{<{Ofyn z_IPp#20*2;-|(Z-k@l9Z>+QLmeLGgqGEkKs8vGA+d1;ZpbBw3Re8Z=6{nPj9a>3!) z0Cd0!-{I3Y{sO-!BW~jYw)(`O!GB+0a%@k~V$MwYp}~K5sf=_$YYl}V!mlsOO4zoP ztH~G|{PzuIS&7^K@8W;@L1@3tP__T3t7XU0{zHcUS%|-E$nd}XSb97$cqsA9jwb~# z<7o}_KkWab_t7~1enI;u1$z$Q%LwBSwBp~W$R_yOOc4>pU#1v2`$o};-}ml9X7b%08z{UiRXYUt=KNTKun6a0bx_YJ?1uGK$yo524%{(Qr) zqYwYXr_0B_jlW;p{}O%ppKhP}d;A{){Juu4Kfca?LxJBk#Q4qM&A&ro{{Vjf_&d=5 zY4pFo>*d+W!Ts~!K>wps=)}Jbe2l+3ie&zgRyA<+rc&zZ>wkUsN>3z*uCV(Xe=U`A zg+~03KV+tat+fA!KWj}yGL7RODU6W>eBFWe*QXu7?_qM7m%Xl%@cpak^H)bC(Q1F+ zm%{B{4tfM%Vw|#`!>S!gwKTqeKs9Y&^$lOw-YYzjX8hjZReU{Vq5Vp__AA5a#(x5Q zec+2oj8)RHTN##ck>2)y61@65{1u^e<9B_<7l-|}z&}T?{k0*hX9?S1TnhNhLlUZK z#s82Q_7D83{|$P{k@pUr8ta^Sso5;+N5g|FY@?3BZ4~U)y$h@ct+N zaohd(zt~=u`)~L>&)}5a|NgfB@f|wK34VY77e`tW?$T|$OK5EW_V4h;#xQ1b7Hx_t zqJ#Z1&_ZqH6%-&LNI8s0zuh=`4noovWozTpsU_+Ng$RFO^A zUu6A*0GPC*Cfj|*USuzz=X!PNTm|-Dh`$khQHe2fQ}lT*5wzj|UVpwkbI%qJYZCt! zK_>2TjOi>+;759_N5ZFub2xwwT)>`z^C!Fj3CZzFGxXNdX8-Gkv&Dy^d|gea$r5}~ zvEgInCaEudPnZ3FT&XTR5V6L|aME~!j|Uhjt*FLDqD^|PXO~XrrG~9^&{r8J#D@z? zG1Lury`u-d@6)U9f|QUI_Ik=+@x{j|s9VfV=z9M0_1kwJJ`DOu@Ub#E^gH`S#HBC> zFWwk`;>-`N-4A=;4t7JlD#q~Z#G2gy{zZV#)UsI?vag`}hnAk+p9cL!ygP;SZ#T1P zas%&QTuM=!w><1XNzdGRAme_${ioWjq~Mi& zeU*Ou(f-8#&?LHIARjN~?x)txF`oQAzGq0{`L7Q+$e$k@3J%ABfH3Xf@oC!we7Rbj zv3v7Ew)(%}2k)h84}t^8%)MKeSkD|Hd<0;H1OSbp!oMyIz}jejhcDa%4u03~_v~9` z!UM=}_+$@E_Mmj(ck~A!wFc}L#vrf0?0m|EH03RA0B^?rjJWbd8?*hKS+sUBcew;xd2E8gg z>FglSyWei#buvHg8$Q85kmgr#Jjr_2A^DhPhR{|y@{t)y+UIB@6T zygIttzp+B-|HA$!COHA#w>mT_tGIzK{A(4t2L{F;q4!zN4~RaPU-1K7_>C39@t4?- z%`a2a+9N1FqvUkMhi^S}aDTXo0BFQ&;rNfi51Wr=+x8wSuDDKD45q0J0XQ$={7dk) zY?f}>eXy{TF8F(A%RzuxZcEl**!&RzW&oA(^We7mx4VDttf?wF8jk>&aQ!Wg?r-Ak zwc+HqUl`mUKHRLS!SEC9KhHvq%s*nIWK?yyb3Ie(asEFWt7}db9Zd*cg#ZM>A2wpF z!Zbr0ehe-6FB__>PZnpTP<{)nHA(yv9Z+&SbGn{>*x)vM|J{GNSW{hDl$9D8=;6Sc zB8-0^{x=$>bCd&Uz<+eU=1gV5v6RTbWsWB5jQ;qSsQ3uUak5ft1|9!%_t_d0l+Q?_ zHY}ZGI#X%!4a(;?e+u&!fBf|6 ze<3FJ{PGz%esB?P?%Wj@w;KgLQBQO) usqu*m>FvnVlE?k~VGrV2ap7sx@w7*eqPXy( + +#include "defc.h" +#include "protos_macdriver.h" + +#define MAX_STATUS_LINES 7 +#define X_LINE_LENGTH 88 +#define MAX_MAC_ARGS 128 + +WindowRef g_main_window; +int g_quit_seen = 0; +EventHandlerUPP g_quit_handler_UPP; +EventHandlerUPP g_dummy_event_handler_UPP; +RgnHandle g_event_rgnhandle = 0; +int g_ignore_next_click = 0; +int g_mainwin_active = 0; +GDHandle g_gdhandle = 0; +int g_mac_mouse_x = 0; +int g_mac_mouse_y = 0; + +FMFontFamily g_status_font_family; + +extern Kimage g_mainwin_kimage; + +int g_mac_argc = 0; +char *g_mac_argv[MAX_MAC_ARGS]; +word32 g_mac_shift_control_state = 0; +extern char g_argv0_path[]; + +extern word32 g_red_mask; +extern word32 g_green_mask; +extern word32 g_blue_mask; +extern int g_red_left_shift; +extern int g_green_left_shift; +extern int g_blue_left_shift; +extern int g_red_right_shift; +extern int g_green_right_shift; +extern int g_blue_right_shift; + +int g_use_shmem = 0; + +extern int Verbose; + +extern int g_warp_pointer; +extern int g_screen_depth; +extern int g_force_depth; +int g_screen_mdepth = 0; + +extern int g_send_sound_to_file; + +extern int g_quit_sim_now; +extern int g_config_control_panel; + +int g_auto_repeat_on = -1; +int g_x_shift_control_state = 0; + + +extern int Max_color_size; + +extern word32 g_palette_8to1624[256]; +extern word32 g_a2palette_8to1624[256]; + +int g_alt_left_up = 1; +int g_alt_right_up = 1; + +extern word32 g_full_refresh_needed; + +extern int g_border_sides_refresh_needed; +extern int g_border_special_refresh_needed; +extern int g_status_refresh_needed; + +extern int g_lores_colors[]; + +extern int g_a2vid_palette; + +extern int g_installed_full_superhires_colormap; + +extern int g_screen_redraw_skip_amt; + +extern word32 g_a2_screen_buffer_changed; +extern char *g_status_ptrs[MAX_STATUS_LINES]; +extern const char g_kegs_version_str[]; + +#if 0 +char g_printf_buf[4096]; +int g_debug_file_fd = -1; + +/* HACK to debug startup issues when launched from Finder */ +int +printf(const char *fmt, ...) +{ + va_list ap; + int ret; + + va_start(ap, fmt); + ret = vsnprintf(g_printf_buf, 4090, fmt, ap); + + if(g_debug_file_fd < 0) { + g_debug_file_fd = open("/tmp/kegs.out", + O_CREAT | O_WRONLY | O_TRUNC, 0x1b6); + fprintf(stdout, "g_debug_file_fd = %d, %d\n", g_debug_file_fd, + errno); + } + write(1, g_printf_buf, strlen(g_printf_buf)); + write(g_debug_file_fd, g_printf_buf, strlen(g_printf_buf)); + + va_end(ap); + + return ret; +} +#endif + +pascal OSStatus +quit_event_handler(EventHandlerCallRef call_ref, EventRef event, void *ignore) +{ + OSStatus err; + + err = CallNextEventHandler(call_ref, event); + if(err == noErr) { + g_quit_seen = 1; + } + return err; +} + +void +show_alert(const char *str1, const char *str2, const char *str3, int num) +{ + char buf[256]; + DialogRef alert; + DialogItemIndex out_item_hit; + CFStringRef cfstrref; + + if(num != 0) { + snprintf(buf, 250, "%s%s%s: %d", str1, str2, str3, num); + } else { + snprintf(buf, 250, "%s%s%s", str1, str2, str3); + } + + cfstrref = CFStringCreateWithCString(NULL, buf, + kCFStringEncodingMacRoman); + + CreateStandardAlert(kAlertStopAlert, cfstrref, CFSTR("Click OK"), + NULL, &alert); + RunStandardAlert(alert, NULL, &out_item_hit); +} + + + +pascal OSStatus +my_cmd_handler( EventHandlerCallRef handlerRef, EventRef event, void *userdata) +{ + OSStatus osresult; + HICommand command; + word32 command_id; + + osresult = eventNotHandledErr; + + GetEventParameter(event, kEventParamDirectObject, typeHICommand, NULL, + sizeof(HICommand), NULL, &command); + + command_id = (word32)command.commandID; + switch(command_id) { + case 'Kbep': + SysBeep(10); + osresult = noErr; + break; + case 'abou': + show_alert("KEGSMAC v", g_kegs_version_str, + ", Copyright 2004 Kent Dickey\n" + "Latest version at http://kegs.sourceforge.net/\n", 0); + osresult = noErr; + break; + case 'KCFG': + g_config_control_panel = !g_config_control_panel; + osresult = noErr; + break; + case 'quit': + break; + case 'swin': + /* not sure what this is, but Panther sends it */ + break; + default: + printf("commandID %08x unknown\n", command_id); + SysBeep(90); + break; + } + return osresult; +} + +int g_upd_count = 0; + +void +update_window(void) +{ + SetPortWindowPort(g_main_window); + + PenNormal(); + + g_full_refresh_needed = -1; + g_a2_screen_buffer_changed = -1; + g_status_refresh_needed = 1; + g_border_sides_refresh_needed = 1; + g_border_special_refresh_needed = 1; + + g_upd_count++; + if(g_upd_count > 250) { + g_upd_count = 0; + } + +} + +struct char_int_un { + union { + char c[4]; + UInt32 uint; + } u; + char c2[2]; +}; + +typedef struct char_int_un Char_int; + +int g_event_count = 0; + +void +show_event(UInt32 event_class, UInt32 event_kind, int handled) +{ + Char_int char_int; + + if(handled == 0 && event_class != 'cgs ') { + char_int.c2[0] = 0; + char_int.u.uint = event_class; + printf("Event %d: %08x = %s, %d\n", g_event_count, + (int)event_class, &(char_int.u.c[0]), (int)event_kind); + } + g_event_count++; +} + +pascal OSStatus +my_win_handler(EventHandlerCallRef handlerRef, EventRef event, void *userdata) +{ + OSStatus os_result; + UInt32 event_kind; + + os_result = eventNotHandledErr; + + // SysBeep(1); + + event_kind = GetEventKind(event); + // show_alert("win handler", event_kind); + if(event_kind == kEventWindowDrawContent) { + update_window(); + } if(event_kind == kEventWindowClose) { + g_quit_sim_now = 1; + g_quit_seen = 1; + my_exit(0); + } else { + show_event(GetEventClass(event), event_kind, 0); + update_window(); + } + + return os_result; +} + + +pascal OSStatus +dummy_event_handler(EventHandlerCallRef call_ref, EventRef in_event, + void *ignore) +{ + OSStatus err; + EventHandlerRef installed_handler; + EventTypeSpec event_spec = { kEventClassApplication, kEventAppQuit }; + + // From http://developer.apple.com/qa/qa2001/qa1061.html + // Trick to move main event queue to use ReceiveNextEvent in an event + // handler called by RunApplicationEventLoop + + err = InstallApplicationEventHandler(g_quit_handler_UPP, 1, &event_spec, + NULL, &installed_handler); + + kegsmain(g_mac_argc, g_mac_argv); + + return noErr; +} + +void +mac_update_modifiers(word32 state) +{ + word32 state_xor; + int is_up; + + state = state & (cmdKey | shiftKey | alphaLock | optionKey | + controlKey); + state_xor = g_mac_shift_control_state ^ state; + is_up = 0; + if(state_xor & controlKey) { + is_up = ((state & controlKey) == 0); + adb_physical_key_update(0x36, is_up); + } + if(state_xor & alphaLock) { + is_up = ((state & alphaLock) == 0); + adb_physical_key_update(0x39, is_up); + } + if(state_xor & shiftKey) { + is_up = ((state & shiftKey) == 0); + adb_physical_key_update(0x38, is_up); + } + if(state_xor & cmdKey) { + is_up = ((state & cmdKey) == 0); + adb_physical_key_update(0x37, is_up); + } + if(state_xor & optionKey) { + is_up = ((state & optionKey) == 0); + adb_physical_key_update(0x3a, is_up); + } + + g_mac_shift_control_state = state; +} + +void +mac_warp_mouse() +{ + Rect port_rect; + Point win_origin_pt; + CGPoint cgpoint; + CGDisplayErr cg_err; + + GetPortBounds(GetWindowPort(g_main_window), &port_rect); + SetPt(&win_origin_pt, port_rect.left, port_rect.top); + LocalToGlobal(&win_origin_pt); + + cgpoint = CGPointMake( (float)(win_origin_pt.h + X_A2_WINDOW_WIDTH/2), + (float)(win_origin_pt.v + X_A2_WINDOW_HEIGHT/2)); + cg_err = CGDisplayMoveCursorToPoint(kCGDirectMainDisplay, cgpoint); +} + +void +check_input_events() +{ + OSStatus err; + EventTargetRef target; + EventRef event; + UInt32 event_class, event_kind; + byte mac_keycode; + UInt32 keycode; + UInt32 modifiers; + Point mouse_point, mouse_delta_point; + WindowRef window_ref; + int button, button_state; + EventMouseButton mouse_button; + int handled; + int mouse_events; + int is_up; + int in_win; + int ignore; + + if(g_quit_seen) { + exit(0); + } + + SetPortWindowPort(g_main_window); + + mouse_events = 0; + target = GetEventDispatcherTarget(); + while(1) { + err = ReceiveNextEvent(0, NULL, kEventDurationNoWait, + true, &event); + + if(err == eventLoopTimedOutErr) { + break; + } + if(err != noErr) { + printf("err: %d\n", (int)err); + break; + } + + event_class = GetEventClass(event); + event_kind = GetEventKind(event); + handled = 0; + switch(event_class) { + case kEventClassKeyboard: + handled = 1; + keycode = 0; + modifiers = 0; + GetEventParameter(event, kEventParamKeyMacCharCodes, + typeChar, NULL, sizeof(byte), NULL, + &mac_keycode); + GetEventParameter(event, kEventParamKeyCode, + typeUInt32, NULL, sizeof(UInt32), NULL, + &keycode); + GetEventParameter(event, kEventParamKeyModifiers, + typeUInt32, NULL, sizeof(UInt32), NULL, + &modifiers); + + mac_update_modifiers((word32)modifiers); + + // Key up/down event + is_up = -1; + switch(event_kind) { + case kEventRawKeyDown: + is_up = 0; + //printf("key down: %02x, %08x\n", + // (int)mac_keycode, (int)keycode); + break; + case kEventRawKeyUp: + is_up = 1; + //printf("key up: %02x, %08x\n", + // (int)mac_keycode, (int)keycode); + break; + case kEventRawKeyModifiersChanged: + is_up = -1; + //printf("key xxx: %08x\n", (int)modifiers); + break; + } + if(is_up >= 0) { + adb_physical_key_update((int)keycode, is_up); + } + break; + case kEventClassMouse: + handled = 2; + mouse_events++; + GetEventParameter(event, kEventParamMouseLocation, + typeQDPoint, NULL, sizeof(Point), NULL, + &mouse_point); + GetWindowRegion(g_main_window, kWindowContentRgn, + g_event_rgnhandle); + in_win = PtInRgn(mouse_point, g_event_rgnhandle); + // in_win = 1 if it was in the contect region of window + err = GetEventParameter(event, kEventParamMouseDelta, + typeQDPoint, NULL, sizeof(Point), NULL, + &mouse_delta_point); + button = 0; + button_state = -1; + switch(event_kind) { + case kEventMouseDown: + button_state = 7; + handled = 3; + break; + case kEventMouseUp: + button_state = 0; + handled = 3; + break; + } + if(button_state >= 0) { + GetEventParameter(event, kEventParamMouseButton, + typeMouseButton, NULL, + sizeof(EventMouseButton), NULL, + &mouse_button); + button = mouse_button; + if(button > 1) { + button = 4 - button; + button = 1 << button; + } + ignore = (button_state != 0) && + (!in_win || g_ignore_next_click); + ignore = ignore || !g_mainwin_active; + if(ignore) { + // Outside of A2 window, ignore clicks + button = 0; + } + if(button_state == 0) { + g_ignore_next_click = 0; + } + } + + GlobalToLocal(&mouse_point); + + if(g_warp_pointer) { + if(err == 0) { + g_mac_mouse_x += mouse_delta_point.h; + g_mac_mouse_y += mouse_delta_point.v; + } + mac_warp_mouse(); + } else { + g_mac_mouse_x = mouse_point.h -BASE_MARGIN_LEFT; + g_mac_mouse_y = mouse_point.v -BASE_MARGIN_TOP; + } + +#if 0 + printf("Mouse %d at: %d,%d button:%d, button_st:%d\n", + mouse_events, g_mac_mouse_x, g_mac_mouse_y, + button, button_state); + printf("Mouse deltas: err:%d, %d,%d\n", (int)err, + mouse_delta_point.h, mouse_delta_point.v); +#endif + + update_mouse(g_mac_mouse_x, g_mac_mouse_y, + button_state, button & 7); + if(g_warp_pointer) { + g_mac_mouse_x = A2_WINDOW_WIDTH/2; + g_mac_mouse_y = A2_WINDOW_HEIGHT/2; + update_mouse(g_mac_mouse_x, g_mac_mouse_y,0,-1); + } + break; + case kEventClassApplication: + switch(event_kind) { + case kEventAppActivated: + handled = 1; + g_mainwin_active = 1; + window_ref = 0; + GetEventParameter(event, kEventParamWindowRef, + typeWindowRef, NULL, sizeof(WindowRef), + NULL, &window_ref); + if(window_ref == g_main_window) { + g_ignore_next_click = 1; + } + break; + case kEventAppDeactivated: + handled = 1; + g_mainwin_active = 0; + g_ignore_next_click = 1; + break; + } + break; + } + show_event(event_class, event_kind, handled); + if(handled != 1) { + (void)SendEventToEventTarget(event, target); + } + ReleaseEvent(event); + } + + return; +} + +void +temp_run_application_event_loop(void) +{ + OSStatus err; + EventRef dummy_event; + EventHandlerRef install_handler; + EventTypeSpec event_spec = { 'KWIN', 'KWIN' }; + + // Create UPP for dummy_event_handler and for quit_event_handler + err = noErr; + dummy_event = 0; + + g_dummy_event_handler_UPP = NewEventHandlerUPP(dummy_event_handler); + g_quit_handler_UPP = NewEventHandlerUPP(quit_event_handler); + if((g_dummy_event_handler_UPP == 0) || (g_quit_handler_UPP == 0)) { + err = memFullErr; + } + + if(err == noErr) { + err = InstallApplicationEventHandler(g_dummy_event_handler_UPP, + 1, &event_spec, 0, &install_handler); + if(err == noErr) { + err = MacCreateEvent(NULL, 'KWIN', 'KWIN', + GetCurrentEventTime(), kEventAttributeNone, + &dummy_event); + if(err == noErr) { + err = PostEventToQueue(GetMainEventQueue(), + dummy_event, kEventPriorityHigh); + } + if(err == noErr) { + RunApplicationEventLoop(); + } + + (void)RemoveEventHandler(install_handler); + } + } + + if(dummy_event != NULL) { + ReleaseEvent(dummy_event); + } +} + +int +main(int argc, char* argv[]) +{ + ProcessSerialNumber my_psn; + IBNibRef nibRef; + EventHandlerUPP handlerUPP; + EventTypeSpec cmd_event[3]; +#if 0 + MenuBarHandle mbar_handle; + MenuRef menu_ref; +#endif + Rect win_rect; + OSStatus err; + char *argptr; + int slash_cnt; + int i; + + /* Prepare argv0 */ + slash_cnt = 0; + argptr = argv[0]; + for(i = strlen(argptr); i >= 0; i--) { + if(argptr[i] == '/') { + slash_cnt++; + if(slash_cnt == 3) { + strncpy(&(g_argv0_path[0]), argptr, i); + g_argv0_path[i] = 0; + } + } + } + + printf("g_argv0_path is %s\n", g_argv0_path); + + g_mac_argv[0] = argv[0]; + g_mac_argc = 1; + i = 1; + while((i < argc) && (g_mac_argc < MAX_MAC_ARGS)) { + if(!strncmp(argv[i], "-psn", 4)) { + /* skip this argument */ + } else { + g_mac_argv[g_mac_argc++] = argv[i]; + } + i++; + } + + InitCursor(); + g_event_rgnhandle = NewRgn(); + g_status_font_family = FMGetFontFamilyFromName("\pCourier"); + SetRect(&win_rect, 0, 0, X_A2_WINDOW_WIDTH, X_A2_WINDOW_HEIGHT + + MAX_STATUS_LINES*16 + 8); + OffsetRect(&win_rect, 64, 50); + + // Create a Nib reference passing the name of the nib file + // CreateNibReference only searches into the application bundle. + err = CreateNibReference(CFSTR("main"), &nibRef); + require_noerr( err, CantGetNibRef ); + + // Once the nib reference is created, set the menu bar. + err = SetMenuBarFromNib(nibRef, CFSTR("MenuBar")); + require_noerr( err, CantSetMenuBar ); + + +#if 0 + mbar_handle = GetMenuBar(); + SetMenuBar(mbar_handle); + printf("mbar_handle: %p\n", mbar_handle); + + menu_ref = NewMenu(1, "\pTest"); + printf("menu_ref: %p\n", menu_ref); + AppendMenu(menu_ref, "\pTest item 1"); + InsertMenu(menu_ref, 0); +// ShowMenuBar(); // Don't call ShowMenuBar: it prevents menubar update! + DrawMenuBar(); + InvalMenuBar(); +#endif + + err = CreateNewWindow(kDocumentWindowClass, + kWindowStandardDocumentAttributes | + kWindowStandardHandlerAttribute, + &win_rect, &g_main_window); + + //printf("CreateNewWindow ret: %d, g_main_window: %p\n", (int)err, + // g_main_window); + + err = SetWindowTitleWithCFString(g_main_window, CFSTR("KEGSMAC")); + + // We don't need the nib reference anymore. + DisposeNibReference(nibRef); + + SysBeep(120); + + handlerUPP = NewEventHandlerUPP( my_cmd_handler ); + + cmd_event[0].eventClass = kEventClassCommand; + cmd_event[0].eventKind = kEventProcessCommand; + InstallWindowEventHandler(g_main_window, handlerUPP, 1, &cmd_event[0], + (void *)g_main_window, NULL); + + handlerUPP = NewEventHandlerUPP(my_win_handler); + cmd_event[0].eventClass = kEventClassWindow; + cmd_event[0].eventKind = kEventWindowDrawContent; + cmd_event[1].eventClass = kEventClassWindow; + cmd_event[1].eventKind = kEventWindowUpdate; + cmd_event[2].eventClass = kEventClassWindow; + cmd_event[2].eventKind = kEventWindowClose; + err = InstallWindowEventHandler(g_main_window, handlerUPP, 3, + &cmd_event[0], (void *)g_main_window, NULL); + require_noerr(err, CantCreateWindow); + + // Get screen depth + g_gdhandle = GetGDevice(); + g_screen_mdepth = (**((**g_gdhandle).gdPMap)).pixelSize; + + g_screen_depth = g_screen_mdepth; + + //printf("g_screen_depth = %d, depth: %d, bytes: %d\n", g_screen_depth, + // (**g_gdhandle).gdCCDepth, (**g_gdhandle).gdCCBytes); + + if(g_screen_depth > 16) { + /* 32-bit display */ + g_red_mask = 0xff; + g_green_mask = 0xff; + g_blue_mask = 0xff; + g_red_left_shift = 16; + g_green_left_shift = 8; + g_blue_left_shift = 0; + g_red_right_shift = 0; + g_green_right_shift = 0; + g_blue_right_shift = 0; + } else if(g_screen_depth > 8) { + /* 16-bit display */ + g_red_mask = 0x1f; + g_green_mask = 0x1f; + g_blue_mask = 0x1f; + g_red_left_shift = 10; + g_green_left_shift = 5; + g_blue_left_shift = 0; + g_red_right_shift = 3; + g_green_right_shift = 3; + g_blue_right_shift = 3; + } + + // show_alert("About to show window", (int)g_main_window); + update_window(); + + // The window was created hidden so show it. + ShowWindow( g_main_window ); + BringToFront( g_main_window ); + update_window(); + + // Make us pop to the front a different way + err = GetCurrentProcess(&my_psn); + if(err == noErr) { + (void)SetFrontProcess(&my_psn); + } + + // Call the event loop + temp_run_application_event_loop(); + +CantCreateWindow: +CantSetMenuBar: +CantGetNibRef: + show_alert("ending", "", "error code", err); + return err; +} + + +void +x_update_color(int col_num, int red, int green, int blue, word32 rgb) +{ +} + + +void +x_update_physical_colormap() +{ +} + +void +show_xcolor_array() +{ + int i; + + for(i = 0; i < 256; i++) { + printf("%02x: %08x\n", i, g_palette_8to1624[i]); + } +} + +void +xdriver_end() +{ + + printf("xdriver_end\n"); +} + +void +x_get_kimage(Kimage *kimage_ptr) +{ + PixMapHandle pixmap_handle; + GWorldPtr world; + Rect world_rect; + OSStatus err; + word32 *wptr; + byte *ptr; + int row_bytes; + int width; + int height; + int depth, mdepth; + int size; + + width = kimage_ptr->width_req; + height = kimage_ptr->height; + depth = kimage_ptr->depth; + mdepth = kimage_ptr->mdepth; + + size = 0; + if(depth == g_screen_depth) { + SetRect(&world_rect, 0, 0, width, height); + err = NewGWorld( &world, 0, &world_rect, NULL, NULL, 0); + pixmap_handle = GetGWorldPixMap(world); + err = LockPixels(pixmap_handle); + ptr = (byte *)GetPixBaseAddr(pixmap_handle); + row_bytes = ((*pixmap_handle)->rowBytes & 0x3fff); + kimage_ptr->width_act = row_bytes / (mdepth >> 3); + mac_printf("Got depth: %d, bitmap_ptr: %p, width: %d\n", depth, + ptr, kimage_ptr->width_act); + mac_printf("pixmap->base: %08x, rowbytes: %08x, pixType:%08x\n", + (int)(*pixmap_handle)->baseAddr, + (*pixmap_handle)->rowBytes, + (*pixmap_handle)->pixelType); + wptr = (word32 *)(*pixmap_handle); + mac_printf("wptr: %p=%08x %08x %08x %08x %08x %08x %08x %08x\n", + wptr, + wptr[0], wptr[1], wptr[2], wptr[3], + wptr[4], wptr[5], wptr[6], wptr[7]); + kimage_ptr->dev_handle = pixmap_handle; + kimage_ptr->data_ptr = ptr; + } else { + + /* allocate buffers for video.c to draw into */ + + size = (width*height*mdepth) >> 3; + ptr = (byte *)malloc(size); + + if(ptr == 0) { + mac_printf("malloc for data fail, mdepth:%d\n", mdepth); + exit(2); + } + + kimage_ptr->data_ptr = ptr; + + kimage_ptr->dev_handle = (void *)-1; + } + + mac_printf("kim: %p, dev:%p data: %p, size: %08x\n", kimage_ptr, + kimage_ptr->dev_handle, kimage_ptr->data_ptr, size); + +} + +void +dev_video_init() +{ + int lores_col; + int i; + + printf("Preparing graphics system\n"); + + video_get_kimages(); + + if(g_screen_depth != 8) { + // Get g_mainwin_kimage + video_get_kimage(&g_mainwin_kimage, 0, g_screen_depth, + g_screen_mdepth); + } + + for(i = 0; i < 256; i++) { + lores_col = g_lores_colors[i & 0xf]; + video_update_color_raw(i, lores_col); + g_a2palette_8to1624[i] = g_palette_8to1624[i]; + } + + g_installed_full_superhires_colormap = 1; + + fflush(stdout); +} + + +void +x_redraw_status_lines() +{ + Rect rect; + Pattern white_pattern; + char tmp_buf[256]; + char *buf; + int len; + int line; + int height; + int margin; + + SetPortWindowPort(g_main_window); + PenNormal(); + height = 16; + margin = 0; + TextFont(g_status_font_family); + TextFace(normal); + TextSize(12); + + SetRect(&rect, 0, X_A2_WINDOW_HEIGHT + margin, X_A2_WINDOW_WIDTH, + X_A2_WINDOW_HEIGHT + margin + MAX_STATUS_LINES*height); + GetQDGlobalsWhite(&white_pattern); + FillRect(&rect, &white_pattern); + + for(line = 0; line < MAX_STATUS_LINES; line++) { + buf = g_status_ptrs[line]; + if(buf == 0) { + /* skip it */ + continue; + } + MoveTo(10, X_A2_WINDOW_HEIGHT + height*line + margin + height); + len = MIN(250, strlen(buf)); + strncpy(&tmp_buf[1], buf, len); + tmp_buf[0] = len; + DrawString(&tmp_buf[0]); + } + +} + + +void +x_push_kimage(Kimage *kimage_ptr, int destx, int desty, int srcx, int srcy, + int width, int height) +{ + PixMapHandle pixmap_handle; + Rect src_rect, dest_rect; + CGrafPtr window_port; + + SetPortWindowPort(g_main_window); + + pixmap_handle = kimage_ptr->dev_handle; + window_port = GetWindowPort(g_main_window); + SetRect(&src_rect, srcx, srcy, srcx + width, srcy + height); + SetRect(&dest_rect, destx, desty, destx + width, desty + height); + + CopyBits( (BitMap *)(*pixmap_handle), + GetPortBitMapForCopyBits(window_port), &src_rect, &dest_rect, + srcCopy, NULL); + +} + +void +x_push_done() +{ +} + +void +x_auto_repeat_on(int must) +{ +} + +void +x_auto_repeat_off(int must) +{ +} + +void +x_hide_pointer(int do_hide) +{ + if(do_hide) { + HideCursor(); + } else { + ShowCursor(); + } +} diff --git a/src/macsnd_driver.c b/src/macsnd_driver.c new file mode 100644 index 0000000..a869d3f --- /dev/null +++ b/src/macsnd_driver.c @@ -0,0 +1,154 @@ +/************************************************************************/ +/* KEGS: Apple //gs Emulator */ +/* Copyright 2002 by Kent Dickey */ +/* */ +/* This code is covered by the GNU GPL */ +/* */ +/* The KEGS web page is kegs.sourceforge.net */ +/* You may contact the author at: kadickey@alumni.princeton.edu */ +/************************************************************************/ + +const char rcsid_macsnd_driver_c[] = "@(#)$KmKId: macsnd_driver.c,v 1.4 2003-10-17 15:57:40-04 kentd Exp $"; + +#include "defc.h" +#include "sound.h" + +#include +#include + +#define MACSND_REBUF_SIZE (64*1024) +#define MACSND_QUANTA 512 +/* MACSND_QUANTA must be >= 128 and a power of 2 */ + +word32 g_macsnd_rebuf[MACSND_REBUF_SIZE]; +volatile word32 *g_macsnd_rebuf_ptr; +volatile word32 *g_macsnd_rebuf_cur; +volatile int g_macsnd_playing = 0; + +extern int Verbose; + +extern int g_audio_rate; +extern word32 *g_sound_shm_addr; +extern int g_sound_size; + + +SndChannelPtr g_snd_channel_ptr; +ExtSoundHeader g_snd_hdr; +SndCommand g_snd_cmd; + +void +mac_snd_callback(SndChannelPtr snd_chan_ptr, SndCommand *in_sndcmd) +{ + OSStatus err; + int samps; + + // This is an interrupt routine--no printf, etc! + + samps = g_macsnd_rebuf_ptr - g_macsnd_rebuf_cur; + if(samps < 0) { + samps += MACSND_REBUF_SIZE; + } + + samps = samps & -(MACSND_QUANTA); // quantize to 1024 samples + if(g_macsnd_rebuf_cur + samps > &(g_macsnd_rebuf[MACSND_REBUF_SIZE])) { + samps = &(g_macsnd_rebuf[MACSND_REBUF_SIZE]) - + g_macsnd_rebuf_cur; + } + if(samps > 0) { + g_macsnd_playing = 1; + g_snd_hdr.numFrames = samps; + g_snd_hdr.loopEnd = samps; + g_snd_hdr.samplePtr = (byte *)g_macsnd_rebuf_cur; + + g_snd_cmd.cmd = bufferCmd; + g_snd_cmd.param1 = 0; + g_snd_cmd.param2 = (long) &g_snd_hdr; + + g_macsnd_rebuf_cur += samps; + if(g_macsnd_rebuf_cur >= &(g_macsnd_rebuf[MACSND_REBUF_SIZE])) { + g_macsnd_rebuf_cur -= MACSND_REBUF_SIZE; + } + + err = SndDoImmediate(g_snd_channel_ptr, &g_snd_cmd); + + // And set-up callback + g_snd_cmd.cmd = callBackCmd; + g_snd_cmd.param1 = 0; + g_snd_cmd.param2 = 0; + err = SndDoCommand(g_snd_channel_ptr, &g_snd_cmd, TRUE); + } else { + g_macsnd_playing = 0; + } +} + +int +mac_send_audio(byte *ptr, int in_size) +{ + SndCommand snd_cmd = {0}; + word32 *wptr, *macptr; + word32 *eptr; + int samps; + int i; + + samps = in_size / 4; + wptr = (word32 *)ptr; + macptr = (word32 *)g_macsnd_rebuf_ptr; + eptr = &g_macsnd_rebuf[MACSND_REBUF_SIZE]; + for(i = 0; i < samps; i++) { + *macptr++ = *wptr++; + if(macptr >= eptr) { + macptr = &g_macsnd_rebuf[0]; + } + } + + g_macsnd_rebuf_ptr = macptr; + + if(!g_macsnd_playing) { + mac_snd_callback(g_snd_channel_ptr, &snd_cmd); + } + + return in_size; +} + +void +child_sound_init_mac() +{ + OSStatus err; + + mac_printf("In mac child\n"); + fflush(stdout); + mac_printf("pid: %d\n", getpid()); + fflush(stdout); + + //return; + + //g_snd_channel_ptr = 0; + err = SndNewChannel(&g_snd_channel_ptr, sampledSynth, initStereo, + NewSndCallBackUPP(mac_snd_callback)); + mac_printf("SndNewChannel ret: %d\n", (int)err); + fflush(stdout); + + memset(&g_snd_hdr, 0, sizeof(g_snd_hdr)); + g_snd_hdr.sampleSize = 16; + g_snd_hdr.numChannels = 2; + g_audio_rate = 44100; + g_snd_hdr.sampleRate = g_audio_rate << 16; + g_snd_hdr.numFrames = 0; // will be set in mac_send_audio + g_snd_hdr.encode = extSH; + g_snd_hdr.baseFrequency = 0; + g_snd_hdr.samplePtr = 0; + + set_audio_rate(g_audio_rate); + + mac_printf("End of child_sound_init_mac\n"); + fflush(stdout); +} + +void +macsnd_init(word32 *shmaddr) +{ + g_macsnd_rebuf_cur = &g_macsnd_rebuf[0]; + g_macsnd_rebuf_ptr = &g_macsnd_rebuf[0]; + mac_printf("macsnd_init called\n"); + child_sound_loop(-1, -1, shmaddr); +} diff --git a/src/make_inst b/src/make_inst new file mode 100755 index 0000000..b84f665 --- /dev/null +++ b/src/make_inst @@ -0,0 +1,31 @@ +#!/usr/local/bin/perl -w + +# $KmKId: make_inst,v 1.5 2002-11-07 08:18:16-08 kadickey Exp $ + +$is_c = shift; +$repl = shift; + +$count = 0; + +while(<>) { + $line = $_; + if(/^inst(..)_SYM(.*)$/) { + if($is_c eq "c") { + if($count > 0) { + printf("\tbreak;\n"); + } + print "case 0x$1: $2\n"; + $count++; + } else { + print "\t.align\t8\n"; + print "inst" . "$1" . "_$repl" . "$2\n"; + } + } elsif(/^(.*)_SYM(.*)$/) { + print "$1" . "_$repl" . "$2\n"; + } else { + print $line; + } +} +# if(/^inst(..)_SYM (.*)$/) { +# print "OPCODE($1) /* $2 */\n"; +# } else if(/^( diff --git a/src/make_size b/src/make_size new file mode 100755 index 0000000..12f4066 --- /dev/null +++ b/src/make_size @@ -0,0 +1,30 @@ +#!/usr/local/bin/perl -w +# $KmKId: make_size,v 1.3 2002-11-07 08:18:16-08 kadickey Exp $ + +$repl = shift; + +while(<>) { + $line = $_; + if(/\.word inst(..)_SYM\+(.)(.*)$/) { + if($repl eq "c") { + print "\t0x$2, /* $1 */ $3\n"; + } elsif($repl eq "s") { + print "\t.byte 0x$2, /* $1 */ $3\n"; + } else { + print "\t.word\tinst$1" . "_$repl" . "\t/*$2*/ $3\n"; + } + } elsif (/\.block.*$/) { + if($repl eq "c") { + print "\n"; + } elsif($repl eq "s") { + print "\n"; + } else { + print $line; + } + } else { + print $line; + } +} +# if(/^inst(..)_SYM (.*)$/) { +# print "OPCODE($1) /* $2 */\n"; +# } else if(/^( diff --git a/src/make_win b/src/make_win new file mode 100755 index 0000000..092ec31 --- /dev/null +++ b/src/make_win @@ -0,0 +1,4 @@ +#!/bin/sh + +export PATH="/mingw/bin:${PATH}" +make diff --git a/src/moremem.c b/src/moremem.c new file mode 100644 index 0000000..30425e7 --- /dev/null +++ b/src/moremem.c @@ -0,0 +1,2353 @@ +/************************************************************************/ +/* KEGS: Apple //gs Emulator */ +/* Copyright 2002 by Kent Dickey */ +/* */ +/* This code is covered by the GNU GPL */ +/* */ +/* The KEGS web page is kegs.sourceforge.net */ +/* You may contact the author at: kadickey@alumni.princeton.edu */ +/************************************************************************/ + +const char rcsid_moremem_c[] = "@(#)$KmKId: moremem.c,v 1.233 2004-03-23 17:27:14-05 kentd Exp $"; + +#include "defc.h" + +extern int daylight; + +extern char g_kegs_version_str[]; + +extern byte *g_memory_ptr; +extern byte *g_dummy_memory1_ptr; +extern byte *g_slow_memory_ptr; +extern byte *g_rom_fc_ff_ptr; +extern byte *g_rom_cards_ptr; +extern word32 g_mem_size_total; + +extern word32 slow_mem_changed[]; + +extern int g_num_breakpoints; +extern word32 g_breakpts[]; + +extern int halt_sim; +extern double g_last_vbl_dcycs; + +extern Page_info page_info_rd_wr[]; + +extern int scr_mode; + +extern int Verbose; +extern int Halt_on; +extern double g_paddle_trig_dcycs; +extern int g_rom_version; + +extern int g_paddle_button[4]; + +extern Fplus *g_cur_fplus_ptr; + +/* from iwm.c */ +extern int head_35; +extern int g_apple35_sel; +extern int cur_drive; + +int g_zipgs_unlock = 0; +int g_zipgs_reg_c059 = 0x5f; + // 7=LC cache dis, 6==5ms paddle del en, 5==5ms ext del en, + // 4==5ms c02e enab, 3==CPS follow enab, 2-0: 111 +int g_zipgs_reg_c05a = 0x0f; + // 7:4 = current ZIP speed, 0=100%, 1=93.75%, F=6.25% + // 3:0: always 1111 +int g_zipgs_reg_c05b = 0x40; + // 7==1ms clock, 6==cshupd: tag data at c05f updated + // 5==LC cache disable, 4==bd is disabled, 3==delay in effect, + // 2==rombank, 1-0==ram size (00:8K, 01=16K, 10=32K, 11=64K) +int g_zipgs_reg_c05c = 0x00; + // 7:1==slot delay enable (for 52-54ms), 0==speaker 5ms delay + +int g_emubyte_cnt = 0; + +int statereg; +int halt_on_c02a = 0; +int g_shadow_all_banks = 0; +int g_num_shadow_all_banks = 0; + +extern Engine_reg engine; + +#define IOR(val) ( (val) ? 0x80 : 0x00 ) + +int linear_vid = 1; +int bank1latch = 0; + +int wrdefram = 0; +int int_crom[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + +extern int g_cur_a2_stat; + +int annunc_0 = 0; +int annunc_1 = 0; +int annunc_2 = 0; + +int shadow_reg = 0x08; + +int stop_on_c03x = 0; + +extern int doc_ptr; + +int shadow_text = 1; + +int g_border_color = 0; + +int speed_fast = 1; +word32 g_slot_motor_detect = 0; +int power_on_clear = 0; + + +int g_c023_val = 0; +int c023_scan_int_irq_pending = 0; +int c023_1sec_int_irq_pending = 0; + +int c02b_val = 0x08; + +int c039_write_val = 0; + +int c041_en_25sec_ints = 0; +int c041_en_vbl_ints = 0; +int c041_en_switch_ints = 0; +int c041_en_move_ints = 0; +int c041_en_mouse = 0; + +int g_c046_val = 0; + +int c046_25sec_irq_pend = 0; +int c046_vbl_irq_pending = 0; + + +#define UNIMPL_READ \ + halt_printf("UNIMP READ to addr %08x\n", loc); \ + return 0; + +#define UNIMPL_WRITE \ + halt_printf("UNIMP WRITE to addr %08x, val: %04x\n", loc, val); \ + return; + +void +fixup_brks() +{ + word32 page; + word32 tmp, tmp2; + Pg_info val; + int i, num; + + num = g_num_breakpoints; + for(i = 0; i < num; i++) { + page = (g_breakpts[i] >> 8) & 0xffff; + val = GET_PAGE_INFO_RD(page); + tmp = PTR2WORD(val) & 0xff; + tmp2 = tmp | BANK_IO_TMP | BANK_BREAK; + SET_PAGE_INFO_RD(page, val - tmp + tmp2); + val = GET_PAGE_INFO_WR(page); + tmp = PTR2WORD(val) & 0xff; + tmp2 = tmp | BANK_IO_TMP | BANK_BREAK; + SET_PAGE_INFO_WR(page, val - tmp + tmp2); + } +} + +void +fixup_hires_on() +{ + if((g_cur_a2_stat & ALL_STAT_ST80) == 0) { + return; + } + + fixup_bank0_2000_4000(); + fixup_brks(); +} + +void +fixup_bank0_2000_4000() +{ + byte *mem0rd; + byte *mem0wr; + + mem0rd = &(g_memory_ptr[0x2000]); + mem0wr = mem0rd; + if((g_cur_a2_stat & ALL_STAT_ST80) && (g_cur_a2_stat & ALL_STAT_HIRES)){ + if(PAGE2) { + mem0rd += 0x10000; + mem0wr += 0x10000; + if((shadow_reg & 0x12) == 0 || (shadow_reg & 0x8) == 0){ + mem0wr += BANK_SHADOW2; + } + } else if((shadow_reg & 0x02) == 0) { + mem0wr += BANK_SHADOW; + } + + } else { + if(RAMRD) { + mem0rd += 0x10000; + } + if(RAMWRT) { + mem0wr += 0x10000; + if((shadow_reg & 0x12) == 0 || (shadow_reg & 0x8) == 0){ + mem0wr += BANK_SHADOW2; + } + } else if((shadow_reg & 0x02) == 0) { + mem0wr += BANK_SHADOW; + } + } + + fixup_any_bank_any_page(0x20, 0x20, mem0rd, mem0wr); +} + +void +fixup_bank0_0400_0800() +{ + byte *mem0rd; + byte *mem0wr; + int shadow; + + mem0rd = &(g_memory_ptr[0x400]); + mem0wr = mem0rd; + shadow = BANK_SHADOW; + if(g_cur_a2_stat & ALL_STAT_ST80) { + if(PAGE2) { + shadow = BANK_SHADOW2; + mem0rd += 0x10000; + mem0wr += 0x10000; + } + } else { + if(RAMWRT) { + shadow = BANK_SHADOW2; + mem0wr += 0x10000; + } + if(RAMRD) { + mem0rd += 0x10000; + } + } + if((shadow_reg & 0x01) == 0) { + mem0wr += shadow; + } + + fixup_any_bank_any_page(0x4, 4, mem0rd, mem0wr); +} + +void +fixup_any_bank_any_page(int start_page, int num_pages, byte *mem0rd, + byte *mem0wr) +{ + int i; + + for(i = 0; i < num_pages; i++) { + SET_PAGE_INFO_RD(i + start_page, mem0rd); + mem0rd += 0x100; + } + + for(i = 0; i < num_pages; i++) { + SET_PAGE_INFO_WR(i + start_page, mem0wr); + mem0wr += 0x100; + } + +} + +void +fixup_intcx() +{ + byte *rom10000; + byte *rom_inc; + int no_io_shadow; + int off; + int start_k; + int indx; + int j, k; + + rom10000 = &(g_rom_fc_ff_ptr[0x30000]); + + no_io_shadow = (shadow_reg & 0x40); + + start_k = 0; + if(no_io_shadow) { + /* if not shadowing, banks 0 and 1 are not affected by intcx */ + start_k = 2; + } + + for(k = start_k; k < 4; k++) { + off = k; + if(k >= 2) { + off += (0xe0 - 2); + } + /* step off through 0x00, 0x01, 0xe0, 0xe1 */ + + off = off << 8; + SET_PAGE_INFO_RD(0xc0 + off, SET_BANK_IO); + + for(j = 0xc1; j < 0xc8; j++) { + indx = j & 0xf; + if(j < 0xc8) { + rom_inc = SET_BANK_IO; + if((int_crom[indx] == 0) || INTCX) { + rom_inc = rom10000 + (j << 8); + } else { + // User-slot rom + rom_inc = &(g_rom_cards_ptr[0]) + + ((j - 0xc0) << 8); + } + SET_PAGE_INFO_RD(j + off, rom_inc); + } + } + for(j = 0xc8; j < 0xd0; j++) { + /* c800 - cfff */ + if(int_crom[3] == 0 || INTCX) { + rom_inc = rom10000 + (j << 8); + } else { + /* c800 space not necessarily mapped */ + /* just map in ROM */ + rom_inc = rom10000 + (j << 8); + } + SET_PAGE_INFO_RD(j + off, rom_inc); + } + for(j = 0xc0; j < 0xd0; j++) { + SET_PAGE_INFO_WR(j + off, SET_BANK_IO); + } + } + + if(!no_io_shadow) { + SET_PAGE_INFO_RD(0xc7, SET_BANK_IO); /* smartport */ + } + + fixup_brks(); +} + +void +fixup_wrdefram(int new_wrdefram) +{ + byte *mem0wr; + byte *wrptr; + int j; + + wrdefram = new_wrdefram; + + if(shadow_reg & 0x40) { + /* do nothing */ + return; + } + + /* if shadowing, banks 0 and 1 are affected by wrdefram */ + mem0wr = &(g_memory_ptr[0]); + if(!new_wrdefram) { + mem0wr += (BANK_IO_TMP | BANK_IO2_TMP); + } + + wrptr = mem0wr + 0x1e000; + for(j = 0x1e0; j < 0x200; j++) { + SET_PAGE_INFO_WR(j, wrptr); + wrptr += 0x100; + } + + wrptr = mem0wr + 0x0e000; + if(ALTZP) { + wrptr += 0x10000; + } + for(j = 0xe0; j < 0x100; j++) { + SET_PAGE_INFO_WR(j, wrptr); + wrptr += 0x100; + } + + wrptr = mem0wr + 0x1d000; + if(! LCBANK2) { + wrptr -= 0x1000; + } + for(j = 0x1d0; j < 0x1e0; j++) { + SET_PAGE_INFO_WR(j, wrptr); + wrptr += 0x100; + } + + wrptr = mem0wr + 0xd000; + if(! LCBANK2) { + wrptr -= 0x1000; + } + if(ALTZP) { + wrptr += 0x10000; + } + for(j = 0xd0; j < 0xe0; j++) { + SET_PAGE_INFO_WR(j, wrptr); + wrptr += 0x100; + } + + fixup_brks(); +} + +void +fixup_st80col(double dcycs) +{ + int cur_a2_stat; + + cur_a2_stat = g_cur_a2_stat; + + fixup_bank0_0400_0800(); + + if(cur_a2_stat & ALL_STAT_HIRES) { + /* fixup no matter what PAGE2 since PAGE2 and RAMRD/WR */ + /* can work against each other */ + fixup_bank0_2000_4000(); + } + + if(PAGE2) { + change_display_mode(dcycs); + } + + fixup_brks(); +} + +void +fixup_altzp() +{ + byte *mem0rd, *mem0wr; + int altzp; + + altzp = ALTZP; + mem0rd = &(g_memory_ptr[0]); + if(altzp) { + mem0rd += 0x10000; + } + SET_PAGE_INFO_RD(0, mem0rd); + SET_PAGE_INFO_RD(1, mem0rd + 0x100); + SET_PAGE_INFO_WR(0, mem0rd); + SET_PAGE_INFO_WR(1, mem0rd + 0x100); + + mem0rd = &(g_memory_ptr[0xd000]); + mem0wr = mem0rd; + + if(shadow_reg & 0x40) { + if(ALTZP) { + mem0rd += 0x10000; + } + fixup_any_bank_any_page(0xd0, 0x10, mem0rd - 0x1000, + mem0rd - 0x1000); + } else { + if(!wrdefram) { + mem0wr += (BANK_IO_TMP | BANK_IO2_TMP); + } + if(ALTZP) { + mem0rd += 0x10000; + mem0wr += 0x10000; + } + if(! LCBANK2) { + mem0rd -= 0x1000; + mem0wr -= 0x1000; + } + if(RDROM) { + mem0rd = &(g_rom_fc_ff_ptr[0x3d000]); + } + fixup_any_bank_any_page(0xd0, 0x10, mem0rd, mem0wr); + } + + mem0rd = &(g_memory_ptr[0xe000]); + mem0wr = mem0rd; + if(!wrdefram) { + mem0wr += (BANK_IO_TMP | BANK_IO2_TMP); + } + if(ALTZP) { + mem0rd += 0x10000; + mem0wr += 0x10000; + } + if(RDROM) { + mem0rd = &(g_rom_fc_ff_ptr[0x3e000]); + } + fixup_any_bank_any_page(0xe0, 0x20, mem0rd, mem0wr); +} + +void +fixup_page2(double dcycs) +{ + if((g_cur_a2_stat & ALL_STAT_ST80)) { + fixup_bank0_0400_0800(); + if((g_cur_a2_stat & ALL_STAT_HIRES)) { + fixup_bank0_2000_4000(); + } + } else { + change_display_mode(dcycs); + } +} + +void +fixup_ramrd() +{ + byte *mem0rd; + int cur_a2_stat; + int j; + + cur_a2_stat = g_cur_a2_stat; + + if((cur_a2_stat & ALL_STAT_ST80) == 0) { + fixup_bank0_0400_0800(); + } + if( ((cur_a2_stat & ALL_STAT_ST80) == 0) || + ((cur_a2_stat & ALL_STAT_HIRES) == 0) ) { + fixup_bank0_2000_4000(); + } + + mem0rd = &(g_memory_ptr[0x0000]); + if(RAMRD) { + mem0rd += 0x10000; + } + + SET_PAGE_INFO_RD(2, mem0rd + 0x200); + SET_PAGE_INFO_RD(3, mem0rd + 0x300); + + for(j = 8; j < 0x20; j++) { + SET_PAGE_INFO_RD(j, mem0rd + j*0x100); + } + + for(j = 0x40; j < 0xc0; j++) { + SET_PAGE_INFO_RD(j, mem0rd + j*0x100); + } +} + +void +fixup_ramwrt() +{ + byte *mem0wr; + int cur_a2_stat; + int shadow; + int ramwrt; + int j; + + cur_a2_stat = g_cur_a2_stat; + + if((cur_a2_stat & ALL_STAT_ST80) == 0) { + fixup_bank0_0400_0800(); + } + if( ((cur_a2_stat & ALL_STAT_ST80) == 0) || + ((cur_a2_stat & ALL_STAT_HIRES) == 0) ) { + fixup_bank0_2000_4000(); + } + + mem0wr = &(g_memory_ptr[0x0000]); + ramwrt = RAMWRT; + if(ramwrt) { + mem0wr += 0x10000; + } + + SET_PAGE_INFO_WR(2, mem0wr + 0x200); + SET_PAGE_INFO_WR(3, mem0wr + 0x300); + + shadow = BANK_SHADOW; + if(ramwrt) { + shadow = BANK_SHADOW2; + } + if(((shadow_reg & 0x20) != 0) || g_rom_version < 3) { + shadow = 0; + } + for(j = 8; j < 0x0c; j++) { + SET_PAGE_INFO_WR(j, mem0wr + j*0x100 + shadow); + } + + for(j = 0xc; j < 0x20; j++) { + SET_PAGE_INFO_WR(j, mem0wr + j*0x100); + } + + shadow = 0; + if(ramwrt) { + if((shadow_reg & 0x14) == 0 || (shadow_reg & 0x08) == 0) { + shadow = BANK_SHADOW2; + } + } else if((shadow_reg & 0x04) == 0) { + shadow = BANK_SHADOW; + } + for(j = 0x40; j < 0x60; j++) { + SET_PAGE_INFO_WR(j, mem0wr + j*0x100 + shadow); + } + + shadow = 0; + if(ramwrt && (shadow_reg & 0x08) == 0) { + /* shr shadowing */ + shadow = BANK_SHADOW2; + } + for(j = 0x60; j < 0xa0; j++) { + SET_PAGE_INFO_WR(j, mem0wr + j*0x100 + shadow); + } + + for(j = 0xa0; j < 0xc0; j++) { + SET_PAGE_INFO_WR(j, mem0wr + j*0x100); + } +} + +void +fixup_lcbank2() +{ + byte *mem0rd, *mem0wr; + int off; + int k; + + for(k = 0; k < 4; k++) { + off = k; + if(k >= 2) { + off += (0xe0 - 2); + } + /* step off through 0x00, 0x01, 0xe0, 0xe1 */ + + if(k < 2) { + mem0rd = &(g_memory_ptr[k << 16]); + } else { + mem0rd = &(g_slow_memory_ptr[(k & 1) << 16]); + } + if((k == 0) && ALTZP) { + mem0rd += 0x10000; + } + if(! LCBANK2) { + mem0rd -= 0x1000; /* lcbank1, use 0xc000-cfff */ + } + mem0wr = mem0rd; + if((k < 2) && !wrdefram) { + mem0wr += (BANK_IO_TMP | BANK_IO2_TMP); + } + if((k < 2) && RDROM) { + mem0rd = &(g_rom_fc_ff_ptr[0x30000]); + } + fixup_any_bank_any_page(off*0x100 + 0xd0, 0x10, + mem0rd + 0xd000, mem0wr + 0xd000); + } +} + +void +fixup_rdrom() +{ + byte *mem0rd; + int j, k; + + /* fixup_lcbank2 handles 0xd000-dfff for rd & wr*/ + fixup_lcbank2(); + + for(k = 0; k < 2; k++) { + /* k is the bank */ + mem0rd = &(g_memory_ptr[k << 16]); + if((k == 0) && ALTZP) { + mem0rd += 0x10000; + } + if((shadow_reg & 0x40) == 0) { + if(RDROM) { + mem0rd = &(g_rom_fc_ff_ptr[0x30000]); + } + } + for(j = 0xe0; j < 0x100; j++) { + SET_PAGE_INFO_RD(j + k*0x100, mem0rd + j*0x100); + } + } + +} + +void +set_statereg(double dcycs, int val) +{ + int xor; + + xor = val ^ statereg; + statereg = val; + if(xor == 0) { + return; + } + + if(xor & 0x80) { + /* altzp */ + fixup_altzp(); + } + if(xor & 0x40) { + /* page2 */ + fixup_page2(dcycs); + } + + if(xor & 0x20) { + /* RAMRD */ + fixup_ramrd(); + } + + if(xor & 0x10) { + /* RAMWRT */ + fixup_ramwrt(); + } + + if(xor & 0x08) { + /* RDROM */ + fixup_rdrom(); + } + + if(xor & 0x04) { + /* LCBANK2 */ + fixup_lcbank2(); + } + + if(xor & 0x02) { + /* ROMBANK */ + halt_printf("Just set rombank = %d\n", ROMB); + } + + if(xor & 0x01) { + fixup_intcx(); + } + + if(xor) { + fixup_brks(); + } +} + +void +fixup_shadow_txt1() +{ + byte *mem0wr; + int j; + + fixup_bank0_0400_0800(); + + mem0wr = &(g_memory_ptr[0x10000]); + if((shadow_reg & 0x01) == 0) { + mem0wr += BANK_SHADOW2; + } + for(j = 4; j < 8; j++) { + SET_PAGE_INFO_WR(0x100 + j, mem0wr + j*0x100); + } +} + +void +fixup_shadow_txt2() +{ + byte *mem0wr; + int shadow; + int j; + + /* bank 0 */ + mem0wr = &(g_memory_ptr[0x00000]); + shadow = BANK_SHADOW; + if(RAMWRT) { + mem0wr += 0x10000; + shadow = BANK_SHADOW2; + } + if(((shadow_reg & 0x20) == 0) && (g_rom_version >= 3)) { + mem0wr += shadow; + } + for(j = 8; j < 0xc; j++) { + SET_PAGE_INFO_WR(j, mem0wr + j*0x100); + } + + /* and bank 1 */ + mem0wr = &(g_memory_ptr[0x10000]); + if(((shadow_reg & 0x20) == 0) && (g_rom_version >= 3)) { + mem0wr += BANK_SHADOW2; + } + for(j = 8; j < 0xc; j++) { + SET_PAGE_INFO_WR(0x100 + j, mem0wr + j*0x100); + } +} + +void +fixup_shadow_hires1() +{ + byte *mem0wr; + int j; + + fixup_bank0_2000_4000(); + + /* and bank 1 */ + mem0wr = &(g_memory_ptr[0x10000]); + if((shadow_reg & 0x12) == 0 || (shadow_reg & 0x8) == 0) { + mem0wr += BANK_SHADOW2; + } + for(j = 0x20; j < 0x40; j++) { + SET_PAGE_INFO_WR(0x100 + j, mem0wr + j*0x100); + } +} + +void +fixup_shadow_hires2() +{ + byte *mem0wr; + int j; + + /* bank 0 */ + mem0wr = &(g_memory_ptr[0x00000]); + if(RAMWRT) { + mem0wr += 0x10000; + if((shadow_reg & 0x14) == 0 || (shadow_reg & 0x8) == 0) { + mem0wr += BANK_SHADOW2; + } + } else if((shadow_reg & 0x04) == 0) { + mem0wr += BANK_SHADOW; + } + for(j = 0x40; j < 0x60; j++) { + SET_PAGE_INFO_WR(j, mem0wr + j*0x100); + } + + /* and bank 1 */ + mem0wr = &(g_memory_ptr[0x10000]); + if((shadow_reg & 0x14) == 0 || (shadow_reg & 0x8) == 0) { + mem0wr += BANK_SHADOW2; + } + for(j = 0x40; j < 0x60; j++) { + SET_PAGE_INFO_WR(0x100 + j, mem0wr + j*0x100); + } +} + +void +fixup_shadow_shr() +{ + byte *mem0wr; + int j; + + /* bank 0, only pages 0x60 - 0xa0 */ + mem0wr = &(g_memory_ptr[0x00000]); + if(RAMWRT) { + mem0wr += 0x10000; + if((shadow_reg & 0x8) == 0) { + mem0wr += BANK_SHADOW2; + } + } + for(j = 0x60; j < 0xa0; j++) { + SET_PAGE_INFO_WR(j, mem0wr + j*0x100); + } + + /* and bank 1, only pages 0x60 - 0xa0 */ + mem0wr = &(g_memory_ptr[0x10000]); + if((shadow_reg & 0x8) == 0) { + mem0wr += BANK_SHADOW2; + } + for(j = 0x60; j < 0xa0; j++) { + SET_PAGE_INFO_WR(0x100 + j, mem0wr + j*0x100); + } +} + +void +fixup_shadow_iolc() +{ + byte *mem0rd, *mem0wr; + int k; + + for(k = 0; k < 2; k++) { + mem0rd = &(g_memory_ptr[k << 16]); + if(shadow_reg & 0x40) { + fixup_any_bank_any_page((k << 8) + 0xc0, 0x10, + mem0rd + 0xd000, mem0rd + 0xd000); + if(k == 0 && ALTZP) { + mem0rd += 0x10000; + } + fixup_any_bank_any_page((k << 8) + 0xd0, 0x10, + mem0rd + 0xc000, mem0rd + 0xc000); + fixup_any_bank_any_page((k << 8) + 0xe0, 0x20, + mem0rd + 0xe000, mem0rd + 0xe000); + } else { + /* 0xc000 area */ + fixup_intcx(); + + /* 0xd000 area */ + fixup_lcbank2(); + + if(k == 0 && ALTZP) { + mem0rd += 0x10000; + } + mem0wr = mem0rd; + if(!wrdefram) { + mem0wr += (BANK_IO_TMP | BANK_IO2_TMP); + } + if(RDROM) { + mem0rd = &(g_rom_fc_ff_ptr[0x30000]); + } + fixup_any_bank_any_page((k << 8) + 0xe0, 0x20, + mem0rd + 0xe000, mem0wr + 0xe000); + } + } +} + +void +update_shadow_reg(int val) +{ + int xor; + + if(shadow_reg == val) { + return; + } + + xor = shadow_reg ^ val; + shadow_reg = val; + + if(xor & 8) { + fixup_shadow_hires1(); + fixup_shadow_hires2(); + fixup_shadow_shr(); + xor = xor & (~0x16); + } + if(xor & 0x10) { + fixup_shadow_hires1(); + fixup_shadow_hires2(); + xor = xor & (~0x6); + } + if(xor & 2) { + fixup_shadow_hires1(); + } + if(xor & 4) { + fixup_shadow_hires2(); + } + if(xor & 1) { + fixup_shadow_txt1(); + } + if((xor & 0x20) && g_rom_version >= 3) { + fixup_shadow_txt2(); + } + if(xor & 0x40) { + fixup_shadow_iolc(); + } +} + +void +fixup_shadow_all_banks() +{ + byte *mem0rd; + int shadow; + int num_banks; + int j, k; + + /* Assume Ninja Force Megademo */ + /* only do banks 3 - num_banks by 2, shadowing into e1 */ + + shadow = 0; + if(g_shadow_all_banks && ((shadow_reg & 0x08) == 0)) { + shadow = BANK_SHADOW2; + } + num_banks = g_mem_size_total >> 16; + for(k = 3; k < num_banks; k += 2) { + mem0rd = &(g_memory_ptr[k*0x10000 + 0x2000]) + shadow; + for(j = 0x20; j < 0xa0; j++) { + SET_PAGE_INFO_WR(k*0x100 + j, mem0rd); + mem0rd += 0x100; + } + } +} + +void +setup_pageinfo() +{ + byte *mem0rd; + word32 mem_size_pages; + + /* first, set all of memory to point to itself */ + + mem_size_pages = g_mem_size_total >> 8; + mem0rd = &(g_memory_ptr[0]); + fixup_any_bank_any_page(0, mem_size_pages, mem0rd, mem0rd); + + /* mark unused memory as BAD_MEM */ + fixup_any_bank_any_page(mem_size_pages, 0xfc00-mem_size_pages, + BANK_BAD_MEM, BANK_BAD_MEM); + + fixup_shadow_all_banks(); + + /* ROM */ + mem0rd = &(g_rom_fc_ff_ptr[0]); + fixup_any_bank_any_page(0xfc00, 0x400, mem0rd, + mem0rd + (BANK_IO_TMP | BANK_IO2_TMP)); + + /* banks e0, e1 */ + mem0rd = &(g_slow_memory_ptr[0]); + fixup_any_bank_any_page(0xe000, 0x04, mem0rd + 0x0000, mem0rd + 0x0000); + fixup_any_bank_any_page(0xe004, 0x08, mem0rd + 0x0400, + mem0rd + 0x0400 + BANK_SHADOW); + fixup_any_bank_any_page(0xe00c, 0x14, mem0rd + 0x0c00, mem0rd + 0x0c00); + fixup_any_bank_any_page(0xe020, 0x40, mem0rd + 0x2000, + mem0rd + 0x2000 + BANK_SHADOW); + fixup_any_bank_any_page(0xe060, 0xa0, mem0rd + 0x6000, mem0rd + 0x6000); + + mem0rd = &(g_slow_memory_ptr[0x10000]); + fixup_any_bank_any_page(0xe100, 0x04, mem0rd + 0x0000, mem0rd + 0x0000); + fixup_any_bank_any_page(0xe104, 0x08, mem0rd + 0x0400, + mem0rd + 0x0400 + BANK_SHADOW2); + fixup_any_bank_any_page(0xe10c, 0x14, mem0rd + 0x0c00, mem0rd + 0x0c00); + fixup_any_bank_any_page(0xe120, 0x80, mem0rd + 0x2000, + mem0rd + 0x2000 + BANK_SHADOW2); + fixup_any_bank_any_page(0xe1a0, 0x60, mem0rd + 0xa000, mem0rd + 0xa000); + + fixup_intcx(); /* correct banks 0xe0,0xe1, 0xc000-0xcfff area */ + fixup_lcbank2(); /* correct 0xd000-0xdfff area */ + + fixup_bank0_2000_4000(); + fixup_bank0_0400_0800(); + fixup_wrdefram(wrdefram); + fixup_altzp(); + fixup_ramrd(); + fixup_ramwrt(); + fixup_rdrom(); + fixup_shadow_txt1(); + fixup_shadow_txt2(); + fixup_shadow_hires1(); + fixup_shadow_hires2(); + fixup_shadow_shr(); + fixup_shadow_iolc(); + fixup_brks(); +} + +void +show_bankptrs_bank0rdwr() +{ + show_bankptrs(0); + show_bankptrs(1); + show_bankptrs(0xe0); + show_bankptrs(0xe1); + printf("statereg: %02x\n", statereg); +} + +void +show_bankptrs(int bnk) +{ + int i; + Pg_info rd, wr; + byte *ptr_rd, *ptr_wr; + + printf("g_memory_ptr: %p, dummy_mem: %p, slow_mem_ptr: %p\n", + g_memory_ptr, g_dummy_memory1_ptr, g_slow_memory_ptr); + printf("g_rom_fc_ff_ptr: %p\n", g_rom_fc_ff_ptr); + + printf("Showing bank_info array for %02x\n", bnk); + for(i = 0; i < 256; i++) { + rd = GET_PAGE_INFO_RD(bnk*0x100 + i); + wr = GET_PAGE_INFO_WR(bnk*0x100 + i); + ptr_rd = (byte *)rd; + ptr_wr = (byte *)wr; + printf("%04x rd: ", bnk*256 + i); + show_addr(ptr_rd); + printf(" wr: "); + show_addr(ptr_wr); + printf("\n"); + } +} + +void +show_addr(byte *ptr) +{ + word32 mem_size; + + mem_size = g_mem_size_total; + if(ptr >= g_memory_ptr && ptr < &g_memory_ptr[mem_size]) { + printf("%p--memory[%06x]", ptr, + (word32)(ptr - g_memory_ptr)); + } else if(ptr >= g_rom_fc_ff_ptr && ptr < &g_rom_fc_ff_ptr[256*1024]) { + printf("%p--rom_fc_ff[%06x]", ptr, + (word32)(ptr - g_rom_fc_ff_ptr)); + } else if(ptr >= g_slow_memory_ptr && ptr<&g_slow_memory_ptr[128*1024]){ + printf("%p--slow_memory[%06x]", ptr, + (word32)(ptr - g_slow_memory_ptr)); + } else if(ptr >=g_dummy_memory1_ptr && ptr < &g_dummy_memory1_ptr[256]){ + printf("%p--dummy_memory[%06x]", ptr, + (word32)(ptr - g_dummy_memory1_ptr)); + } else { + printf("%p--unknown", ptr); + } +} + + +#if 0 +#define CALC_DCYCS_FROM_CYC_PTR(dcycs, cyc_ptr, fcyc, new_fcyc) \ + fcyc = *cyc_ptr; \ + new_fcyc = (int)(fcyc + g_cur_fplus_ptr->plus_x_minus_1); \ + *cyc_ptr = new_fcyc; \ + dcycs = g_last_vbl_dcycs + new_fcyc; +#endif + +#define CALC_DCYCS_FROM_CYC_PTR(dcycs, cyc_ptr, fcyc, new_fcyc) \ + dcycs = g_last_vbl_dcycs + *cyc_ptr; + + +int dummy = 0; + +int +io_read(word32 loc, double *cyc_ptr) +{ + double dcycs; + word64 word64_tmp; +#if 0 + double fcyc, new_fcyc; +#endif + int new_lcbank2; + int new_wrdefram; + int tmp; + int slot; + int i; + + CALC_DCYCS_FROM_CYC_PTR(dcycs, cyc_ptr, fcyc, new_fcyc); + +/* IO space */ + switch((loc >> 8) & 0xf) { + case 0: /* 0xc000 - 0xc0ff */ + switch(loc & 0xff) { + /* 0xc000 - 0xc00f */ + case 0x00: case 0x01: case 0x02: case 0x03: + case 0x04: case 0x05: case 0x06: case 0x07: + case 0x08: case 0x09: case 0x0a: case 0x0b: + case 0x0c: case 0x0d: case 0x0e: case 0x0f: + return(adb_read_c000()); + + /* 0xc010 - 0xc01f */ + case 0x10: /* c010 */ + return(adb_access_c010()); + case 0x11: /* c011 = RDLCBANK2 */ + return IOR(LCBANK2); + case 0x12: /* c012= RDLCRAM */ + return IOR(!RDROM); + case 0x13: /* c013=rdramd */ + return IOR(RAMRD); + case 0x14: /* c014=rdramwrt */ + return IOR(RAMWRT); + case 0x15: /* c015 = INTCX */ + return IOR(INTCX); + case 0x16: /* c016: ALTZP */ + return IOR(ALTZP); + case 0x17: /* c017: rdc3rom */ + return IOR(int_crom[3]); + case 0x18: /* c018: rd80c0l */ + return IOR((g_cur_a2_stat & ALL_STAT_ST80)); + case 0x19: /* c019: rdvblbar */ + tmp = in_vblank(dcycs); + return IOR(tmp); + case 0x1a: /* c01a: rdtext */ + return IOR(g_cur_a2_stat & ALL_STAT_TEXT); + case 0x1b: /* c01b: rdmix */ + return IOR(g_cur_a2_stat & ALL_STAT_MIX_T_GR); + case 0x1c: /* c01c: rdpage2 */ + return IOR(PAGE2); + case 0x1d: /* c01d: rdhires */ + return IOR(g_cur_a2_stat & ALL_STAT_HIRES); + case 0x1e: /* c01e: altcharset on? */ + return IOR(g_cur_a2_stat & ALL_STAT_ALTCHARSET); + case 0x1f: /* c01f: rd80vid */ + return IOR(g_cur_a2_stat & ALL_STAT_VID80); + + /* 0xc020 - 0xc02f */ + case 0x20: /* 0xc020 */ + /* Click cassette port */ + return 0x00; + case 0x21: /* 0xc021 */ + UNIMPL_READ; + case 0x22: /* 0xc022 */ + return (g_cur_a2_stat >> BIT_ALL_STAT_BG_COLOR) & 0xff; + case 0x23: /* 0xc023 */ + return g_c023_val; + case 0x24: /* 0xc024 */ + return mouse_read_c024(dcycs); + case 0x25: /* 0xc025 */ + return adb_read_c025(); + case 0x26: /* 0xc026 */ + return adb_read_c026(); + case 0x27: /* 0xc027 */ + return adb_read_c027(); + case 0x28: /* 0xc028 */ + UNIMPL_READ; + case 0x29: /* 0xc029 */ + return((g_cur_a2_stat & 0xa0) | (linear_vid<<6) | + (bank1latch)); + case 0x2a: /* 0xc02a */ +#if 0 + printf("Reading c02a...returning 0\n"); +#endif + return 0; + case 0x2b: /* 0xc02b */ + return c02b_val; + case 0x2c: /* 0xc02c */ + /* printf("reading c02c, returning 0\n"); */ + return 0; + case 0x2d: /* 0xc02d */ + tmp = 0; + for(i = 0; i < 8; i++) { + tmp = tmp | (int_crom[i] << i); + } + return tmp; + case 0x2e: /* 0xc02e */ + case 0x2f: /* 0xc02f */ + return read_vid_counters(loc, dcycs); + + /* 0xc030 - 0xc03f */ + case 0x30: /* 0xc030 */ + /* click speaker */ + return doc_read_c030(dcycs); + case 0x31: /* 0xc031 */ + /* 3.5" control */ + return (head_35 << 7) | (g_apple35_sel << 6); + case 0x32: /* 0xc032 */ + /* scan int */ + return 0; + case 0x33: /* 0xc033 = CLOCKDATA*/ + return clock_read_c033(); + case 0x34: /* 0xc034 = CLOCKCTL */ + return clock_read_c034(); + case 0x35: /* 0xc035 */ + return shadow_reg; + case 0x36: /* 0xc036 = CYAREG */ + tmp = (speed_fast << 7) + (power_on_clear << 6) + + (g_shadow_all_banks << 4) + g_slot_motor_detect; + return tmp; + case 0x37: /* 0xc037 */ + return 0; + case 0x38: /* 0xc038 */ + return scc_read_reg(1, dcycs); + case 0x39: /* 0xc039 */ + return scc_read_reg(0, dcycs); + case 0x3a: /* 0xc03a */ + return scc_read_data(1, dcycs); + case 0x3b: /* 0xc03b */ + return scc_read_data(0, dcycs); + case 0x3c: /* 0xc03c */ + /* doc control */ + return doc_read_c03c(dcycs); + case 0x3d: /* 0xc03d */ + return doc_read_c03d(dcycs); + case 0x3e: /* 0xc03e */ + return (doc_ptr & 0xff); + case 0x3f: /* 0xc03f */ + return (doc_ptr >> 8); + + /* 0xc040 - 0xc04f */ + case 0x40: /* 0xc040 */ + /* cassette */ + return 0; + case 0x41: /* 0xc041 */ + tmp = ((c041_en_25sec_ints << 4) + + (c041_en_vbl_ints << 3) + + (c041_en_switch_ints << 2) + + (c041_en_move_ints << 1) + (c041_en_mouse) ); + return tmp; + case 0x45: /* 0xc045 */ + halt_printf("Mega II mouse read: c045\n"); + return 0; + case 0x46: /* 0xc046 */ + tmp = g_c046_val; + g_c046_val = (tmp & 0xbf) + ((tmp & 0x80) >> 1); + return tmp; + case 0x47: /* 0xc047 */ + if(c046_vbl_irq_pending) { + remove_irq(); + c046_vbl_irq_pending = 0; + } + if(c046_25sec_irq_pend) { + remove_irq(); + c046_25sec_irq_pend = 0; + } + g_c046_val &= 0xe7; /* clear vbl_int, 1/4sec int*/ + return 0; + case 0x42: /* 0xc042 */ + case 0x43: /* 0xc043 */ + return 0; + case 0x4f: /* 0xc04f */ + /* for information on c04f, see: */ + /* www.sheppyware.net/tech/hardware/softswitches.html */ + /* write to $c04f to start. Then read $c04f to get */ + /* emulator ($16=sweet16, $fe=bernie II). */ + /* Then read again to get version: $21 == 2.1 */ + switch(g_emubyte_cnt) { + case 1: + g_emubyte_cnt = 2; + return 'K'; + case 2: + g_emubyte_cnt = 0; + tmp = g_kegs_version_str[0] - '0'; + i = g_kegs_version_str[2] - '0'; + return ((tmp & 0xf) << 4) + (i & 0xf); + default: + g_emubyte_cnt = 0; + return 0; + } + case 0x44: /* 0xc044 */ + case 0x48: /* 0xc048 */ + case 0x49: /* 0xc049 */ + case 0x4a: /* 0xc04a */ + case 0x4b: /* 0xc04b */ + case 0x4c: /* 0xc04c */ + case 0x4d: /* 0xc04d */ + case 0x4e: /* 0xc04e */ + UNIMPL_READ; + + /* 0xc050 - 0xc05f */ + case 0x50: /* 0xc050 */ + if(g_cur_a2_stat & ALL_STAT_TEXT) { + g_cur_a2_stat &= (~ALL_STAT_TEXT); + change_display_mode(dcycs); + } + return 0; + case 0x51: /* 0xc051 */ + if((g_cur_a2_stat & ALL_STAT_TEXT) == 0) { + g_cur_a2_stat |= (ALL_STAT_TEXT); + change_display_mode(dcycs); + } + return 0; + case 0x52: /* 0xc052 */ + if(g_cur_a2_stat & ALL_STAT_MIX_T_GR) { + g_cur_a2_stat &= (~ALL_STAT_MIX_T_GR); + change_display_mode(dcycs); + } + return 0; + case 0x53: /* 0xc053 */ + if((g_cur_a2_stat & ALL_STAT_MIX_T_GR) == 0) { + g_cur_a2_stat |= (ALL_STAT_MIX_T_GR); + change_display_mode(dcycs); + } + return 0; + case 0x54: /* 0xc054 */ + set_statereg(dcycs, statereg & (~0x40)); + return 0; + case 0x55: /* 0xc055 */ + set_statereg(dcycs, statereg | 0x40); + return 0; + case 0x56: /* 0xc056 */ + if(g_cur_a2_stat & ALL_STAT_HIRES) { + g_cur_a2_stat &= (~ALL_STAT_HIRES); + fixup_hires_on(); + change_display_mode(dcycs); + } + return 0; + case 0x57: /* 0xc057 */ + if((g_cur_a2_stat & ALL_STAT_HIRES) == 0) { + g_cur_a2_stat |= (ALL_STAT_HIRES); + fixup_hires_on(); + change_display_mode(dcycs); + } + return 0; + case 0x58: /* 0xc058 */ + if(g_zipgs_unlock < 4) { + annunc_0 = 0; + } + return 0; + case 0x59: /* 0xc059 */ + if(g_zipgs_unlock >= 4) { + return g_zipgs_reg_c059; + } else { + annunc_0 = 1; + } + return 0; + case 0x5a: /* 0xc05a */ + if(g_zipgs_unlock >= 4) { + return g_zipgs_reg_c05a; + } else { + annunc_1 = 0; + } + return 0; + case 0x5b: /* 0xc05b */ + if(g_zipgs_unlock >= 4) { + word64_tmp = (word64)dcycs; + tmp = (word64_tmp >> 9) & 1; + return (tmp << 7) + (g_zipgs_reg_c05b & 0x7f); + } else { + annunc_1 = 1; + } + return 0; + case 0x5c: /* 0xc05c */ + if(g_zipgs_unlock >= 4) { + return g_zipgs_reg_c05c; + } else { + annunc_2 = 0; + } + return 0; + case 0x5d: /* 0xc05d */ + if(g_zipgs_unlock >= 4) { + halt_printf("Reading ZipGS $c05d!\n"); + } else { + annunc_2 = 1; + } + return 0; + case 0x5e: /* 0xc05e */ + if(g_zipgs_unlock >= 4) { + halt_printf("Reading ZipGS $c05e!\n"); + } else if(g_cur_a2_stat & ALL_STAT_ANNUNC3) { + g_cur_a2_stat &= (~ALL_STAT_ANNUNC3); + change_display_mode(dcycs); + } + return 0; + case 0x5f: /* 0xc05f */ + if(g_zipgs_unlock >= 4) { + halt_printf("Reading ZipGS $c05f!\n"); + } else if((g_cur_a2_stat & ALL_STAT_ANNUNC3) == 0) { + g_cur_a2_stat |= (ALL_STAT_ANNUNC3); + change_display_mode(dcycs); + } + return 0; + + + /* 0xc060 - 0xc06f */ + case 0x60: /* 0xc060 */ + return IOR(g_paddle_button[3]); + case 0x61: /* 0xc061 */ + return IOR(adb_is_cmd_key_down() || g_paddle_button[0]); + case 0x62: /* 0xc062 */ + return IOR(adb_is_option_key_down() || + g_paddle_button[1]); + case 0x63: /* 0xc063 */ + return IOR(g_paddle_button[2]); + case 0x64: /* 0xc064 */ + return read_paddles(0, dcycs); + case 0x65: /* 0xc065 */ + return read_paddles(1, dcycs); + case 0x66: /* 0xc066 */ + return read_paddles(2, dcycs); + case 0x67: /* 0xc067 */ + return read_paddles(3, dcycs); + case 0x68: /* 0xc068 = STATEREG */ + return statereg; + case 0x69: /* 0xc069 */ + /* Reserved reg, return 0 */ + return 0; + case 0x6a: /* 0xc06a */ + case 0x6b: /* 0xc06b */ + case 0x6c: /* 0xc06c */ + case 0x6d: /* 0xc06d */ + case 0x6e: /* 0xc06e */ + case 0x6f: /* 0xc06f */ + UNIMPL_READ; + + /* 0xc070 - 0xc07f */ + case 0x70: /* c070 */ + paddle_trigger(dcycs); + return 0; + case 0x71: /* 0xc071 */ + case 0x72: case 0x73: + case 0x74: case 0x75: case 0x76: case 0x77: + case 0x78: case 0x79: case 0x7a: case 0x7b: + case 0x7c: case 0x7d: case 0x7e: case 0x7f: + return g_rom_fc_ff_ptr[3*65536 + 0xc000 + (loc & 0xff)]; + + /* 0xc080 - 0xc08f */ + case 0x80: case 0x81: case 0x82: case 0x83: + case 0x84: case 0x85: case 0x86: case 0x87: + case 0x88: case 0x89: case 0x8a: case 0x8b: + case 0x8c: case 0x8d: case 0x8e: case 0x8f: + new_lcbank2 = ((loc & 0x8) >> 1) ^ 0x4; + new_wrdefram = (loc & 1); + if(new_wrdefram != wrdefram) { + fixup_wrdefram(new_wrdefram); + } + switch(loc & 0x3) { + case 0x1: /* 0xc081 */ + case 0x2: /* 0xc082 */ + /* Read rom, set lcbank2 */ + set_statereg(dcycs, (statereg & ~(0x04)) | + (new_lcbank2 | 0x08)); + break; + case 0x0: /* 0xc080 */ + case 0x3: /* 0xc083 */ + /* Read ram (clear RDROM), set lcbank2 */ + set_statereg(dcycs, (statereg & ~(0x0c)) | + (new_lcbank2)); + break; + } + return 0xa0; + /* 0xc090 - 0xc09f */ + case 0x90: case 0x91: case 0x92: case 0x93: + case 0x94: case 0x95: case 0x96: case 0x97: + case 0x98: case 0x99: case 0x9a: case 0x9b: + case 0x9c: case 0x9d: case 0x9e: case 0x9f: + /* UNIMPL_READ; */ + return 0; + /* 0xc0a0 - 0xc0af */ + case 0xa0: case 0xa1: case 0xa2: case 0xa3: + case 0xa4: case 0xa5: case 0xa6: case 0xa7: + case 0xa8: case 0xa9: case 0xaa: case 0xab: + case 0xac: case 0xad: case 0xae: case 0xaf: + return 0; + /* UNIMPL_READ; */ + + /* 0xc0b0 - 0xc0bf */ + case 0xb0: + /* c0b0: female voice tool033 look at this */ + return 0; + case 0xb1: case 0xb2: case 0xb3: + case 0xb4: case 0xb5: case 0xb6: case 0xb7: + case 0xb9: case 0xba: case 0xbb: + case 0xbc: case 0xbd: case 0xbe: case 0xbf: + /* UNIMPL_READ; */ + return 0; + /* c0b8: Second Sight card stuff: return 0 */ + case 0xb8: + return 0; + break; + + /* 0xc0c0 - 0xc0cf */ + case 0xc0: case 0xc1: case 0xc2: case 0xc3: + case 0xc4: case 0xc5: case 0xc6: case 0xc7: + case 0xc8: case 0xc9: case 0xca: case 0xcb: + case 0xcc: case 0xcd: case 0xce: case 0xcf: + return 0; + /* 0xc0d0 - 0xc0df */ + case 0xd0: case 0xd1: case 0xd2: case 0xd3: + case 0xd4: case 0xd5: case 0xd6: case 0xd7: + case 0xd8: case 0xd9: case 0xda: case 0xdb: + case 0xdc: case 0xdd: case 0xde: case 0xdf: + return 0; + /* 0xc0e0 - 0xc0ef */ + case 0xe0: case 0xe1: case 0xe2: case 0xe3: + case 0xe4: case 0xe5: case 0xe6: case 0xe7: + case 0xe8: case 0xe9: case 0xea: case 0xeb: + case 0xed: case 0xee: case 0xef: + return read_iwm(loc, dcycs); + case 0xec: + return iwm_read_c0ec(dcycs); + /* 0xc0f0 - 0xc0ff */ + case 0xf0: case 0xf1: case 0xf2: case 0xf3: + case 0xf4: case 0xf5: case 0xf6: case 0xf7: + case 0xf8: case 0xf9: case 0xfa: case 0xfb: + case 0xfc: case 0xfd: case 0xfe: case 0xff: + return 0; + + default: + printf("loc: %04x bad\n", loc); + UNIMPL_READ; + } + case 1: case 2: case 3: case 4: case 5: case 6: + /* c100 - c6ff */ + slot = ((loc >> 8) & 7); + if(INTCX || (int_crom[slot] == 0)) { + return(g_rom_fc_ff_ptr[0x3c000 + (loc & 0xfff)]); + } + return (dummy++) & 0xff; + case 7: + /* c700 */ + if(INTCX || (int_crom[7] == 0)) { + return(g_rom_fc_ff_ptr[0x3c000 + (loc & 0xfff)]); + } + tmp = g_rom_fc_ff_ptr[0x3c500 + (loc & 0xff)]; + if((loc & 0xff) == 0xfb) { + tmp = tmp & 0xbf; /* clear bit 6 for ROM 03 */ + } + return tmp; + case 8: case 9: case 0xa: case 0xb: case 0xc: case 0xd: case 0xe: + if(INTCX || (int_crom[3] == 0)) { + return(g_rom_fc_ff_ptr[0x3c000 + (loc & 0xfff)]); + } + UNIMPL_READ; + case 0xf: + if(INTCX || (int_crom[3] == 0)) { + return(g_rom_fc_ff_ptr[0x3c000 + (loc & 0xfff)]); + } + if((loc & 0xfff) == 0xfff) { + return g_rom_fc_ff_ptr[0x3cfff]; + } + UNIMPL_READ; + } + + halt_printf("io_read: hit end, loc: %06x\n", loc); + + return 0xff; +} + +void +io_write(word32 loc, int val, double *cyc_ptr) +{ + double dcycs; +#if 0 + double fcyc, new_fcyc; +#endif + int new_tmp; + int new_lcbank2; + int new_wrdefram; + int i; + int tmp, tmp2; + int fixup; + + CALC_DCYCS_FROM_CYC_PTR(dcycs, cyc_ptr, fcyc, new_fcyc); + + val = val & 0xff; + switch((loc >> 8) & 0xf) { + case 0: /* 0xc000 - 0xc0ff */ + switch(loc & 0xff) { + /* 0xc000 - 0xc00f */ + case 0x00: /* 0xc000 */ + if(g_cur_a2_stat & ALL_STAT_ST80) { + g_cur_a2_stat &= (~ALL_STAT_ST80); + fixup_st80col(dcycs); + } + return; + case 0x01: /* 0xc001 */ + if((g_cur_a2_stat & ALL_STAT_ST80) == 0) { + g_cur_a2_stat |= (ALL_STAT_ST80); + fixup_st80col(dcycs); + } + return; + case 0x02: /* 0xc002 */ + set_statereg(dcycs, statereg & ~0x20); + return; + case 0x03: /* 0xc003 */ + set_statereg(dcycs, statereg | 0x20); + return; + case 0x04: /* 0xc004 */ + set_statereg(dcycs, statereg & ~0x10); + return; + case 0x05: /* 0xc005 */ + set_statereg(dcycs, statereg | 0x10); + return; + case 0x06: /* 0xc006 */ + set_statereg(dcycs, statereg & ~0x01); + return; + case 0x07: /* 0xc007 */ + set_statereg(dcycs, statereg | 0x01); + return; + case 0x08: /* 0xc008 */ + set_statereg(dcycs, statereg & ~0x80); + return; + case 0x09: /* 0xc009 */ + set_statereg(dcycs, statereg | 0x80); + return; + case 0x0a: /* 0xc00a */ + if(int_crom[3] != 0) { + int_crom[3] = 0; + fixup_intcx(); + } + return; + case 0x0b: /* 0xc00b */ + if(int_crom[3] == 0) { + int_crom[3] = 1; + fixup_intcx(); + } + return; + case 0x0c: /* 0xc00c */ + if(g_cur_a2_stat & ALL_STAT_VID80) { + g_cur_a2_stat &= (~ALL_STAT_VID80); + change_display_mode(dcycs); + } + return; + case 0x0d: /* 0xc00d */ + if((g_cur_a2_stat & ALL_STAT_VID80) == 0) { + g_cur_a2_stat |= (ALL_STAT_VID80); + change_display_mode(dcycs); + } + return; + case 0x0e: /* 0xc00e */ + if(g_cur_a2_stat & ALL_STAT_ALTCHARSET) { + g_cur_a2_stat &= (~ALL_STAT_ALTCHARSET); + change_display_mode(dcycs); + } + return; + case 0x0f: /* 0xc00f */ + if((g_cur_a2_stat & ALL_STAT_ALTCHARSET) == 0) { + g_cur_a2_stat |= (ALL_STAT_ALTCHARSET); + change_display_mode(dcycs); + } + return; + /* 0xc010 - 0xc01f */ + case 0x10: case 0x11: case 0x12: case 0x13: + case 0x14: case 0x15: case 0x16: case 0x17: + case 0x18: case 0x19: case 0x1a: case 0x1b: + case 0x1c: case 0x1d: case 0x1e: case 0x1f: + adb_access_c010(); + return; + /* 0xc020 - 0xc02f */ + case 0x20: /* 0xc020 */ + /* WRITE CASSETTE?? */ + return; + case 0x21: /* 0xc021 */ + new_tmp = ((val >> 7) & 1) << + (31 - BIT_ALL_STAT_COLOR_C021); + if((g_cur_a2_stat & ALL_STAT_COLOR_C021) != new_tmp) { + g_cur_a2_stat ^= new_tmp; + change_display_mode(dcycs); + } + return; + case 0x22: /* 0xc022 */ + /* change text color */ + tmp = (g_cur_a2_stat >> BIT_ALL_STAT_BG_COLOR) & 0xff; + if(val != tmp) { + /* change text/bg color! */ + g_cur_a2_stat &= ~(ALL_STAT_TEXT_COLOR | + ALL_STAT_BG_COLOR); + g_cur_a2_stat += (val << BIT_ALL_STAT_BG_COLOR); + change_display_mode(dcycs); + } + return; + case 0x23: /* 0xc023 */ + if((val & 0x19) != 0) { + halt_printf("c023 write of %02x!!!\n",val); + } + tmp = (g_c023_val & 0x70) | (val & 0x0f); + if(((tmp & 0x22)==0x22) && !c023_scan_int_irq_pending){ + c023_scan_int_irq_pending = 1; + add_irq(); + } + if(!(tmp & 2) && c023_scan_int_irq_pending) { + c023_scan_int_irq_pending = 0; + remove_irq(); + } + if(((tmp & 0x44)==0x44)&& !c023_1sec_int_irq_pending){ + c023_1sec_int_irq_pending = 1; + add_irq(); + } + if(!(tmp & 0x4) && c023_1sec_int_irq_pending) { + c023_1sec_int_irq_pending = 0; + remove_irq(); + } + + if(c023_1sec_int_irq_pending || + c023_scan_int_irq_pending) { + tmp |= 0x80; + } + g_c023_val = tmp; + return; + case 0x24: /* 0xc024 */ + /* Write to mouse reg: Throw it away */ + return; + case 0x26: /* 0xc026 */ + adb_write_c026(val); + return; + case 0x27: /* 0xc027 */ + adb_write_c027(val); + return; + case 0x29: /* 0xc029 */ + bank1latch = val & 1; + linear_vid = (val >> 6) & 1; + new_tmp = val & 0xa0; + if(bank1latch == 0) { + halt_printf("c029: %02x\n", val); + } + if(new_tmp != (g_cur_a2_stat & 0xa0)) { + g_cur_a2_stat = (g_cur_a2_stat & (~0xa0)) + + new_tmp; + change_display_mode(dcycs); + } + return; + case 0x2a: /* 0xc02a */ +#if 0 + printf("Writing c02a with %02x\n", val); +#endif + return; + case 0x2b: /* 0xc02b */ + c02b_val = val; + if(val != 0x08 && val != 0x00) { + printf("Writing c02b with %02x\n", val); + } + return; + case 0x2d: /* 0xc02d */ + if((val & 0x9) != 0) { + halt_printf("Illegal c02d write: %02x!\n", val); + } + fixup = 0; + for(i = 0; i < 8; i++) { + tmp = ((val & (1 << i)) != 0); + if(int_crom[i] != tmp) { + fixup = 1; + int_crom[i] = tmp; + } + } + if(fixup) { + vid_printf("Write c02d of %02x\n", val); + fixup_intcx(); + } + return; + case 0x28: /* 0xc028 */ + case 0x2c: /* 0xc02c */ + UNIMPL_WRITE; + case 0x25: /* 0xc025 */ + /* Space Shark writes to c025--ignore */ + case 0x2e: /* 0xc02e */ + case 0x2f: /* 0xc02f */ + /* Modulae writes to this--just ignore them */ + return; + break; + + /* 0xc030 - 0xc03f */ + case 0x30: /* 0xc030 */ +#if 0 + printf("Write speaker?\n"); +#endif + (void)doc_read_c030(dcycs); + return; + case 0x31: /* 0xc031 */ + tmp = ((val & 0x80) != 0); + tmp2 = ((val & 0x40) != 0); + head_35 = tmp; + iwm_set_apple35_sel(tmp2); + iwm_printf("write c031: %02x, h: %d, 35: %d\n", + val, head_35, g_apple35_sel); + return; + case 0x32: /* 0xc032 */ + tmp = g_c023_val & 0x7f; + if(((val & 0x40) == 0) && (tmp & 0x40)) { + /* clear 1 sec int */ + irq_printf("Clear 1sec int\n"); + if(c023_1sec_int_irq_pending) { + remove_irq(); + } + tmp &= 0xbf; + g_c023_val = tmp; + c023_1sec_int_irq_pending = 0; + } + if(((val & 0x20) == 0) && (tmp & 0x20)) { + /* clear scan line int */ + irq_printf("Clear scn int1\n"); + if(c023_scan_int_irq_pending) { + remove_irq(); + } + c023_scan_int_irq_pending = 0; + g_c023_val = tmp & 0xdf; + check_for_new_scan_int(dcycs); + } + if(c023_1sec_int_irq_pending || + c023_scan_int_irq_pending) { + g_c023_val |= 0x80; + } + if((val & 0x9f) != 0x9f) { + irq_printf("c032: wrote %02x!\n", val); + } + return; + case 0x33: /* 0xc033 = CLOCKDATA*/ + clock_write_c033(val); + return; + case 0x34: /* 0xc034 = CLOCKCTL */ + clock_write_c034(val); + if((val & 0xf) != g_border_color) { + g_border_color = val & 0xf; + change_border_color(dcycs, val & 0xf); + } + return; + case 0x35: /* 0xc035 */ + update_shadow_reg(val); + return; + case 0x36: /* 0xc036 = CYAREG */ + tmp = (val>>7) & 1; + if(speed_fast != tmp) { + /* to recalculate times */ + set_halt(HALT_EVENT); + } + speed_fast = tmp; + if((val & 0xf) != (int)g_slot_motor_detect) { + set_halt(HALT_EVENT); + } + g_slot_motor_detect = (val & 0xf); + + power_on_clear = (val >> 6) & 1; + if((val & 0x60) != 0) { + halt_printf("c036: %2x\n", val); + } + tmp = (val >> 4) & 1; + if(tmp != g_shadow_all_banks) { + if(g_num_shadow_all_banks++ == 0) { + printf("Shadowing all banks...This " + "must be the NFC Megademo\n"); + } + g_shadow_all_banks = tmp; + fixup_shadow_all_banks(); + } + return; + case 0x37: /* 0xc037 */ + /* just ignore, probably someone writing c036 m=0 */ + return; + case 0x38: /* 0xc038 */ + scc_write_reg(1, val, dcycs); + return; + case 0x39: /* 0xc039 */ + scc_write_reg(0, val, dcycs); + return; + case 0x3a: /* 0xc03a */ + scc_write_data(1, val, dcycs); + return; + case 0x3b: /* 0xc03b */ + scc_write_data(0, val, dcycs); + return; + case 0x3c: /* 0xc03c */ + /* doc ctl */ + doc_write_c03c(val, dcycs); + return; + case 0x3d: /* 0xc03d */ + /* doc data reg */ + doc_write_c03d(val, dcycs); + return; + case 0x3e: /* 0xc03e */ + doc_write_c03e(val); + return; + case 0x3f: /* 0xc03f */ + doc_write_c03f(val); + return; + + /* 0xc040 - 0xc04f */ + case 0x41: /* c041 */ + c041_en_25sec_ints = ((val & 0x10) != 0); + c041_en_vbl_ints = ((val & 0x8) != 0); + c041_en_switch_ints = ((val & 0x4) != 0); + c041_en_move_ints = ((val & 0x2) != 0); + c041_en_mouse = ((val & 0x1) != 0); + if((val & 0xe7) != 0) { + halt_printf("write c041: %02x\n", val); + } + + if(!c041_en_vbl_ints && c046_vbl_irq_pending) { + /* there was an interrupt, but no more*/ + remove_irq(); + c046_vbl_irq_pending = 0; + } + if(!c041_en_25sec_ints && c046_25sec_irq_pend) { + /* there was an interrupt, but no more*/ + remove_irq(); + c046_25sec_irq_pend = 0; + } + return; + case 0x46: /* c046 */ + /* ignore writes to c046 */ + return; + case 0x47: /* c047 */ + if(c046_vbl_irq_pending) { + remove_irq(); + c046_vbl_irq_pending = 0; + } + if(c046_25sec_irq_pend) { + remove_irq(); + c046_25sec_irq_pend = 0; + } + g_c046_val &= 0xe7; /* clear vblint, 1/4sec int*/ + return; + case 0x48: /* c048 */ + /* diversitune writes this--ignore it */ + return; + case 0x42: /* c042 */ + case 0x43: /* c043 */ + return; + case 0x4f: /* c04f */ + g_emubyte_cnt = 1; + return; + case 0x40: /* c040 */ + case 0x44: /* c044 */ + case 0x45: /* c045 */ + case 0x49: /* c049 */ + case 0x4a: /* c04a */ + case 0x4b: /* c04b */ + case 0x4c: /* c04c */ + case 0x4d: /* c04d */ + case 0x4e: /* c04e */ + UNIMPL_WRITE; + + /* 0xc050 - 0xc05f */ + case 0x50: /* 0xc050 */ + if(g_cur_a2_stat & ALL_STAT_TEXT) { + g_cur_a2_stat &= (~ALL_STAT_TEXT); + change_display_mode(dcycs); + } + return; + case 0x51: /* 0xc051 */ + if((g_cur_a2_stat & ALL_STAT_TEXT) == 0) { + g_cur_a2_stat |= (ALL_STAT_TEXT); + change_display_mode(dcycs); + } + return; + case 0x52: /* 0xc052 */ + if(g_cur_a2_stat & ALL_STAT_MIX_T_GR) { + g_cur_a2_stat &= (~ALL_STAT_MIX_T_GR); + change_display_mode(dcycs); + } + return; + case 0x53: /* 0xc053 */ + if((g_cur_a2_stat & ALL_STAT_MIX_T_GR) == 0) { + g_cur_a2_stat |= (ALL_STAT_MIX_T_GR); + change_display_mode(dcycs); + } + return; + case 0x54: /* 0xc054 */ + set_statereg(dcycs, statereg & (~0x40)); + return; + case 0x55: /* 0xc055 */ + set_statereg(dcycs, statereg | 0x40); + return; + case 0x56: /* 0xc056 */ + if(g_cur_a2_stat & ALL_STAT_HIRES) { + g_cur_a2_stat &= (~ALL_STAT_HIRES); + fixup_hires_on(); + change_display_mode(dcycs); + } + return; + case 0x57: /* 0xc057 */ + if((g_cur_a2_stat & ALL_STAT_HIRES) == 0) { + g_cur_a2_stat |= (ALL_STAT_HIRES); + fixup_hires_on(); + change_display_mode(dcycs); + } + return; + case 0x58: /* 0xc058 */ + if(g_zipgs_unlock >= 4) { + g_zipgs_reg_c059 &= 0x4; /* last reset cold */ + } else { + annunc_0 = 0; + } + return; + case 0x59: /* 0xc059 */ + if(g_zipgs_unlock >= 4) { + g_zipgs_reg_c059 = (val & 0xf8) | + (g_zipgs_reg_c059 & 0x7); + } else { + annunc_0 = 1; + } + return; + case 0x5a: /* 0xc05a */ + annunc_1 = 0; + if((val & 0xf0) == 0x50) { + g_zipgs_unlock++; + } else if((val & 0xf0) == 0xa0) { + g_zipgs_unlock = 0; + } else if(g_zipgs_unlock >= 4) { + if((g_zipgs_reg_c05b & 0x10) == 0) { + /* to recalculate times */ + set_halt(HALT_EVENT); + } + g_zipgs_reg_c05b |= 0x10; // disable + } + return; + case 0x5b: /* 0xc05b */ + if(g_zipgs_unlock >= 4) { + if((g_zipgs_reg_c05b & 0x10) != 0) { + /* to recalculate times */ + set_halt(HALT_EVENT); + } + g_zipgs_reg_c05b &= (~0x10); // enable + } else { + annunc_1 = 1; + } + return; + case 0x5c: /* 0xc05c */ + if(g_zipgs_unlock >= 4) { + g_zipgs_reg_c05c = val; + } else { + annunc_2 = 0; + } + return; + case 0x5d: /* 0xc05d */ + if(g_zipgs_unlock >= 4) { + if(((g_zipgs_reg_c05a ^ val) >= 0x10) && + ((g_zipgs_reg_c05b & 0x10) == 0)) { + set_halt(HALT_EVENT); + } + g_zipgs_reg_c05a = val | 0xf; + } else { + annunc_2 = 1; + } + return; + case 0x5e: /* 0xc05e */ + if(g_zipgs_unlock >= 4) { + /* Zippy writes 0x80 and 0x00 here... */ + } else if(g_cur_a2_stat & ALL_STAT_ANNUNC3) { + g_cur_a2_stat &= (~ALL_STAT_ANNUNC3); + change_display_mode(dcycs); + } + return; + case 0x5f: /* 0xc05f */ + if(g_zipgs_unlock >= 4) { + halt_printf("Wrote ZipGS $c05f: %02x\n", val); + } else if((g_cur_a2_stat & ALL_STAT_ANNUNC3) == 0) { + g_cur_a2_stat |= (ALL_STAT_ANNUNC3); + change_display_mode(dcycs); + } + return; + + + /* 0xc060 - 0xc06f */ + case 0x60: /* 0xc060 */ + case 0x61: /* 0xc061 */ + case 0x62: /* 0xc062 */ + case 0x63: /* 0xc063 */ + case 0x64: /* 0xc064 */ + case 0x65: /* 0xc065 */ + case 0x66: /* 0xc066 */ + case 0x67: /* 0xc067 */ + /* all the above do nothing--return */ + return; + case 0x68: /* 0xc068 = STATEREG */ + set_statereg(dcycs, val); + return; + case 0x69: /* 0xc069 */ + /* just ignore, someone writing c068 with m=0 */ + return; + case 0x6a: /* 0xc06a */ + case 0x6b: /* 0xc06b */ + case 0x6c: /* 0xc06c */ + case 0x6d: /* 0xc06d */ + case 0x6e: /* 0xc06e */ + case 0x6f: /* 0xc06f */ + UNIMPL_WRITE; + + /* 0xc070 - 0xc07f */ + case 0x70: /* 0xc070 = Trigger paddles */ + paddle_trigger(dcycs); + return; + case 0x73: /* 0xc073 = multibank ram card bank addr? */ + return; + case 0x71: /* 0xc071 = another multibank ram card enable? */ + case 0x7e: /* 0xc07e */ + case 0x7f: /* 0xc07f */ + return; + case 0x72: /* 0xc072 */ + case 0x74: /* 0xc074 */ + case 0x75: /* 0xc075 */ + case 0x76: /* 0xc076 */ + case 0x77: /* 0xc077 */ + case 0x78: /* 0xc078 */ + case 0x79: /* 0xc079 */ + case 0x7a: /* 0xc07a */ + case 0x7b: /* 0xc07b */ + case 0x7c: /* 0xc07c */ + case 0x7d: /* 0xc07d */ + UNIMPL_WRITE; + + /* 0xc080 - 0xc08f */ + case 0x80: case 0x81: case 0x82: case 0x83: + case 0x84: case 0x85: case 0x86: case 0x87: + case 0x88: case 0x89: case 0x8a: case 0x8b: + case 0x8c: case 0x8d: case 0x8e: case 0x8f: + new_lcbank2 = ((loc & 0x8) >> 1) ^ 0x4; + new_wrdefram = (loc & 1); + if(new_wrdefram != wrdefram) { + fixup_wrdefram(new_wrdefram); + } + switch(loc & 0xf) { + case 0x1: /* 0xc081 */ + case 0x2: /* 0xc082 */ + case 0x5: /* 0xc085 */ + case 0x6: /* 0xc086 */ + case 0x9: /* 0xc089 */ + case 0xa: /* 0xc08a */ + case 0xd: /* 0xc08d */ + case 0xe: /* 0xc08e */ + /* Read rom, set lcbank2 */ + set_statereg(dcycs, (statereg & ~(0x04)) | + (new_lcbank2 | 0x08)); + break; + case 0x0: /* 0xc080 */ + case 0x3: /* 0xc083 */ + case 0x4: /* 0xc084 */ + case 0x7: /* 0xc087 */ + case 0x8: /* 0xc088 */ + case 0xb: /* 0xc08b */ + case 0xc: /* 0xc08c */ + case 0xf: /* 0xc08f */ + /* Read ram (clear RDROM), set lcbank2 */ + set_statereg(dcycs, (statereg & ~(0x0c)) | + (new_lcbank2)); + break; + } + return; + + /* 0xc090 - 0xc09f */ + case 0x90: case 0x91: case 0x92: case 0x93: + case 0x94: case 0x95: case 0x96: case 0x97: + case 0x98: case 0x99: case 0x9a: case 0x9b: + case 0x9c: case 0x9d: case 0x9e: case 0x9f: + UNIMPL_WRITE; + + /* 0xc0a0 - 0xc0af */ + case 0xa0: case 0xa1: case 0xa2: case 0xa3: + case 0xa4: case 0xa5: case 0xa6: case 0xa7: + case 0xa9: case 0xaa: case 0xab: + case 0xac: case 0xad: case 0xae: case 0xaf: + UNIMPL_WRITE; + case 0xa8: + /* Kurzweil SMP writes to 0xc0a8, ignore it */ + return; + + /* 0xc0b0 - 0xc0bf */ + case 0xb0: + /* Second sight stuff--ignore it */ + return; + case 0xb1: case 0xb2: case 0xb3: + case 0xb4: case 0xb5: case 0xb6: case 0xb7: + case 0xb8: case 0xb9: case 0xba: case 0xbb: + case 0xbc: case 0xbd: case 0xbe: case 0xbf: + UNIMPL_WRITE; + + /* 0xc0c0 - 0xc0cf */ + case 0xc0: case 0xc1: case 0xc2: case 0xc3: + case 0xc4: case 0xc5: case 0xc6: case 0xc7: + case 0xc8: case 0xc9: case 0xca: case 0xcb: + case 0xcc: case 0xcd: case 0xce: case 0xcf: + UNIMPL_WRITE; + + /* 0xc0d0 - 0xc0df */ + case 0xd0: case 0xd1: case 0xd2: case 0xd3: + case 0xd4: case 0xd5: case 0xd6: case 0xd7: + case 0xd8: case 0xd9: case 0xda: case 0xdb: + case 0xdc: case 0xdd: case 0xde: case 0xdf: + UNIMPL_WRITE; + + /* 0xc0e0 - 0xc0ef */ + case 0xe0: case 0xe1: case 0xe2: case 0xe3: + case 0xe4: case 0xe5: case 0xe6: case 0xe7: + case 0xe8: case 0xe9: case 0xea: case 0xeb: + case 0xec: case 0xed: case 0xee: case 0xef: + write_iwm(loc, val, dcycs); + return; + + /* 0xc0f0 - 0xc0ff */ + case 0xf0: case 0xf1: case 0xf2: case 0xf3: + case 0xf4: case 0xf5: case 0xf6: case 0xf7: + case 0xf8: case 0xf9: case 0xfa: case 0xfb: + case 0xfc: case 0xfd: case 0xfe: case 0xff: + UNIMPL_WRITE; + default: + printf("WRite loc: %x\n",loc); + exit(-300); + } + break; + case 1: case 2: case 3: case 4: case 5: case 6: case 7: + /* c1000 - c7ff */ + UNIMPL_WRITE; + case 8: case 9: case 0xa: case 0xb: case 0xc: case 0xd: case 0xe: + UNIMPL_WRITE; + case 0xf: + if((loc & 0xfff) == 0xfff) { + /* cfff */ + return; + } + UNIMPL_WRITE; + } + printf("Huh2? Write loc: %x\n", loc); + exit(-290); +} + + + +#if 0 +int +get_slow_mem(word32 loc, int duff_cycles) +{ + int val; + + loc = loc & 0x1ffff; + + if((loc &0xf000) == 0xc000) { + return(io_read(loc &0xfff, duff_cycles)); + } + if((loc & 0xf000) >= 0xd000) { + if((loc & 0xf000) == 0xd000) { + if(!LCBANK2) { + /* Not LCBANK2 == be 0xc000 - 0xd000 */ + loc = loc - 0x1000; + } + } + } + + val = g_slow_memory_ptr[loc]; + + halt_printf("get_slow_mem: %06x = %02x\n", loc, val); + + return val; +} + +int +set_slow_mem(word32 loc, int val, int duff_cycles) +{ + int or_pos; + word32 or_val; + + loc = loc & 0x1ffff; + if((loc & 0xf000) == 0xc000) { + return(io_write(loc & 0xfff, val, duff_cycles)); + } + + if((loc & 0xf000) == 0xd000) { + if(!LCBANK2) { + /* Not LCBANK2 == be 0xc000 - 0xd000 */ + loc = loc - 0x1000; + } + } + + if(g_slow_memory_ptr[loc] != val) { + or_pos = (loc >> SHIFT_PER_CHANGE) & 0x1f; + or_val = DEP1(1, or_pos, 0); + if((loc >> CHANGE_SHIFT) >= SLOW_MEM_CH_SIZE || loc < 0) { + printf("loc: %08x!!\n", loc); + exit(11); + } + slow_mem_changed[(loc & 0xffff) >> CHANGE_SHIFT] |= or_val; + } + +/* doesn't shadow text/hires graphics properly! */ + g_slow_memory_ptr[loc] = val; + + return val; +} +#endif + +/* IIgs vertical line counters */ +/* 0x7d - 0x7f: in vbl, top of screen? */ +/* 0x80 - 0xdf: not in vbl, drawing screen */ +/* 0xe0 - 0xff: in vbl, bottom of screen */ + +/* Note: lines are then 0-0x60 effectively, for 192 lines */ +/* vertical blanking engages on line 192, even if in super hires mode */ +/* (Last 8 lines in SHR are drawn with vbl_active set */ + +word32 +get_lines_since_vbl(double dcycs) +{ + double dcycs_since_last_vbl; + double dlines_since_vbl; + double dcyc_line_start; + word32 lines_since_vbl; + int offset; + + dcycs_since_last_vbl = dcycs - g_last_vbl_dcycs; + + dlines_since_vbl = (262.0/DCYCS_IN_16MS) * dcycs_since_last_vbl; + lines_since_vbl = (int)dlines_since_vbl; + dcyc_line_start = (double)lines_since_vbl * (DCYCS_IN_16MS/262.0); + + offset = ((int)(dcycs_since_last_vbl - dcyc_line_start)) & 0xff; + + lines_since_vbl = (lines_since_vbl << 8) + offset; + + if(lines_since_vbl < 0x10680) { + return lines_since_vbl; + } else { + halt_printf("lines_since_vbl: %08x!\n", lines_since_vbl); + printf("dc_s_l_v: %f, dcycs: %f, last_vbl_cycs: %f\n", + dcycs_since_last_vbl, dcycs, g_last_vbl_dcycs); + show_dtime_array(); + show_all_events(); + /* U_STACK_TRACE(); */ + } + + return lines_since_vbl; +} + + +int +in_vblank(double dcycs) +{ + int lines_since_vbl; + + lines_since_vbl = get_lines_since_vbl(dcycs); + + if(lines_since_vbl >= 0xc000) { + return 1; + } + + return 0; +} + +/* horizontal video counter goes from 0x00,0x40 - 0x7f, then 0x80,0xc0-0xff */ +/* over 2*65 cycles. The last visible screen pos is 0x7f and 0xff */ +/* This matches KEGS starting line 0 at the border for line -1 */ +int +read_vid_counters(int loc, double dcycs) +{ + word32 mask; + int lines_since_vbl; + + loc = loc & 0xf; + + lines_since_vbl = get_lines_since_vbl(dcycs); + + lines_since_vbl += 0x10000; + if(lines_since_vbl >= 0x20000) { + lines_since_vbl = lines_since_vbl - 0x20000 + 0xfa00; + } + + if(lines_since_vbl > 0x1ffff) { + halt_printf("lines_since_vbl: %04x, dcycs: %f, last_vbl: %f\n", + lines_since_vbl, dcycs, g_last_vbl_dcycs); + } + + if(loc == 0xe) { + /* Vertical count */ + return (lines_since_vbl >> 9) & 0xff; + } + + mask = (lines_since_vbl >> 1) & 0x80; + + lines_since_vbl = (lines_since_vbl & 0xff); + if(lines_since_vbl >= 0x01) { + lines_since_vbl = (lines_since_vbl + 0x3f) & 0x7f; + } + return (mask | (lines_since_vbl & 0xff)); +} + diff --git a/src/objects.xib b/src/objects.xib new file mode 100644 index 0000000..616ea10 --- /dev/null +++ b/src/objects.xib @@ -0,0 +1,169 @@ + + + IBCarbonFramework + + NSApplication + + + + main + + + KEGSMAC + + KEGSMAC + + + About KEGSMAC + 0 + abou + + + TRUE + + + Quit + 0 + quit + + + _NSAppleMenu + + + + File + + File + + + Configuration F4 + 0 + KCFG + Enter KEGS Configuration Panel + + + + + + Window + + Window + + + Zoom Window + zoom + + + TRUE + Minimize Window + 0 + mini + + + TRUE + Minimize All Windows + 0 + mina + + + TRUE + + + TRUE + Bring All to Front + bfrt + + + TRUE + Arrange in Front + 1572864 + frnt + + + _NSWindowsMenu + + + + _NSMainMenu + + + + + Window + + Window + + + TRUE + Minimize Window + m + mini + + + TRUE + Minimize All Windows + m + 1572864 + mini + + + TRUE + + + TRUE + Bring All to Front + frnt + + + TRUE + Bring in Front + 1572864 + frnt + + + _NSWindowsMenu + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Files Owner + + MenuBar + + + 201 + diff --git a/src/op_routs.h b/src/op_routs.h new file mode 100644 index 0000000..a8d6489 --- /dev/null +++ b/src/op_routs.h @@ -0,0 +1,466 @@ +/************************************************************************/ +/* KEGS: Apple //gs Emulator */ +/* Copyright 2002 by Kent Dickey */ +/* */ +/* This code is covered by the GNU GPL */ +/* */ +/* The KEGS web page is kegs.sourceforge.net */ +/* You may contact the author at: kadickey@alumni.princeton.edu */ +/************************************************************************/ + +#ifdef ASM +# ifdef INCLUDE_RCSID_S + .data + .export rcsdif_op_routs_h,data +rcsdif_op_routs_h + .stringz "@(#)$KmKId: op_routs.h,v 1.40 2004-01-10 15:49:46-05 kentd Exp $" + .code +# endif + + .import get_mem_b0_16,code + .import get_mem_b0_8,code + + .export op_routs_start,data +op_routs_start .word 0 +#endif /* ASM */ + +#ifdef ASM +# define CMP_INDEX_REG_MEAT8(index_reg) \ + extru ret0,31,8,ret0 ! \ + ldi 0xff,scratch3 ! \ + subi 0x100,ret0,ret0 ! \ + add index_reg,ret0,ret0 ! \ + extru ret0,23,1,scratch1 ! \ + and ret0,scratch3,zero ! \ + extru ret0,24,1,neg ! \ + b dispatch ! \ + dep scratch1,31,1,psr + +# define CMP_INDEX_REG_MEAT16(index_reg) \ + extru ret0,31,16,ret0 ! \ + ldil l%0x10000,scratch2 ! \ + zdepi -1,31,16,scratch3 ! \ + sub scratch2,ret0,ret0 ! \ + add index_reg,ret0,ret0 ! \ + extru ret0,15,1,scratch1 ! \ + and ret0,scratch3,zero ! \ + extru ret0,16,1,neg ! \ + b dispatch ! \ + dep scratch1,31,1,psr + +# define CMP_INDEX_REG_LOAD(new_label, index_reg) \ + bb,>=,n psr,27,new_label ! \ + bl get_mem_long_8,link ! \ + nop ! \ + CMP_INDEX_REG_MEAT8(index_reg) ! \ + .label new_label ! \ + bl get_mem_long_16,link ! \ + nop ! \ + CMP_INDEX_REG_MEAT16(index_reg) +#endif + + +#ifdef ASM +#define GET_DLOC_X_IND_WR() \ + CYCLES_PLUS_1 ! \ + add xreg,direct,scratch2 ! \ + INC_KPC_2 ! \ + add scratch2,arg0,arg0 ! \ + bl get_mem_b0_direct_page_16,link ! \ + extru arg0,31,16,arg0 ! \ + copy ret0,arg0 ! \ + extru,= direct,31,8,0 ! \ + CYCLES_PLUS_1 ! \ + dep dbank,15,8,arg0 +#else /* C */ +# define GET_DLOC_X_IND_WR() \ + CYCLES_PLUS_1; \ + INC_KPC_2; \ + if(direct & 0xff) { \ + CYCLES_PLUS_1; \ + } \ + arg = arg + xreg + direct; \ + GET_MEMORY_DIRECT_PAGE16(arg & 0xffff, arg); \ + arg = (dbank << 16) + arg; +#endif + + +#ifdef ASM +# define GET_DLOC_X_IND_ADDR() \ + ldb 1(scratch1),arg0 ! \ + GET_DLOC_X_IND_WR() +#else /* C */ +# define GET_DLOC_X_IND_ADDR() \ + GET_1BYTE_ARG; \ + GET_DLOC_X_IND_WR() +#endif + +#ifdef ASM +# define GET_DISP8_S_WR() \ + CYCLES_PLUS_1 ! \ + add stack,arg0,arg0 ! \ + INC_KPC_2 ! \ + extru arg0,31,16,arg0 +#else /* C */ +#define GET_DISP8_S_WR() \ + CYCLES_PLUS_1; \ + arg = (arg + stack) & 0xffff; \ + INC_KPC_2; +#endif + + +#ifdef ASM +# define GET_DISP8_S_ADDR() \ + ldb 1(scratch1),arg0 ! \ + GET_DISP8_S_WR() +#else /* C */ +# define GET_DISP8_S_ADDR() \ + GET_1BYTE_ARG; \ + GET_DISP8_S_WR() +#endif + +#ifdef ASM +# define GET_DLOC_WR() \ + INC_KPC_2 ! \ + extru,= direct,31,8,0 ! \ + CYCLES_PLUS_1 ! \ + add direct,arg0,arg0 ! \ + extru arg0,31,16,arg0 +#else /* C */ +# define GET_DLOC_WR() \ + arg = (arg + direct) & 0xffff; \ + if(direct & 0xff) { \ + CYCLES_PLUS_1; \ + } \ + INC_KPC_2; +#endif + +#ifdef ASM +# define GET_DLOC_ADDR() \ + ldb 1(scratch1),arg0 ! \ + GET_DLOC_WR() +#else /* C */ +# define GET_DLOC_ADDR() \ + GET_1BYTE_ARG; \ + GET_DLOC_WR() +#endif + +#ifdef ASM +# define GET_DLOC_L_IND_WR() \ + INC_KPC_2 ! \ + extru,= direct,31,8,0 ! \ + CYCLES_PLUS_1 ! \ + add direct,arg0,arg0 ! \ + bl get_mem_b0_24,link ! \ + extru arg0,31,16,arg0 ! \ + copy ret0,arg0 +#else /* C */ +# define GET_DLOC_L_IND_WR() \ + arg = (arg + direct) & 0xffff; \ + if(direct & 0xff) { \ + CYCLES_PLUS_1; \ + } \ + INC_KPC_2; \ + GET_MEMORY24(arg, arg, 1); +#endif + + +#ifdef ASM +# define GET_DLOC_L_IND_ADDR() \ + ldb 1(scratch1),arg0 ! \ + GET_DLOC_L_IND_WR() +#else /* C */ +# define GET_DLOC_L_IND_ADDR() \ + GET_1BYTE_ARG; \ + GET_DLOC_L_IND_WR() +#endif + +#ifdef ASM +# define GET_DLOC_IND_Y_ADDR_FOR_WR() \ + ldb 1(scratch1),arg0 ! \ + CYCLES_PLUS_1 ! \ + GET_DLOC_IND_Y_WR_SPECIAL() +#else /* C */ +# define GET_DLOC_IND_Y_ADDR_FOR_WR() \ + GET_1BYTE_ARG; \ + if(direct & 0xff) { \ + CYCLES_PLUS_1; \ + } \ + GET_MEMORY_DIRECT_PAGE16((direct + arg) & 0xffff, tmp1); \ + tmp1 += (dbank << 16); \ + arg = tmp1 + yreg; \ + CYCLES_PLUS_1; \ + INC_KPC_2; +#endif + + +#ifdef ASM +# define GET_DLOC_IND_WR() \ + extru,= direct,31,8,0 ! \ + CYCLES_PLUS_1 ! \ + INC_KPC_2 ! \ + add direct,arg0,arg0 ! \ + bl get_mem_b0_direct_page_16,link ! \ + extru arg0,31,16,arg0 ! \ + copy ret0,arg0 ! \ + dep dbank,15,16,arg0 +#else /* C */ +# define GET_DLOC_IND_WR() \ + INC_KPC_2; \ + if(direct & 0xff) { \ + CYCLES_PLUS_1; \ + } \ + GET_MEMORY_DIRECT_PAGE16((direct + arg) & 0xffff, arg); \ + arg = (dbank << 16) + arg; +#endif + + +#ifdef ASM +# define GET_DLOC_IND_ADDR() \ + ldb 1(scratch1),arg0 ! \ + GET_DLOC_IND_WR() +#else +# define GET_DLOC_IND_ADDR() \ + GET_1BYTE_ARG; \ + GET_DLOC_IND_WR(); +#endif + +#ifdef ASM +#define GET_DLOC_INDEX_WR(index_reg) \ + GET_DLOC_INDEX_WR_A(index_reg) ! GET_DLOC_INDEX_WR_B(index_reg) + +#define GET_DLOC_INDEX_WR_A(index_reg) \ + CYCLES_PLUS_1 ! \ + add index_reg,direct,scratch2 ! \ + extru direct,23,8,scratch1 ! \ + INC_KPC_2 ! \ + extru,= direct,31,8,0 ! \ + CYCLES_PLUS_1 ! \ + bb,>= psr,23,.+16 ! \ +/* 4*/ add scratch2,arg0,arg0 ! \ +/* 8*/ extru,<> direct,31,8,0 ! \ +/*12*/ dep scratch1,23,8,arg0 + +/* GET_DLOC_INDeX_WR_B must be exactly one instruction! */ +#define GET_DLOC_INDEX_WR_B(index_reg) \ +/*16*/ extru arg0,31,16,arg0 + +#define GET_DLOC_Y_WR() \ + GET_DLOC_INDEX_WR(yreg) + +#define GET_DLOC_X_WR() \ + GET_DLOC_INDEX_WR(xreg) + +#define GET_DLOC_Y_ADDR() \ + ldb 1(scratch1),arg0 ! \ + GET_DLOC_Y_WR() + +# define GET_DLOC_X_ADDR() \ + ldb 1(scratch1),arg0 ! \ + GET_DLOC_X_WR() + +#else +# define GET_DLOC_INDEX_WR(index_reg) \ + CYCLES_PLUS_1; \ + arg = (arg & 0xff) + index_reg; \ + INC_KPC_2; \ + if(direct & 0xff) { \ + CYCLES_PLUS_1; \ + } \ + if((psr & 0x100) && ((direct & 0xff) == 0)) { \ + arg = (arg & 0xff); \ + } \ + arg = (arg + direct) & 0xffff; + +# define GET_DLOC_X_WR() \ + GET_DLOC_INDEX_WR(xreg) +# define GET_DLOC_Y_WR() \ + GET_DLOC_INDEX_WR(yreg) + +# define GET_DLOC_X_ADDR() \ + GET_1BYTE_ARG; \ + GET_DLOC_INDEX_WR(xreg) + +# define GET_DLOC_Y_ADDR() \ + GET_1BYTE_ARG; \ + GET_DLOC_INDEX_WR(yreg) +#endif + + +#ifdef ASM +# define GET_DISP8_S_IND_Y_WR() \ + add stack,arg0,arg0 ! \ + bl get_mem_b0_16,link ! \ + extru arg0,31,16,arg0 ! \ + dep dbank,15,16,ret0 ! \ + CYCLES_PLUS_2 ! \ + add ret0,yreg,arg0 ! \ + INC_KPC_2 ! \ + extru arg0,31,24,arg0 + +# define GET_DISP8_S_IND_Y_ADDR() \ + ldb 1(scratch1),arg0 ! \ + GET_DISP8_S_IND_Y_WR() +#else /* C */ + +# define GET_DISP8_S_IND_Y_WR() \ + arg = (stack + arg) & 0xffff; \ + GET_MEMORY16(arg,arg,1); \ + CYCLES_PLUS_2; \ + arg += (dbank << 16); \ + INC_KPC_2; \ + arg = (arg + yreg) & 0xffffff; + +# define GET_DISP8_S_IND_Y_ADDR() \ + GET_1BYTE_ARG; \ + GET_DISP8_S_IND_Y_WR() +#endif + + +#ifdef ASM +# define GET_DLOC_L_IND_Y_WR() \ + extru,= direct,31,8,0 ! \ + CYCLES_PLUS_1 ! \ + INC_KPC_2 ! \ + add direct,arg0,arg0 ! \ + bl get_mem_b0_24,link ! \ + extru arg0,31,16,arg0 ! \ + add ret0,yreg,arg0 ! \ + extru arg0,31,24,arg0 + +# define GET_DLOC_L_IND_Y_ADDR() \ + ldb 1(scratch1),arg0 ! \ + GET_DLOC_L_IND_Y_WR() +#else /* C */ + +# define GET_DLOC_L_IND_Y_WR() \ + arg = (direct + arg) & 0xffff; \ + if(direct & 0xff) { \ + CYCLES_PLUS_1; \ + } \ + GET_MEMORY24(arg,arg,1); \ + INC_KPC_2; \ + arg = (arg + yreg) & 0xffffff; + +# define GET_DLOC_L_IND_Y_ADDR() \ + GET_1BYTE_ARG; \ + GET_DLOC_L_IND_Y_WR() +#endif + + +#ifdef ASM +# define GET_ABS_ADDR() \ + ldb 1(scratch1),arg0 ! \ + ldb 2(scratch1),scratch1 ! \ + CYCLES_PLUS_1 ! \ + dep dbank,15,8,arg0 ! \ + INC_KPC_3 ! \ + dep scratch1,23,8,arg0 + +# define GET_LONG_ADDR() \ + ldb 1(scratch1),arg0 ! \ + ldb 2(scratch1),scratch2 ! \ + CYCLES_PLUS_2 ! \ + ldb 3(scratch1),scratch1 ! \ + INC_KPC_4 ! \ + dep scratch2,23,8,arg0 ! \ + dep scratch1,15,8,arg0 +#else /* C */ + +# define GET_ABS_ADDR() \ + GET_2BYTE_ARG; \ + CYCLES_PLUS_1; \ + arg = arg + (dbank << 16); \ + INC_KPC_3; + +# define GET_LONG_ADDR() \ + GET_3BYTE_ARG; \ + CYCLES_PLUS_2; \ + INC_KPC_4; +#endif + +#ifdef ASM +#define GET_ABS_INDEX_ADDR_FOR_WR(index_reg) \ + ldb 1(scratch1),arg0 ! \ + copy index_reg,scratch3 ! \ + ldb 2(scratch1),scratch2 ! \ + dep dbank,15,8,scratch3 ! \ + INC_KPC_3 ! \ + dep scratch2,23,8,arg0 ! \ + CYCLES_PLUS_2 ! \ + add arg0,scratch3,arg0 ! \ + extru arg0,31,24,arg0 + +#define GET_LONG_X_ADDR_FOR_WR() \ + ldb 3(scratch1),scratch2 ! \ + copy xreg,scratch3 ! \ + ldb 1(scratch1),arg0 ! \ + ldb 2(scratch1),scratch1 ! \ + CYCLES_PLUS_2 ! \ + dep scratch2,15,8,scratch3 ! \ + INC_KPC_4 ! \ + dep scratch1,23,8,arg0 ! \ + add arg0,scratch3,arg0 ! \ + extru arg0,31,24,arg0 +#else /* C */ + +#define GET_ABS_INDEX_ADDR_FOR_WR(index_reg) \ + GET_2BYTE_ARG; \ + arg = arg + (dbank << 16); \ + INC_KPC_3; \ + CYCLES_PLUS_2; \ + arg = (arg + index_reg) & 0xffffff; + +#define GET_LONG_X_ADDR_FOR_WR() \ + GET_3BYTE_ARG; \ + INC_KPC_4; \ + arg = (arg + xreg) & 0xffffff; \ + CYCLES_PLUS_2; + +#endif /* ASM */ + + +#ifdef ASM + .export op_routs_end,data +op_routs_end .word 0 + + +#define GET_DLOC_IND_Y_WR_SPECIAL() \ + add direct,arg0,arg0 ! \ + extru,= direct,31,8,0 ! \ + CYCLES_PLUS_1 ! \ + bl get_mem_b0_direct_page_16,link ! \ + extru arg0,31,16,arg0 ! \ + dep dbank,15,8,ret0 ! \ + INC_KPC_2 ! \ + add yreg,ret0,arg0 /* don't change this instr */ + /* or add any after */ + /* to preserve ret0 & arg0 */ + + +/* cycle calc: if yreg is 16bit or carry into 2nd byte, inc cycle */ +/* So, if y==16bit, add 1. If x==8bit, add 1 if carry */ +get_dloc_ind_y_rd_8 + stw link,STACK_SAVE_OP_LINK(sp) + GET_DLOC_IND_Y_WR_SPECIAL() + xor arg0,ret0,scratch1 + extru,= psr,27,1,0 + extru,= scratch1,23,8,0 + CYCLES_PLUS_1 + b get_mem_long_8 + ldw STACK_SAVE_OP_LINK(sp),link + +get_dloc_ind_y_rd_16 + stw link,STACK_SAVE_OP_LINK(sp) + GET_DLOC_IND_Y_WR_SPECIAL() + xor arg0,ret0,scratch1 + extru,= psr,27,1,0 + extru,= scratch1,23,8,0 + CYCLES_PLUS_1 + b get_mem_long_16 + ldw STACK_SAVE_OP_LINK(sp),link + + + +#endif /* ASM */ + diff --git a/src/paddles.c b/src/paddles.c new file mode 100644 index 0000000..ec62a0e --- /dev/null +++ b/src/paddles.c @@ -0,0 +1,114 @@ +/************************************************************************/ +/* KEGS: Apple //gs Emulator */ +/* Copyright 2002 by Kent Dickey */ +/* */ +/* This code is covered by the GNU GPL */ +/* */ +/* The KEGS web page is kegs.sourceforge.net */ +/* You may contact the author at: kadickey@alumni.princeton.edu */ +/************************************************************************/ + +const char rcsid_paddles_c[] = "@(#)$KmKId: paddles.c,v 1.7 2004-03-23 17:28:06-05 kentd Exp $"; + +#include "defc.h" + +extern int g_mouse_fifo_x[]; /* from adb.c */ +extern int g_mouse_fifo_y[]; + +double g_paddle_trig_dcycs = 0.0; +int g_swap_paddles = 0; +int g_invert_paddles = 0; + +int g_joystick_type = JOYSTICK_MOUSE; + +int g_paddle_button[4] = { 0, 0, 0, 0 }; + /* g_paddle_button[0] = button 0, etc */ + +int g_paddle_val[4] = { 0, 0, 0, 0 }; + /* g_paddle_val[0]: Joystick X coord, [1]:Y coord */ + + +void +paddle_trigger(double dcycs) +{ + /* Called by read/write to $c070 */ + g_paddle_trig_dcycs = dcycs; + + /* Determine what all the paddle values are right now */ + + switch(g_joystick_type) { + case JOYSTICK_MOUSE: + paddle_trigger_mouse(dcycs); + break; + case JOYSTICK_LINUX: + case JOYSTICK_WIN32_1: + case JOYSTICK_WIN32_2: + joystick_update(); + } +} + +void +paddle_trigger_mouse(double dcycs) +{ + int val_x; + int val_y; + + val_x = 0; + /* mous_phys_x is 0->560, convert that to 0-255 cyc */ + /* so, mult by 117 then divide by 256 */ + if(g_mouse_fifo_x[0] > BASE_MARGIN_LEFT) { + val_x = (g_mouse_fifo_x[0] - BASE_MARGIN_LEFT) * 117; + val_x = val_x >> 8; + } + + /* mous_phys_y is 0->384, convert that to 0-255 cyc */ + /* so, mult by 170 then divide by 256 (shift right by 8) */ + val_y = 0; + if(g_mouse_fifo_y[0] > BASE_MARGIN_TOP) { + val_y = ((g_mouse_fifo_y[0] - BASE_MARGIN_TOP) * 170) >> 8; + } + + if(val_x > 280) { + val_x = 280; + } + if(val_y > 280) { + val_y = 280; + } + + g_paddle_val[0] = val_x; + g_paddle_val[1] = val_y; + g_paddle_val[2] = 255; + g_paddle_val[3] = 255; + g_paddle_button[2] = 1; + g_paddle_button[3] = 1; +} + +int +read_paddles(int paddle, double dcycs) +{ + double trig_dcycs; + int val; + + /* This routine is called by any read to $c064-$c067 */ + if(g_swap_paddles) { + paddle = paddle ^ 1; + } + + paddle = paddle & 3; + + val = g_paddle_val[paddle]; + + if(g_invert_paddles) { + val = 255 - val; + } + + /* convert 0->255 into 0->2816.0 cycles (the paddle delay const) */ + trig_dcycs = g_paddle_trig_dcycs + (val * 11.0); + + if(dcycs < trig_dcycs) { + return 0x80; + } else { + return 0x00; + } +} + diff --git a/src/partls.c b/src/partls.c new file mode 100755 index 0000000..364ee94 --- /dev/null +++ b/src/partls.c @@ -0,0 +1,135 @@ + +#include "defc.h" +#include +#include +#include + + +#define BUF_SIZE 65536 +char buf[BUF_SIZE]; + +void +read_block(int fd, char *buf, int blk, int blk_size) +{ + int ret; + + ret = lseek(fd, blk * blk_size, SEEK_SET); + if(ret != blk * blk_size) { + printf("lseek: %d, errno: %d\n", ret, errno); + exit(1); + } + + ret = read(fd, buf, blk_size); + if(ret != blk_size) { + printf("ret: %d, errno: %d\n", ret, errno); + exit(1); + } +} + +int +main(int argc, char **argv) +{ + Driver_desc *driver_desc_ptr; + Part_map *part_map_ptr; + double dsize; + int fd; + int block_size; + word32 sig; + word32 map_blk_cnt; + word32 phys_part_start; + word32 part_blk_cnt; + word32 data_start; + word32 data_cnt; + int map_blocks; + int cur; + int long_form; + int last_arg; + int i; + + /* parse args */ + long_form = 0; + last_arg = 1; + for(i = 1; i < argc; i++) { + if(!strcmp("-l", argv[i])) { + long_form = 1; + } else { + last_arg = i; + break; + } + } + + + fd = open(argv[last_arg], O_RDONLY | O_BINARY, 0x1b6); + if(fd < 0) { + printf("open %s, ret: %d, errno:%d\n", argv[last_arg],fd,errno); + exit(1); + } + if(long_form) { + printf("fd: %d\n", fd); + } + + block_size = 512; + read_block(fd, buf, 0, block_size); + + driver_desc_ptr = (Driver_desc *)buf; + sig = GET_BE_WORD16(driver_desc_ptr->sig); + block_size = GET_BE_WORD16(driver_desc_ptr->blk_size); + if(long_form) { + printf("sig: %04x, blksize: %04x\n", sig, block_size); + } + if(block_size == 0) { + block_size = 512; + } + + if(sig == 0x4552 && block_size >= 0x200) { + if(long_form) { + printf("good!\n"); + } + } else { + printf("bad sig:%04x or block_size:%04x!\n", sig, block_size); + exit(1); + } + + map_blocks = 1; + cur = 0; + while(cur < map_blocks) { + read_block(fd, buf, cur + 1, block_size); + part_map_ptr = (Part_map *)buf; + sig = GET_BE_WORD16(part_map_ptr->sig); + map_blk_cnt = GET_BE_WORD32(part_map_ptr->map_blk_cnt); + phys_part_start = GET_BE_WORD32(part_map_ptr->phys_part_start); + part_blk_cnt = GET_BE_WORD32(part_map_ptr->part_blk_cnt); + data_start = GET_BE_WORD32(part_map_ptr->data_start); + data_cnt = GET_BE_WORD32(part_map_ptr->data_cnt); + + if(cur == 0) { + map_blocks = MIN(100, map_blk_cnt); + } + + if(long_form) { + printf("%2d: sig: %04x, map_blk_cnt: %d, " + "phys_part_start: %08x, part_blk_cnt: %08x\n", + cur, sig, map_blk_cnt, phys_part_start, + part_blk_cnt); + printf(" part_name: %s, part_type: %s\n", + part_map_ptr->part_name, + part_map_ptr->part_type); + printf(" data_start:%08x, data_cnt:%08x status:%08x\n", + GET_BE_WORD32(part_map_ptr->data_start), + GET_BE_WORD32(part_map_ptr->data_cnt), + GET_BE_WORD32(part_map_ptr->part_status)); + printf(" processor: %s\n", part_map_ptr->processor); + } else { + dsize = (double)GET_BE_WORD32(part_map_ptr->data_cnt); + printf("%2d:%-20s size=%6.2fMB type=%s\n", cur, + part_map_ptr->part_name, + (dsize/(1024.0*2.0)), + part_map_ptr->part_type); + } + + cur++; + } + + close(fd); + return 0; +} diff --git a/src/prodos.h b/src/prodos.h new file mode 100755 index 0000000..8ecca11 --- /dev/null +++ b/src/prodos.h @@ -0,0 +1,116 @@ +/****************************************************************/ +/* Apple //gs emulator */ +/* Copyright 1996 Kent Dickey */ +/* */ +/* This code may not be used in a commercial product */ +/* without prior written permission of the author. */ +/* */ +/* You may freely distribute this code. */ +/* */ +/* You can contact the author at kentd@cup.hp.com. */ +/* HP has nothing to do with this software. */ +/****************************************************************/ + +#ifdef INCLUDE_RCSID_C +const char rcsid_defc_h[] = "@(#)$Id: prodos.h,v 1.4 2002/11/19 07:49:31 kadickey Exp $"; +#endif + + +typedef struct l2byte_st L2byte; +struct l2byte_st { + byte low; + byte hi; +}; + +typedef struct l3byte_st L3byte; +struct l3byte_st { + byte low; + byte hi; + byte higher; +}; + +typedef L2byte Block; + +typedef struct pro_time_st Pro_time; +struct pro_time_st { + byte times[4]; +}; + +typedef struct file_entry_st File_entry; +struct file_entry_st { + byte storage_type_name_len; + byte file_name[15]; +/* 0x10 */ + byte file_type; + Block key_pointer; +/* 0x13 */ + L2byte blocks_used; +/* 0x15 */ + L3byte eof; +/* 0x18 */ + Pro_time creation_time; +/* 0x1c */ + byte version; + byte min_version; +/* 0x1e */ + byte access; +/* 0x1f */ + L2byte aux_type; +/* 0x21 */ + Pro_time last_mod; +/* 0x25 */ + Block header_pointer; +}; + +STRUCT(Vol_hdr) { +/* 0x4 */ + byte storage_type_name_len; +/* 0x5 */ + byte vol_name[15]; +/* 0x14 */ + byte res1[8]; +/* 0x1c */ + Pro_time creation_time; +/* 0x20 */ + byte version; + byte min_version; + byte access; + byte entry_length; +/* 0x24 */ + byte entries_per_block; + L2byte file_count; +/* 0x27 */ + Block bit_map; +/* 0x29 */ + L2byte total_blocks; +}; + +typedef struct directory_st Directory; +struct directory_st { + Block prev_blk; + Block next_blk; + File_entry file_entries[13]; +}; + +STRUCT(ProDisk) { + int fd; + int total_blocks; + int size_bitmap_blocks; + int disk_bytes_left; + + int bitmap_bytes; + int bitmap_cur_pos; + byte *bitmap_ptr; + + int file_open; + File_entry *file_ptr; + int dir_blk_num; + int ind_blk_num; + int master_ind_blk_num; + byte dir_blk_data[512]; + byte ind_blk_data[512]; + byte master_ind_blk_data[512]; +}; + + +#include "prodos_protos.h" diff --git a/src/prodos_protos.h b/src/prodos_protos.h new file mode 100755 index 0000000..bccdf8e --- /dev/null +++ b/src/prodos_protos.h @@ -0,0 +1,46 @@ +/****************************************************************/ +/* Apple //gs emulator */ +/* Copyright 1996 Kent Dickey */ +/* */ +/* This code may not be used in a commercial product */ +/* without prior written permission of the author. */ +/* */ +/* You may freely distribute this code. */ +/* */ +/* You can contact the author at kentd@cup.hp.com. */ +/* HP has nothing to do with this software. */ +/****************************************************************/ + +const char rcsid_prodos_protos_h[] = "@(#)$Id: prodos_protos.h,v 1.4 2002/11/19 07:49:31 kadickey Exp $"; + +/* to_pro.c */ +void flush_disk(ProDisk *disk); +void close_file(ProDisk *disk); +ProDisk *allocate_memdisk(char *out_name, int size); +void format_memdisk(ProDisk *ptr, char *name); +void disk_write_data(ProDisk *disk, int blk_num, byte *buf, int size); +void disk_read_data(ProDisk *disk, int blk_num, byte *buf, int size); +Directory *disk_read_dir(ProDisk *disk, int blk_num); +void disk_write_dir(ProDisk *disk, int blk_num); +void create_new_file(ProDisk *disk, int dir_block, int storage_type, + char *name, int file_type, word32 creation_time, int version, + int min_version, int access, int aux_type, word32 last_mod, word32 eof); +int pro_write_file(ProDisk *disk, byte *in_buf, int pos, int size); +int get_disk_block(ProDisk *disk, int pos, int create); +void get_new_ind_block(ProDisk *disk); +void write_ind_block(ProDisk *disk); +void get_new_master_ind_block(ProDisk *disk); +void write_master_ind_block(ProDisk *disk); +int find_next_free_block(ProDisk *disk); +void set_bitmap_used(byte *ptr, int i); +void set_bitmap_free(byte *ptr, int i); +void set_file_entry(File_entry *entry, int storage_type_name_len, + char *file_name, int file_type, int key_pointer, int blocks_used, + int eof, word32 creation, int version, int min_version, int access, + int aux_type, word32 last_mod, int header_pointer); +void set_l2byte(L2byte *ptr, int val); +void set_l3byte(L3byte *ptr, int val); +void set_pro_time(Pro_time *ptr, word32 val); +int get_l2byte(L2byte *ptr); +int get_l3byte(L3byte *ptr); +void inc_l2byte(L2byte *ptr); diff --git a/src/protos.h b/src/protos.h new file mode 100644 index 0000000..680ced2 --- /dev/null +++ b/src/protos.h @@ -0,0 +1,495 @@ +/************************************************************************/ +/* KEGS: Apple //gs Emulator */ +/* Copyright 2002 by Kent Dickey */ +/* */ +/* This code is covered by the GNU GPL */ +/* */ +/* The KEGS web page is kegs.sourceforge.net */ +/* You may contact the author at: kadickey@alumni.princeton.edu */ +/************************************************************************/ + +#ifdef INCLUDE_RCSID_C +const char rcsid_protos_h[] = "@(#)$KmKId: protos.h,v 1.173 2004-03-23 17:26:19-05 kentd Exp $"; +#endif + +/* xdriver.c and macdriver.c and windriver.c */ +void update_color_array(int col_num, int a2_color); +void set_border_color(int val); +void x_update_physical_colormap(void); +void show_xcolor_array(void); +void x_auto_repeat_on(int must); +void install_text_colormap(void); +void set_border_color(int val); +void draw_status(int line_num, const char *string1); +void xdriver_end(void); +void dev_video_init(void); +void x_update_color(int col_num, int red, int green, int blue, word32 rgb); +void redraw_border(void); +void check_input_events(void); +void x_redraw_status_lines(void); +void x_push_kimage(Kimage *kimage_ptr, int destx, int desty, int srcx, int srcy, int width, int height); +void x_push_done(); +void x_hide_pointer(int); +void x_get_kimage(Kimage *kimage_ptr); + +/* test65.c */ +void do_gen_test(int got_num, int base_seed); + + +/* engine.s and engine_c.c */ +void fixed_memory_ptrs_init(); +word32 get_itimer(void); + +word32 get_memory_c(word32 addr, int cycs); +word32 get_memory16_c(word32 addr, int cycs); +word32 get_memory24_c(word32 addr, int cycs); + +int get_memory_asm(word32 addr, int cycs); +int get_memory16_asm(word32 addr, int cycs); +int get_memory16_asm_fail(word32 addr, int cycs); +int get_memory_act_stub_asm(word32 addr, int cycs); +int get_memory16_act_stub_asm(word32 addr, int cycs); + +void set_memory_c(word32 addr, word32 val, int cycs); +void set_memory16_c(word32 addr, word32 val, int cycs); + +int enter_engine(Engine_reg *ptr); +void clr_halt_act(void); +void set_halt_act(int val); + +/* special scc_macdriver.c prototypes */ +int scc_serial_mac_init(int port); +void scc_serial_mac_change_params(int port); +void scc_serial_mac_fill_readbuf(int port, double dcycs); +void scc_serial_mac_empty_writebuf(int port); + +/* special scc_windriver.c prototypes */ +int scc_serial_win_init(int port); +void scc_serial_win_change_params(int port); +void scc_serial_win_fill_readbuf(int port, double dcycs); +void scc_serial_win_empty_writebuf(int port); + +/* special joystick_driver.c prototypes */ +void joystick_init(void); +void joystick_update(void); +void joystick_update_button(void); + + +/* END_HDR */ + +/* adb.c */ +void adb_init(void); +void adb_reset(void); +void adb_log(word32 addr, int val); +void show_adb_log(void); +void adb_error(void); +void adb_add_kbd_srq(void); +void adb_clear_kbd_srq(void); +void adb_add_data_int(void); +void adb_add_mouse_int(void); +void adb_clear_data_int(void); +void adb_clear_mouse_int(void); +void adb_send_bytes(int num_bytes, word32 val0, word32 val1, word32 val2); +void adb_send_1byte(word32 val); +void adb_response_packet(int num_bytes, word32 val); +void adb_kbd_reg0_data(int a2code, int is_up); +void adb_kbd_talk_reg0(void); +void adb_set_config(word32 val0, word32 val1, word32 val2); +void adb_set_new_mode(word32 val); +int adb_read_c026(void); +void adb_write_c026(int val); +void do_adb_cmd(void); +int adb_read_c027(void); +void adb_write_c027(int val); +int read_adb_ram(word32 addr); +void write_adb_ram(word32 addr, int val); +int update_mouse(int x, int y, int button_states, int buttons_valid); +int mouse_read_c024(double dcycs); +void adb_key_event(int a2code, int is_up); +word32 adb_read_c000(void); +word32 adb_access_c010(void); +word32 adb_read_c025(void); +int adb_is_cmd_key_down(void); +int adb_is_option_key_down(void); +void adb_increment_speed(void); +void adb_physical_key_update(int a2code, int is_up); +void adb_virtual_key_update(int a2code, int is_up); +void adb_kbd_repeat_off(void); + + +/* clock.c */ +double get_dtime(void); +int micro_sleep(double dtime); +void clk_bram_zero(void); +void clk_bram_set(int bram_num, int offset, int val); +void clk_setup_bram_version(void); +void clk_write_bram(FILE *fconf); +void update_cur_time(void); +void clock_update(void); +void clock_update_if_needed(void); +word32 clock_read_c033(void); +word32 clock_read_c034(void); +void clock_write_c033(word32 val); +void clock_write_c034(word32 val); +void do_clock_data(void); + + +/* compile_time.c */ + + +/* config.c */ +void config_init_menus(Cfg_menu *menuptr); +void config_init(void); +void cfg_exit(void); +void cfg_text_screen_dump(void); +void config_vbl_update(int doit_3_persec); +void config_parse_option(char *buf, int pos, int len, int line); +void config_parse_bram(char *buf, int pos, int len); +void config_parse_config_kegs_file(void); +Disk *cfg_get_dsk_from_slot_drive(int slot, int drive); +void config_generate_config_kegs_name(char *outstr, int maxlen, Disk *dsk, int with_extras); +void config_write_config_kegs_file(void); +void insert_disk(int slot, int drive, const char *name, int ejected, int force_size, const char *partition_name, int part_num); +void eject_named_disk(Disk *dsk, const char *name, const char *partition_name); +void eject_disk_by_num(int slot, int drive); +void eject_disk(Disk *dsk); +int cfg_get_fd_size(int fd); +int cfg_partition_read_block(int fd, void *buf, int blk, int blk_size); +int cfg_partition_find_by_name_or_num(int fd, const char *partnamestr, int part_num, Disk *dsk); +int cfg_partition_make_list(int fd); +int cfg_maybe_insert_disk(int slot, int drive, const char *namestr); +int cfg_stat(char *path, struct stat *sb); +void cfg_htab_vtab(int x, int y); +void cfg_home(void); +void cfg_cleol(void); +void cfg_putchar(int c); +void cfg_printf(const char *fmt, ...); +void cfg_print_num(int num, int max_len); +void cfg_get_disk_name(char *outstr, int maxlen, int type_ext, int with_extras); +void cfg_parse_menu(Cfg_menu *menu_ptr, int menu_pos, int highlight_pos, int change); +void cfg_get_base_path(char *pathptr, const char *inptr, int go_up); +void cfg_file_init(void); +void cfg_free_alldirents(Cfg_listhdr *listhdrptr); +void cfg_file_add_dirent(Cfg_listhdr *listhdrptr, const char *nameptr, int is_dir, int size, int image_start, int part_num); +int cfg_dirent_sortfn(const void *obj1, const void *obj2); +int cfg_str_match(const char *str1, const char *str2, int len); +void cfg_file_readdir(const char *pathptr); +char *cfg_shorten_filename(const char *in_ptr, int maxlen); +void cfg_fix_topent(Cfg_listhdr *listhdrptr); +void cfg_file_draw(void); +void cfg_partition_selected(void); +void cfg_file_selected(void); +void cfg_file_handle_key(int key); +void config_control_panel(void); + + +/* dis.c */ +int get_num(void); +void debugger_help(void); +void do_debug_intfc(void); +word32 dis_get_memory_ptr(word32 addr); +void show_one_toolset(FILE *toolfile, int toolnum, word32 addr); +void show_toolset_tables(word32 a2bank, word32 addr); +void set_bp(word32 addr); +void show_bp(void); +void delete_bp(word32 addr); +void do_blank(void); +void do_go(void); +void do_step(void); +void xam_mem(int count); +void show_hex_mem(int startbank, word32 start, int endbank, word32 end, int count); +int read_line(char *buf, int len); +void do_debug_list(void); +void load_roms(void); +void do_debug_unix(void); +void do_debug_load(void); +int do_dis(FILE *outfile, word32 kpc, int accsize, int xsize, int op_provided, word32 instr); +void show_line(FILE *outfile, word32 kaddr, word32 operand, int size, char *string); +void halt_printf(const char *fmt, ...); +void halt2_printf(const char *fmt, ...); + + +/* scc.c */ +void scc_init(void); +void scc_reset(void); +void scc_hard_reset_port(int port); +void scc_reset_port(int port); +void scc_regen_clocks(int port); +void scc_port_init(int port); +void scc_try_to_empty_writebuf(int port); +void scc_try_fill_readbuf(int port, double dcycs); +void scc_update(double dcycs); +void do_scc_event(int type, double dcycs); +void show_scc_state(void); +void scc_log(int regnum, word32 val, double dcycs); +void show_scc_log(void); +word32 scc_read_reg(int port, double dcycs); +void scc_write_reg(int port, word32 val, double dcycs); +void scc_maybe_br_event(int port, double dcycs); +void scc_evaluate_ints(int port); +void scc_maybe_rx_event(int port, double dcycs); +void scc_maybe_rx_int(int port, double dcycs); +void scc_clr_rx_int(int port); +void scc_handle_tx_event(int port, double dcycs); +void scc_maybe_tx_event(int port, double dcycs); +void scc_clr_tx_int(int port); +void scc_set_zerocnt_int(int port); +void scc_clr_zerocnt_int(int port); +void scc_add_to_readbuf(int port, word32 val, double dcycs); +void scc_add_to_writebuf(int port, word32 val, double dcycs); +word32 scc_read_data(int port, double dcycs); +void scc_write_data(int port, word32 val, double dcycs); + + +/* scc_socket_driver.c */ +void scc_socket_init(int port); +void scc_socket_change_params(int port); +void scc_accept_socket(int port); +void scc_socket_fill_readbuf(int port, double dcycs); +void scc_socket_empty_writebuf(int port); + + +/* scc_windriver.c */ + + +/* scc_macdriver.c */ + + +/* iwm.c */ +void iwm_init_drive(Disk *dsk, int smartport, int drive, int disk_525); +void iwm_init(void); +void iwm_reset(void); +void draw_iwm_status(int line, char *buf); +void iwm_flush_disk_to_unix(Disk *dsk); +void iwm_vbl_update(int doit_3_persec); +void iwm_show_stats(void); +void iwm_touch_switches(int loc, double dcycs); +void iwm_move_to_track(Disk *dsk, int new_track); +void iwm525_phase_change(int drive, int phase); +int iwm_read_status35(double dcycs); +void iwm_do_action35(double dcycs); +void iwm_set_apple35_sel(int newval); +int iwm_read_c0ec(double dcycs); +int read_iwm(int loc, double dcycs); +void write_iwm(int loc, int val, double dcycs); +int iwm_read_enable2(double dcycs); +int iwm_read_enable2_handshake(double dcycs); +void iwm_write_enable2(int val, double dcycs); +int iwm_read_data(Disk *dsk, int fast_disk_emul, double dcycs); +void iwm_write_data(Disk *dsk, word32 val, int fast_disk_emul, double dcycs); +void sector_to_partial_nib(byte *in, byte *nib_ptr); +int disk_unnib_4x4(Disk *dsk); +int iwm_denib_track525(Disk *dsk, Track *trk, int qtr_track, byte *outbuf); +int iwm_denib_track35(Disk *dsk, Track *trk, int qtr_track, byte *outbuf); +int disk_track_to_unix(Disk *dsk, int qtr_track, byte *outbuf); +void show_hex_data(byte *buf, int count); +void disk_check_nibblization(Disk *dsk, int qtr_track, byte *buf, int size); +void disk_unix_to_nib(Disk *dsk, int qtr_track, int unix_pos, int unix_len, int nib_len); +void iwm_nibblize_track_nib525(Disk *dsk, Track *trk, byte *track_buf, int qtr_track); +void iwm_nibblize_track_525(Disk *dsk, Track *trk, byte *track_buf, int qtr_track); +void iwm_nibblize_track_35(Disk *dsk, Track *trk, byte *track_buf, int qtr_track); +void disk_4x4_nib_out(Disk *dsk, word32 val); +void disk_nib_out(Disk *dsk, byte val, int size); +void disk_nib_end_track(Disk *dsk); +void iwm_show_track(int slot_drive, int track); +void iwm_show_a_track(Track *trk); + + +/* joystick_driver.c */ + + +/* moremem.c */ +void fixup_brks(void); +void fixup_hires_on(void); +void fixup_bank0_2000_4000(void); +void fixup_bank0_0400_0800(void); +void fixup_any_bank_any_page(int start_page, int num_pages, byte *mem0rd, byte *mem0wr); +void fixup_intcx(void); +void fixup_wrdefram(int new_wrdefram); +void fixup_st80col(double dcycs); +void fixup_altzp(void); +void fixup_page2(double dcycs); +void fixup_ramrd(void); +void fixup_ramwrt(void); +void fixup_lcbank2(void); +void fixup_rdrom(void); +void set_statereg(double dcycs, int val); +void fixup_shadow_txt1(void); +void fixup_shadow_txt2(void); +void fixup_shadow_hires1(void); +void fixup_shadow_hires2(void); +void fixup_shadow_shr(void); +void fixup_shadow_iolc(void); +void update_shadow_reg(int val); +void fixup_shadow_all_banks(void); +void setup_pageinfo(void); +void show_bankptrs_bank0rdwr(void); +void show_bankptrs(int bnk); +void show_addr(byte *ptr); +int io_read(word32 loc, double *cyc_ptr); +void io_write(word32 loc, int val, double *cyc_ptr); +word32 get_lines_since_vbl(double dcycs); +int in_vblank(double dcycs); +int read_vid_counters(int loc, double dcycs); + + +/* paddles.c */ +void paddle_trigger(double dcycs); +void paddle_trigger_mouse(double dcycs); +int read_paddles(int paddle, double dcycs); + + +/* sim65816.c */ +void show_pc_log(void); +word32 toolbox_debug_4byte(word32 addr); +void toolbox_debug_c(word32 xreg, word32 stack, double *cyc_ptr); +void show_toolbox_log(void); +word32 get_memory_io(word32 loc, double *cyc_ptr); +void set_memory_io(word32 loc, int val, double *cyc_ptr); +void show_regs_act(Engine_reg *eptr); +void show_regs(void); +void my_exit(int ret); +void do_reset(void); +void check_engine_asm_defines(void); +byte *memalloc_align(int size, int skip_amt); +void memory_ptr_init(void); +int kegsmain(int argc, char **argv); +void kegs_expand_path(char *out_ptr, const char *in_ptr, int maxlen); +void setup_kegs_file(char *outname, int maxlen, int ok_if_missing, const char **name_ptr); +void initialize_events(void); +void check_for_one_event_type(int type); +void add_event_entry(double dcycs, int type); +double remove_event_entry(int type); +void add_event_stop(double dcycs); +void add_event_doc(double dcycs, int osc); +void add_event_scc(double dcycs, int type); +void add_event_vbl(void); +void add_event_vid_upd(int line); +double remove_event_doc(int osc); +double remove_event_scc(int type); +void show_all_events(void); +void show_pmhz(void); +void setup_zip_speeds(void); +void run_prog(void); +void add_irq(void); +void remove_irq(void); +void take_irq(int is_it_brk); +void show_dtime_array(void); +void update_60hz(double dcycs, double dtime_now); +void do_vbl_int(void); +void do_scan_int(double dcycs, int line); +void check_scan_line_int(double dcycs, int cur_video_line); +void check_for_new_scan_int(double dcycs); +void init_reg(void); +void handle_action(word32 ret); +void do_break(word32 ret); +void do_cop(word32 ret); +void do_wdm(word32 arg); +void do_wai(void); +void do_stp(void); +void size_fail(int val, word32 v1, word32 v2); + + +/* smartport.c */ +void smartport_error(void); +void smartport_log(word32 start_addr, int cmd, int rts_addr, int cmd_list); +void do_c70d(word32 arg0); +void do_c70a(word32 arg0); +int do_read_c7(int unit_num, word32 buf, int blk); +int do_write_c7(int unit_num, word32 buf, int blk); +int do_format_c7(int unit_num); +void do_c700(word32 ret); + + +/* sound.c */ +void doc_log_rout(char *msg, int osc, double dsamps, int etc); +void show_doc_log(void); +void sound_init(void); +void sound_init_general(void); +void parent_sound_get_sample_rate(int read_fd); +void set_audio_rate(int rate); +void sound_reset(double dcycs); +void sound_shutdown(void); +void sound_update(double dcycs); +void open_sound_file(void); +void close_sound_file(void); +void check_for_range(word32 *addr, int num_samps, int offset); +void send_sound_to_file(word32 *addr, int shm_pos, int num_samps); +void send_sound(int real_samps, int size); +void show_c030_state(void); +void show_c030_samps(int *outptr, int num); +void sound_play(double dsamps); +void doc_handle_event(int osc, double dcycs); +void doc_sound_end(int osc, int can_repeat, double eff_dsamps, double dsamps); +void add_sound_irq(int osc); +void remove_sound_irq(int osc, int must); +void start_sound(int osc, double eff_dsamps, double dsamps); +void wave_end_estimate(int osc, double eff_dsamps, double dsamps); +void remove_sound_event(int osc); +void doc_write_ctl_reg(int osc, int val, double dsamps); +void doc_recalc_sound_parms(int osc, double eff_dcycs, double dsamps); +int doc_read_c030(double dcycs); +int doc_read_c03c(double dcycs); +int doc_read_c03d(double dcycs); +void doc_write_c03c(int val, double dcycs); +void doc_write_c03d(int val, double dcycs); +void doc_write_c03e(int val); +void doc_write_c03f(int val); +void doc_show_ensoniq_state(int osc); + + +/* sound_driver.c */ +void reliable_buf_write(word32 *shm_addr, int pos, int size); +void reliable_zero_write(int amt); +void child_sound_loop(int read_fd, int write_fd, word32 *shm_addr); +void child_sound_playit(word32 tmp); + + +/* video.c */ +void video_init(void); +void show_a2_line_stuff(void); +void video_reset(void); +void video_update(void); +int video_all_stat_to_line_stat(int line, int new_all_stat); +int *video_update_kimage_ptr(int line, int new_stat); +void change_a2vid_palette(int new_palette); +void check_a2vid_palette(void); +void change_display_mode(double dcycs); +void video_update_all_stat_through_line(int line); +void change_border_color(double dcycs, int val); +void update_border_info(void); +void update_border_line(int st_line_offset, int end_line_offset, int color); +void video_border_pixel_write(Kimage *kimage_ptr, int starty, int num_lines, word32 val, int st_off, int end_off); +void redraw_changed_text_40(int start_offset, int start_line, int num_lines, int reparse, byte *screen_data, int altcharset, int bg_val, int fg_val, int pixels_per_line); +void redraw_changed_text_80(int start_offset, int start_line, int num_lines, int reparse, byte *screen_data, int altcharset, int bg_val, int fg_val, int pixels_per_line); +void redraw_changed_gr(int start_offset, int start_line, int num_lines, int reparse, byte *screen_data, int pixels_per_line); +void redraw_changed_dbl_gr(int start_offset, int start_line, int num_lines, int reparse, byte *screen_data, int pixels_per_line); +void redraw_changed_hires(int start_offset, int start_line, int num_lines, int color, int reparse, byte *screen_data, int pixels_per_line); +void redraw_changed_hires_bw(int start_offset, int start_line, int num_lines, int reparse, byte *screen_data, int pixels_per_line); +void redraw_changed_hires_color(int start_offset, int start_line, int num_lines, int reparse, byte *screen_data, int pixels_per_line); +void redraw_changed_dbl_hires(int start_offset, int start_line, int num_lines, int color, int reparse, byte *screen_data, int pixels_per_line); +void redraw_changed_dbl_hires_bw(int start_offset, int start_line, int num_lines, int reparse, byte *screen_data, int pixels_per_line); +void redraw_changed_dbl_hires_color(int start_offset, int start_line, int num_lines, int reparse, byte *screen_data, int pixels_per_line); +int video_rebuild_super_hires_palette(word32 scan_info, int line, int reparse); +void redraw_changed_super_hires(int start_offset, int start_line, int num_lines, int in_reparse, byte *screen_data); +void display_screen(void); +void video_update_event_line(int line); +void video_update_through_line(int line); +void video_refresh_lines(int st_line, int num_lines, int must_reparse); +void refresh_border(void); +void end_screen(void); +void read_a2_font(void); +void video_get_kimage(Kimage *kimage_ptr, int extend_info, int depth, int mdepth); +void video_get_kimages(void); +void video_convert_kimage_depth(Kimage *kim_in, Kimage *kim_out, int startx, int starty, int width, int height); +void video_push_lines(Kimage *kimage_ptr, int start_line, int end_line, int left_pix, int right_pix); +void video_push_border_sides_lines(int src_x, int dest_x, int width, int start_line, int end_line); +void video_push_border_sides(void); +void video_push_border_special(void); +void video_push_kimages(void); +void video_update_color_raw(int col_num, int a2_color); +void video_update_color_array(int col_num, int a2_color); +void video_update_colormap(void); +void video_update_status_line(int line, const char *string); +void video_show_debug_info(void); + diff --git a/src/protos_engine_c.h b/src/protos_engine_c.h new file mode 100644 index 0000000..195f3f3 --- /dev/null +++ b/src/protos_engine_c.h @@ -0,0 +1,39 @@ +/************************************************************************/ +/* KEGS: Apple //gs Emulator */ +/* Copyright 2002 by Kent Dickey */ +/* */ +/* This code is covered by the GNU GPL */ +/* */ +/* The KEGS web page is kegs.sourceforge.net */ +/* You may contact the author at: kadickey@alumni.princeton.edu */ +/************************************************************************/ + +#ifdef INCLUDE_RCSID_C +const char rcsid_protos_engine_c_h[] = "@(#)$KmKId: protos_engine_c.h,v 1.10 2004-01-10 15:50:02-05 kentd Exp $"; +#endif + +/* END_HDR */ + +/* engine_c.c */ +void check_breakpoints(word32 addr); +word32 get_memory8_io_stub(word32 addr, byte *stat, double *fcycs_ptr, double fplus_x_m1); +word32 get_memory16_pieces_stub(word32 addr, byte *stat, double *fcycs_ptr, Fplus *fplus_ptr, int in_bank); +word32 get_memory24_pieces_stub(word32 addr, byte *stat, double *fcycs_ptr, Fplus *fplus_ptr, int in_bank); +void set_memory8_io_stub(word32 addr, word32 val, byte *stat, double *fcycs_ptr, double fplus_x_m1); +void set_memory16_pieces_stub(word32 addr, word32 val, double *fcycs_ptr, Fplus *fplus_ptr, int in_bank); +void set_memory24_pieces_stub(word32 addr, word32 val, double *fcycs_ptr, Fplus *fplus_ptr, int in_bank); +word32 get_memory_c(word32 addr, int cycs); +word32 get_memory16_c(word32 addr, int cycs); +word32 get_memory24_c(word32 addr, int cycs); +void set_memory_c(word32 addr, word32 val, int cycs); +void set_memory16_c(word32 addr, word32 val, int cycs); +void set_memory24_c(word32 addr, word32 val, int cycs); +word32 do_adc_sbc8(word32 in1, word32 in2, word32 psr, int sub); +word32 do_adc_sbc16(word32 in1, word32 in2, word32 psr, int sub); +void fixed_memory_ptrs_init(void); +word32 get_itimer(void); +void set_halt_act(int val); +void clr_halt_act(void); +word32 get_remaining_operands(word32 addr, word32 opcode, word32 psr, Fplus *fplus_ptr); +int enter_engine(Engine_reg *engine_ptr); + diff --git a/src/protos_macdriver.h b/src/protos_macdriver.h new file mode 100644 index 0000000..3fd840f --- /dev/null +++ b/src/protos_macdriver.h @@ -0,0 +1,40 @@ +/************************************************************************/ +/* KEGS: Apple //gs Emulator */ +/* Copyright 2002 by Kent Dickey */ +/* */ +/* This code is covered by the GNU GPL */ +/* */ +/* The KEGS web page is kegs.sourceforge.net */ +/* You may contact the author at: kadickey@alumni.princeton.edu */ +/************************************************************************/ + +const char rcsid_protos_mac_h[] = "@(#)$KmKId: protos_macdriver.h,v 1.6 2004-03-23 17:27:31-05 kentd Exp $"; + +/* END_HDR */ + +/* macdriver.c */ +pascal OSStatus quit_event_handler(EventHandlerCallRef call_ref, EventRef event, void *ignore); +void show_alert(const char *str1, const char *str2, const char *str3, int num); +pascal OSStatus my_cmd_handler(EventHandlerCallRef handlerRef, EventRef event, void *userdata); +void update_window(void); +void show_event(UInt32 event_class, UInt32 event_kind, int handled); +pascal OSStatus my_win_handler(EventHandlerCallRef handlerRef, EventRef event, void *userdata); +pascal OSStatus dummy_event_handler(EventHandlerCallRef call_ref, EventRef in_event, void *ignore); +void mac_update_modifiers(word32 state); +void mac_warp_mouse(void); +void check_input_events(void); +void temp_run_application_event_loop(void); +int main(int argc, char *argv[]); +void x_update_color(int col_num, int red, int green, int blue, word32 rgb); +void x_update_physical_colormap(void); +void show_xcolor_array(void); +void xdriver_end(void); +void x_get_kimage(Kimage *kimage_ptr); +void dev_video_init(void); +void x_redraw_status_lines(void); +void x_push_kimage(Kimage *kimage_ptr, int destx, int desty, int srcx, int srcy, int width, int height); +void x_push_done(void); +void x_auto_repeat_on(int must); +void x_auto_repeat_off(int must); +void x_hide_pointer(int do_hide); + diff --git a/src/protos_macsnd_driver.h b/src/protos_macsnd_driver.h new file mode 100644 index 0000000..73a79ee --- /dev/null +++ b/src/protos_macsnd_driver.h @@ -0,0 +1,20 @@ +/************************************************************************/ +/* KEGS: Apple //gs Emulator */ +/* Copyright 2003 by Kent Dickey */ +/* */ +/* This code is covered by the GNU GPL */ +/* */ +/* The KEGS web page is kegs.sourceforge.net */ +/* You may contact the author at: kadickey@alumni.princeton.edu */ +/************************************************************************/ + +const char rcsid_protos_macsnd_driver_h[] = "@(#)$KmKId: protos_macsnd_driver.h,v 1.2 2003-11-19 19:57:02-05 kentd Exp $"; + +/* END_HDR */ + +/* macsnd_driver.c */ +void mac_snd_callback(SndChannelPtr snd_chan_ptr, SndCommand *in_sndcmd); +int mac_send_audio(byte *ptr, int in_size); +void child_sound_init_mac(void); +void macsnd_init(word32 *shmaddr); + diff --git a/src/protos_windriver.h b/src/protos_windriver.h new file mode 100644 index 0000000..c7f020f --- /dev/null +++ b/src/protos_windriver.h @@ -0,0 +1,36 @@ +/************************************************************************/ +/* KEGS: Apple //gs Emulator */ +/* Copyright 2002 by Kent Dickey */ +/* */ +/* This code is covered by the GNU GPL */ +/* */ +/* The KEGS web page is kegs.sourceforge.net */ +/* You may contact the author at: kadickey@alumni.princeton.edu */ +/************************************************************************/ + +// $KmKId: protos_windriver.h,v 1.4 2004-03-23 17:27:26-05 kentd Exp $ + +/* END_HDR */ + +/* windriver.c */ +int win_update_mouse(int x, int y, int button_states, int buttons_valid); +void win_event_mouse(WPARAM wParam, LPARAM lParam); +void win_event_key(HWND hwnd, UINT raw_vk, BOOL down, int repeat, UINT flags); +void win_event_quit(HWND hwnd); +void win_event_redraw(void); +LRESULT CALLBACK win_event_handler(HWND hwnd, UINT umsg, WPARAM wParam, LPARAM lParam); +int main(int argc, char **argv); +void check_input_events(void); +void x_update_color(int col_num, int red, int green, int blue, word32 rgb); +void x_update_physical_colormap(void); +void show_xcolor_array(void); +void xdriver_end(void); +void x_get_kimage(Kimage *kimage_ptr); +void dev_video_init(void); +void x_redraw_status_lines(void); +void x_push_kimage(Kimage *kimage_ptr, int destx, int desty, int srcx, int srcy, int width, int height); +void x_push_done(void); +void x_auto_repeat_on(int must); +void x_auto_repeat_off(int must); +void x_hide_pointer(int do_hide); + diff --git a/src/protos_xdriver.h b/src/protos_xdriver.h new file mode 100644 index 0000000..f43bf8d --- /dev/null +++ b/src/protos_xdriver.h @@ -0,0 +1,36 @@ +/************************************************************************/ +/* KEGS: Apple //gs Emulator */ +/* Copyright 2002 by Kent Dickey */ +/* */ +/* This code is covered by the GNU GPL */ +/* */ +/* The KEGS web page is kegs.sourceforge.net */ +/* You may contact the author at: kadickey@alumni.princeton.edu */ +/************************************************************************/ + +const char rcsid_protos_x_h[] = "@(#)$KmKId: protos_xdriver.h,v 1.18 2002-11-19 03:10:38-05 kadickey Exp $"; + +/* END_HDR */ + +/* xdriver.c */ +int main(int argc, char **argv); +void x_update_physical_colormap(void); +void show_xcolor_array(void); +int my_error_handler(Display *display, XErrorEvent *ev); +void xdriver_end(void); +void show_colormap(char *str, Colormap cmap, int index1, int index2, int index3); +void dev_video_init(void); +Visual *x_try_find_visual(int depth, int screen_num, XVisualInfo **visual_list_ptr); +void x_set_mask_and_shift(word32 x_mask, word32 *mask_ptr, int *shift_left_ptr, int *shift_right_ptr); +int xhandle_shm_error(Display *display, XErrorEvent *event); +int get_shm(Kimage *kimage_ptr); +void get_ximage(Kimage *kimage_ptr); +void update_status_line(int line, const char *string); +void redraw_status_lines(void); +void check_input_events(void); +void handle_keysym(XEvent *xev_in); +int x_keysym_to_a2code(int keysym, int is_up); +void x_update_modifier_state(int state); +void x_auto_repeat_on(int must); +void x_auto_repeat_off(int must); + diff --git a/src/scc.c b/src/scc.c new file mode 100644 index 0000000..b21f5fd --- /dev/null +++ b/src/scc.c @@ -0,0 +1,1153 @@ +/************************************************************************/ +/* KEGS: Apple //gs Emulator */ +/* Copyright 2002 by Kent Dickey */ +/* */ +/* This code is covered by the GNU GPL */ +/* */ +/* The KEGS web page is kegs.sourceforge.net */ +/* You may contact the author at: kadickey@alumni.princeton.edu */ +/************************************************************************/ + +const char rcsid_scc_c[] = "@(#)$KmKId: scc.c,v 1.38 2003-11-20 23:50:32-05 kentd Exp $"; + +#include "defc.h" + +extern int Verbose; +extern int g_code_yellow; +extern double g_cur_dcycs; +extern int g_raw_serial; +extern int g_serial_out_masking; + +/* my scc port 0 == channel A = slot 1 */ +/* port 1 == channel B = slot 2 */ + +#include "scc.h" +#define SCC_R14_DPLL_SOURCE_BRG 0x100 +#define SCC_R14_FM_MODE 0x200 + +#define SCC_DCYCS_PER_PCLK ((DCYCS_1_MHZ) / ((DCYCS_28_MHZ) /8)) +#define SCC_DCYCS_PER_XTAL ((DCYCS_1_MHZ) / 3686400.0) + +#define SCC_BR_EVENT 1 +#define SCC_TX_EVENT 2 +#define SCC_RX_EVENT 3 +#define SCC_MAKE_EVENT(port, a) (((a) << 1) + (port)) + +Scc scc_stat[2]; + +int g_baud_table[] = { + 110, 300, 600, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200 +}; + +int g_scc_overflow = 0; + +void +scc_init() +{ + Scc *scc_ptr; + int i; + + for(i = 0; i < 2; i++) { + scc_ptr = &(scc_stat[i]); + scc_ptr->accfd = -1; + scc_ptr->rdwrfd = -1; + scc_ptr->state = 0; + scc_ptr->host_handle = 0; + scc_ptr->host_handle2 = 0; + scc_ptr->int_pending_rx = 0; + scc_ptr->int_pending_tx = 0; + scc_ptr->int_pending_zerocnt = 0; + scc_ptr->br_event_pending = 0; + scc_ptr->rx_event_pending = 0; + scc_ptr->tx_event_pending = 0; + scc_ptr->char_size = 8; + scc_ptr->baud_rate = 9600; + } + + scc_reset(); +} + +void +scc_reset() +{ + Scc *scc_ptr; + int i; + + for(i = 0; i < 2; i++) { + scc_ptr = &(scc_stat[i]); + + scc_ptr->port = i; + scc_ptr->mode = 0; + scc_ptr->reg_ptr = 0; + scc_ptr->in_rdptr = 0; + scc_ptr->in_wrptr = 0; + scc_ptr->out_rdptr = 0; + scc_ptr->out_wrptr = 0; + scc_ptr->wantint_rx = 0; + scc_ptr->wantint_tx = 0; + scc_ptr->wantint_zerocnt = 0; + scc_ptr->read_called_this_vbl = 0; + scc_ptr->write_called_this_vbl = 0; + scc_evaluate_ints(i); + scc_hard_reset_port(i); + } +} + +void +scc_hard_reset_port(int port) +{ + Scc *scc_ptr; + + scc_reset_port(port); + + scc_ptr = &(scc_stat[port]); + scc_ptr->reg[14] = 0; /* zero bottom two bits */ + scc_ptr->reg[13] = 0; + scc_ptr->reg[12] = 0; + scc_ptr->reg[11] = 0x08; + scc_ptr->reg[10] = 0; + scc_ptr->reg[7] = 0; + scc_ptr->reg[6] = 0; + scc_ptr->reg[5] = 0; + scc_ptr->reg[4] = 0x04; + scc_ptr->reg[3] = 0; + scc_ptr->reg[2] = 0; + scc_ptr->reg[1] = 0; + + /* HACK HACK: */ + scc_stat[0].reg[9] = 0; /* Clear all interrupts */ + + scc_evaluate_ints(port); + + scc_regen_clocks(port); +} + +void +scc_reset_port(int port) +{ + Scc *scc_ptr; + + scc_ptr = &(scc_stat[port]); + scc_ptr->reg[15] = 0xf8; + scc_ptr->reg[14] &= 0x03; /* 0 most (including >= 0x100) bits */ + scc_ptr->reg[10] = 0; + scc_ptr->reg[5] &= 0x65; /* leave tx bits and sdlc/crc bits */ + scc_ptr->reg[4] |= 0x04; /* Set async mode */ + scc_ptr->reg[3] &= 0xfe; /* clear receiver enable */ + scc_ptr->reg[1] &= 0xfe; /* clear ext int enable */ + + scc_ptr->br_is_zero = 0; + scc_ptr->tx_buf_empty = 1; + + scc_ptr->wantint_rx = 0; + scc_ptr->wantint_tx = 0; + scc_ptr->wantint_zerocnt = 0; + + scc_ptr->rx_queue_depth = 0; + + scc_evaluate_ints(port); + + scc_regen_clocks(port); + + scc_clr_tx_int(port); + scc_clr_rx_int(port); +} + +void +scc_regen_clocks(int port) +{ + Scc *scc_ptr; + double br_dcycs, tx_dcycs, rx_dcycs; + double rx_char_size, tx_char_size; + double clock_mult; + word32 reg4; + word32 reg14; + word32 reg11; + word32 br_const; + word32 baud; + word32 max_diff; + word32 diff; + int state; + int baud_entries; + int pos; + int i; + + /* Always do baud rate generator */ + scc_ptr = &(scc_stat[port]); + br_const = (scc_ptr->reg[13] << 8) + scc_ptr->reg[12]; + br_const += 2; /* counts down past 0 */ + + reg4 = scc_ptr->reg[4]; + clock_mult = 1.0; + switch((reg4 >> 6) & 3) { + case 0: /* x1 */ + clock_mult = 1.0; + break; + case 1: /* x16 */ + clock_mult = 16.0; + break; + case 2: /* x32 */ + clock_mult = 32.0; + break; + case 3: /* x64 */ + clock_mult = 64.0; + break; + } + + br_dcycs = 0.01; + reg14 = scc_ptr->reg[14]; + if(reg14 & 0x1) { + br_dcycs = SCC_DCYCS_PER_XTAL; + if(reg14 & 0x2) { + br_dcycs = SCC_DCYCS_PER_PCLK; + } + } + + br_dcycs = br_dcycs * (double)br_const; + + tx_dcycs = 1; + rx_dcycs = 1; + reg11 = scc_ptr->reg[11]; + if(((reg11 >> 3) & 3) == 2) { + tx_dcycs = 2.0 * br_dcycs * clock_mult; + } + if(((reg11 >> 5) & 3) == 2) { + rx_dcycs = 2.0 * br_dcycs * clock_mult; + } + + tx_char_size = 8.0; + switch((scc_ptr->reg[5] >> 5) & 0x3) { + case 0: // 5 bits + tx_char_size = 5.0; + break; + case 1: // 7 bits + tx_char_size = 7.0; + break; + case 2: // 6 bits + tx_char_size = 6.0; + break; + } + + scc_ptr->char_size = (int)tx_char_size; + + switch((scc_ptr->reg[4] >> 2) & 0x3) { + case 1: // 1 stop bit + tx_char_size += 2.0; // 1 stop + 1 start bit + break; + case 2: // 1.5 stop bit + tx_char_size += 2.5; // 1.5 stop + 1 start bit + break; + case 3: // 2 stop bits + tx_char_size += 3.0; // 2.0 stop + 1 start bit + break; + } + + if(scc_ptr->reg[4] & 1) { + // parity enabled + tx_char_size += 1.0; + } + + if(scc_ptr->reg[14] & 0x10) { + /* loopback mode, make it go faster...*/ + rx_char_size = 1.0; + tx_char_size = 1.0; + } + + rx_char_size = tx_char_size; /* HACK */ + + baud = (int)(DCYCS_1_MHZ / tx_dcycs); + max_diff = 5000000; + pos = 0; + baud_entries = sizeof(g_baud_table)/sizeof(g_baud_table[0]); + for(i = 0; i < baud_entries; i++) { + diff = abs(g_baud_table[i] - baud); + if(diff < max_diff) { + pos = i; + max_diff = diff; + } + } + + scc_ptr->baud_rate = g_baud_table[pos]; + + scc_ptr->br_dcycs = br_dcycs; + scc_ptr->tx_dcycs = tx_dcycs * tx_char_size; + scc_ptr->rx_dcycs = rx_dcycs * rx_char_size; + + state = scc_ptr->state; + if(state == 2) { + /* real serial ports */ +#ifdef MAC + scc_serial_mac_change_params(port); +#endif +#ifdef _WIN32 + scc_serial_win_change_params(port); +#endif + } else { + scc_socket_change_params(port); + } +} + +void +scc_port_init(int port) +{ + int state; + + state = 0; + if(g_raw_serial) { +#ifdef MAC + state = scc_serial_mac_init(port); +#endif +#ifdef _WIN32 + state = scc_serial_win_init(port); +#endif + } + + if(state <= 0) { + scc_socket_init(port); + } +} + +void +scc_try_to_empty_writebuf(int port) +{ + Scc *scc_ptr; + int state; + + scc_ptr = &(scc_stat[port]); + state = scc_ptr->state; + if(scc_ptr->write_called_this_vbl) { + return; + } + + scc_ptr->write_called_this_vbl = 1; + + if(state == 2) { +#if defined(MAC) + scc_serial_mac_empty_writebuf(port); +#endif +#if defined(_WIN32) + scc_serial_win_empty_writebuf(port); +#endif + } else if(state == 1) { + scc_socket_empty_writebuf(port); + } +} + +void +scc_try_fill_readbuf(int port, double dcycs) +{ + Scc *scc_ptr; + int state; + + scc_ptr = &(scc_stat[port]); + state = scc_ptr->state; +#if 0 + if(scc_ptr->read_called_this_vbl) { + return; + } +#endif + + scc_ptr->read_called_this_vbl = 1; + + if(state == 2) { +#if defined(MAC) + scc_serial_mac_fill_readbuf(port, dcycs); +#endif +#if defined(_WIN32) + scc_serial_win_fill_readbuf(port, dcycs); +#endif + } else if(state == 1) { + scc_socket_fill_readbuf(port, dcycs); + } +} + +void +scc_update(double dcycs) +{ + /* called each VBL update */ + scc_stat[0].write_called_this_vbl = 0; + scc_stat[1].write_called_this_vbl = 0; + scc_stat[0].read_called_this_vbl = 0; + scc_stat[1].read_called_this_vbl = 0; + + scc_try_to_empty_writebuf(0); + scc_try_to_empty_writebuf(1); + scc_try_fill_readbuf(0, dcycs); + scc_try_fill_readbuf(1, dcycs); + + scc_stat[0].write_called_this_vbl = 0; + scc_stat[1].write_called_this_vbl = 0; + scc_stat[0].read_called_this_vbl = 0; + scc_stat[1].read_called_this_vbl = 0; +} + +void +do_scc_event(int type, double dcycs) +{ + Scc *scc_ptr; + int port; + + port = type & 1; + type = (type >> 1); + + scc_ptr = &(scc_stat[port]); + if(type == SCC_BR_EVENT) { + /* baud rate generator counted down to 0 */ + scc_ptr->br_event_pending = 0; + scc_set_zerocnt_int(port); + scc_maybe_br_event(port, dcycs); + } else if(type == SCC_TX_EVENT) { + scc_ptr->tx_event_pending = 0; + scc_ptr->tx_buf_empty = 1; + scc_handle_tx_event(port, dcycs); + } else if(type == SCC_RX_EVENT) { + scc_ptr->rx_event_pending = 0; + scc_maybe_rx_event(port, dcycs); + } else { + halt_printf("do_scc_event: %08x!\n", type); + } + return; +} + +void +show_scc_state() +{ + Scc *scc_ptr; + int i, j; + + for(i = 0; i < 2; i++) { + scc_ptr = &(scc_stat[i]); + printf("SCC port: %d\n", i); + for(j = 0; j < 16; j += 4) { + printf("Reg %2d-%2d: %02x %02x %02x %02x\n", j, j+3, + scc_ptr->reg[j], scc_ptr->reg[j+1], + scc_ptr->reg[j+2], scc_ptr->reg[j+3]); + } + printf("in_rdptr: %04x, in_wr:%04x, out_rd:%04x, out_wr:%04x\n", + scc_ptr->in_rdptr, scc_ptr->in_wrptr, + scc_ptr->out_rdptr, scc_ptr->out_wrptr); + printf("rx_queue_depth: %d, queue: %02x, %02x, %02x, %02x\n", + scc_ptr->rx_queue_depth, scc_ptr->rx_queue[0], + scc_ptr->rx_queue[1], scc_ptr->rx_queue[2], + scc_ptr->rx_queue[3]); + printf("int_pendings: rx:%d, tx:%d, zc:%d\n", + scc_ptr->int_pending_rx, scc_ptr->int_pending_tx, + scc_ptr->int_pending_zerocnt); + printf("want_ints: rx:%d, tx:%d, zc:%d\n", + scc_ptr->wantint_rx, scc_ptr->wantint_tx, + scc_ptr->wantint_zerocnt); + printf("ev_pendings: rx:%d, tx:%d, br:%d\n", + scc_ptr->rx_event_pending, + scc_ptr->tx_event_pending, + scc_ptr->br_event_pending); + printf("br_dcycs: %f, tx_dcycs: %f, rx_dcycs: %f\n", + scc_ptr->br_dcycs, scc_ptr->tx_dcycs, + scc_ptr->rx_dcycs); + printf("char_size: %d, baud_rate: %d, mode: %d\n", + scc_ptr->char_size, scc_ptr->baud_rate, + scc_ptr->mode); + } + +} + +#define LEN_SCC_LOG 50 +STRUCT(Scc_log) { + int regnum; + word32 val; + double dcycs; +}; + +Scc_log g_scc_log[LEN_SCC_LOG]; +int g_scc_log_pos = 0; + +#define SCC_REGNUM(wr,port,reg) ((wr << 8) + (port << 4) + reg) + +void +scc_log(int regnum, word32 val, double dcycs) +{ + int pos; + + pos = g_scc_log_pos; + g_scc_log[pos].regnum = regnum; + g_scc_log[pos].val = val; + g_scc_log[pos].dcycs = dcycs; + pos++; + if(pos >= LEN_SCC_LOG) { + pos = 0; + } + g_scc_log_pos = pos; +} + +void +show_scc_log(void) +{ + double dcycs; + int regnum; + int pos; + int i; + + pos = g_scc_log_pos; + dcycs = g_cur_dcycs; + printf("SCC log pos: %d, cur dcycs:%f\n", pos, dcycs); + for(i = 0; i < LEN_SCC_LOG; i++) { + pos--; + if(pos < 0) { + pos = LEN_SCC_LOG - 1; + } + regnum = g_scc_log[pos].regnum; + printf("%d:%d: port:%d wr:%d reg: %d val:%02x at t:%f\n", + i, pos, (regnum >> 4) & 0xf, (regnum >> 8), + (regnum & 0xf), + g_scc_log[pos].val, + g_scc_log[pos].dcycs - dcycs); + } +} + +word32 +scc_read_reg(int port, double dcycs) +{ + Scc *scc_ptr; + word32 ret; + int regnum; + + scc_ptr = &(scc_stat[port]); + scc_ptr->mode = 0; + regnum = scc_ptr->reg_ptr; + + /* port 0 is channel A, port 1 is channel B */ + switch(regnum) { + case 0: + case 4: + ret = 0x68; /* 0x44 = no dcd, no cts,0x6c = dcd ok, cts ok*/ + if(scc_ptr->rx_queue_depth) { + ret |= 0x01; + } + if(scc_ptr->tx_buf_empty) { + ret |= 0x04; + } + if(scc_ptr->br_is_zero) { + ret |= 0x02; + } + //printf("Read scc[%d] stat: %f : %02x\n", port, dcycs, ret); + break; + case 1: + case 5: + /* HACK: residue codes not right */ + ret = 0x07; /* all sent */ + break; + case 2: + case 6: + if(port == 0) { + ret = scc_ptr->reg[2]; + } else { + + halt_printf("Read of RR2B...stopping\n"); + ret = 0; +#if 0 + ret = scc_stat[0].reg[2]; + wr9 = scc_stat[0].reg[9]; + for(i = 0; i < 8; i++) { + if(ZZZ){}; + } + if(wr9 & 0x10) { + /* wr9 status high */ + + } +#endif + } + break; + case 3: + case 7: + if(port == 0) { + ret = (scc_stat[1].int_pending_zerocnt) | + (scc_stat[1].int_pending_tx << 1) | + (scc_stat[1].int_pending_rx << 2) | + (scc_stat[0].int_pending_zerocnt << 3) | + (scc_stat[0].int_pending_tx << 4) | + (scc_stat[0].int_pending_rx << 5); + } else { + ret = 0; + } + break; + case 8: + ret = scc_read_data(port, dcycs); + break; + case 9: + case 13: + ret = scc_ptr->reg[13]; + break; + case 10: + case 14: + ret = 0; + break; + case 11: + case 15: + ret = scc_ptr->reg[15]; + break; + case 12: + ret = scc_ptr->reg[12]; + break; + default: + halt_printf("Tried reading c03%x with regnum: %d!\n", 8+port, + regnum); + ret = 0; + } + + scc_ptr->reg_ptr = 0; + scc_printf("Read c03%x, rr%d, ret: %02x\n", 8+port, regnum, ret); + if(regnum != 0 && regnum != 3) { + scc_log(SCC_REGNUM(0,port,regnum), ret, dcycs); + } + + return ret; +} + +void +scc_write_reg(int port, word32 val, double dcycs) +{ + Scc *scc_ptr; + word32 old_val; + word32 changed_bits; + int regnum; + int mode; + int tmp1; + + scc_ptr = &(scc_stat[port]); + regnum = scc_ptr->reg_ptr & 0xf; + mode = scc_ptr->mode; + + if(mode == 0) { + if((val & 0xf0) == 0) { + /* Set reg_ptr */ + scc_ptr->reg_ptr = val & 0xf; + regnum = 0; + scc_ptr->mode = 1; + } else { + scc_log(SCC_REGNUM(1,port,0), val, dcycs); + } + } else { + scc_ptr->reg_ptr = 0; + scc_ptr->mode = 0; + } + + if(regnum != 0) { + scc_log(SCC_REGNUM(1,port,regnum), val, dcycs); + } + + changed_bits = (scc_ptr->reg[regnum] ^ val) & 0xff; + + /* Set reg reg */ + switch(regnum) { + case 0: /* wr0 */ + tmp1 = (val >> 3) & 0x7; + switch(tmp1) { + case 0x0: + case 0x1: + break; + case 0x2: /* reset ext/status ints */ + /* should clear other ext ints */ + scc_clr_zerocnt_int(port); + break; + case 0x5: /* reset tx int pending */ + scc_clr_tx_int(port); + break; + case 0x6: /* reset rr1 bits */ + break; + case 0x7: /* reset highest pri int pending */ + if(scc_ptr->int_pending_rx) { + scc_clr_rx_int(port); + } else if(scc_ptr->int_pending_tx) { + scc_clr_tx_int(port); + } else if(scc_ptr->int_pending_zerocnt) { + scc_clr_zerocnt_int(port); + } + break; + case 0x4: /* enable int on next rx char */ + default: + halt_printf("Wr c03%x to wr0 of %02x, bad cmd cd:%x!\n", + 8+port, val, tmp1); + } + tmp1 = (val >> 6) & 0x3; + switch(tmp1) { + case 0x0: /* null code */ + break; + case 0x1: /* reset rx crc */ + case 0x2: /* reset tx crc */ + halt_printf("Wr c03%x to wr0 of %02x!\n", 8+port, val); + break; + case 0x3: /* reset tx underrun/eom latch */ + /* if no extern status pending, or being reset now */ + /* and tx disabled, ext int with tx underrun */ + /* ah, just do nothing */ + break; + } + return; + case 1: /* wr1 */ + /* proterm sets this == 0x10, which is int on all rx */ + scc_ptr->reg[regnum] = val; + return; + case 2: /* wr2 */ + /* All values do nothing, let 'em all through! */ + scc_ptr->reg[regnum] = val; + return; + case 3: /* wr3 */ + if((val & 0x1e) != 0x0) { + halt_printf("Wr c03%x to wr3 of %02x!\n", 8+port, val); + } + scc_ptr->reg[regnum] = val; + return; + case 4: /* wr4 */ + if((val & 0x30) != 0x00 || (val & 0x0c) == 0) { + halt_printf("Wr c03%x to wr4 of %02x!\n", 8+port, val); + } + scc_ptr->reg[regnum] = val; + if(changed_bits) { + scc_regen_clocks(port); + } + return; + case 5: /* wr5 */ + if((val & 0x15) != 0x0) { + halt_printf("Wr c03%x to wr5 of %02x!\n", 8+port, val); + } + scc_ptr->reg[regnum] = val; + if(changed_bits & 0x60) { + scc_regen_clocks(port); + } + return; + case 6: /* wr6 */ + if(val != 0) { + halt_printf("Wr c03%x to wr6 of %02x!\n", 8+port, val); + } + scc_ptr->reg[regnum] = val; + return; + case 7: /* wr7 */ + if(val != 0) { + halt_printf("Wr c03%x to wr7 of %02x!\n", 8+port, val); + } + scc_ptr->reg[regnum] = val; + return; + case 8: /* wr8 */ + scc_write_data(port, val, dcycs); + return; + case 9: /* wr9 */ + if((val & 0xc0)) { + if(val & 0x80) { + scc_reset_port(0); + } + if(val & 0x40) { + scc_reset_port(1); + } + if((val & 0xc0) == 0xc0) { + scc_hard_reset_port(0); + scc_hard_reset_port(1); + } + } + if((val & 0x35) != 0x00) { + printf("Write c03%x to wr9 of %02x!\n", 8+port, val); + halt_printf("val & 0x35: %02x\n", (val & 0x35)); + } + old_val = scc_stat[0].reg[9]; + scc_stat[0].reg[regnum] = val; + scc_evaluate_ints(0); + scc_evaluate_ints(1); + return; + case 10: /* wr10 */ + if((val & 0xff) != 0x00) { + printf("Wr c03%x to wr10 of %02x!\n", 8+port, val); + } + scc_ptr->reg[regnum] = val; + return; + case 11: /* wr11 */ + scc_ptr->reg[regnum] = val; + if(changed_bits) { + scc_regen_clocks(port); + } + return; + case 12: /* wr12 */ + scc_ptr->reg[regnum] = val; + if(changed_bits) { + scc_regen_clocks(port); + } + return; + case 13: /* wr13 */ + scc_ptr->reg[regnum] = val; + if(changed_bits) { + scc_regen_clocks(port); + } + return; + case 14: /* wr14 */ + old_val = scc_ptr->reg[regnum]; + val = val + (old_val & (~0xff)); + switch((val >> 5) & 0x7) { + case 0x0: + case 0x1: + case 0x2: + case 0x3: + break; + + case 0x4: /* DPLL source is BR gen */ + val |= SCC_R14_DPLL_SOURCE_BRG; + break; + default: + halt_printf("Wr c03%x to wr14 of %02x, bad dpll cd!\n", + 8+port, val); + } + if((val & 0x0c) != 0x0) { + halt_printf("Wr c03%x to wr14 of %02x!\n", 8+port, val); + } + scc_ptr->reg[regnum] = val; + if(changed_bits) { + scc_regen_clocks(port); + } + scc_maybe_br_event(port, dcycs); + return; + case 15: /* wr15 */ + /* ignore all accesses since IIgs self test messes with it */ + if((val & 0xff) != 0x0) { + scc_printf("Write c03%x to wr15 of %02x!\n", 8+port, + val); + } + if((scc_stat[0].reg[9] & 0x8) && (val != 0)) { + printf("Write wr15:%02x and master int en = 1!\n",val); + /* set_halt(1); */ + } + scc_ptr->reg[regnum] = val; + scc_maybe_br_event(port, dcycs); + scc_evaluate_ints(port); + return; + default: + halt_printf("Wr c03%x to wr%d of %02x!\n", 8+port, regnum, val); + return; + } +} + +void +scc_maybe_br_event(int port, double dcycs) +{ + Scc *scc_ptr; + double br_dcycs; + + scc_ptr = &(scc_stat[port]); + + if(((scc_ptr->reg[14] & 0x01) == 0) || scc_ptr->br_event_pending) { + return; + } + /* also, if ext ints not enabled, don't do baud rate ints */ + if((scc_ptr->reg[15] & 0x02) == 0) { + return; + } + + br_dcycs = scc_ptr->br_dcycs; + if(br_dcycs < 1.0) { + halt_printf("br_dcycs: %f!\n", br_dcycs); + } + + scc_ptr->br_event_pending = 1; + add_event_scc(dcycs + br_dcycs, SCC_MAKE_EVENT(port, SCC_BR_EVENT)); +} + +void +scc_evaluate_ints(int port) +{ + Scc *scc_ptr; + int mie; + + scc_ptr = &(scc_stat[port]); + mie = scc_stat[0].reg[9] & 0x8; /* Master int en */ + + if(mie && scc_ptr->wantint_rx && !scc_ptr->int_pending_rx) { + scc_ptr->int_pending_rx = 1; + add_irq(); + } + if(scc_ptr->int_pending_rx && (!mie || !scc_ptr->wantint_rx)) { + scc_ptr->int_pending_rx = 0; + remove_irq(); + } + if(mie && scc_ptr->wantint_tx && !scc_ptr->int_pending_tx) { + scc_ptr->int_pending_tx = 1; + add_irq(); + } + if(scc_ptr->int_pending_tx && (!mie || !scc_ptr->wantint_tx)) { + scc_ptr->int_pending_tx = 0; + remove_irq(); + } + if(mie && scc_ptr->wantint_zerocnt && !scc_ptr->int_pending_zerocnt) { + scc_ptr->int_pending_zerocnt = 1; + add_irq(); + } + if(scc_ptr->int_pending_zerocnt && (!mie || !scc_ptr->wantint_zerocnt)){ + scc_ptr->int_pending_zerocnt = 0; + remove_irq(); + } +} + + +void +scc_maybe_rx_event(int port, double dcycs) +{ + Scc *scc_ptr; + double rx_dcycs; + int in_rdptr, in_wrptr; + int depth; + + scc_ptr = &(scc_stat[port]); + + if(scc_ptr->rx_event_pending) { + /* one pending already, wait for the event to arrive */ + return; + } + + in_rdptr = scc_ptr->in_rdptr; + in_wrptr = scc_ptr->in_wrptr; + depth = scc_ptr->rx_queue_depth; + if((in_rdptr == in_wrptr) || (depth >= 3)) { + /* no more chars or no more space, just get out */ + return; + } + + if(depth < 0) { + depth = 0; + } + + /* pull char from in_rdptr into queue */ + scc_ptr->rx_queue[depth] = scc_ptr->in_buf[in_rdptr]; + scc_ptr->in_rdptr = (in_rdptr + 1) & (SCC_INBUF_SIZE - 1); + scc_ptr->rx_queue_depth = depth + 1; + scc_maybe_rx_int(port, dcycs); + rx_dcycs = scc_ptr->rx_dcycs; + scc_ptr->rx_event_pending = 1; + add_event_scc(dcycs + rx_dcycs, SCC_MAKE_EVENT(port, SCC_RX_EVENT)); +} + +void +scc_maybe_rx_int(int port, double dcycs) +{ + Scc *scc_ptr; + int depth; + int rx_int_mode; + + scc_ptr = &(scc_stat[port]); + + depth = scc_ptr->rx_queue_depth; + if(depth <= 0) { + /* no more chars, just get out */ + scc_clr_rx_int(port); + return; + } + rx_int_mode = (scc_ptr->reg[1] >> 3) & 0x3; + if(rx_int_mode == 1 || rx_int_mode == 2) { + scc_ptr->wantint_rx = 1; + } + scc_evaluate_ints(port); +} + +void +scc_clr_rx_int(int port) +{ + scc_stat[port].wantint_rx = 0; + scc_evaluate_ints(port); +} + +void +scc_handle_tx_event(int port, double dcycs) +{ + Scc *scc_ptr; + int tx_int_mode; + + scc_ptr = &(scc_stat[port]); + + /* nothing pending, see if ints on */ + tx_int_mode = (scc_ptr->reg[1] & 0x2); + if(tx_int_mode) { + scc_ptr->wantint_tx = 1; + } + scc_evaluate_ints(port); +} + +void +scc_maybe_tx_event(int port, double dcycs) +{ + Scc *scc_ptr; + double tx_dcycs; + + scc_ptr = &(scc_stat[port]); + + if(scc_ptr->tx_event_pending) { + /* one pending already, tx_buf is full */ + scc_ptr->tx_buf_empty = 0; + } else { + /* nothing pending, see ints on */ + scc_evaluate_ints(port); + tx_dcycs = scc_ptr->tx_dcycs; + scc_ptr->tx_event_pending = 1; + add_event_scc(dcycs + tx_dcycs, + SCC_MAKE_EVENT(port, SCC_TX_EVENT)); + } +} + +void +scc_clr_tx_int(int port) +{ + scc_stat[port].wantint_tx = 0; + scc_evaluate_ints(port); +} + +void +scc_set_zerocnt_int(int port) +{ + Scc *scc_ptr; + + scc_ptr = &(scc_stat[port]); + + if(scc_ptr->reg[15] & 0x2) { + scc_ptr->wantint_zerocnt = 1; + } + scc_evaluate_ints(port); +} + +void +scc_clr_zerocnt_int(int port) +{ + scc_stat[port].wantint_zerocnt = 0; + scc_evaluate_ints(port); +} + +void +scc_add_to_readbuf(int port, word32 val, double dcycs) +{ + Scc *scc_ptr; + int in_wrptr; + int in_wrptr_next; + int in_rdptr; + + scc_ptr = &(scc_stat[port]); + + in_wrptr = scc_ptr->in_wrptr; + in_rdptr = scc_ptr->in_rdptr; + in_wrptr_next = (in_wrptr + 1) & (SCC_INBUF_SIZE - 1); + if(in_wrptr_next != in_rdptr) { + scc_ptr->in_buf[in_wrptr] = val; + scc_ptr->in_wrptr = in_wrptr_next; + scc_printf("scc in port[%d] add char 0x%02x, %d,%d != %d\n", + scc_ptr->port, val, + in_wrptr, in_wrptr_next, in_rdptr); + g_scc_overflow = 0; + } else { + if(g_scc_overflow == 0) { + g_code_yellow++; + printf("scc inbuf overflow port %d\n", port); + } + g_scc_overflow = 1; + } + + scc_maybe_rx_event(port, dcycs); +} + +void +scc_add_to_writebuf(int port, word32 val, double dcycs) +{ + Scc *scc_ptr; + int out_wrptr; + int out_wrptr_next; + int out_rdptr; + + scc_ptr = &(scc_stat[port]); + + /* See if port initialized, if not, do so now */ + if(scc_ptr->state == 0) { + scc_port_init(port); + } + if(scc_ptr->state < 0) { + /* No working serial port, just toss it and go */ + return; + } + + if(!scc_ptr->tx_buf_empty) { + /* toss character! */ + printf("Tossing char\n"); + return; + } + + out_wrptr = scc_ptr->out_wrptr; + out_rdptr = scc_ptr->out_rdptr; + if(scc_ptr->tx_dcycs < 1.0) { + if(out_wrptr != out_rdptr) { + /* do just one char, then get out */ + printf("tx_dcycs < 1\n"); + return; + } + } + if(g_serial_out_masking) { + val = val & 0x7f; + } + + out_wrptr_next = (out_wrptr + 1) & (SCC_OUTBUF_SIZE - 1); + if(out_wrptr_next != out_rdptr) { + scc_ptr->out_buf[out_wrptr] = val; + scc_ptr->out_wrptr = out_wrptr_next; + scc_printf("scc wrbuf port %d had char 0x%02x added\n", + scc_ptr->port, val); + g_scc_overflow = 0; + } else { + if(g_scc_overflow == 0) { + g_code_yellow++; + printf("scc outbuf overflow port %d\n", port); + } + g_scc_overflow = 1; + } +} + +word32 +scc_read_data(int port, double dcycs) +{ + Scc *scc_ptr; + word32 ret; + int depth; + int i; + + scc_ptr = &(scc_stat[port]); + + scc_try_fill_readbuf(port, dcycs); + + depth = scc_ptr->rx_queue_depth; + + ret = 0; + if(depth != 0) { + ret = scc_ptr->rx_queue[0]; + for(i = 1; i < depth; i++) { + scc_ptr->rx_queue[i-1] = scc_ptr->rx_queue[i]; + } + scc_ptr->rx_queue_depth = depth - 1; + scc_maybe_rx_event(port, dcycs); + scc_maybe_rx_int(port, dcycs); + } + + scc_printf("SCC read %04x: ret %02x, depth:%d\n", 0xc03b-port, ret, + depth); + + scc_log(SCC_REGNUM(0,port,8), ret, dcycs); + + return ret; +} + + +void +scc_write_data(int port, word32 val, double dcycs) +{ + Scc *scc_ptr; + + scc_printf("SCC write %04x: %02x\n", 0xc03b-port, val); + scc_log(SCC_REGNUM(1,port,8), val, dcycs); + + scc_ptr = &(scc_stat[port]); + if(scc_ptr->reg[14] & 0x10) { + /* local loopback! */ + scc_add_to_readbuf(port, val, dcycs); + } else { + scc_add_to_writebuf(port, val, dcycs); + } + scc_try_to_empty_writebuf(port); + + scc_maybe_tx_event(port, dcycs); +} + diff --git a/src/scc.h b/src/scc.h new file mode 100644 index 0000000..ace256c --- /dev/null +++ b/src/scc.h @@ -0,0 +1,78 @@ +/************************************************************************/ +/* KEGS: Apple //gs Emulator */ +/* Copyright 2002 by Kent Dickey */ +/* */ +/* This code is covered by the GNU GPL */ +/* */ +/* The KEGS web page is kegs.sourceforge.net */ +/* You may contact the author at: kadickey@alumni.princeton.edu */ +/************************************************************************/ + +#ifdef INCLUDE_RCSID_C +const char rcsid_scc_h[] = "@(#)$KmKId: scc.h,v 1.12 2003-11-21 00:27:00-05 kentd Exp $"; +#endif + +#ifdef _WIN32 +# include +#else +# include +# include +#endif + +#if defined(HPUX) || defined(__linux__) || defined(SOLARIS) || defined(MAC) || defined(__MACH__) +# define SCC_SOCKETS +#endif + + +/* my scc port 0 == channel A, port 1 = channel B */ + +#define SCC_INBUF_SIZE 4096 /* must be a power of 2 */ +#define SCC_OUTBUF_SIZE 4096 /* must be a power of 2 */ + +STRUCT(Scc) { + int port; + int state; + int accfd; + int rdwrfd; + void *host_handle; + void *host_handle2; + int host_aux1; + int read_called_this_vbl; + int write_called_this_vbl; + + int mode; + int reg_ptr; + int reg[16]; + + int rx_queue_depth; + byte rx_queue[4]; + + int in_rdptr; + int in_wrptr; + byte in_buf[SCC_INBUF_SIZE]; + + int out_rdptr; + int out_wrptr; + byte out_buf[SCC_OUTBUF_SIZE]; + + int br_is_zero; + int tx_buf_empty; + int wantint_rx; + int wantint_tx; + int wantint_zerocnt; + int int_pending_rx; + int int_pending_tx; + int int_pending_zerocnt; + + double br_dcycs; + double tx_dcycs; + double rx_dcycs; + + int br_event_pending; + int rx_event_pending; + int tx_event_pending; + + int char_size; + int baud_rate; +}; + diff --git a/src/scc_macdriver.c b/src/scc_macdriver.c new file mode 100644 index 0000000..11c4184 --- /dev/null +++ b/src/scc_macdriver.c @@ -0,0 +1,211 @@ +/************************************************************************/ +/* KEGS: Apple //gs Emulator */ +/* Copyright 2002 by Kent Dickey */ +/* */ +/* This code is covered by the GNU GPL */ +/* */ +/* The KEGS web page is kegs.sourceforge.net */ +/* You may contact the author at: kadickey@alumni.princeton.edu */ +/************************************************************************/ + +const char rcsid_scc_macdriver_c[] = "@(#)$KmKId: scc_macdriver.c,v 1.3 2003-11-03 01:52:49-05 kentd Exp $"; + +/* This file contains the Mac serial calls */ + +#include "defc.h" +#include "scc.h" + +#ifndef _WIN32 +# include +#endif + +extern Scc scc_stat[2]; +extern word32 g_c025_val; + +#ifdef MAC +int +scc_serial_mac_init(int port) +{ + char str_buf[1024]; + Scc *scc_ptr; + int state; + int fd; + + scc_ptr = &(scc_stat[port]); + + scc_ptr->state = 0; /* mark as uninitialized */ + + sprintf(&str_buf[0], "/dev/tty.USA19QW11P1.1"); + /* HACK: fix this... */ + + fd = open(&str_buf[0], O_RDWR | O_NONBLOCK); + + scc_ptr->host_handle = (void *)fd; + scc_ptr->host_handle2 = 0; + + printf("scc_serial_mac_init %d called, fd: %d\n", port, fd); + + if(fd < 0) { + scc_ptr->host_handle = (void *)-1; + return -1; + } + + scc_serial_mac_change_params(port); + + state = 2; /* raw serial */ + scc_ptr->state = state; + + return state; +} + +void +scc_serial_mac_change_params(int port) +{ + struct termios termios_buf; + Scc *scc_ptr; + int fd; + int csz; + int ret; + + scc_ptr = &(scc_stat[port]); + + fd = (int)scc_ptr->host_handle; + printf("scc_serial_mac_change_parms port: %d, fd: %d\n", port, fd); + if(fd <= 0) { + return; + } + + ret = tcgetattr(fd, &termios_buf); + if(ret != 0) { + printf("tcgetattr port%d ret: %d\n", port, ret); + } + +#if 1 + printf("baudrate: %d, iflag:%x, oflag:%x, cflag:%x, lflag:%x\n", + (int)termios_buf.c_ispeed, (int)termios_buf.c_iflag, + (int)termios_buf.c_oflag, (int)termios_buf.c_cflag, + (int)termios_buf.c_lflag); +#endif + + memset(&termios_buf, 0, sizeof(struct termios)); + cfmakeraw(&termios_buf); + cfsetspeed(&termios_buf, scc_ptr->baud_rate); + + csz = scc_ptr->char_size; + termios_buf.c_cflag = CREAD | CLOCAL; + termios_buf.c_cflag |= (csz == 5) ? CS5 : + (csz == 6) ? CS6 : + (csz == 7) ? CS7 : + CS8; + switch((scc_ptr->reg[4] >> 2) & 0x3) { + case 2: // 1.5 stop bits + termios_buf.c_cflag |= CSTOPB; /* no 1.5 stop bit setting.*/ + break; + case 3: // 2 stop bits + termios_buf.c_cflag |= CSTOPB; + break; + } + + switch((scc_ptr->reg[4]) & 0x3) { + case 1: // Odd parity + termios_buf.c_cflag |= (PARENB | PARODD); + break; + case 3: // Even parity + termios_buf.c_cflag |= PARENB; + break; + } + + /* always enabled DTR and RTS control */ + termios_buf.c_cflag |= CDTR_IFLOW | CRTS_IFLOW; + + printf("fd: %d, baudrate: %d, iflag:%x, oflag:%x, cflag:%x, lflag:%x\n", + fd, (int)termios_buf.c_ispeed, (int)termios_buf.c_iflag, + (int)termios_buf.c_oflag, (int)termios_buf.c_cflag, + (int)termios_buf.c_lflag); + ret = tcsetattr(fd, TCSANOW, &termios_buf); + if(ret != 0) { + printf("tcsetattr ret: %d\n", ret); + } +} + +void +scc_serial_mac_fill_readbuf(int port, double dcycs) +{ + byte tmp_buf[256]; + Scc *scc_ptr; + int fd; + int i; + int ret; + + scc_ptr = &(scc_stat[port]); + + fd = (int)scc_ptr->host_handle; + if(fd <= 0) { + return; + } + + /* Try reading some bytes */ + ret = read(fd, tmp_buf, 256); + + if(ret > 0) { + for(i = 0; i < ret; i++) { + scc_add_to_readbuf(port, tmp_buf[i], dcycs); + } + } + +} + +void +scc_serial_mac_empty_writebuf(int port) +{ + Scc *scc_ptr; + int fd; + int rdptr; + int wrptr; + int done; + int ret; + int len; + + scc_ptr = &(scc_stat[port]); + + fd = (int)scc_ptr->host_handle; + if(fd <= 0) { + return; + } + + /* Try writing some bytes */ + done = 0; + while(!done) { + rdptr = scc_ptr->out_rdptr; + wrptr = scc_ptr->out_wrptr; + if(rdptr == wrptr) { + //printf("...rdptr == wrptr\n"); + done = 1; + break; + } + len = wrptr - rdptr; + if(len < 0) { + len = SCC_OUTBUF_SIZE - rdptr; + } + if(len > 32) { + len = 32; + } + if(len <= 0) { + done = 1; + break; + } + ret = write(fd, &(scc_ptr->out_buf[rdptr]), len); + + if(ret <= 0) { + done = 1; + break; + } else { + rdptr = rdptr + ret; + if(rdptr >= SCC_OUTBUF_SIZE) { + rdptr = rdptr - SCC_OUTBUF_SIZE; + } + scc_ptr->out_rdptr = rdptr; + } + } +} +#endif /* MAC */ diff --git a/src/scc_socket_driver.c b/src/scc_socket_driver.c new file mode 100644 index 0000000..1decca5 --- /dev/null +++ b/src/scc_socket_driver.c @@ -0,0 +1,255 @@ +/************************************************************************/ +/* KEGS: Apple //gs Emulator */ +/* Copyright 2002 by Kent Dickey */ +/* */ +/* This code is covered by the GNU GPL */ +/* */ +/* The KEGS web page is kegs.sourceforge.net */ +/* You may contact the author at: kadickey@alumni.princeton.edu */ +/************************************************************************/ + +const char rcsid_scc_socket_driver_c[] = "@(#)$KmKId: scc_socket_driver.c,v 1.4 2003-11-20 23:43:41-05 kentd Exp $"; + +/* This file contains the Unix socket calls */ + +#include "defc.h" +#include "scc.h" +#include + +extern Scc scc_stat[2]; + +void +scc_socket_init(int port) +{ +#ifdef SCC_SOCKETS + Scc *scc_ptr; + struct sockaddr_in sa_in; + int on; + int flags; + int ret; + int sockfd; + int inc; + + inc = 0; + + scc_ptr = &(scc_stat[port]); + + scc_ptr->state = -1; /* mark as failed for now */ + scc_ptr->host_aux1 = sizeof(struct sockaddr_in); + scc_ptr->host_handle = malloc(scc_ptr->host_aux1); + memset(scc_ptr->host_handle, 0, scc_ptr->host_aux1); + + while(1) { + sockfd = socket(AF_INET, SOCK_STREAM, 0); + if(sockfd < 0) { + printf("socket ret: %d, errno: %d\n", sockfd, errno); + return; + } + /* printf("socket ret: %d\n", sockfd); */ + + on = 1; + ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, + (char *)&on, sizeof(on)); + if(ret < 0) { + printf("setsockopt REUSEADDR ret: %d, err:%d\n", + ret, errno); + return; + } + + memset(&sa_in, 0, sizeof(sa_in)); + sa_in.sin_family = AF_INET; + sa_in.sin_port = htons(6501 + port + inc); + sa_in.sin_addr.s_addr = htonl(INADDR_ANY); + + ret = bind(sockfd, (struct sockaddr *)&sa_in, sizeof(sa_in)); + + if(ret < 0) { + printf("bind ret: %d, errno: %d\n", ret, errno); + inc++; + close(sockfd); + printf("Trying next port: %d\n", 6501 + port + inc); + if(inc >= 10) { + printf("Too many retries, quitting\n"); + return; + } + } else { + break; + } + } + + printf("SCC port %d is at unix port %d\n", port, 6501 + port + inc); + + ret = listen(sockfd, 1); + + flags = fcntl(sockfd, F_GETFL, 0); + if(flags == -1) { + printf("fcntl GETFL ret: %d, errno: %d\n", flags, errno); + return; + } + ret = fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); + if(ret == -1) { + printf("fcntl SETFL ret: %d, errno: %d\n", ret, errno); + return; + } + + scc_ptr->accfd = sockfd; + scc_ptr->state = 1; /* successful socket */ +#endif /* SCC_SOCKETS */ +} + +void +scc_socket_change_params(int port) +{ +#ifdef SCC_SOCKETS +#endif +} + +void +scc_accept_socket(int port) +{ +#ifdef SCC_SOCKETS + Scc *scc_ptr; + int flags; + int rdwrfd; + int ret; + + scc_ptr = &(scc_stat[port]); + + if(scc_ptr->rdwrfd <= 0) { + rdwrfd = accept(scc_ptr->accfd, scc_ptr->host_handle, + &(scc_ptr->host_aux1)); + if(rdwrfd < 0) { + return; + } + + /* For Linux, we need to set O_NONBLOCK on the rdwrfd */ + flags = fcntl(rdwrfd, F_GETFL, 0); + if(flags == -1) { + printf("fcntl GETFL ret: %d, errno: %d\n", flags,errno); + return; + } + ret = fcntl(rdwrfd, F_SETFL, flags | O_NONBLOCK); + if(ret == -1) { + printf("fcntl SETFL ret: %d, errno: %d\n", ret, errno); + return; + } + + scc_ptr->rdwrfd = rdwrfd; + } +#endif +} + +void +scc_socket_fill_readbuf(int port, double dcycs) +{ +#ifdef SCC_SOCKETS + byte tmp_buf[256]; + Scc *scc_ptr; + int rdwrfd; + int i; + int ret; + + scc_ptr = &(scc_stat[port]); + + /* Accept socket if not already open */ + scc_accept_socket(port); + + rdwrfd = scc_ptr->rdwrfd; + if(rdwrfd < 0) { + return; + } + + /* Try reading some bytes */ + ret = read(rdwrfd, tmp_buf, 256); + if(ret > 0) { + for(i = 0; i < ret; i++) { + if(tmp_buf[i] == 0) { + /* Skip null chars */ + continue; + } + scc_add_to_readbuf(port, tmp_buf[i], dcycs); + } + } else if(ret == 0) { + /* assume socket close */ + close(rdwrfd); + scc_ptr->rdwrfd = -1; + } +#endif +} + +void +scc_socket_empty_writebuf(int port) +{ +#ifdef SCC_SOCKETS + struct sigaction newact, oldact; + Scc *scc_ptr; + int rdptr; + int wrptr; + int rdwrfd; + int done; + int ret; + int len; + + scc_ptr = &(scc_stat[port]); + + scc_accept_socket(port); + + rdwrfd = scc_ptr->rdwrfd; + if(rdwrfd < 0) { + return; + } + + /* Try writing some bytes */ + done = 0; + while(!done) { + rdptr = scc_ptr->out_rdptr; + wrptr = scc_ptr->out_wrptr; + if(rdptr == wrptr) { + done = 1; + break; + } + len = wrptr - rdptr; + if(len < 0) { + len = SCC_OUTBUF_SIZE - rdptr; + } + if(len > 32) { + len = 32; + } + if(len <= 0) { + done = 1; + break; + } + + /* ignore SIGPIPE around writes to the socket, so we can */ + /* catch a closed socket and prepare to re-accept a new */ + /* connection. Otherwise, SIGPIPE kills KEGS */ + sigemptyset(&newact.sa_mask); + newact.sa_handler = SIG_IGN; + newact.sa_flags = 0; + sigaction(SIGPIPE, &newact, &oldact); + + ret = write(rdwrfd, &(scc_ptr->out_buf[rdptr]), len); + + sigaction(SIGPIPE, &oldact, 0); + /* restore previous SIGPIPE behavior */ + + if(ret == 0) { + done = 1; /* give up for now */ + break; + } else if(ret < 0) { + /* assume socket is dead */ + close(rdwrfd); + scc_ptr->rdwrfd = -1; + done = 1; + break; + } else { + rdptr = rdptr + ret; + if(rdptr >= SCC_OUTBUF_SIZE) { + rdptr = rdptr - SCC_OUTBUF_SIZE; + } + scc_ptr->out_rdptr = rdptr; + } + } +#endif +} + diff --git a/src/scc_windriver.c b/src/scc_windriver.c new file mode 100644 index 0000000..57efd0b --- /dev/null +++ b/src/scc_windriver.c @@ -0,0 +1,252 @@ +/************************************************************************/ +/* KEGS: Apple //gs Emulator */ +/* Copyright 2002 by Kent Dickey */ +/* */ +/* This code is covered by the GNU GPL */ +/* */ +/* The KEGS web page is kegs.sourceforge.net */ +/* You may contact the author at: kadickey@alumni.princeton.edu */ +/************************************************************************/ + +const char rcsid_scc_windriver_c[] = "@(#)$KmKId: scc_windriver.c,v 1.3 2003-09-20 15:05:15-04 kentd Exp $"; + +/* This file contains the Win32 COM1/COM2 calls */ + +#include "defc.h" +#include "scc.h" + +extern Scc scc_stat[2]; +extern word32 g_c025_val; + +#ifdef _WIN32 +int +scc_serial_win_init(int port) +{ + COMMTIMEOUTS commtimeouts; + char str_buf[8]; + Scc *scc_ptr; + HANDLE host_handle; + int state; + int ret; + + scc_ptr = &(scc_stat[port]); + + scc_ptr->state = 0; /* mark as failed */ + + sprintf(&str_buf[0], "COM%d", port+1); + + host_handle = CreateFile(&str_buf[0], GENERIC_READ | GENERIC_WRITE, + 0, NULL, OPEN_EXISTING, 0, NULL); + + scc_ptr->host_handle = host_handle; + scc_ptr->host_handle2 = malloc(sizeof(DCB)); + + printf("scc_socket_init %d called, host_handle: %p\n", port, + host_handle); + + if(host_handle == INVALID_HANDLE_VALUE) { + scc_ptr->host_handle = 0; + return 0; + } + + scc_serial_win_change_params(port); + + + commtimeouts.ReadIntervalTimeout = MAXDWORD; + commtimeouts.ReadTotalTimeoutMultiplier = 0; + commtimeouts.ReadTotalTimeoutConstant = 0; + commtimeouts.WriteTotalTimeoutMultiplier = 0; + commtimeouts.WriteTotalTimeoutConstant = 10; + ret = SetCommTimeouts(host_handle, &commtimeouts); + if(ret == 0) { + printf("setcommtimeout ret: %d\n", ret); + } + + state = 2; /* raw serial */ + scc_ptr->state = state; + + return state; +} + +void +scc_serial_win_change_params(int port) +{ + DCB *dcbptr; + HANDLE host_handle; + Scc *scc_ptr; + int ret; + + scc_ptr = &(scc_stat[port]); + + host_handle = scc_ptr->host_handle; + dcbptr = scc_ptr->host_handle2; + if(host_handle == 0) { + return; + } + + ret = GetCommState(host_handle, dcbptr); + if(ret == 0) { + printf("getcomm port%d ret: %d\n", port, ret); + } + +#if 1 + printf("dcb.baudrate: %d, bytesize:%d, stops:%d, parity:%d\n", + (int)dcbptr->BaudRate, (int)dcbptr->ByteSize, + (int)dcbptr->StopBits, (int)dcbptr->Parity); + printf("dcb.binary: %d, ctsflow: %d, dsrflow: %d, dtr: %d, dsr: %d\n", + (int)dcbptr->fBinary, + (int)dcbptr->fOutxCtsFlow, + (int)dcbptr->fOutxDsrFlow, + (int)dcbptr->fDtrControl, + (int)dcbptr->fDsrSensitivity); + printf("dcb.txonxoff:%d, outx:%d, inx: %d, null: %d, rts: %d\n", + (int)dcbptr->fTXContinueOnXoff, + (int)dcbptr->fOutX, + (int)dcbptr->fInX, + (int)dcbptr->fNull, + (int)dcbptr->fRtsControl); + printf("dcb.fAbortOnErr:%d, fParity:%d\n", (int)dcbptr->fAbortOnError, + (int)dcbptr->fParity); +#endif + + dcbptr->fAbortOnError = 0; + + dcbptr->BaudRate = scc_ptr->baud_rate; + dcbptr->ByteSize = scc_ptr->char_size; + dcbptr->StopBits = ONESTOPBIT; + switch((scc_ptr->reg[4] >> 2) & 0x3) { + case 2: // 1.5 stop bits + dcbptr->StopBits = ONE5STOPBITS; + break; + case 3: // 2 stop bits + dcbptr->StopBits = TWOSTOPBITS; + break; + } + + dcbptr->Parity = NOPARITY; + switch((scc_ptr->reg[4]) & 0x3) { + case 1: // Odd parity + dcbptr->Parity = ODDPARITY; + break; + case 3: // Even parity + dcbptr->Parity = EVENPARITY; + break; + } + + dcbptr->fNull = 0; + dcbptr->fDtrControl = DTR_CONTROL_ENABLE; + dcbptr->fDsrSensitivity = 0; + dcbptr->fOutxCtsFlow = 0; + dcbptr->fOutxDsrFlow = 0; + dcbptr->fParity = 0; + dcbptr->fInX = 0; + dcbptr->fOutX = 0; + dcbptr->fRtsControl = RTS_CONTROL_ENABLE; + + ret = SetCommState(host_handle, dcbptr); + if(ret == 0) { + printf("SetCommState ret: %d, new baud: %d\n", ret, + (int)dcbptr->BaudRate); + } +} + +void +scc_serial_win_fill_readbuf(int port, double dcycs) +{ + byte tmp_buf[256]; + Scc *scc_ptr; + HANDLE host_handle; + DWORD bytes_read; + int i; + int ret; + + scc_ptr = &(scc_stat[port]); + + host_handle = scc_ptr->host_handle; + if(host_handle == 0) { + return; + } + + /* Try reading some bytes */ + ret = ReadFile(host_handle, tmp_buf, 256, &bytes_read, NULL); + + if(ret == 0) { + printf("ReadFile ret 0\n"); + } + + if(ret && (bytes_read > 0)) { + for(i = 0; i < bytes_read; i++) { + scc_add_to_readbuf(port, tmp_buf[i], dcycs); + } + } + +} + +void +scc_serial_win_empty_writebuf(int port) +{ + Scc *scc_ptr; + HANDLE host_handle; + int rdptr; + int wrptr; + int done; + word32 err_code; + DWORD bytes_written; + int ret; + int len; + + scc_ptr = &(scc_stat[port]); + + //printf("win_empty_writebuf, host_handle: %d\n", scc_ptr->host_handle); + host_handle = scc_ptr->host_handle; + if(host_handle == 0) { + return; + } + + /* Try writing some bytes */ + done = 0; + while(!done) { + rdptr = scc_ptr->out_rdptr; + wrptr = scc_ptr->out_wrptr; + if(rdptr == wrptr) { + //printf("...rdptr == wrptr\n"); + done = 1; + break; + } + len = wrptr - rdptr; + if(len < 0) { + len = SCC_OUTBUF_SIZE - rdptr; + } + if(len > 32) { + len = 32; + } + if(len <= 0) { + done = 1; + break; + } + bytes_written = 1; + ret = WriteFile(host_handle, &(scc_ptr->out_buf[rdptr]), len, + &bytes_written, NULL); + printf("WriteFile ret: %d, bytes_written:%d, len:%d\n", ret, + (int)bytes_written, len); + + err_code = (word32)-1; + if(ret == 0) { + err_code = (word32)GetLastError(); + printf("WriteFile ret:0, err_code: %08x\n", err_code); + } + + if(ret == 0 || (bytes_written == 0)) { + done = 1; + break; + } else { + rdptr = rdptr + bytes_written; + if(rdptr >= SCC_OUTBUF_SIZE) { + rdptr = rdptr - SCC_OUTBUF_SIZE; + } + scc_ptr->out_rdptr = rdptr; + } + } +} + +#endif diff --git a/src/sim65816.c b/src/sim65816.c new file mode 100644 index 0000000..0f38d36 --- /dev/null +++ b/src/sim65816.c @@ -0,0 +1,2285 @@ +/************************************************************************/ +/* KEGS: Apple //gs Emulator */ +/* Copyright 2002 by Kent Dickey */ +/* */ +/* This code is covered by the GNU GPL */ +/* */ +/* The KEGS web page is kegs.sourceforge.net */ +/* You may contact the author at: kadickey@alumni.princeton.edu */ +/************************************************************************/ + +const char rcsid_sim65816_c[] = "@(#)$KmKId: sim65816.c,v 1.346 2004-03-23 17:26:10-05 kentd Exp $"; + +#include + +#define INCLUDE_RCSID_C +#include "defc.h" +#undef INCLUDE_RCSID_C + +char g_argv0_path[256] = "./"; + +const char *g_kegs_default_paths[] = { "", "./", "${HOME}/", + "${0}/Contents/Resources/", "/usr/local/lib/", + "/usr/local/kegs/", "/usr/local/lib/kegs/", "/usr/share/kegs/", + "/usr/share/", "/var/lib/", "/usr/lib/", "/lib/", "/etc/", + "/etc/kegs/", "${0}/", 0 }; + +#define MAX_EVENTS 64 + +/* All EV_* must be less than 256, since upper bits reserved for other use */ +/* e.g., DOC_INT uses upper bits to encode oscillator */ +#define EV_60HZ 1 +#define EV_STOP 2 +#define EV_SCAN_INT 3 +#define EV_DOC_INT 4 +#define EV_VBL_INT 5 +#define EV_SCC 6 +#define EV_VID_UPD 7 + +extern int g_stepping; + +extern int statereg; +extern int g_cur_a2_stat; + +extern int wrdefram; +extern int int_crom[8]; + +extern int shadow_text; + +extern int shadow_reg; +extern int speed_fast; +extern word32 g_slot_motor_detect; + +extern int g_c023_val; +extern int c023_1sec_int_irq_pending; +extern int c023_scan_int_irq_pending; +extern int c041_en_25sec_ints; +extern int c041_en_vbl_ints; +extern int g_c046_val; +extern int c046_25sec_irq_pend; +extern int c046_vbl_irq_pending; +extern int g_zipgs_reg_c059; +extern int g_zipgs_reg_c05a; +extern int g_zipgs_reg_c05b; +extern int g_zipgs_unlock; + +extern int g_engine_c_mode; +extern int defs_instr_start_8; +extern int defs_instr_start_16; +extern int defs_instr_end_8; +extern int defs_instr_end_16; +extern int op_routs_start; +extern int op_routs_end; + +extern int updated_mod_latch; +extern int capslock_key_down; + +Engine_reg engine; +extern word32 table8[]; +extern word32 table16[]; + +extern byte doc_ram[]; + +extern int g_iwm_motor_on; +extern int g_fast_disk_emul; +extern int g_slow_525_emul_wr; +extern int g_apple35_sel; +extern int g_config_control_panel; + +extern int g_audio_enable; +extern int g_preferred_rate; + +void U_STACK_TRACE(); + +double g_fcycles_stop = 0.0; +int halt_sim = 0; +int enter_debug = 0; +int g_rom_version = 0; +int g_halt_on_bad_read = 0; +int g_ignore_bad_acc = 1; +int g_ignore_halts = 1; +int g_code_red = 0; +int g_code_yellow = 0; +int g_use_alib = 0; +int g_raw_serial = 1; +int g_serial_out_masking = 0; + +int g_config_iwm_vbl_count = 0; +const char g_kegs_version_str[] = "0.86"; + +#if 0 +const double g_drecip_cycles_in_16ms = (1.0/(DCYCS_IN_16MS)); +const double g_dcycles_in_16ms = DCYCS_IN_16MS; +#endif + +#define START_DCYCS (0.0) + +double g_last_vbl_dcycs = START_DCYCS; +double g_cur_dcycs = START_DCYCS; + +double g_last_vbl_dadjcycs = 0.0; +double g_dadjcycs = 0.0; + + +int g_wait_pending = 0; +int g_irq_pending = 0; + +int g_num_irq = 0; +int g_num_brk = 0; +int g_num_cop = 0; +int g_num_enter_engine = 0; +int g_io_amt = 0; +int g_engine_action = 0; +int g_engine_halt_event = 0; +int g_engine_scan_int = 0; +int g_engine_doc_int = 0; + +int g_testing = 0; +int g_testing_enabled = 0; + + +word32 stop_run_at; + +int g_25sec_cntr = 0; +int g_1sec_cntr = 0; + +double g_dnatcycs_1sec = 0.0; +word32 g_natcycs_lastvbl = 0; + +int Verbose = 0; +int Halt_on = 0; + +word32 g_mem_size_base = 256*1024; /* size of motherboard memory */ +word32 g_mem_size_exp = 8*1024*1024; /* size of expansion RAM card */ +word32 g_mem_size_total = 256*1024; /* Total contiguous RAM from 0 */ + +extern word32 slow_mem_changed[]; + +byte *g_slow_memory_ptr = 0; +byte *g_memory_ptr = 0; +byte *g_dummy_memory1_ptr = 0; +byte *g_rom_fc_ff_ptr = 0; +byte *g_rom_cards_ptr = 0; + +Page_info page_info_rd_wr[2*65536 + PAGE_INFO_PAD_SIZE]; + +int kbd_in_end = 0; +byte kbd_in_buf[LEN_KBD_BUF]; + + +#define PC_LOG_LEN (8*1024) + +Pc_log pc_log_array[PC_LOG_LEN + 2]; + +Pc_log *log_pc_ptr = &(pc_log_array[0]); +Pc_log *log_pc_start_ptr = &(pc_log_array[0]); +Pc_log *log_pc_end_ptr = &(pc_log_array[PC_LOG_LEN]); + + +void +show_pc_log() +{ + FILE *pcfile; + double dcycs; + double start_dcycs; + word32 instr; + word32 psr; + word32 acc, xreg, yreg; + word32 stack, direct; + word32 dbank; + word32 kpc; + int accsize, xsize; + int num; + int i; + + pcfile = fopen("pc_log_out", "wt"); + if(pcfile == 0) { + fprintf(stderr,"fopen failed...errno: %d\n", errno); + exit(2); + } +#if 0 + fprintf(pcfile, "current pc_log_ptr: %p, start: %p, end: %p\n", + log_pc_ptr, log_pc_start_ptr, log_pc_end_ptr); +#endif + + start_dcycs = log_pc_ptr->dcycs; + + for(i = 0; i < PC_LOG_LEN; i++) { + dbank = (log_pc_ptr->dbank_kpc >> 24) & 0xff; + kpc = log_pc_ptr->dbank_kpc & 0xffffff; + instr = log_pc_ptr->instr; + psr = (log_pc_ptr->psr_acc >> 16) & 0xffff;; + acc = log_pc_ptr->psr_acc & 0xffff;; + xreg = (log_pc_ptr->xreg_yreg >> 16) & 0xffff;; + yreg = log_pc_ptr->xreg_yreg & 0xffff;; + stack = (log_pc_ptr->stack_direct >> 16) & 0xffff;; + direct = log_pc_ptr->stack_direct & 0xffff;; + dcycs = log_pc_ptr->dcycs; + + num = log_pc_ptr - log_pc_start_ptr; + + accsize = 2; + xsize = 2; + if(psr & 0x20) { + accsize = 1; + } + if(psr & 0x10) { + xsize = 1; + } + + fprintf(pcfile, "%04x: A:%04x X:%04x Y:%04x P:%03x " + "S:%04x D:%04x B:%02x %9.2f ", i, + acc, xreg, yreg, psr, stack, direct, dbank, + (dcycs-start_dcycs)); + + do_dis(pcfile, kpc, accsize, xsize, 1, instr); + log_pc_ptr++; + if(log_pc_ptr >= log_pc_end_ptr) { + log_pc_ptr = log_pc_start_ptr; + } + } + + fclose(pcfile); +} + + +#define TOOLBOX_LOG_LEN 64 + +int g_toolbox_log_pos = 0; +word32 g_toolbox_log_array[TOOLBOX_LOG_LEN][8]; + +word32 +toolbox_debug_4byte(word32 addr) +{ + word32 part1, part2; + + /* If addr looks safe, use it */ + if(addr > 0xbffc) { + return (word32)-1; + } + + part1 = get_memory16_c(addr, 0); + part1 = (part1 >> 8) + ((part1 & 0xff) << 8); + part2 = get_memory16_c(addr+2, 0); + part2 = (part2 >> 8) + ((part2 & 0xff) << 8); + + return (part1 << 16) + part2; +} + +void +toolbox_debug_c(word32 xreg, word32 stack, double *cyc_ptr) +{ + int pos; + + pos = g_toolbox_log_pos; + + stack += 9; + g_toolbox_log_array[pos][0] = g_last_vbl_dcycs + *cyc_ptr; + g_toolbox_log_array[pos][1] = stack+1; + g_toolbox_log_array[pos][2] = xreg; + g_toolbox_log_array[pos][3] = toolbox_debug_4byte(stack+1); + g_toolbox_log_array[pos][4] = toolbox_debug_4byte(stack+5); + g_toolbox_log_array[pos][5] = toolbox_debug_4byte(stack+9); + g_toolbox_log_array[pos][6] = toolbox_debug_4byte(stack+13); + g_toolbox_log_array[pos][7] = toolbox_debug_4byte(stack+17); + + pos++; + if(pos >= TOOLBOX_LOG_LEN) { + pos = 0; + } + + g_toolbox_log_pos = pos; +} + +void +show_toolbox_log() +{ + int pos; + int i; + + pos = g_toolbox_log_pos; + + for(i = TOOLBOX_LOG_LEN - 1; i >= 0; i--) { + printf("%2d:%2d: %08x %06x %04x: %08x %08x %08x %08x %08x\n", + i, pos, + g_toolbox_log_array[pos][0], + g_toolbox_log_array[pos][1], + g_toolbox_log_array[pos][2], + g_toolbox_log_array[pos][3], + g_toolbox_log_array[pos][4], + g_toolbox_log_array[pos][5], + g_toolbox_log_array[pos][6], + g_toolbox_log_array[pos][7]); + pos++; + if(pos >= TOOLBOX_LOG_LEN) { + pos = 0; + } + } +} + +#if 0 +/* get_memory_c is not used, get_memory_asm is, but this does what the */ +/* assembly language would do */ +word32 +get_memory_c(word32 loc, int diff_cycles) +{ + byte *addr; + word32 result; + int index; + +#ifdef CHECK_BREAKPOINTS + check_breakpoints_c(loc); +#endif + + index = loc >> 8; + result = page_info[index].rd; + if(result & BANK_IO_BIT) { + return get_memory_io(loc, diff_cycles); + } + + addr = (byte *)((result & 0xffffff00) + (loc & 0xff)); + + return *addr; +} +#endif + + +word32 +get_memory_io(word32 loc, double *cyc_ptr) +{ + int tmp; + + if(loc > 0xffffff) { + halt_printf("get_memory_io:%08x out of range==halt!\n", loc); + return 0; + } + + tmp = loc & 0xfef000; + if(tmp == 0xc000 || tmp == 0xe0c000) { + return(io_read(loc & 0xfff, cyc_ptr)); + } + + /* Else it's an illegal addr...skip if memory sizing */ + if(loc >= g_mem_size_total) { + if((loc & 0xfffe) == 0) { +#if 0 + printf("get_io assuming mem sizing, not halting\n"); +#endif + return 0; + } + } + + /* Skip reads to f80000 and f00000, just return 0 */ + if((loc & 0xf70000) == 0xf00000) { + return 0; + } + + if((loc & 0xff0000) == 0xef0000) { + /* DOC RAM */ + return (doc_ram[loc & 0xffff]); + } + + g_code_yellow++; + if(g_ignore_bad_acc) { + /* print no message, just get out. User doesn't want */ + /* to be bothered by buggy programs */ + return 0; + } + + printf("get_memory_io for addr: %06x\n", loc); + printf("stat for addr: %06x = %p\n", loc, + GET_PAGE_INFO_RD((loc >> 8) & 0xffff)); + set_halt(g_halt_on_bad_read); + + return 0; +} + +#if 0 +word32 +get_memory16_pieces(word32 loc, int diff_cycles) +{ + return(get_memory_c(loc, diff_cycles) + + (get_memory_c(loc+1, diff_cycles) << 8)); +} + +word32 +get_memory24(word32 loc, int diff_cycles) +{ + return(get_memory_c(loc, diff_cycles) + + (get_memory_c(loc+1, diff_cycles) << 8) + + (get_memory_c(loc+2, diff_cycles) << 16)); +} +#endif + +#if 0 +void +set_memory(word32 loc, int val, int diff_cycles) +{ + byte *ptr; + word32 new_addr; + word32 tmp; + word32 or_val; + int or_pos; + int old_slow_val; + +#ifdef CHECK_BREAKPOINTS + check_breakpoints_c(loc); +#endif + + tmp = GET_PAGE_INFO_WR((loc>>8) & 0xffff); + if(tmp & BANK_IO) { + set_memory_io(loc, val, diff_cycles); + return; + } + + if((loc & 0xfef000) == 0xe0c000) { + printf("set_memory_special: non-io for addr %08x, %02x, %d\n", + loc, val, diff_cycles); + halt_printf("tmp: %08x\n", tmp); + } + + ptr = (byte *)(tmp & (~0xff)); + + new_addr = loc & 0xffff; + old_slow_val = val; + + if(tmp & BANK_SHADOW) { + old_slow_val = g_slow_memory_ptr[new_addr]; + } else if(tmp & BANK_SHADOW2) { + new_addr += 0x10000; + old_slow_val = g_slow_memory_ptr[new_addr]; + } + + if(old_slow_val != val) { + g_slow_memory_ptr[new_addr] = val; + or_pos = (new_addr >> SHIFT_PER_CHANGE) & 0x1f; + or_val = DEP1(1, or_pos, 0); + if((new_addr >> CHANGE_SHIFT) >= SLOW_MEM_CH_SIZE) { + printf("new_addr: %08x\n", new_addr); + exit(12); + } + slow_mem_changed[(new_addr & 0xffff) >> CHANGE_SHIFT] |= or_val; + } + + ptr[loc & 0xff] = val; + +} +#endif + +void +set_memory_io(word32 loc, int val, double *cyc_ptr) +{ + word32 tmp; + + tmp = loc & 0xfef000; + if(tmp == 0xc000 || tmp == 0xe0c000) { + io_write(loc, val, cyc_ptr); + return; + } + + /* Else it's an illegal addr */ + if(loc >= g_mem_size_total) { + if((loc & 0xfffe) == 0) { +#if 0 + printf("set_io assuming mem sizing, not halting\n"); +#endif + return; + } + } + + /* ignore writes to ROM */ + if((loc & 0xfc0000) == 0xfc0000) { + return; + } + + if((loc & 0xff0000) == 0xef0000) { + /* DOC RAM */ + doc_ram[loc & 0xffff] = val; + return; + } + + if(g_ignore_bad_acc) { + /* print no message, just get out. User doesn't want */ + /* to be bothered by buggy programs */ + return; + } + + if((loc & 0xffc000) == 0x00c000) { + printf("set_memory %06x = %02x, warning\n", loc, val); + return; + } + + halt_printf("set_memory %06x = %02x, stopping\n", loc, val); + + return; +} + + +#if 0 +void +check_breakpoints_c(word32 loc) +{ + int index; + int count; + int i; + + index = (loc & (MAX_BP_INDEX-1)); + count = breakpoints[index].count; + if(count) { + for(i = 0; i < count; i++) { + if(loc == breakpoints[index].addrs[i]) { + halt_printf("Write hit breakpoint %d!\n", i); + } + } + } +} +#endif + + +void +show_regs_act(Engine_reg *eptr) +{ + int tmp_acc, tmp_x, tmp_y, tmp_psw; + int kpc; + int direct_page, dbank; + int stack; + + kpc = eptr->kpc; + tmp_acc = eptr->acc; + direct_page = eptr->direct; + dbank = eptr->dbank; + stack = eptr->stack; + + tmp_x = eptr->xreg; + tmp_y = eptr->yreg; + + tmp_psw = eptr->psr; + + printf(" PC=%02x.%04x A=%04x X=%04x Y=%04x P=%03x", + kpc>>16, kpc & 0xffff ,tmp_acc,tmp_x,tmp_y,tmp_psw); + printf(" S=%04x D=%04x B=%02x,cyc:%.3f\n", stack, direct_page, + dbank, g_cur_dcycs); +} + +void +show_regs() +{ + show_regs_act(&engine); +} + +void +my_exit(int ret) +{ + end_screen(); + printf("exiting\n"); + exit(ret); +} + + +void +do_reset() +{ + int i; + + statereg = 0x08 + 0x04 + 0x01; /* rdrom, lcbank2, intcx */ + + wrdefram = 1; + for(i = 1; i < 7; i++) { + int_crom[i] = 0; + } + int_crom[7] = 0; + + engine.psr = (engine.psr | 0x134) & ~(0x08); + engine.stack = 0x100 + (engine.stack & 0xff); + engine.dbank = 0; + engine.direct = 0; + engine.xreg &= 0xff; + engine.yreg &= 0xff; + g_wait_pending = 0; + + + video_reset(); + adb_reset(); + iwm_reset(); + scc_reset(); + sound_reset(g_cur_dcycs); + setup_pageinfo(); + change_display_mode(g_cur_dcycs); + + engine.kpc = get_memory16_c(0x00fffc, 0); + + g_stepping = 0; + +} + +#define CHECK(start, var, value, var1, var2) \ + var2 = PTR2WORD(&(var)); \ + var1 = PTR2WORD((start)); \ + if((var2 - var1) != value) { \ + printf("CHECK: " #var " is 0x%x, but " #value " is 0x%x\n", \ + (var2 - var1), value); \ + exit(5); \ + } + +void +check_engine_asm_defines() +{ + Fplus fplus; + Fplus *fplusptr; + Pc_log pclog; + Pc_log *pcptr; + Engine_reg ereg; + Engine_reg *eptr; + word32 val1; + word32 val2; + + eptr = &ereg; + CHECK(eptr, eptr->fcycles, ENGINE_FCYCLES, val1, val2); + CHECK(eptr, eptr->fplus_ptr, ENGINE_FPLUS_PTR, val1, val2); + CHECK(eptr, eptr->acc, ENGINE_REG_ACC, val1, val2); + CHECK(eptr, eptr->xreg, ENGINE_REG_XREG, val1, val2); + CHECK(eptr, eptr->yreg, ENGINE_REG_YREG, val1, val2); + CHECK(eptr, eptr->stack, ENGINE_REG_STACK, val1, val2); + CHECK(eptr, eptr->dbank, ENGINE_REG_DBANK, val1, val2); + CHECK(eptr, eptr->direct, ENGINE_REG_DIRECT, val1, val2); + CHECK(eptr, eptr->psr, ENGINE_REG_PSR, val1, val2); + CHECK(eptr, eptr->kpc, ENGINE_REG_KPC, val1, val2); + + pcptr = &pclog; + CHECK(pcptr, pcptr->dbank_kpc, LOG_PC_DBANK_KPC, val1, val2); + CHECK(pcptr, pcptr->instr, LOG_PC_INSTR, val1, val2); + CHECK(pcptr, pcptr->psr_acc, LOG_PC_PSR_ACC, val1, val2); + CHECK(pcptr, pcptr->xreg_yreg, LOG_PC_XREG_YREG, val1, val2); + CHECK(pcptr, pcptr->stack_direct, LOG_PC_STACK_DIRECT, val1, val2); + if(LOG_PC_SIZE != sizeof(pclog)) { + printf("LOG_PC_SIZE: %d != sizeof=%d\n", LOG_PC_SIZE, + (int)sizeof(pclog)); + exit(2); + } + + fplusptr = &fplus; + CHECK(fplusptr, fplusptr->plus_1, FPLUS_PLUS_1, val1, val2); + CHECK(fplusptr, fplusptr->plus_2, FPLUS_PLUS_2, val1, val2); + CHECK(fplusptr, fplusptr->plus_3, FPLUS_PLUS_3, val1, val2); + CHECK(fplusptr, fplusptr->plus_x_minus_1, FPLUS_PLUS_X_M1, val1, val2); +} + +byte * +memalloc_align(int size, int skip_amt) +{ + byte *bptr; + word32 addr; + word32 offset; + + skip_amt = MAX(256, skip_amt); + bptr = malloc(size + skip_amt); + + addr = PTR2WORD(bptr) & 0xff; + + /* must align bptr to be 256-byte aligned */ + /* this code should work even if ptrs are > 32 bits */ + + offset = ((addr + skip_amt - 1) & (~0xff)) - addr; + + bptr += offset; + + /* Gilles Tschopp recommended zeroing memory, this is a good idea */ + memset(bptr, 0, size); + return bptr; +} + +void +memory_ptr_init() +{ + word32 mem_size; + + mem_size = MIN(0xdf0000, g_mem_size_base + g_mem_size_exp); + g_mem_size_total = mem_size; + g_memory_ptr = memalloc_align(mem_size, 3*1024); + + printf("RAM size is 0 - %06x (%.2fMB)\n", mem_size, + (double)mem_size/(1024.0*1024.0)); +} + +extern int g_screen_redraw_skip_amt; +extern int g_use_shmem; +extern int g_use_dhr140; +extern int g_use_bw_hires; + +char g_display_env[512]; +int g_force_depth = -1; +int g_screen_depth = 8; + + +int +kegsmain(int argc, char **argv) +{ + int skip_amt; + int diff; + int tmp1; + int i; + + /* parse args */ + for(i = 1; i < argc; i++) { + if(!strcmp("-badrd", argv[i])) { + printf("Halting on bad reads\n"); + g_halt_on_bad_read = 2; + } else if(!strcmp("-noignbadacc", argv[i])) { + printf("Not ignoring bad memory accesses\n"); + g_ignore_bad_acc = 0; + } else if(!strcmp("-noignhalt", argv[i])) { + printf("Not ignoring code red halts\n"); + g_ignore_halts = 0; + } else if(!strcmp("-test", argv[i])) { + printf("Allowing testing\n"); + g_testing_enabled = 1; + } else if(!strcmp("-hpdev", argv[i])) { + printf("Using /dev/audio\n"); + g_use_alib = 0; + } else if(!strcmp("-alib", argv[i])) { + printf("Using Aserver audio server\n"); + g_use_alib = 1; + } else if(!strcmp("-24", argv[i])) { + printf("Using 24-bit visual\n"); + g_force_depth = 24; + } else if(!strcmp("-16", argv[i])) { + printf("Using 16-bit visual\n"); + g_force_depth = 16; + } else if(!strcmp("-15", argv[i])) { + printf("Using 15-bit visual\n"); + g_force_depth = 15; + } else if(!strcmp("-mem", argv[i])) { + if((i+1) >= argc) { + printf("Missing argument\n"); + exit(1); + } + g_mem_size_exp = strtol(argv[i+1], 0, 0) & 0x00ff0000; + printf("Using %d as memory size\n", g_mem_size_exp); + i++; + } else if(!strcmp("-skip", argv[i])) { + if((i+1) >= argc) { + printf("Missing argument\n"); + exit(1); + } + skip_amt = strtol(argv[i+1], 0, 0); + printf("Using %d as skip_amt\n", skip_amt); + g_screen_redraw_skip_amt = skip_amt; + i++; + } else if(!strcmp("-audio", argv[i])) { + if((i+1) >= argc) { + printf("Missing argument\n"); + exit(1); + } + tmp1 = strtol(argv[i+1], 0, 0); + printf("Using %d as audio enable val\n", tmp1); + g_audio_enable = tmp1; + i++; + } else if(!strcmp("-arate", argv[i])) { + if((i+1) >= argc) { + printf("Missing argument\n"); + exit(1); + } + tmp1 = strtol(argv[i+1], 0, 0); + printf("Using %d as preferred audio rate\n", tmp1); + g_preferred_rate = tmp1; + i++; + } else if(!strcmp("-v", argv[i])) { + if((i+1) >= argc) { + printf("Missing argument\n"); + exit(1); + } + tmp1 = strtol(argv[i+1], 0, 0); + printf("Setting Verbose = 0x%03x\n", tmp1); + Verbose = tmp1; + i++; +#ifndef __NeXT__ + } else if(!strcmp("-display", argv[i])) { + if((i+1) >= argc) { + printf("Missing argument\n"); + exit(1); + } + printf("Using %s as display\n", argv[i+1]); + sprintf(g_display_env, "DISPLAY=%s", argv[i+1]); + putenv(&g_display_env[0]); + i++; +#endif + } else if(!strcmp("-noshm", argv[i])) { + printf("Not using X shared memory\n"); + g_use_shmem = 0; + } else if(!strcmp("-joystick", argv[i])) { + printf("Trying to use joystick\n"); + joystick_init(); + } else if(!strcmp("-dhr140", argv[i])) { + printf("Using simple dhires color map\n"); + g_use_dhr140 = 1; + } else if(!strcmp("-bw", argv[i])) { + printf("Forcing black-and-white hires modes\n"); + g_cur_a2_stat |= ALL_STAT_COLOR_C021; + g_use_bw_hires = 1; + } else { + printf("Bad option: %s\n", argv[i]); + exit(3); + } + } + + check_engine_asm_defines(); + fixed_memory_ptrs_init(); + + if(sizeof(word32) != 4) { + printf("sizeof(word32) = %d, must be 4!\n", + (int)sizeof(word32)); + exit(1); + } + + if(!g_engine_c_mode) { + diff = &defs_instr_end_8 - &defs_instr_start_8; + if(diff != 1) { + printf("defs_instr_end_8 - start is %d\n",diff); + exit(1); + } + + diff = &defs_instr_end_16 - &defs_instr_start_16; + if(diff != 1) { + printf("defs_instr_end_16 - start is %d\n", diff); + exit(1); + } + + diff = &op_routs_end - &op_routs_start; + if(diff != 1) { + printf("op_routs_end - start is %d\n", diff); + exit(1); + } + } + + iwm_init(); + config_init(); + + load_roms(); + memory_ptr_init(); + + init_reg(); + clear_halt(); + + initialize_events(); + + video_init(); + +#ifndef _WIN32 + //sleep(1); +#endif + sound_init(); + + scc_init(); + clk_setup_bram_version(); /* load_roms must be called first! */ + adb_init(); + + do_reset(); + g_stepping = 0; + do_go(); + + /* If we get here, we hit a breakpoint, call debug intfc */ + do_debug_intfc(); + + my_exit(0); + return 0; +} + +void +kegs_expand_path(char *out_ptr, const char *in_ptr, int maxlen) +{ + char name_buf[256]; + char *tmp_ptr; + int name_len; + int in_char; + int state; + + out_ptr[0] = 0; + + name_len = 0; + state = 0; + + /* See if in_ptr has ${} notation, replace with getenv or argv0 */ + while(maxlen > 0) { + in_char = *in_ptr++; + *out_ptr++ = in_char; + maxlen--; + if(state == 0) { + /* No $ seen yet, look for it */ + if(in_char == '$') { + state = 1; + } + } else if(state == 1) { + /* See if next char is '{' (dummy }) */ + if(in_char == '{') { /* add dummy } */ + state = 2; + name_len = 0; + out_ptr -= 2; + } else { + state = 0; + } + } else if(state == 2) { + /* fill name_buf ... dummy '{' */ + out_ptr--; + if(in_char == '}') { + name_buf[name_len] = 0; + + /* got token, now look it up */ + tmp_ptr = ""; + if(!strncmp("0", name_buf, 128)) { + /* Replace ${0} with g_argv0_path */ + tmp_ptr = &(g_argv0_path[0]); + } else { + tmp_ptr = getenv(name_buf); + if(tmp_ptr == 0) { + tmp_ptr = ""; + } + } + strncpy(out_ptr, tmp_ptr, maxlen); + out_ptr += strlen(tmp_ptr); + maxlen -= strlen(tmp_ptr); + state = 0; + } else { + name_buf[name_len++] = in_char; + } + } + if(in_char == 0) { + /* make sure its null terminated */ + *out_ptr++ = 0; + break; + } + } +} + +void +setup_kegs_file(char *outname, int maxlen, int ok_if_missing, + const char **name_ptr) +{ + char local_path[256]; + struct stat stat_buf; + const char **path_ptr; + const char **cur_name_ptr, **save_path_ptr; + int ret; + + outname[0] = 0; + + path_ptr = &g_kegs_default_paths[0]; + + save_path_ptr = path_ptr; + while(*path_ptr) { + kegs_expand_path(&(local_path[0]), *path_ptr, 250); + cur_name_ptr = name_ptr; + while(*cur_name_ptr) { + strcpy(outname, &(local_path[0])); + strncat(outname, *cur_name_ptr, 255-strlen(outname)); + if(!ok_if_missing) { + printf("Trying '%s'\n", outname); + } + ret = stat(outname, &stat_buf); + if(ret == 0) { + /* got it! */ + return; + } + cur_name_ptr++; + } + path_ptr++; + } + + if(ok_if_missing) { + outname[0] = 0; + return; + } + + /* couldn't find it, print out all the attempts */ + path_ptr = save_path_ptr; + printf("Could not find %s in any of these directories:\n", *name_ptr); + while(*path_ptr) { + printf(" %s\n", *path_ptr++); + } + system("pwd"); + + exit(2); +} + +Event g_event_list[MAX_EVENTS]; +Event g_event_free; +Event g_event_start; + +void +initialize_events() +{ + int i; + + for(i = 1; i < MAX_EVENTS; i++) { + g_event_list[i-1].next = &g_event_list[i]; + } + g_event_free.next = &g_event_list[0]; + g_event_list[MAX_EVENTS-1].next = 0; + + g_event_start.next = 0; + g_event_start.dcycs = 0.0; + + add_event_entry(DCYCS_IN_16MS, EV_60HZ); +} + +void +check_for_one_event_type(int type) +{ + Event *ptr; + int count; + int depth; + + count = 0; + depth = 0; + ptr = g_event_start.next; + while(ptr != 0) { + depth++; + if(ptr->type == type) { + count++; + if(count != 1) { + halt_printf("in check_for_1, type %d found at " + "depth: %d, count: %d, at %f\n", + type, depth, count, ptr->dcycs); + } + } + ptr = ptr->next; + } +} + + +void +add_event_entry(double dcycs, int type) +{ + Event *this_event; + Event *ptr, *prev_ptr; + int tmp_type; + int done; + + this_event = g_event_free.next; + if(this_event == 0) { + halt_printf("Out of queue entries!\n"); + show_all_events(); + return; + } + g_event_free.next = this_event->next; + + this_event->type = type; + + tmp_type = type & 0xff; + if((dcycs < 0.0) || (dcycs > (g_cur_dcycs + 50*1000*1000.0)) || + ((dcycs < g_cur_dcycs) && (tmp_type != EV_SCAN_INT))) { + halt_printf("add_event: dcycs: %f, type:%05x, cur_dcycs: %f!\n", + dcycs, type, g_cur_dcycs); + dcycs = g_cur_dcycs + 1000.0; + } + + ptr = g_event_start.next; + if(ptr && (dcycs < ptr->dcycs)) { + /* create event before next expected event */ + /* do this by setting HALT_EVENT */ + set_halt(HALT_EVENT); + } + + prev_ptr = &g_event_start; + ptr = g_event_start.next; + + done = 0; + while(!done) { + if(ptr == 0) { + this_event->next = ptr; + this_event->dcycs = dcycs; + prev_ptr->next = this_event; + return; + } else { + if(ptr->dcycs < dcycs) { + /* step across this guy */ + prev_ptr = ptr; + ptr = ptr->next; + } else { + /* go in front of this guy */ + this_event->dcycs = dcycs; + this_event->next = ptr; + prev_ptr->next = this_event; + return; + } + } + } +} + +extern int g_doc_saved_ctl; + +double +remove_event_entry(int type) +{ + Event *ptr, *prev_ptr; + Event *next_ptr; + + ptr = g_event_start.next; + prev_ptr = &g_event_start; + + while(ptr != 0) { + if((ptr->type & 0xffff) == type) { + /* got it, remove it */ + next_ptr = ptr->next; + prev_ptr->next = next_ptr; + + /* Add ptr to free list */ + ptr->next = g_event_free.next; + g_event_free.next = ptr; + + return ptr->dcycs; + } + prev_ptr = ptr; + ptr = ptr->next; + } + + halt_printf("remove event_entry: %08x, but not found!\n", type); + if((type & 0xff) == EV_DOC_INT) { + printf("DOC, g_doc_saved_ctl = %02x\n", g_doc_saved_ctl); + } +#ifdef HPUX + U_STACK_TRACE(); +#endif + show_all_events(); + + return 0.0; +} + +void +add_event_stop(double dcycs) +{ + add_event_entry(dcycs, EV_STOP); +} + +void +add_event_doc(double dcycs, int osc) +{ + if(dcycs < g_cur_dcycs) { + dcycs = g_cur_dcycs; +#if 0 + halt_printf("add_event_doc: dcycs: %f, cur_dcycs: %f\n", + dcycs, g_cur_dcycs); +#endif + } + + add_event_entry(dcycs, EV_DOC_INT + (osc << 8)); +} + +void +add_event_scc(double dcycs, int type) +{ + if(dcycs < g_cur_dcycs) { + dcycs = g_cur_dcycs; + } + + add_event_entry(dcycs, EV_SCC + (type << 8)); +} + +void +add_event_vbl() +{ + double dcycs; + + dcycs = g_last_vbl_dcycs + (DCYCS_IN_16MS * (192.0/262.0)); + add_event_entry(dcycs, EV_VBL_INT); +} + +void +add_event_vid_upd(int line) +{ + double dcycs; + + dcycs = g_last_vbl_dcycs + ((DCYCS_IN_16MS * line) / 262.0); + add_event_entry(dcycs, EV_VID_UPD + (line << 8)); +} + +double +remove_event_doc(int osc) +{ + return remove_event_entry(EV_DOC_INT + (osc << 8)); +} + +double +remove_event_scc(int type) +{ + return remove_event_entry(EV_SCC + (type << 8)); +} + +void +show_all_events() +{ + Event *ptr; + int count; + double dcycs; + + count = 0; + ptr = g_event_start.next; + while(ptr != 0) { + dcycs = ptr->dcycs; + printf("Event: %02x: type: %05x, dcycs: %f (%f)\n", + count, ptr->type, dcycs, dcycs - g_cur_dcycs); + ptr = ptr->next; + count++; + } + +} + +word32 g_vbl_count = 0; +int g_vbl_index_count = 0; +double dtime_array[60]; +double g_dadjcycs_array[60]; +double g_dtime_diff3_array[60]; +double g_dtime_this_vbl_array[60]; +double g_dtime_exp_array[60]; +double g_dtime_pmhz_array[60]; +double g_dtime_eff_pmhz_array[60]; +int g_limit_speed = 0; +double sim_time[60]; +double g_sim_sum = 0.0; + +double g_cur_sim_dtime = 0.0; +double g_projected_pmhz = 1.0; +double g_zip_pmhz = 8.0; +double g_sim_mhz = 100.0; +int g_line_ref_amt = 1; +int g_video_line_update_interval = 0; + +Fplus g_recip_projected_pmhz_slow; +Fplus g_recip_projected_pmhz_fast; +Fplus g_recip_projected_pmhz_zip; +Fplus g_recip_projected_pmhz_unl; + +Fplus *g_cur_fplus_ptr = 0; + +void +show_pmhz() +{ + printf("Pmhz: %f, plus_1: %f, fast: %d, limit: %d\n", + g_projected_pmhz, g_cur_fplus_ptr->plus_1, speed_fast, + g_limit_speed); + +} + +void +setup_zip_speeds() +{ + double frecip; + double fmhz; + int mult; + + mult = 16 - ((g_zipgs_reg_c05a >> 4) & 0xf); + // 16 = full speed, 1 = 1/16th speed + fmhz = (8.0 * mult) / 16.0; +#if 0 + if(mult == 16) { + /* increase full speed by 19% to make zipgs freq measuring */ + /* programs work correctly */ + fmhz = fmhz * 1.19; + } +#endif + frecip = 1.0 / fmhz; + g_zip_pmhz = fmhz; + g_recip_projected_pmhz_zip.plus_1 = frecip; + g_recip_projected_pmhz_zip.plus_2 = 2.0 * frecip; + g_recip_projected_pmhz_zip.plus_3 = 3.0 * frecip; + if(frecip >= 0.5) { + g_recip_projected_pmhz_zip.plus_x_minus_1 = 1.01; + } else { + g_recip_projected_pmhz_zip.plus_x_minus_1 = 1.01 - frecip; + } +} + +void +run_prog() +{ + Fplus *fplus_ptr; + Event *this_event; + Event *db1; + double dcycs; + double now_dtime; + double prev_dtime; + double prerun_fcycles; + double fspeed_mult; + double fcycles_stop; + word32 ret; + word32 zip_speed_0tof, zip_speed_0tof_new; + int zip_en, zip_follow_cps; + int type; + int motor_on; + int iwm_1; + int iwm_25; + int limit_speed; + int apple35_sel; + int fast, zip_speed, faster_than_28, unl_speed; + int this_type; + + fflush(stdout); + + g_cur_sim_dtime = 0.0; + + g_recip_projected_pmhz_slow.plus_1 = 1.0; + g_recip_projected_pmhz_slow.plus_2 = 2.0; + g_recip_projected_pmhz_slow.plus_3 = 3.0; + g_recip_projected_pmhz_slow.plus_x_minus_1 = 0.9; + + g_recip_projected_pmhz_fast.plus_1 = (1.0 / 2.5); + g_recip_projected_pmhz_fast.plus_2 = (2.0 / 2.5); + g_recip_projected_pmhz_fast.plus_3 = (3.0 / 2.5); + g_recip_projected_pmhz_fast.plus_x_minus_1 = (1.98 - (1.0/2.5)); + + zip_speed_0tof = g_zipgs_reg_c05a & 0xf0; + setup_zip_speeds(); + + if(g_cur_fplus_ptr == 0) { + g_recip_projected_pmhz_unl = g_recip_projected_pmhz_slow; + } + + while(1) { + fflush(stdout); + + if(g_irq_pending && !(engine.psr & 0x4)) { + irq_printf("taking an irq!\n"); + take_irq(0); + /* Interrupt! */ + } + + motor_on = g_iwm_motor_on; + limit_speed = g_limit_speed; + apple35_sel = g_apple35_sel; + zip_en = ((g_zipgs_reg_c05b & 0x10) == 0); + zip_follow_cps = ((g_zipgs_reg_c059 & 0x8) != 0); + zip_speed_0tof_new = g_zipgs_reg_c05a & 0xf0; + fast = speed_fast || (zip_en && !zip_follow_cps); + + if(zip_speed_0tof_new != zip_speed_0tof) { + zip_speed_0tof = zip_speed_0tof_new; + setup_zip_speeds(); + } + + iwm_1 = motor_on && !apple35_sel && + (g_slot_motor_detect & 0x4) && + (g_slow_525_emul_wr || !g_fast_disk_emul); + iwm_25 = (motor_on && apple35_sel) && !g_fast_disk_emul; + faster_than_28 = fast && (!iwm_1 && !iwm_25) && zip_en && + ((limit_speed == 0) || (limit_speed == 3)); + zip_speed = faster_than_28 && + ((zip_speed_0tof != 0) || (limit_speed == 3) || + (g_zipgs_unlock >= 4) ); + unl_speed = faster_than_28 && !zip_speed; + if(unl_speed) { + /* use unlimited speed */ + fspeed_mult = g_projected_pmhz; + fplus_ptr = &g_recip_projected_pmhz_unl; + } else if(zip_speed) { + fspeed_mult = g_zip_pmhz; + fplus_ptr = &g_recip_projected_pmhz_zip; + } else if(fast && !iwm_1 && !(limit_speed == 1)) { + fspeed_mult = 2.5; + fplus_ptr = &g_recip_projected_pmhz_fast; + } else { + /* else run slow */ + fspeed_mult = 1.0; + fplus_ptr = &g_recip_projected_pmhz_slow; + } + + g_cur_fplus_ptr = fplus_ptr; + engine.fplus_ptr = fplus_ptr; + + this_type = g_event_start.next->type; + + prerun_fcycles = g_cur_dcycs - g_last_vbl_dcycs; + engine.fcycles = prerun_fcycles; + fcycles_stop = (g_event_start.next->dcycs - g_last_vbl_dcycs) + + 0.001; + if(g_stepping) { + fcycles_stop = prerun_fcycles; + } + g_fcycles_stop = fcycles_stop; + +#if 0 + printf("Enter engine, fcycs: %f, stop: %f\n", + prerun_fcycles, fcycles_stop); + printf("g_cur_dcycs: %f, last_vbl_dcyc: %f\n", g_cur_dcycs, + g_last_vbl_dcycs); +#endif + + g_num_enter_engine++; + prev_dtime = get_dtime(); + + ret = enter_engine(&engine); + + now_dtime = get_dtime(); + + g_cur_sim_dtime += (now_dtime - prev_dtime); + + dcycs = g_last_vbl_dcycs + (double)(engine.fcycles); + + g_dadjcycs += (engine.fcycles - prerun_fcycles) * + fspeed_mult; + +#if 0 + printf("...back, engine.fcycles: %f, dcycs: %f\n", + (double)engine.fcycles, dcycs); +#endif + + g_cur_dcycs = dcycs; + + if(ret != 0) { + g_engine_action++; + handle_action(ret); + } + + if(halt_sim == HALT_EVENT) { + g_engine_halt_event++; + /* if we needed to stop to check for interrupts, */ + /* clear halt */ + halt_sim = 0; + } + +#if 0 + if(!g_testing && run_cycles < -2000000) { + halt_printf("run_cycles: %d, cycles: %d\n", run_cycles, + cycles); + printf("this_type: %05x\n", this_type); + printf("duff_cycles: %d\n", duff_cycles); + printf("start.next->rel_time: %d, type: %05x\n", + g_event_start.next->rel_time, + g_event_start.next->type); + } +#endif + + this_event = g_event_start.next; + while(dcycs >= this_event->dcycs) { + /* Pop this guy off of the queue */ + g_event_start.next = this_event->next; + + type = this_event->type; + this_event->next = g_event_free.next; + g_event_free.next = this_event; + switch(type & 0xff) { + case EV_60HZ: + update_60hz(dcycs, now_dtime); + break; + case EV_STOP: + printf("type: EV_STOP\n"); + printf("next: %p, dcycs: %f\n", + g_event_start.next, dcycs); + db1 = g_event_start.next; + halt_printf("next.dcycs: %f\n", db1->dcycs); + break; + case EV_SCAN_INT: + g_engine_scan_int++; + irq_printf("type: scan int\n"); + do_scan_int(dcycs, type >> 8); + break; + case EV_DOC_INT: + g_engine_doc_int++; + doc_handle_event(type >> 8, dcycs); + break; + case EV_VBL_INT: + do_vbl_int(); + break; + case EV_SCC: + do_scc_event(type >> 8, dcycs); + break; + case EV_VID_UPD: + video_update_event_line(type >> 8); + break; + default: + printf("Unknown event: %d!\n", type); + exit(3); + } + + this_event = g_event_start.next; + + } + + if(g_event_start.next == 0) { + halt_printf("ERROR...run_prog, event_start.n=0!\n"); + } + +#if 0 + if(!g_testing && g_event_start.next->rel_time > 2000000) { + printf("Z:start.next->rel_time: %d, duff_cycles: %d\n", + g_event_start.next->rel_time, duff_cycles); + halt_printf("Zrun_cycles:%d, cycles:%d\n", run_cycles, + cycles); + + show_all_events(); + } +#endif + + if(halt_sim != 0 && halt_sim != HALT_EVENT) { + break; + } + if(g_stepping) { + break; + } + if(g_config_control_panel) { + config_control_panel(); + } + } + + if(!g_testing) { + printf("leaving run_prog, halt_sim:%d\n", halt_sim); + } + + x_auto_repeat_on(0); +} + +void +add_irq() +{ + g_irq_pending++; + set_halt(HALT_EVENT); +} + +void +remove_irq() +{ + g_irq_pending--; + if(g_irq_pending < 0) { + halt_printf("remove_irq: g_irq_pending: %d\n", g_irq_pending); + } +} + +void +take_irq(int is_it_brk) +{ + word32 new_kpc; + word32 va; + + irq_printf("Taking irq, at: %02x/%04x, psw: %02x, dcycs: %f\n", + engine.kpc>>16, engine.kpc & 0xffff, engine.psr, + g_cur_dcycs); + + g_num_irq++; + if(g_wait_pending) { + /* step over WAI instruction */ + engine.kpc++; + g_wait_pending = 0; + } + + if(g_irq_pending < 0) { + halt_printf("g_irq_pending: %d!\n", g_irq_pending); + } + + if(engine.psr & 0x100) { + /* Emulation */ + set_memory_c(engine.stack, (engine.kpc >> 8) & 0xff, 0); + engine.stack = ((engine.stack -1) & 0xff) + 0x100; + + set_memory_c(engine.stack, engine.kpc & 0xff, 0); + engine.stack = ((engine.stack -1) & 0xff) + 0x100; + + set_memory_c(engine.stack, + (engine.psr & 0xef)|(is_it_brk<<4),0); + /* Clear B bit in psr on stack */ + engine.stack = ((engine.stack -1) & 0xff) + 0x100; + + va = 0xfffffe; + if(shadow_reg & 0x40) { + /* I/O shadowing off...use ram locs */ + va = 0x00fffe; + } + + } else { + /* native */ + set_memory_c(engine.stack, (engine.kpc >> 16) & 0xff, 0); + engine.stack = ((engine.stack -1) & 0xffff); + + set_memory_c(engine.stack, (engine.kpc >> 8) & 0xff, 0); + engine.stack = ((engine.stack -1) & 0xffff); + + set_memory_c(engine.stack, engine.kpc & 0xff, 0); + engine.stack = ((engine.stack -1) & 0xffff); + + set_memory_c(engine.stack, engine.psr & 0xff, 0); + engine.stack = ((engine.stack -1) & 0xffff); + + if(is_it_brk) { + /* break */ + va = 0xffffe6; + if(shadow_reg & 0x40) { + va = 0xffe6; + } + } else { + /* irq */ + va = 0xffffee; + if(shadow_reg & 0x40) { + va = 0xffee; + } + } + + } + + new_kpc = get_memory_c(va, 0); + new_kpc = new_kpc + (get_memory_c(va+1, 0) << 8); + + engine.psr = ((engine.psr & 0x1f3) | 0x4); + + engine.kpc = new_kpc; + HALT_ON(HALT_ON_IRQ, "Halting on IRQ\n"); + +} + +double g_dtime_last_vbl = 0.0; +double g_dtime_expected = (1.0/60.0); + +int g_scan_int_events = 0; + + + +void +show_dtime_array() +{ + double dfirst_time; + double first_total_cycs; + int i; + int pos; + + dfirst_time = 0.0; + first_total_cycs = 0.0; + + + for(i = 0; i < 60; i++) { + pos = (g_vbl_index_count + i) % 60; + printf("%2d:%2d dt:%.5f adjc:%9.1f this_vbl:%.6f " + "exp:%.5f p:%2.2f ep:%2.2f\n", + i, pos, + dtime_array[pos] - dfirst_time, + g_dadjcycs_array[pos] - first_total_cycs, + g_dtime_this_vbl_array[pos], + g_dtime_exp_array[pos] - dfirst_time, + g_dtime_pmhz_array[pos], + g_dtime_eff_pmhz_array[pos]); + dfirst_time = dtime_array[pos]; + first_total_cycs = g_dadjcycs_array[pos]; + } +} + +extern word32 g_cycs_in_40col; +extern word32 g_cycs_in_xredraw; +extern word32 g_cycs_in_check_input; +extern word32 g_cycs_in_refresh_line; +extern word32 g_cycs_in_refresh_ximage; +extern word32 g_cycs_in_io_read; +extern word32 g_cycs_in_sound1; +extern word32 g_cycs_in_sound2; +extern word32 g_cycs_in_sound3; +extern word32 g_cycs_in_sound4; +extern word32 g_cycs_in_start_sound; +extern word32 g_cycs_in_est_sound; +extern word32 g_refresh_bytes_xfer; + +extern int g_num_snd_plays; +extern int g_num_doc_events; +extern int g_num_start_sounds; +extern int g_num_scan_osc; +extern int g_num_recalc_snd_parms; +extern float g_fvoices; + +extern int g_doc_vol; +extern int g_a2vid_palette; + +extern int g_status_refresh_needed; + + +void +update_60hz(double dcycs, double dtime_now) +{ + register word32 end_time; + char status_buf[1024]; + char sim_mhz_buf[128]; + char total_mhz_buf[128]; + char *sim_mhz_ptr, *total_mhz_ptr; + char *code_str1, *code_str2, *sp_str; + double eff_pmhz; + double planned_dcycs; + double predicted_pmhz; + double recip_predicted_pmhz; + double dtime_this_vbl_sim; + double dtime_diff_1sec; + double dratio; + double dtime_till_expected; + double dtime_diff; + double dtime_this_vbl; + double dadjcycs_this_vbl; + double dadj_cycles_1sec; + double dtmp1, dtmp2, dtmp3, dtmp4, dtmp5; + double dnatcycs_1sec; + int tmp; + int doit_3_persec; + int cur_vbl_index; + int prev_vbl_index; + + g_vbl_count++; + + /* NOTE: this event is defined to occur before line 0 */ + /* It's actually happening at the start of the border for line (-1) */ + /* All other timings should be adjusted for this */ + + irq_printf("vbl_60hz: vbl: %d, dcycs: %f, last_vbl_dcycs: %f\n", + g_vbl_count, dcycs, g_last_vbl_dcycs); + + planned_dcycs = DCYCS_IN_16MS; + + g_last_vbl_dcycs = g_last_vbl_dcycs + planned_dcycs; + + add_event_entry(g_last_vbl_dcycs + planned_dcycs, EV_60HZ); + check_for_one_event_type(EV_60HZ); + + cur_vbl_index = g_vbl_index_count; + + /* figure out dtime spent running SIM, not all the overhead */ + dtime_this_vbl_sim = g_cur_sim_dtime; + g_cur_sim_dtime = 0.0; + g_sim_sum = g_sim_sum - sim_time[cur_vbl_index] + dtime_this_vbl_sim; + sim_time[cur_vbl_index] = dtime_this_vbl_sim; + + dadj_cycles_1sec = g_dadjcycs - g_dadjcycs_array[cur_vbl_index]; + + /* dtime_diff_1sec is dtime total spent over the last 60 ticks */ + dtime_diff_1sec = dtime_now - dtime_array[cur_vbl_index]; + + dtime_array[cur_vbl_index] = dtime_now; + g_dadjcycs_array[cur_vbl_index] = g_dadjcycs; + + prev_vbl_index = cur_vbl_index; + cur_vbl_index = prev_vbl_index + 1; + if(cur_vbl_index >= 60) { + cur_vbl_index = 0; + } + g_vbl_index_count = cur_vbl_index; + + GET_ITIMER(end_time); + g_dnatcycs_1sec += (double)(end_time - g_natcycs_lastvbl); + g_natcycs_lastvbl = end_time; + + if(prev_vbl_index == 0) { + if(g_sim_sum < (1.0/250.0)) { + sim_mhz_ptr = "???"; + g_sim_mhz = 250.0; + } else { + g_sim_mhz = (dadj_cycles_1sec / g_sim_sum) / + (1000.0*1000.0); + sprintf(sim_mhz_buf, "%6.2f", g_sim_mhz); + sim_mhz_ptr = sim_mhz_buf; + } + if(dtime_diff_1sec < (1.0/250.0)) { + total_mhz_ptr = "???"; + } else { + sprintf(total_mhz_buf, "%6.2f", + (dadj_cycles_1sec / dtime_diff_1sec) / + (1000000.0)); + total_mhz_ptr = total_mhz_buf; + } + + switch(g_limit_speed) { + case 1: sp_str = "1Mhz"; break; + case 2: sp_str = "2.8Mhz"; break; + case 3: sp_str = "8.0Mhz"; break; + default: sp_str = "Unlimited"; break; + } + + sprintf(status_buf, "dcycs:%9.1f sim MHz:%s " + "Eff MHz:%s, sec:%1.3f vol:%02x pal:%x, Limit:%s", + dcycs/(1000.0*1000.0), sim_mhz_ptr, total_mhz_ptr, + dtime_diff_1sec, g_doc_vol, g_a2vid_palette, + sp_str); + video_update_status_line(0, status_buf); + + if(g_video_line_update_interval == 0) { + if(g_sim_mhz > 12.0) { + /* just set video line_ref_amt to 1 */ + g_line_ref_amt = 1; + } else if(g_line_ref_amt == 1 && g_sim_mhz < 4.0) { + g_line_ref_amt = 8; + } + } else { + g_line_ref_amt = g_video_line_update_interval; + } + + if(g_dnatcycs_1sec < (1000.0*1000.0)) { + /* make it so large that all %'s become 0 */ + g_dnatcycs_1sec = 800.0*1000.0*1000.0*1000.0; + } + dnatcycs_1sec = g_dnatcycs_1sec / 100.0; /* eff mult by 100 */ + + dtmp2 = (double)(g_cycs_in_check_input) / dnatcycs_1sec; + dtmp3 = (double)(g_cycs_in_refresh_line) / dnatcycs_1sec; + dtmp4 = (double)(g_cycs_in_refresh_ximage) / dnatcycs_1sec; + sprintf(status_buf, "xfer:%08x, %5.1f ref_amt:%d " + "ch_in:%4.1f%% ref_l:%4.1f%% ref_x:%4.1f%%", + g_refresh_bytes_xfer, g_dnatcycs_1sec/(1000.0*1000.0), + g_line_ref_amt, dtmp2, dtmp3, dtmp4); + video_update_status_line(1, status_buf); + + sprintf(status_buf, "Ints:%3d I/O:%4dK BRK:%3d COP:%2d " + "Eng:%3d act:%3d hev:%3d esi:%3d edi:%3d", + g_num_irq, g_io_amt>>10, g_num_brk, g_num_cop, + g_num_enter_engine, g_engine_action, + g_engine_halt_event, g_engine_scan_int, + g_engine_doc_int); + video_update_status_line(2, status_buf); + + dtmp1 = (double)(g_cycs_in_sound1) / dnatcycs_1sec; + dtmp2 = (double)(g_cycs_in_sound2) / dnatcycs_1sec; + dtmp3 = (double)(g_cycs_in_sound3) / dnatcycs_1sec; + dtmp4 = (double)(g_cycs_in_start_sound) / dnatcycs_1sec; + dtmp5 = (double)(g_cycs_in_est_sound) / dnatcycs_1sec; + sprintf(status_buf, "snd1:%4.1f%%, 2:%4.1f%%, " + "3:%4.1f%%, st:%4.1f%% est:%4.1f%% %4.2f", + dtmp1, dtmp2, dtmp3, dtmp4, dtmp5, g_fvoices); + video_update_status_line(3, status_buf); + + code_str1 = ""; + code_str2 = ""; + if(g_code_yellow) { + code_str1 = "Code: Yellow"; + code_str2 = "Emulated system state suspect, save work"; + } + if(g_code_red) { + code_str1 = "Code: RED"; + code_str2 = "Emulated system state probably corrupt"; + } + sprintf(status_buf, "snd_plays:%4d, doc_ev:%4d, st_snd:%4d " + "snd_parms: %4d %s", + g_num_snd_plays, g_num_doc_events, g_num_start_sounds, + g_num_recalc_snd_parms, code_str1); + video_update_status_line(4, status_buf); + + draw_iwm_status(5, status_buf); + + sprintf(status_buf, "KEGS v%-6s " + "Press F4 for Config Menu %s", + g_kegs_version_str, code_str2); + video_update_status_line(6, status_buf); + + g_status_refresh_needed = 1; + + g_num_irq = 0; + g_num_brk = 0; + g_num_cop = 0; + g_num_enter_engine = 0; + g_io_amt = 0; + g_engine_action = 0; + g_engine_halt_event = 0; + g_engine_scan_int = 0; + g_engine_doc_int = 0; + + g_cycs_in_40col = 0; + g_cycs_in_xredraw = 0; + g_cycs_in_check_input = 0; + g_cycs_in_refresh_line = 0; + g_cycs_in_refresh_ximage = 0; + g_cycs_in_io_read = 0; + g_cycs_in_sound1 = 0; + g_cycs_in_sound2 = 0; + g_cycs_in_sound3 = 0; + g_cycs_in_sound4 = 0; + g_cycs_in_start_sound = 0; + g_cycs_in_est_sound = 0; + g_dnatcycs_1sec = 0.0; + g_refresh_bytes_xfer = 0; + + g_num_snd_plays = 0; + g_num_doc_events = 0; + g_num_start_sounds = 0; + g_num_scan_osc = 0; + g_num_recalc_snd_parms = 0; + + g_fvoices = (float)0.0; + } + + dtime_this_vbl = dtime_now - g_dtime_last_vbl; + if(dtime_this_vbl < 0.001) { + dtime_this_vbl = 0.001; + } + + g_dtime_last_vbl = dtime_now; + + dadjcycs_this_vbl = g_dadjcycs - g_last_vbl_dadjcycs; + g_last_vbl_dadjcycs = g_dadjcycs; + + g_dtime_expected += (1.0/60.0); + + eff_pmhz = ((dadjcycs_this_vbl) / (dtime_this_vbl)) / + DCYCS_1_MHZ; + + /* using eff_pmhz, predict how many cycles can be run by */ + /* g_dtime_expected */ + + dtime_till_expected = g_dtime_expected - dtime_now; + + dratio = 60.0 * dtime_till_expected; + + predicted_pmhz = eff_pmhz * dratio; + + if(! (predicted_pmhz < (1.4 * g_projected_pmhz))) { + predicted_pmhz = 1.4 * g_projected_pmhz; + } + + if(! (predicted_pmhz > (0.7 * g_projected_pmhz))) { + predicted_pmhz = 0.7 * g_projected_pmhz; + } + + if(!(predicted_pmhz >= 1.0)) { + irq_printf("predicted: %f, setting to 1.0\n", predicted_pmhz); + predicted_pmhz = 1.0; + } + + if(!(predicted_pmhz < 250.0)) { + irq_printf("predicted: %f, setting to 250.0\n", predicted_pmhz); + predicted_pmhz = 250.0; + } + + recip_predicted_pmhz = 1.0/predicted_pmhz; + g_projected_pmhz = predicted_pmhz; + + g_recip_projected_pmhz_unl.plus_1 = 1.0*recip_predicted_pmhz; + g_recip_projected_pmhz_unl.plus_2 = 2.0*recip_predicted_pmhz; + g_recip_projected_pmhz_unl.plus_3 = 3.0*recip_predicted_pmhz; + g_recip_projected_pmhz_unl.plus_x_minus_1 = 1.01 - recip_predicted_pmhz; + + if(dtime_till_expected < -0.125) { + /* If we were way off, get back on track */ + /* this happens because our sim took much longer than */ + /* expected, so we're going to skip some VBL */ + irq_printf("adj1: dtexp:%f, dt_new:%f\n", + g_dtime_expected, dtime_now); + + dtime_diff = -dtime_till_expected; + + irq_printf("dtime_till_exp: %f, dtime_diff: %f, dcycs: %f\n", + dtime_till_expected, dtime_diff, dcycs); + + g_dtime_expected += dtime_diff; + } + + if(dtime_till_expected > (3/60.0)) { + /* we're running fast, usleep */ + micro_sleep(dtime_till_expected - (1/60.0)); + } + + g_dtime_this_vbl_array[prev_vbl_index] = dtime_this_vbl; + g_dtime_exp_array[prev_vbl_index] = g_dtime_expected; + g_dtime_pmhz_array[prev_vbl_index] = predicted_pmhz; + g_dtime_eff_pmhz_array[prev_vbl_index] = eff_pmhz; + + + if(c041_en_vbl_ints) { + add_event_vbl(); + } + + g_25sec_cntr++; + if(g_25sec_cntr >= 16) { + g_25sec_cntr = 0; + if(c041_en_25sec_ints && !c046_25sec_irq_pend) { + g_c046_val |= 0x10; + c046_25sec_irq_pend = 1; + add_irq(); + irq_printf("Setting c046 .25 sec int to 1, " + "g_irq_pend: %d\n", g_irq_pending); + } + } + + g_1sec_cntr++; + if(g_1sec_cntr >= 60) { + g_1sec_cntr = 0; + tmp = g_c023_val; + tmp |= 0x40; /* set 1sec int */ + if((tmp & 0x04) && !c023_1sec_int_irq_pending) { + c023_1sec_int_irq_pending = 1; + tmp |= 0x80; + add_irq(); + irq_printf("Setting c023 to %02x irq_pend: %d\n", + tmp, g_irq_pending); + } + g_c023_val = tmp; + } + + if(!g_scan_int_events) { + check_scan_line_int(dcycs, 0); + } + + doit_3_persec = 0; + if(g_config_iwm_vbl_count > 0) { + g_config_iwm_vbl_count--; + } else { + g_config_iwm_vbl_count = 20; + doit_3_persec = 1; + } + + iwm_vbl_update(doit_3_persec); + config_vbl_update(doit_3_persec); + + video_update(); + sound_update(dcycs); + clock_update(); + scc_update(dcycs); + joystick_update_button(); +} + +void +do_vbl_int() +{ + if(c041_en_vbl_ints && !c046_vbl_irq_pending) { + g_c046_val |= 0x08; + c046_vbl_irq_pending = 1; + add_irq(); + irq_printf("Setting c046 vbl_int_status to 1, irq_pend: %d\n", + g_irq_pending); + } +} + + +void +do_scan_int(double dcycs, int line) +{ + int c023_val; + g_scan_int_events = 0; + + c023_val = g_c023_val; + if(c023_val & 0x20) { + halt_printf("c023 scan_int and another on line %03x\n", line); + } + + /* make sure scan int is still enabled for this line */ + if(g_slow_memory_ptr[0x19d00 + line] & 0x40) { + /* valid interrupt, do it */ + c023_val |= 0xa0; /* vgc_int and scan_int */ + if((c023_val & 0x02) && !c023_scan_int_irq_pending) { + add_irq(); + c023_scan_int_irq_pending = 1; + irq_printf("Setting c023 to %02x, irq_pend: %d\n", + c023_val, g_irq_pending); + } + g_c023_val = c023_val; + HALT_ON(HALT_ON_SCAN_INT, "In do_scan_int\n"); + } else { + /* scan int bit cleared on scan line control byte */ + /* look for next line, if any */ + check_scan_line_int(dcycs, line+1); + } +} + + +void +check_scan_line_int(double dcycs, int cur_video_line) +{ + int delay; + int start; + int line; + int i; + /* Called during VBL interrupt phase */ + + if(!(g_cur_a2_stat & ALL_STAT_SUPER_HIRES)) { + return; + } + + if(g_c023_val & 0x20) { + /* don't check for any more */ + return; + } + + start = cur_video_line; + if(start < 0) { + halt_printf("check_scan_line_int: cur_video_line: %d\n", + cur_video_line); + start = 0; + } + + for(line = start; line < 200; line++) { + i = line; + + if(i < 0 || i >= 200) { + halt_printf("check_new_scan_int:i:%d, line:%d, st:%d\n", + i, line, start); + i = 0; + } + if(g_slow_memory_ptr[0x19d00+i] & 0x40) { + irq_printf("Adding scan_int for line %d\n", i); + delay = (DCYCS_IN_16MS/262.0) * ((double)line); + add_event_entry(g_last_vbl_dcycs + delay, EV_SCAN_INT + + (line << 8)); + g_scan_int_events = 1; + check_for_one_event_type(EV_SCAN_INT); + break; + } + } +} + +void +check_for_new_scan_int(double dcycs) +{ + int cur_video_line; + + cur_video_line = get_lines_since_vbl(dcycs) >> 8; + + check_scan_line_int(dcycs, cur_video_line); +} + +void +init_reg() +{ + engine.acc = 0; + engine.xreg = 0; + engine.yreg = 0; + engine.stack = 0x1ff; + engine.direct = 0; + engine.psr = 0x134; + +} + + +void +handle_action(word32 ret) +{ + int type; + + type = EXTRU(ret,3,4); + switch(type) { + case RET_BREAK: + do_break(ret & 0xff); + break; + case RET_COP: + do_cop(ret & 0xff); + break; +#if 0 + case RET_MVN: + do_mvn(ret & 0xffff); + break; +#endif + case RET_C700: + do_c700(ret); + break; + case RET_C70A: + do_c70a(ret); + break; + case RET_C70D: + do_c70d(ret); + break; +#if 0 + case RET_ADD_DEC_8: + do_add_dec_8(ret); + break; + case RET_ADD_DEC_16: + do_add_dec_16(ret); + break; +#endif + case RET_IRQ: + irq_printf("Special fast IRQ response. irq_pending: %x\n", + g_irq_pending); + break; + case RET_WDM: + do_wdm(ret & 0xff); + break; + default: + halt_printf("Unknown special action: %08x!\n", ret); + } + +} + +#if 0 +void +do_add_dec_8(word32 ret) +{ + halt_printf("do_add_dec_8 called, ret: %08x\n", ret); +} + +void +do_add_dec_16(word32 ret) +{ + halt_printf("do_add_dec_16 called, ret: %08x\n", ret); +} +#endif + +void +do_break(word32 ret) +{ + if(!g_testing) { + printf("I think I got a break, second byte: %02x!\n", ret); + printf("kpc: %06x\n", engine.kpc); + } + + halt_printf("do_break, kpc: %06x\n", engine.kpc); + enter_debug = 1; +} + +void +do_cop(word32 ret) +{ + halt_printf("COP instr %02x!\n", ret); + fflush(stdout); +} + +#if 0 +void +do_mvn(word32 banks) +{ + int src_bank, dest_bank; + int dest, src; + int num; + int i; + int val; + + halt_printf("In MVN...just quitting\n"); + return; + printf("MVN instr with %04x, cycles: %08x\n", banks, engine.cycles); + src_bank = banks >> 8; + dest_bank = banks & 0xff; + printf("psr: %03x\n", engine.psr); + if((engine.psr & 0x30) != 0) { + halt_printf("MVN in non-native mode unimplemented!\n"); + } + + dest = dest_bank << 16 | engine.yreg; + src = src_bank << 16 | engine.xreg; + num = engine.acc; + printf("Moving %08x+1 bytes from %08x to %08x\n", num, src, dest); + + for(i = 0; i <= num; i++) { + val = get_memory_c(src, 0); + set_memory_c(dest, val, 0); + src = (src_bank << 16) | ((src + 1) & 0xffff); + dest = (dest_bank << 16) | ((dest + 1) & 0xffff); + } + engine.dbank = dest_bank; + engine.acc = 0xffff; + engine.yreg = dest & 0xffff; + engine.xreg = src & 0xffff; + engine.kpc = (engine.kpc + 3); + printf("move done. db: %02x, acc: %04x, y: %04x, x: %04x, num: %08x\n", + engine.dbank, engine.acc, engine.yreg, engine.xreg, num); +} +#endif + +void +do_wdm(word32 arg) +{ + switch(arg) { + case 0x8d: /* Bouncin Ferno does WDM 8d */ + break; + default: + halt_printf("do_wdm: %02x!\n", arg); + } +} + +void +do_wai() +{ + halt_printf("do_wai!\n"); +} + +void +do_stp() +{ + halt_printf("Hit do_stp at addr: %06x\n", engine.kpc); +} + +void +size_fail(int val, word32 v1, word32 v2) +{ + halt_printf("Size failure, val: %08x, %08x %08x\n", val, v1, v2); +} + diff --git a/src/size_tab.h b/src/size_tab.h new file mode 100644 index 0000000..73c87d8 --- /dev/null +++ b/src/size_tab.h @@ -0,0 +1,274 @@ +/************************************************************************/ +/* KEGS: Apple //gs Emulator */ +/* Copyright 2002 by Kent Dickey */ +/* */ +/* This code is covered by the GNU GPL */ +/* */ +/* The KEGS web page is kegs.sourceforge.net */ +/* You may contact the author at: kadickey@alumni.princeton.edu */ +/************************************************************************/ + +#ifdef INCLUDE_RCSID_S + .stringz "@(#)$KmKId: size_tab.h,v 1.13 2002-11-19 03:10:38-05 kadickey Exp $" +#else + + .word inst00_SYM+1 /* brk */ + .word inst01_SYM+1 /* ORA (Dloc,X) */ + .word inst02_SYM+1 /* COP */ + .word inst03_SYM+1 /* ORA Disp8,S */ + .word inst04_SYM+1 /* TSB Dloc */ + .word inst05_SYM+1 /* ORA Dloc */ + .word inst06_SYM+1 /* ASL Dloc */ + .word inst07_SYM+1 /* ORA [Dloc] */ + .word inst08_SYM+0 /* PHP */ + .word inst09_SYM+4 /* ORA #imm */ + .word inst0a_SYM+0 /* ASL a */ + .word inst0b_SYM+0 /* PHD */ + .word inst0c_SYM+2 /* TSB abs */ + .word inst0d_SYM+2 /* ORA abs */ + .word inst0e_SYM+2 /* ASL abs */ + .word inst0f_SYM+3 /* ORA long */ + .word inst10_SYM+1 /* BPL disp8 */ + .word inst11_SYM+1 /* ORA (),y */ + .word inst12_SYM+1 /* ORA () */ + .word inst13_SYM+1 /* ORA (disp8,s),y */ + .word inst14_SYM+1 /* TRB Dloc */ + .word inst15_SYM+1 /* ORA Dloc,x */ + .word inst16_SYM+1 /* ASL Dloc,x */ + .word inst17_SYM+1 /* ORA [],y */ + .word inst18_SYM+0 /* clc */ + .word inst19_SYM+2 /* ORA abs,y */ + .word inst1a_SYM+0 /* INC a */ + .word inst1b_SYM+0 /* TCS */ + .word inst1c_SYM+2 /* TRB Abs */ + .word inst1d_SYM+2 /* ORA Abs,X */ + .word inst1e_SYM+2 /* ASL abs,x */ + .word inst1f_SYM+3 /* ORA Long,x */ + .word inst20_SYM+2 /* JSR abs */ + .word inst21_SYM+1 /* AND (Dloc,X) */ + .word inst22_SYM+3 /* JSL Abslong */ + .word inst23_SYM+1 /* AND Disp8,S */ + .word inst24_SYM+1 /* BIT Dloc */ + .word inst25_SYM+1 /* AND Dloc */ + .word inst26_SYM+1 /* ROL Dloc */ + .word inst27_SYM+1 /* AND [Dloc] */ + .word inst28_SYM+0 /* PLP */ + .word inst29_SYM+4 /* AND #imm */ + .word inst2a_SYM+0 /* ROL a */ + .word inst2b_SYM+0 /* PLD */ + .word inst2c_SYM+2 /* BIT abs */ + .word inst2d_SYM+2 /* AND abs */ + .word inst2e_SYM+2 /* ROL abs */ + .word inst2f_SYM+3 /* AND long */ + .word inst30_SYM+1 /* BMI disp8 */ + .word inst31_SYM+1 /* AND (),y */ + .word inst32_SYM+1 /* AND () */ + .word inst33_SYM+1 /* AND (disp8,s),y */ + .word inst34_SYM+1 /* BIT Dloc,X */ + .word inst35_SYM+1 /* AND Dloc,x */ + .word inst36_SYM+1 /* ROL Dloc,x */ + .word inst37_SYM+1 /* AND [],y */ + .word inst38_SYM+0 /* SEC */ + .word inst39_SYM+2 /* AND abs,y */ + .word inst3a_SYM+0 /* DEC a */ + .word inst3b_SYM+0 /* TSC */ + .word inst3c_SYM+2 /* BIT Abs,X */ + .word inst3d_SYM+2 /* AND Abs,X */ + .word inst3e_SYM+2 /* ROL abs,x */ + .word inst3f_SYM+3 /* AND Long,x */ + .word inst40_SYM+0 /* RTI */ + .word inst41_SYM+1 /* EOR (Dloc,X) */ + .word inst42_SYM+1 /* WDM */ + .word inst43_SYM+1 /* EOR Disp8,S */ + .word inst44_SYM+2 /* MVP I,J */ + .word inst45_SYM+1 /* EOR Dloc */ + .word inst46_SYM+1 /* LSR Dloc */ + .word inst47_SYM+1 /* EOR [Dloc] */ + .word inst48_SYM+0 /* PHA */ + .word inst49_SYM+4 /* EOR #imm */ + .word inst4a_SYM+0 /* LSR a */ + .word inst4b_SYM+0 /* PHK */ + .word inst4c_SYM+2 /* JMP abs */ + .word inst4d_SYM+2 /* EOR abs */ + .word inst4e_SYM+2 /* LSR abs */ + .word inst4f_SYM+3 /* EOR long */ + .word inst50_SYM+1 /* BVC disp8 */ + .word inst51_SYM+1 /* EOR (),y */ + .word inst52_SYM+1 /* EOR () */ + .word inst53_SYM+1 /* EOR (disp8,s),y */ + .word inst54_SYM+2 /* MVN I,J */ + .word inst55_SYM+1 /* EOR Dloc,x */ + .word inst56_SYM+1 /* LSR Dloc,x */ + .word inst57_SYM+1 /* EOR [],y */ + .word inst58_SYM+0 /* CLI */ + .word inst59_SYM+2 /* EOR abs,y */ + .word inst5a_SYM+0 /* PHY */ + .word inst5b_SYM+0 /* TCD */ + .word inst5c_SYM+3 /* JMP Long */ + .word inst5d_SYM+2 /* EOR Abs,X */ + .word inst5e_SYM+2 /* LSR abs,x */ + .word inst5f_SYM+3 /* EOR Long,x */ + .word inst60_SYM+0 /* RTS */ + .word inst61_SYM+1 /* ADC (Dloc,X) */ + .word inst62_SYM+2 /* PER DISP16 */ + .word inst63_SYM+1 /* ADC Disp8,S */ + .word inst64_SYM+1 /* STZ Dloc */ + .word inst65_SYM+1 /* ADC Dloc */ + .word inst66_SYM+1 /* ROR Dloc */ + .word inst67_SYM+1 /* ADC [Dloc] */ + .word inst68_SYM+0 /* PLA */ + .word inst69_SYM+4 /* ADC #imm */ + .word inst6a_SYM+0 /* ROR a */ + .word inst6b_SYM+0 /* RTL */ + .word inst6c_SYM+2 /* JMP (abs) */ + .word inst6d_SYM+2 /* ADC abs */ + .word inst6e_SYM+2 /* ROR abs */ + .word inst6f_SYM+3 /* ADC long */ + .word inst70_SYM+1 /* BVS disp8 */ + .word inst71_SYM+1 /* ADC (),y */ + .word inst72_SYM+1 /* ADC () */ + .word inst73_SYM+1 /* ADC (disp8,s),y */ + .word inst74_SYM+1 /* STZ Dloc,X */ + .word inst75_SYM+1 /* ADC Dloc,x */ + .word inst76_SYM+1 /* ROR Dloc,x */ + .word inst77_SYM+1 /* ADC [],y */ + .word inst78_SYM+0 /* SEI */ + .word inst79_SYM+2 /* ADC abs,y */ + .word inst7a_SYM+0 /* PLY */ + .word inst7b_SYM+0 /* TDC */ + .word inst7c_SYM+2 /* JMP (abs,x) */ + .word inst7d_SYM+2 /* ADC Abs,X */ + .word inst7e_SYM+2 /* ROR abs,x */ + .word inst7f_SYM+3 /* ADC Long,x */ + .word inst80_SYM+1 /* BRA Disp8 */ + .word inst81_SYM+1 /* STA (Dloc,X) */ + .word inst82_SYM+2 /* BRL DISP16 */ + .word inst83_SYM+1 /* STA Disp8,S */ + .word inst84_SYM+1 /* STY Dloc */ + .word inst85_SYM+1 /* STA Dloc */ + .word inst86_SYM+1 /* STX Dloc */ + .word inst87_SYM+1 /* STA [Dloc] */ + .word inst88_SYM+0 /* DEY */ + .word inst89_SYM+4 /* BIT #imm */ + .word inst8a_SYM+0 /* TXA */ + .word inst8b_SYM+0 /* PHB */ + .word inst8c_SYM+2 /* STY abs */ + .word inst8d_SYM+2 /* STA abs */ + .word inst8e_SYM+2 /* STX abs */ + .word inst8f_SYM+3 /* STA long */ + .word inst90_SYM+1 /* BCC disp8 */ + .word inst91_SYM+1 /* STA (),y */ + .word inst92_SYM+1 /* STA () */ + .word inst93_SYM+1 /* STA (disp8,s),y */ + .word inst94_SYM+1 /* STY Dloc,X */ + .word inst95_SYM+1 /* STA Dloc,x */ + .word inst96_SYM+1 /* STX Dloc,y */ + .word inst97_SYM+1 /* STA [],y */ + .word inst98_SYM+0 /* TYA */ + .word inst99_SYM+2 /* STA abs,y */ + .word inst9a_SYM+0 /* TXS */ + .word inst9b_SYM+0 /* TXY */ + .word inst9c_SYM+2 /* STX abs */ + .word inst9d_SYM+2 /* STA Abs,X */ + .word inst9e_SYM+2 /* STZ abs,x */ + .word inst9f_SYM+3 /* STA Long,x */ + .word insta0_SYM+5 /* LDY #imm */ + .word insta1_SYM+1 /* LDA (Dloc,X) */ + .word insta2_SYM+5 /* LDX #imm */ + .word insta3_SYM+1 /* LDA Disp8,S */ + .word insta4_SYM+1 /* LDY Dloc */ + .word insta5_SYM+1 /* LDA Dloc */ + .word insta6_SYM+1 /* LDX Dloc */ + .word insta7_SYM+1 /* LDA [Dloc] */ + .word insta8_SYM+0 /* TAY */ + .word insta9_SYM+4 /* LDA #imm */ + .word instaa_SYM+0 /* TAX */ + .word instab_SYM+0 /* PLB */ + .word instac_SYM+2 /* LDY abs */ + .word instad_SYM+2 /* LDA abs */ + .word instae_SYM+2 /* LDX abs */ + .word instaf_SYM+3 /* LDA long */ + .word instb0_SYM+1 /* BCS disp8 */ + .word instb1_SYM+1 /* LDA (),y */ + .word instb2_SYM+1 /* LDA () */ + .word instb3_SYM+1 /* LDA (disp8,s),y */ + .word instb4_SYM+1 /* LDY Dloc,X */ + .word instb5_SYM+1 /* LDA Dloc,x */ + .word instb6_SYM+1 /* LDX Dloc,y */ + .word instb7_SYM+1 /* LDA [],y */ + .word instb8_SYM+0 /* CLV */ + .word instb9_SYM+2 /* LDA abs,y */ + .word instba_SYM+0 /* TSX */ + .word instbb_SYM+0 /* TYX */ + .word instbc_SYM+2 /* LDY abs,x */ + .word instbd_SYM+2 /* LDA Abs,X */ + .word instbe_SYM+2 /* LDX abs,y */ + .word instbf_SYM+3 /* LDA Long,x */ + .word instc0_SYM+5 /* CPY #Imm */ + .word instc1_SYM+1 /* CMP (Dloc,X) */ + .word instc2_SYM+1 /* REP #8bit */ + .word instc3_SYM+1 /* CMP Disp8,S */ + .word instc4_SYM+1 /* CPY Dloc */ + .word instc5_SYM+1 /* CMP Dloc */ + .word instc6_SYM+1 /* DEC Dloc */ + .word instc7_SYM+1 /* CMP [Dloc] */ + .word instc8_SYM+0 /* INY */ + .word instc9_SYM+4 /* CMP #imm */ + .word instca_SYM+0 /* DEX */ + .word instcb_SYM+0 /* WAI */ + .word instcc_SYM+2 /* CPY abs */ + .word instcd_SYM+2 /* CMP abs */ + .word instce_SYM+2 /* DEC abs */ + .word instcf_SYM+3 /* CMP long */ + .word instd0_SYM+1 /* BNE disp8 */ + .word instd1_SYM+1 /* CMP (),y */ + .word instd2_SYM+1 /* CMP () */ + .word instd3_SYM+1 /* CMP (disp8,s),y */ + .word instd4_SYM+1 /* PEI Dloc */ + .word instd5_SYM+1 /* CMP Dloc,x */ + .word instd6_SYM+1 /* DEC Dloc,x */ + .word instd7_SYM+1 /* CMP [],y */ + .word instd8_SYM+0 /* CLD */ + .word instd9_SYM+2 /* CMP abs,y */ + .word instda_SYM+0 /* PHX */ + .word instdb_SYM+0 /* STP */ + .word instdc_SYM+2 /* JML (Abs) */ + .word instdd_SYM+2 /* CMP Abs,X */ + .word instde_SYM+2 /* DEC abs,x */ + .word instdf_SYM+3 /* CMP Long,x */ + .word inste0_SYM+5 /* CPX #Imm */ + .word inste1_SYM+1 /* SBC (Dloc,X) */ + .word inste2_SYM+1 /* SEP #8bit */ + .word inste3_SYM+1 /* SBC Disp8,S */ + .word inste4_SYM+1 /* CPX Dloc */ + .word inste5_SYM+1 /* SBC Dloc */ + .word inste6_SYM+1 /* INC Dloc */ + .word inste7_SYM+1 /* SBC [Dloc] */ + .word inste8_SYM+0 /* INX */ + .word inste9_SYM+4 /* SBC #imm */ + .word instea_SYM+0 /* NOP */ + .word insteb_SYM+0 /* XBA */ + .word instec_SYM+2 /* CPX abs */ + .word insted_SYM+2 /* SBC abs */ + .word instee_SYM+2 /* INC abs */ + .word instef_SYM+3 /* SBC long */ + .word instf0_SYM+1 /* BEQ disp8 */ + .word instf1_SYM+1 /* SBC (),y */ + .word instf2_SYM+1 /* SBC () */ + .word instf3_SYM+1 /* SBC (disp8,s),y */ + .word instf4_SYM+2 /* PEA Imm */ + .word instf5_SYM+1 /* SBC Dloc,x */ + .word instf6_SYM+1 /* INC Dloc,x */ + .word instf7_SYM+1 /* SBC [],y */ + .word instf8_SYM+0 /* SED */ + .word instf9_SYM+2 /* SBC abs,y */ + .word instfa_SYM+0 /* PLX */ + .word instfb_SYM+0 /* XCE */ + .word instfc_SYM+2 /* JSR (Abs,x) */ + .word instfd_SYM+2 /* SBC Abs,X */ + .word instfe_SYM+2 /* INC abs,x */ + .word instff_SYM+3 /* SBC Long,x */ + + .block 4*16 + +#endif diff --git a/src/smartport.c b/src/smartport.c new file mode 100644 index 0000000..24ca31e --- /dev/null +++ b/src/smartport.c @@ -0,0 +1,783 @@ +/************************************************************************/ +/* KEGS: Apple //gs Emulator */ +/* Copyright 2002 by Kent Dickey */ +/* */ +/* This code is covered by the GNU GPL */ +/* */ +/* The KEGS web page is kegs.sourceforge.net */ +/* You may contact the author at: kadickey@alumni.princeton.edu */ +/************************************************************************/ + +const char rcsid_smartport_c[] = "@(#)$KmKId: smartport.c,v 1.29 2003-11-17 15:44:44-05 kentd Exp $"; + +#include "defc.h" + +extern int Verbose; +extern int Halt_on; +extern int g_rom_version; +extern int g_io_amt; +extern int g_highest_smartport_unit; + +int g_cycs_in_io_read = 0; + +extern Engine_reg engine; + +extern Iwm iwm; + +#define LEN_SMPT_LOG 16 +STRUCT(Smpt_log) { + word32 start_addr; + int cmd; + int rts_addr; + int cmd_list; + int extras; + int unit; + int buf; + int blk; +}; + +Smpt_log g_smpt_log[LEN_SMPT_LOG]; +int g_smpt_log_pos = 0; + +void +smartport_error(void) +{ + int pos; + int i; + + pos = g_smpt_log_pos; + printf("Smartport log pos: %d\n", pos); + for(i = 0; i < LEN_SMPT_LOG; i++) { + pos--; + if(pos < 0) { + pos = LEN_SMPT_LOG - 1; + } + printf("%d:%d: t:%04x, cmd:%02x, rts:%04x, " + "cmd_l:%04x, x:%d, unit:%d, buf:%04x, blk:%04x\n", + i, pos, + g_smpt_log[pos].start_addr, + g_smpt_log[pos].cmd, + g_smpt_log[pos].rts_addr, + g_smpt_log[pos].cmd_list, + g_smpt_log[pos].extras, + g_smpt_log[pos].unit, + g_smpt_log[pos].buf, + g_smpt_log[pos].blk); + } +} +void +smartport_log(word32 start_addr, int cmd, int rts_addr, int cmd_list) +{ + int pos; + + pos = g_smpt_log_pos; + if(start_addr != 0) { + g_smpt_log[pos].start_addr = start_addr; + g_smpt_log[pos].cmd = cmd; + g_smpt_log[pos].rts_addr = rts_addr; + g_smpt_log[pos].cmd_list = cmd_list; + g_smpt_log[pos].extras = 0; + g_smpt_log[pos].unit = 0; + g_smpt_log[pos].buf = 0; + g_smpt_log[pos].blk = 0; + } else { + pos--; + if(pos < 0) { + pos = LEN_SMPT_LOG - 1; + } + g_smpt_log[pos].extras = 1; + g_smpt_log[pos].unit = cmd; + g_smpt_log[pos].buf = rts_addr; + g_smpt_log[pos].blk = cmd_list; + } + pos++; + if(pos >= LEN_SMPT_LOG) { + pos = 0; + } + g_smpt_log_pos = pos; +} + +void +do_c70d(word32 arg0) +{ + int cmd; + int cmd_list_lo, cmd_list_mid, cmd_list_hi; + int rts_lo, rts_hi; + word32 rts_addr; + word32 cmd_list; + int unit; + int param_cnt; + int status_ptr_lo, status_ptr_mid, status_ptr_hi; + int buf_ptr_lo, buf_ptr_hi; + int buf_ptr; + int block_lo, block_mid, block_hi; + int block; + word32 status_ptr; + int status_code; + int ctl_ptr_lo, ctl_ptr_hi; + int ctl_ptr; + int ctl_code; + int mask; + int stat_val; + int size; + int ret; + int ext; + int i; + + set_memory_c(0x7f8, 0xc7, 0); + + if((engine.psr & 0x100) == 0) { + disk_printf("c70d called in native mode!\n"); + if((engine.psr & 0x30) != 0x30) { + halt_printf("c70d called native, psr: %03x!\n", + engine.psr); + } + } + + engine.stack = ((engine.stack + 1) & 0xff) + 0x100; + rts_lo = get_memory_c(engine.stack, 0); + engine.stack = ((engine.stack + 1) & 0xff) + 0x100; + rts_hi = get_memory_c(engine.stack, 0); + rts_addr = (rts_lo + (256*rts_hi) + 1) & 0xffff; + disk_printf("rts_addr: %04x\n", rts_addr); + + cmd = get_memory_c(rts_addr, 0); + cmd_list_lo = get_memory_c((rts_addr + 1) & 0xffff, 0); + cmd_list_mid = get_memory_c((rts_addr + 2) & 0xffff, 0); + cmd_list_hi = 0; + mask = 0xffff; + if(cmd & 0x40) { + /* extended */ + mask = 0xffffff; + cmd_list_hi = get_memory_c((rts_addr + 3) & 0xffff, 0); + } + + cmd_list = cmd_list_lo + (256*cmd_list_mid) + (65536*cmd_list_hi); + + disk_printf("cmd: %02x, cmd_list: %06x\n", cmd, cmd_list); + param_cnt = get_memory_c(cmd_list, 0); + + ext = 0; + if(cmd & 0x40) { + ext = 2; + } + + smartport_log(0xc70d, cmd, rts_addr, cmd_list); + + switch(cmd & 0x3f) { + case 0x00: /* Status == 0x00 and 0x40 */ + if(param_cnt != 3) { + disk_printf("param_cnt %d is != 3!\n", param_cnt); + exit(8); + } + unit = get_memory_c((cmd_list+1) & mask, 0); + status_ptr_lo = get_memory_c((cmd_list+2) & mask, 0); + status_ptr_mid = get_memory_c((cmd_list+3) & mask, 0); + status_ptr_hi = 0; + if(cmd & 0x40) { + status_ptr_hi = get_memory_c((cmd_list+4) & mask, 0); + } + + status_ptr = status_ptr_lo + (256*status_ptr_mid) + + (65536*status_ptr_hi); + if(cmd & 0x40) { + status_code = get_memory_c((cmd_list+6) & mask, 0); + } else { + status_code = get_memory_c((cmd_list+4) & mask, 0); + } + + smartport_log(0, unit, status_ptr, status_code); + + disk_printf("unit: %02x, status_ptr: %06x, code: %02x\n", + unit, status_ptr, status_code); + if(unit == 0 && status_code == 0) { + /* Smartport driver status */ + /* see technotes/smpt/tn-smpt-002 */ + set_memory_c(status_ptr, g_highest_smartport_unit+1, 0); + set_memory_c(status_ptr+1, 0xff, 0); /* interrupt stat*/ + set_memory_c(status_ptr+2, 0x02, 0); /* vendor id */ + set_memory_c(status_ptr+3, 0x00, 0); /* vendor id */ + set_memory_c(status_ptr+4, 0x00, 0); /* version lo */ + set_memory_c(status_ptr+5, 0x10, 0); /* version hi */ + set_memory_c(status_ptr+6, 0x00, 0); + set_memory_c(status_ptr+7, 0x00, 0); + + engine.xreg = 8; + engine.yreg = 0; + engine.acc &= 0xff00; + engine.psr &= ~1; + engine.kpc = (rts_addr + 3 + ext) & mask; + return; + } else if(unit > 0 && status_code == 0) { + /* status for unit x */ + if(unit > MAX_C7_DISKS || iwm.smartport[unit-1].fd < 0){ + stat_val = 0x80; + size = 0; + } else { + stat_val = 0xf8; + size = iwm.smartport[unit-1].image_size; + size = (size+511) / 512; + } + set_memory_c(status_ptr, stat_val, 0); + set_memory_c(status_ptr +1, size & 0xff, 0); + set_memory_c(status_ptr +2, (size >> 8) & 0xff, 0); + set_memory_c(status_ptr +3, (size >> 16) & 0xff, 0); + engine.xreg = 4; + if(cmd & 0x40) { + set_memory_c(status_ptr + 4, + (size >> 16) & 0xff, 0); + engine.xreg = 5; + } + engine.yreg = 0; + engine.acc &= 0xff00; + engine.psr &= ~1; + engine.kpc = (rts_addr + 3 + ext) & mask; + + disk_printf("just finished unit %d, stat 0\n", unit); + return; + } else if(status_code == 3) { + if(unit > MAX_C7_DISKS || iwm.smartport[unit-1].fd < 0){ + stat_val = 0x80; + size = 0; + } else { + stat_val = 0xf8; + size = iwm.smartport[unit-1].image_size; + size = (size+511) / 512; + } + if(cmd & 0x40) { + disk_printf("extended for stat_code 3!\n"); + } + /* DIB for unit 1 */ + set_memory_c(status_ptr, stat_val, 0); + set_memory_c(status_ptr +1, size & 0xff, 0); + set_memory_c(status_ptr +2, (size >> 8) & 0xff, 0); + set_memory_c(status_ptr +3, (size >> 16) & 0xff, 0); + if(cmd & 0x40) { + set_memory_c(status_ptr + 4, + (size >> 24) & 0xff, 0); + status_ptr++; + } + set_memory_c(status_ptr +4, 4, 0); + for(i = 5; i < 21; i++) { + set_memory_c(status_ptr +i, 0x20, 0); + } + set_memory_c(status_ptr +5, 'K', 0); + set_memory_c(status_ptr +6, 'E', 0); + set_memory_c(status_ptr +7, 'G', 0); + set_memory_c(status_ptr +8, 'S', 0); + + /* hard disk supporting extended calls */ + set_memory_c(status_ptr + 21, 0x02, 0); + set_memory_c(status_ptr + 22, 0xa0, 0); + + set_memory_c(status_ptr + 23, 0x00, 0); + set_memory_c(status_ptr + 24, 0x00, 0); + + if(cmd & 0x40) { + engine.xreg = 26; + } else { + engine.xreg = 25; + } + engine.yreg = 0; + engine.acc &= 0xff00; + engine.psr &= ~1; + engine.kpc = (rts_addr + 3 + ext) & 0xffff; + + disk_printf("Just finished unit %d, stat 3\n", unit); + if(unit == 0 || unit > MAX_C7_DISKS) { + engine.acc |= 0x28; + engine.psr |= 1; + } + return; + } + printf("cmd: 00, unknown unit/status code!\n"); + break; + case 0x01: /* Read Block == 0x01 and 0x41 */ + if(param_cnt != 3) { + halt_printf("param_cnt %d is != 3!\n", param_cnt); + return; + } + unit = get_memory_c((cmd_list+1) & mask, 0); + buf_ptr_lo = get_memory_c((cmd_list+2) & mask, 0); + buf_ptr_hi = get_memory_c((cmd_list+3) & mask, 0); + + buf_ptr = buf_ptr_lo + (256*buf_ptr_hi); + if(cmd & 0x40) { + buf_ptr_lo = get_memory_c((cmd_list+4) & mask, 0); + buf_ptr_hi = get_memory_c((cmd_list+5) & mask, 0); + buf_ptr += ((buf_ptr_hi*256) + buf_ptr_lo)*65536; + cmd_list += 2; + } + block_lo = get_memory_c((cmd_list+4) & mask, 0); + block_mid = get_memory_c((cmd_list+5) & mask, 0); + block_hi = get_memory_c((cmd_list+6) & mask, 0); + block = ((block_hi*256) + block_mid)*256 + block_lo; + disk_printf("smartport read unit %d of block %04x into %04x\n", + unit, block, buf_ptr); + if(unit < 1 || unit > MAX_C7_DISKS) { + halt_printf("Unknown unit #: %d\n", unit); + } + + smartport_log(0, unit - 1, buf_ptr, block); + + ret = do_read_c7(unit - 1, buf_ptr, block); + engine.xreg = 0; + engine.yreg = 2; + engine.acc = (engine.acc & 0xff00) | (ret & 0xff); + engine.psr &= ~1; + if(ret != 0) { + engine.psr |= 1; + } + engine.kpc = (rts_addr + 3 + ext) & 0xffff; + + return; + break; + case 0x02: /* Write Block == 0x02 and 0x42 */ + if(param_cnt != 3) { + halt_printf("param_cnt %d is != 3!\n", param_cnt); + return; + } + unit = get_memory_c((cmd_list+1) & mask, 0); + buf_ptr_lo = get_memory_c((cmd_list+2) & mask, 0); + buf_ptr_hi = get_memory_c((cmd_list+3) & mask, 0); + + buf_ptr = buf_ptr_lo + (256*buf_ptr_hi); + if(cmd & 0x40) { + buf_ptr_lo = get_memory_c((cmd_list+4) & mask, 0); + buf_ptr_hi = get_memory_c((cmd_list+5) & mask, 0); + buf_ptr += ((buf_ptr_hi*256) + buf_ptr_lo)*65536; + cmd_list += 2; + } + block_lo = get_memory_c((cmd_list+4) & mask, 0); + block_mid = get_memory_c((cmd_list+5) & mask, 0); + block_hi = get_memory_c((cmd_list+6) & mask, 0); + block = ((block_hi*256) + block_mid)*256 + block_lo; + disk_printf("smartport write unit %d of block %04x from %04x\n", + unit, block, buf_ptr); + if(unit < 1 || unit > MAX_C7_DISKS) { + halt_printf("Unknown unit #: %d\n", unit); + } + + smartport_log(0, unit - 1, buf_ptr, block); + + ret = do_write_c7(unit - 1, buf_ptr, block); + engine.xreg = 0; + engine.yreg = 2; + engine.acc = (engine.acc & 0xff00) | (ret & 0xff); + engine.psr &= ~1; + if(ret != 0) { + engine.psr |= 1; + } + engine.kpc = (rts_addr + 3 + ext) & 0xffff; + + HALT_ON(HALT_ON_C70D_WRITES, "c70d Write done\n"); + return; + break; + case 0x03: /* Format == 0x03 and 0x43 */ + if(param_cnt != 1) { + halt_printf("param_cnt %d is != 1!\n", param_cnt); + return; + } + unit = get_memory_c((cmd_list+1) & mask, 0); + + if(unit < 1 || unit > MAX_C7_DISKS) { + halt_printf("Unknown unit #: %d\n", unit); + } + + smartport_log(0, unit - 1, 0, 0); + + ret = do_format_c7(unit - 1); + engine.xreg = 0; + engine.yreg = 2; + engine.acc = (engine.acc & 0xff00) | (ret & 0xff); + engine.psr &= ~1; + if(ret != 0) { + engine.psr |= 1; + } + engine.kpc = (rts_addr + 3 + ext) & 0xffff; + + HALT_ON(HALT_ON_C70D_WRITES, "c70d Format done\n"); + return; + break; + case 0x04: /* Control == 0x04 and 0x44 */ + if(cmd == 0x44) { + halt_printf("smartport code 0x44 not supported\n"); + } + if(param_cnt != 3) { + halt_printf("param_cnt %d is != 3!\n", param_cnt); + return; + } + unit = get_memory_c((cmd_list+1) & mask, 0); + ctl_ptr_lo = get_memory_c((cmd_list+2) & mask, 0); + ctl_ptr_hi = get_memory_c((cmd_list+3) & mask, 0); + + ctl_ptr = (ctl_ptr_hi << 8) + ctl_ptr_lo; + if(cmd & 0x40) { + ctl_ptr_lo = get_memory_c((cmd_list+4) & mask, 0); + ctl_ptr_hi = get_memory_c((cmd_list+5) & mask, 0); + ctl_ptr += ((ctl_ptr_hi << 8) + ctl_ptr_lo) << 16; + cmd_list += 2; + } + + ctl_code = get_memory_c((cmd_list +4) & mask, 0); + + switch(ctl_code) { + case 0x00: + printf("Performing a reset on unit %d\n", unit); + break; + default: + halt_printf("control code: %02x unknown!\n", ctl_code); + } + + engine.xreg = 0; + engine.yreg = 2; + engine.acc &= 0xff00; + engine.psr &= ~1; + engine.kpc = (rts_addr + 3 + ext) & 0xffff; + return; + break; + default: /* Unknown command! */ + /* set acc = 1, and set carry, and set kpc */ + engine.xreg = (rts_addr) & 0xff; + engine.yreg = (rts_addr >> 8) & 0xff; + engine.acc = (engine.acc & 0xff00) + 0x01; + engine.psr |= 0x01; /* set carry */ + engine.kpc = (rts_addr + 3 + ext) & 0xffff; + if(cmd != 0x4a && cmd != 0x48) { + /* Finder does 0x4a call before formatting disk */ + /* Many things do 0x48 call to see online drives */ + halt_printf("Just did smtport cmd:%02x rts_addr:%04x, " + "cmdlst:%06x\n", cmd, rts_addr, cmd_list); + } + return; + } + + halt_printf("Unknown smtport cmd:%02x, cmd_list:%06x, rts_addr:%06x\n", + cmd, cmd_list, rts_addr); +} + +void +do_c70a(word32 arg0) +{ + int cmd, unit; + int buf_lo, buf_hi; + int blk_lo, blk_hi; + int blk, buf; + int prodos_unit; + int size; + int ret; + + set_memory_c(0x7f8, 0xc7, 0); + + cmd = get_memory_c((engine.direct + 0x42) & 0xffff, 0); + prodos_unit = get_memory_c((engine.direct + 0x43) & 0xffff, 0); + buf_lo = get_memory_c((engine.direct + 0x44) & 0xffff, 0); + buf_hi = get_memory_c((engine.direct + 0x45) & 0xffff, 0); + blk_lo = get_memory_c((engine.direct + 0x46) & 0xffff, 0); + blk_hi = get_memory_c((engine.direct + 0x47) & 0xffff, 0); + + blk = (blk_hi << 8) + blk_lo; + buf = (buf_hi << 8) + buf_lo; + disk_printf("cmd: %02x, pro_unit: %02x, buf: %04x, blk: %04x\n", + cmd, prodos_unit, buf, blk); + + if((prodos_unit & 0x7f) == 0x70) { + unit = 0 + (prodos_unit >> 7); + } else if((prodos_unit & 0x7f) == 0x40) { + unit = 2 + (prodos_unit >> 7); + } else { + halt_printf("Unknown prodos_unit: %d\n", prodos_unit); + return; + } + + smartport_log(0xc70a, cmd, blk, buf); + + engine.psr &= ~1; /* clear carry */ + if(g_rom_version >= 3) { + engine.kpc = 0xc764; + } else { + engine.kpc = 0xc765; + } + + ret = 0x27; /* I/O error */ + if(cmd == 0x00) { + size = iwm.smartport[unit].image_size; + size = (size+511) / 512; + + smartport_log(0, unit, size, 0); + + ret = 0; + engine.xreg = size & 0xff; + engine.yreg = size >> 8; + } else if(cmd == 0x01) { + smartport_log(0, unit, buf, blk); + ret = do_read_c7(unit, buf, blk); + } else if(cmd == 0x02) { + smartport_log(0, unit, buf, blk); + ret = do_write_c7(unit, buf, blk); + } else if(cmd == 0x03) { /* format */ + smartport_log(0, unit, buf, blk); + ret = do_format_c7(unit); + } + + engine.acc = (engine.acc & 0xff00) | (ret & 0xff); + if(ret != 0) { + engine.psr |= 1; + } + return; +} + +int +do_read_c7(int unit_num, word32 buf, int blk) +{ + byte local_buf[0x200]; + register word32 start_time; + register word32 end_time; + word32 val; + int len; + int fd; + int image_start; + int image_size; + int ret; + int i; + + if(unit_num < 0 || unit_num > MAX_C7_DISKS) { + halt_printf("do_read_c7: unit_num: %d\n", unit_num); + smartport_error(); + return 0x28; + } + + fd = iwm.smartport[unit_num].fd; + image_start = iwm.smartport[unit_num].image_start; + image_size = iwm.smartport[unit_num].image_size; + if(fd < 0) { + printf("c7_fd == %d!\n", fd); +#if 0 + if(blk != 2 && blk != 0) { + /* don't print error if only reading directory */ + smartport_error(); + halt_printf("Read unit:%02x blk:%04x\n", unit_num, blk); + } +#endif + return 0x2f; + } + + ret = lseek(fd, image_start + blk*0x200, SEEK_SET); + if(ret != image_start + blk*0x200) { + halt_printf("lseek returned %08x, errno: %d\n", ret, errno); + smartport_error(); + return 0x27; + } + + if(ret >= image_start + image_size) { + halt_printf("Tried to read from pos %08x on disk, (blk:%04x)\n", + ret, blk); + smartport_error(); + return 0x27; + } + + len = read(fd, &local_buf[0], 0x200); + if(len != 0x200) { + printf("read returned %08x, errno:%d, blk:%04x, unit: %02x\n", + len, errno, blk, unit_num); + halt_printf("name: %s\n", iwm.smartport[unit_num].name_ptr); + smartport_error(); + return 0x27; + } + + g_io_amt += 0x200; + + if(buf >= 0xfc0000) { + disk_printf("reading into ROM, just returning\n"); + return 0; + } + + GET_ITIMER(start_time); + + for(i = 0; i < 0x200; i += 2) { + val = (local_buf[i+1] << 8) + local_buf[i]; + set_memory16_c(buf + i, val, 0); + } + + GET_ITIMER(end_time); + + g_cycs_in_io_read += (end_time - start_time); + + return 0; + +} + +int +do_write_c7(int unit_num, word32 buf, int blk) +{ + word32 local_buf[0x200/4]; + Disk *dsk; + word32 *ptr; + word32 val1, val2; + word32 val; + int len; + int ret; + int fd; + int image_start; + int image_size; + int i; + + if(unit_num < 0 || unit_num > MAX_C7_DISKS) { + halt_printf("do_write_c7: unit_num: %d\n", unit_num); + smartport_error(); + return 0x28; + } + + dsk = &(iwm.smartport[unit_num]); + fd = dsk->fd; + image_start = dsk->image_start; + image_size = dsk->image_size; + if(fd < 0) { + halt_printf("c7_fd == %d!\n", fd); + smartport_error(); + return 0x28; + } + + ptr = &(local_buf[0]); + for(i = 0; i < 0x200; i += 4) { + val1 = get_memory16_c(buf + i, 0); + val2 = get_memory16_c(buf + i + 2, 0); + /* reorder the little-endian bytes to be big-endian */ +#ifdef KEGS_LITTLE_ENDIAN + val = (val2 << 16) + val1; +#else + val = (val1 << 24) + ((val1 << 8) & 0xff0000) + + ((val2 << 8) & 0xff00) + (val2 >> 8); +#endif + *ptr++ = val; + } + + ret = lseek(fd, image_start + blk*0x200, SEEK_SET); + if(ret != image_start + blk*0x200) { + halt_printf("lseek returned %08x, errno: %d\n", ret, errno); + smartport_error(); + return 0x27; + } + + if(ret >= image_start + image_size) { + halt_printf("Tried to write to %08x\n", ret); + smartport_error(); + return 0x27; + } + + if(dsk->write_prot) { + printf("Write, but %s is write protected!\n", dsk->name_ptr); + return 0x2b; + } + + if(dsk->write_through_to_unix == 0) { + halt_printf("Write to %s, but not wr_thru!\n", dsk->name_ptr); + return 0x00; + } + + len = write(fd, (byte *)&local_buf[0], 0x200); + if(len != 0x200) { + halt_printf("write ret %08x bytes, errno: %d\n", len, errno); + smartport_error(); + return 0x27; + } + + g_io_amt += 0x200; + + return 0; + +} + +int +do_format_c7(int unit_num) +{ + byte local_buf[0x1000]; + Disk *dsk; + int len; + int ret; + int sum; + int total; + int max; + int image_start; + int image_size; + int fd; + int i; + + if(unit_num < 0 || unit_num > MAX_C7_DISKS) { + halt_printf("do_format_c7: unit_num: %d\n", unit_num); + smartport_error(); + return 0x28; + } + + dsk = &(iwm.smartport[unit_num]); + fd = dsk->fd; + image_start = dsk->image_start; + image_size = dsk->image_size; + if(fd < 0) { + halt_printf("c7_fd == %d!\n", fd); + smartport_error(); + return 0x28; + } + + for(i = 0; i < 0x1000; i++) { + local_buf[i] = 0; + } + + ret = lseek(fd, image_start, SEEK_SET); + if(ret != image_start) { + halt_printf("lseek returned %08x, errno: %d\n", ret, errno); + smartport_error(); + return 0x27; + } + + if(dsk->write_prot) { + printf("Format, but %s is write protected!\n", dsk->name_ptr); + return 0x2b; + } + + if(dsk->write_through_to_unix == 0) { + printf("Format of %s ignored\n", dsk->name_ptr); + return 0x00; + } + + sum = 0; + total = image_size; + + while(sum < total) { + max = MIN(0x1000, total-sum); + len = write(fd, &local_buf[0], max); + if(len != max) { + halt_printf("write ret %08x, errno:%d\n", len, errno); + smartport_error(); + return 0x27; + } + sum += len; + } + + return 0; +} + + +void +do_c700(word32 ret) +{ + disk_printf("do_c700 called, ret: %08x\n", ret); + + ret = do_read_c7(0, 0x800, 0); + + set_memory_c(0x7f8, 7, 0); + set_memory_c(0x42, 0x01, 0); + set_memory_c(0x43, 0x70, 0); + set_memory_c(0x44, 0x0, 0); + set_memory_c(0x45, 0x8, 0); + set_memory_c(0x46, 0x0, 0); + set_memory_c(0x47, 0x0, 0); + engine.xreg = 0x70; + engine.kpc = 0x801; + + if(ret != 0) { + printf("Failure reading boot disk in s7d1!\n"); + engine.kpc = 0xe000; + } +} + diff --git a/src/sound.c b/src/sound.c new file mode 100644 index 0000000..2ba8d06 --- /dev/null +++ b/src/sound.c @@ -0,0 +1,2031 @@ +/************************************************************************/ +/* KEGS: Apple //gs Emulator */ +/* Copyright 2002 by Kent Dickey */ +/* */ +/* This code is covered by the GNU GPL */ +/* */ +/* The KEGS web page is kegs.sourceforge.net */ +/* You may contact the author at: kadickey@alumni.princeton.edu */ +/************************************************************************/ + +const char rcsid_sound_c[] = "@(#)$KmKId: sound.c,v 1.103 2003-10-17 15:07:47-04 kentd Exp $"; + +#include "defc.h" + +#define INCLUDE_RCSID_C +#include "sound.h" +#undef INCLUDE_RCSID_C + +#if 0 +# define DO_DOC_LOG +#endif + +extern int Verbose; +extern int g_use_shmem; +extern word32 g_vbl_count; +extern int g_preferred_rate; + + +extern double g_last_vbl_dcycs; + +void U_STACK_TRACE(); + +byte doc_ram[0x10000 + 16]; + +word32 doc_sound_ctl = 0; +word32 doc_saved_val = 0; +word32 doc_ptr = 0; +int g_doc_num_osc_en = 1; +double g_dcycs_per_doc_update = 1.0; +double g_dupd_per_dcyc = 1.0; +double g_drecip_osc_en_plus_2 = 1.0 / (double)(1 + 2); + +int g_doc_saved_ctl = 0; +int g_queued_samps = 0; +int g_queued_nonsamps = 0; +int g_num_osc_interrupting = 0; + +#if defined(HPUX) || defined(__linux__) || defined(_WIN32) || defined(MAC) +int g_audio_enable = -1; +#else +# if defined(OSS) +/* default to off for now */ +int g_audio_enable = 0; +# else +/* Default to sound off */ +int g_audio_enable = 0; +# endif +#endif + +Doc_reg g_doc_regs[32]; + +word32 doc_reg_e0 = 0xff; + +/* local function prototypes */ +void doc_write_ctl_reg(int osc, int val, double dsamps); + + +int g_audio_rate = 0; +double g_daudio_rate = 0.0; +double g_drecip_audio_rate = 0.0; +double g_dsamps_per_dcyc = 0.0; +double g_dcycs_per_samp = 0.0; +float g_fsamps_per_dcyc = 0.0; + +int g_doc_vol = 2; + +#define MAX_C030_TIMES 18000 + +double g_last_sound_play_dsamp = 0.0; + +float c030_fsamps[MAX_C030_TIMES + 1]; +int g_num_c030_fsamps = 0; + +#define DOC_SCAN_RATE (DCYCS_28_MHZ/32.0) + +int g_pipe_fd[2] = { -1, -1 }; +int g_pipe2_fd[2] = { -1, -1 }; +word32 *g_sound_shm_addr = 0; +int g_sound_shm_pos = 0; + +#define LEN_DOC_LOG 128 + +STRUCT(Doc_log) { + char *msg; + int osc; + double dsamps; + double dtmp2; + int etc; + Doc_reg doc_reg; +}; + +Doc_log g_doc_log[LEN_DOC_LOG]; +int g_doc_log_pos = 0; + + +#ifdef DO_DOC_LOG +# define DOC_LOG(a,b,c,d) doc_log_rout(a,b,c,d) +#else +# define DOC_LOG(a,b,c,d) +#endif + +#define UPDATE_G_DCYCS_PER_DOC_UPDATE(osc_en) \ + g_dcycs_per_doc_update = (double)((osc_en + 2) * DCYCS_1_MHZ) / \ + DOC_SCAN_RATE; \ + g_dupd_per_dcyc = 1.0 / g_dcycs_per_doc_update; \ + g_drecip_osc_en_plus_2 = 1.0 / (double)(osc_en + 2); + +#define SND_PTR_SHIFT 14 +#define SND_PTR_SHIFT_DBL ((double)(1 << SND_PTR_SHIFT)) + +void +doc_log_rout(char *msg, int osc, double dsamps, int etc) +{ + int pos; + + pos = g_doc_log_pos; + g_doc_log[pos].msg = msg; + g_doc_log[pos].osc = osc; + g_doc_log[pos].dsamps = dsamps; + g_doc_log[pos].dtmp2 = g_last_sound_play_dsamp; + g_doc_log[pos].etc = etc; + if(osc >= 0 && osc < 32) { + g_doc_log[pos].doc_reg = g_doc_regs[osc]; + } + pos++; + if(pos >= LEN_DOC_LOG) { + pos = 0; + } + + doc_printf("log: %s, osc:%d dsamp:%f, etc:%d\n", msg, osc, dsamps, etc); + + g_doc_log_pos = pos; +} + +extern double g_cur_dcycs; + +void +show_doc_log(void) +{ + FILE *docfile; + Doc_reg *rptr; + double dsamp_start; + int osc, ctl, freq; + int pos; + int i; + + docfile = fopen("doc_log_out", "wt"); + if(docfile == 0) { + printf("fopen failed, errno: %d\n", errno); + return; + } + pos = g_doc_log_pos; + fprintf(docfile, "DOC log pos: %d\n", pos); + dsamp_start = g_doc_log[pos].dsamps; + for(i = 0; i < LEN_DOC_LOG; i++) { + rptr = &(g_doc_log[pos].doc_reg); + osc = g_doc_log[pos].osc; + ctl = rptr->ctl; + freq = rptr->freq; + if(osc < 0) { + ctl = 0; + freq = 0; + } + fprintf(docfile, "%03x:%03x: %-11s ds:%11.1f dt2:%10.1f " + "etc:%08x o:%02x c:%02x fq:%04x\n", + i, pos, g_doc_log[pos].msg, + g_doc_log[pos].dsamps - dsamp_start, + g_doc_log[pos].dtmp2, + g_doc_log[pos].etc, osc & 0xff, ctl, freq); + if(osc >= 0) { + fprintf(docfile, " ire:%d,%d,%d ptr4:%08x " + "inc4:%08x comp_ds:%.1f left:%04x, vol:%02x " + "wptr:%02x, wsz:%02x, 4st:%08x, 4end:%08x\n", + rptr->has_irq_pending, rptr->running, + rptr->event, 4*rptr->cur_acc, 4*rptr->cur_inc, + rptr->complete_dsamp - dsamp_start, + rptr->samps_left, rptr->vol, rptr->waveptr, + rptr->wavesize, 4*rptr->cur_start, + 4*rptr->cur_end); + } + pos++; + if(pos >= LEN_DOC_LOG) { + pos = 0; + } + } + + fprintf(docfile, "cur_dcycs: %f\n", g_cur_dcycs); + fprintf(docfile, "dsamps_now: %f\n", + (g_cur_dcycs * g_dsamps_per_dcyc) - dsamp_start); + fprintf(docfile, "g_doc_num_osc_en: %d\n", g_doc_num_osc_en); + fclose(docfile); +} + +void +sound_init() +{ + Doc_reg *rptr; + int i; + + for(i = 0; i < 32; i++) { + rptr = &(g_doc_regs[i]); + rptr->dsamp_ev = 0.0; + rptr->dsamp_ev2 = 0.0; + rptr->complete_dsamp = 0.0; + rptr->samps_left = 0; + rptr->cur_acc = 0; + rptr->cur_inc = 0; + rptr->cur_start = 0; + rptr->cur_end = 0; + rptr->cur_mask = 0; + rptr->size_bytes = 0; + rptr->event = 0; + rptr->running = 0; + rptr->has_irq_pending = 0; + rptr->freq = 0; + rptr->vol = 0; + rptr->waveptr = 0; + rptr->ctl = 1; + rptr->wavesize = 0; + rptr->last_samp_val = 0; + } + + sound_init_general(); +} + + +void +sound_init_general() +{ +#if !defined(_WIN32) && !defined(__CYGWIN__) && !defined(MAC) + int pid; + int shmid; + int tmp; + int i; +#endif + word32 *shmaddr; + int size; + int ret; + +#if !defined(_WIN32) && !defined(__CYGWIN__) && !defined(MAC) + if(!g_use_shmem) { + if(g_audio_enable < 0) { + printf("Defaulting audio off for slow X display\n"); + g_audio_enable = 0; + } + } +#endif + ret = 0; + + if(g_audio_enable == 0) { + set_audio_rate(g_preferred_rate); + return; + } + + size = SOUND_SHM_SAMP_SIZE * SAMPLE_CHAN_SIZE; + +#if !defined(_WIN32) && !defined(__CYGWIN__) && !defined(MAC) + shmid = shmget(IPC_PRIVATE, size, IPC_CREAT | 0777); + if(shmid < 0) { + printf("sound_init: shmget ret: %d, errno: %d\n", shmid, + errno); + exit(2); + } + + shmaddr = shmat(shmid, 0, 0); + tmp = (int)PTR2WORD(shmaddr); + if(tmp == -1) { + printf("sound_init: shmat ret: %p, errno: %d\n", shmaddr, + errno); + exit(3); + } + + ret = shmctl(shmid, IPC_RMID, 0); + if(ret < 0) { + printf("sound_init: shmctl ret: %d, errno: %d\n", ret, errno); + exit(4); + } +#else +/* windows and mac */ + shmaddr = malloc(size); + memset(shmaddr, 0, size); +#endif + + g_sound_shm_addr = shmaddr; + + fflush(stdout); + +#if !defined(MAC) && !defined(_WIN32) && !defined(__CYGWIN__) + /* prepare pipe so parent can signal child each other */ + /* pipe[0] = read side, pipe[1] = write end */ + ret = pipe(&g_pipe_fd[0]); + if(ret < 0) { + printf("sound_init: pipe ret: %d, errno: %d\n", ret, errno); + exit(5); + } + ret = pipe(&g_pipe2_fd[0]); + if(ret < 0) { + printf("sound_init: pipe ret: %d, errno: %d\n", ret, errno); + exit(5); + } + + + printf("pipes: pipe_fd = %d, %d pipe2_fd: %d,%d\n", + g_pipe_fd[0], g_pipe_fd[1], g_pipe2_fd[0], g_pipe2_fd[1]); + fflush(stdout); + + pid = fork(); + switch(pid) { + case 0: + /* child */ + /* close stdin and write-side of pipe */ + close(0); + /* Close other fds to make sure X window fd is closed */ + for(i = 3; i < 100; i++) { + if((i != g_pipe_fd[0]) && (i != g_pipe2_fd[1])) { + close(i); + } + } + close(g_pipe_fd[1]); /*make sure write pipe closed*/ + close(g_pipe2_fd[0]); /*make sure read pipe closed*/ + child_sound_loop(g_pipe_fd[0], g_pipe2_fd[1], g_sound_shm_addr); + printf("Child sound loop returned\n"); + exit(0); + case -1: + /* error */ + printf("sound_init: fork ret: -1, errno: %d\n", errno); + exit(6); + default: + /* parent */ + /* close read-side of pipe1, and the write side of pipe2 */ + close(g_pipe_fd[0]); + close(g_pipe2_fd[1]); + doc_printf("Child is pid: %d\n", pid); + } + + parent_sound_get_sample_rate(g_pipe2_fd[0]); +#else +# ifdef MAC + macsnd_init(shmaddr); +# else +/* windows */ + win32snd_init(shmaddr); +# endif +#endif /* _WIN32 */ + +} + +void +parent_sound_get_sample_rate(int read_fd) +{ + word32 tmp; + int ret; + + ret = read(read_fd, &tmp, 4); + if(ret != 4) { + printf("parent dying, could not get sample rate from child\n"); + printf("ret: %d, fd: %d errno:%d\n", ret, read_fd, errno); + exit(1); + } + close(read_fd); + + set_audio_rate(tmp); +} + +void +set_audio_rate(int rate) +{ + g_audio_rate = rate; + g_daudio_rate = (rate)*1.0; + g_drecip_audio_rate = 1.0/(rate); + g_dsamps_per_dcyc = ((rate*1.0) / DCYCS_1_MHZ); + g_dcycs_per_samp = (DCYCS_1_MHZ / (rate*1.0)); + g_fsamps_per_dcyc = (float)((rate*1.0) / DCYCS_1_MHZ); +} + +void +sound_reset(double dcycs) +{ + double dsamps; + int i; + + dsamps = dcycs * g_dsamps_per_dcyc; + for(i = 0; i < 32; i++) { + doc_write_ctl_reg(i, g_doc_regs[i].ctl | 1, dsamps); + doc_reg_e0 = 0xff; + if(g_doc_regs[i].has_irq_pending) { + halt_printf("reset: has_irq[%02x] = %d\n", i, + g_doc_regs[i].has_irq_pending); + } + } + if(g_num_osc_interrupting) { + halt_printf("reset: num_osc_int:%d\n", g_num_osc_interrupting); + } + + g_doc_num_osc_en = 1; + UPDATE_G_DCYCS_PER_DOC_UPDATE(1); +} + +void +sound_shutdown() +{ +#ifdef _WIN32 + win32snd_shutdown(); +#else + if((g_audio_enable != 0) && g_pipe_fd[1] != 0) { + close(g_pipe_fd[1]); + } +#endif +} + + + +void +sound_update(double dcycs) +{ + double dsamps; + /* Called every VBL time to update sound status */ + + /* "play" sounds for this vbl */ + + dsamps = dcycs * g_dsamps_per_dcyc; + DOC_LOG("do_snd_pl", -1, dsamps, 0); + sound_play(dsamps); +} + +#define MAX_SND_BUF 65536 + +int g_samp_buf[2*MAX_SND_BUF]; +word32 zero_buf[SOUND_SHM_SAMP_SIZE]; + +double g_doc_dsamps_extra = 0.0; + +float g_fvoices = 0.0; + +word32 g_cycs_in_sound1 = 0; +word32 g_cycs_in_sound2 = 0; +word32 g_cycs_in_sound3 = 0; +word32 g_cycs_in_sound4 = 0; +word32 g_cycs_in_start_sound = 0; +word32 g_cycs_in_est_sound = 0; + +int g_num_snd_plays = 0; +int g_num_doc_events = 0; +int g_num_start_sounds = 0; +int g_num_scan_osc = 0; +int g_num_recalc_snd_parms = 0; + +word32 g_last_c030_vbl_count = 0; +int g_c030_state = 0; + +#define VAL_C030_RANGE (32768) +#define VAL_C030_BASE (-16384) + +int g_sound_file_num = 0; +int g_sound_file_fd = -1; +int g_send_sound_to_file = 0; +int g_send_file_bytes = 0; + +void +open_sound_file() +{ + char name[256]; + int fd; + + sprintf(name, "snd.out.%d", g_sound_file_num); + + fd = open(name, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0x1ff); + if(fd < 0) { + printf("open_sound_file open ret: %d, errno: %d\n", fd, errno); + exit(1); + } + + g_sound_file_fd = fd; + g_sound_file_num++; + g_send_file_bytes = 0; +} + +void +close_sound_file() +{ + if(g_sound_file_fd >= 0) { + close(g_sound_file_fd); + } + + g_sound_file_fd = -1; +} + +void +check_for_range(word32 *addr, int num_samps, int offset) +{ + short *shortptr; + int i; + int left; + int right; + int max; + + max = -32768; + + if(num_samps > SOUND_SHM_SAMP_SIZE) { + halt_printf("num_samps: %d > %d!\n", num_samps, + SOUND_SHM_SAMP_SIZE); + } + + for(i = 0; i < num_samps; i++) { + shortptr = (short *)&(addr[i]); + left = shortptr[0]; + right = shortptr[1]; + if((left > 0x3000) || (right > 0x3000)) { + halt_printf("Sample %d of %d at snd_buf: %p is: " + "%d/%d\n", i + offset, num_samps, + &addr[i], left, right); + return; + } + + max = MAX(max, left); + max = MAX(max, right); + } + + printf("check4 max: %d over %d\n", max, num_samps); +} + +void +send_sound_to_file(word32 *addr, int shm_pos, int num_samps) +{ + int size; + int ret; + + if(g_sound_file_fd < 0) { + open_sound_file(); + } + + size = 0; + if((num_samps + shm_pos) > SOUND_SHM_SAMP_SIZE) { + size = SOUND_SHM_SAMP_SIZE - shm_pos; + g_send_file_bytes += (size * 4); + + ret = write(g_sound_file_fd, &(addr[shm_pos]), 4*size); + if(ret != 4*size) { + halt_printf("wrote %d not %d\n", ret, 4*size); + } + + if(g_doc_vol < 3) { + check_for_range(&(addr[shm_pos]), size, 0); + } else { + printf("Not checking %d bytes since vol: %d\n", + 4*size, g_doc_vol); + } + shm_pos = 0; + num_samps -= size; + } + + g_send_file_bytes += (num_samps * 4); + + ret = write(g_sound_file_fd, &(addr[shm_pos]), 4*num_samps); + if(ret != 4*num_samps) { + halt_printf("wrote %d not %d\n", ret, 4*num_samps); + } + + if(g_doc_vol < 3) { + check_for_range(&(addr[shm_pos]), num_samps, size); + } else { + printf("Not checking2 %d bytes since vol: %d\n", + 4*num_samps, g_doc_vol); + } + +} + +void +send_sound(int real_samps, int size) +{ + word32 tmp; + int ret; + + if(g_audio_enable == 0) { + printf("Entered send_sound but audio off!\n"); + exit(2); + } + + if(real_samps) { + tmp = size + 0xa2000000; + } else { + tmp = size + 0xa1000000; + } + DOC_LOG("send_sound", -1, g_last_sound_play_dsamp, + (real_samps << 30) + size); + +#if defined(MAC) || defined(_WIN32) + ret = 0; + child_sound_playit(tmp); +#else + /* Although this looks like a big/little-endian issue, since the */ + /* child is also reading an int, it just works with no byte swap */ + ret = write(g_pipe_fd[1], &tmp, 4); + if(ret != 4) { + halt_printf("send_sound, wr ret: %d, errno: %d\n", ret, errno); + } +#endif +} + +void +show_c030_state() +{ + show_c030_samps(&(g_samp_buf[0]), 100); +} + +void +show_c030_samps(int *outptr, int num) +{ + int i; + + printf("c030_fsamps[]: %d\n", g_num_c030_fsamps); + + for(i = 0; i < g_num_c030_fsamps+2; i++) { + printf("%3d: %5.3f\n", i, c030_fsamps[i]); + } + + printf("Samples[] = %d\n", num); + + for(i = 0; i < num+2; i++) { + printf("%4d: %d %d\n", i, outptr[0], outptr[1]); + outptr += 2; + } +} + +int g_sound_play_depth = 0; + +void +sound_play(double dsamps) +{ + register word32 start_time1, start_time2, start_time3, start_time4; + register word32 end_time1, end_time2, end_time3; + Doc_reg *rptr; + int *outptr; + int *outptr_start; + word32 *sndptr; + double complete_dsamp; + double cur_dsamp; + double last_dsamp; + double dsamp_now; + double dnum_samps; + int val, val2; + int new_val; + float ftmp; + int imul; + int off; + int num; + float fsampnum; + float next_fsampnum; + int c030_lo_val, c030_hi_val; + float fc030_range; + float fc030_base; + int sampnum; + int next_sampnum; + float fpercent; + int c030_state; + int val0, val1; + word32 cur_acc; + word32 cur_pos; + word32 cur_mask; + word32 cur_inc; + word32 cur_end; + int ctl; + int num_osc_en; + int samps_left; + int samps_to_do; + int samps_played; + int samp_offset; + int snd_buf_init; + int pos; + int num_running; + int num_samps; + int osc; + int done; + int i, j; + + + GET_ITIMER(start_time1); + + g_num_snd_plays++; + if(g_sound_play_depth) { + halt_printf("Nested sound_play!\n"); + } + + g_sound_play_depth++; + + /* calc sample num */ + + last_dsamp = g_last_sound_play_dsamp; + num_samps = (int)(dsamps - g_last_sound_play_dsamp); + dnum_samps = (double)num_samps; + + dsamp_now = last_dsamp + dnum_samps; + + if(num_samps < 1) { + /* just say no */ + g_sound_play_depth--; + return; + } + + DOC_LOG("sound_play", -1, dsamp_now, num_samps); + + if(num_samps > MAX_SND_BUF) { + printf("num_samps: %d, too big!\n", num_samps); + g_sound_play_depth--; + return; + } + + + GET_ITIMER(start_time4); + + outptr_start = &(g_samp_buf[0]); + outptr = outptr_start; + + snd_buf_init = 0; + + samps_played = 0; + + num = g_num_c030_fsamps; + + if(num || ((g_vbl_count - g_last_c030_vbl_count) < 240)) { + + if(num) { + g_last_c030_vbl_count = g_vbl_count; + } + + pos = 0; + outptr = outptr_start; + c030_state = g_c030_state; + + c030_hi_val = ((VAL_C030_BASE + VAL_C030_RANGE)*g_doc_vol) >> 4; + c030_lo_val = (VAL_C030_BASE * g_doc_vol) >> 4; + + fc030_range = (float)(((VAL_C030_RANGE) * g_doc_vol) >> 4); + fc030_base = (float)(((VAL_C030_BASE) * g_doc_vol) >> 4); + + val = c030_lo_val; + if(c030_state) { + val = c030_hi_val; + } + + snd_buf_init++; + + c030_fsamps[num] = (float)(num_samps); + c030_fsamps[num+1] = (float)(num_samps+1); + + ftmp = (float)num_samps; + /* ensure that all samps are in range */ + for(i = num - 1; i >= 0; i--) { + if(c030_fsamps[i] > ftmp) { + c030_fsamps[i] = ftmp; + } + } + + num++; + fsampnum = c030_fsamps[0]; + sampnum = (int)fsampnum; + fpercent = (float)0.0; + i = 0; + + while(i < num) { + next_fsampnum = c030_fsamps[i+1]; + next_sampnum = (int)next_fsampnum; + if(sampnum < 0 || sampnum > num_samps) { + halt_printf("play c030: [%d]:%f is %d, > %d\n", + i, fsampnum, sampnum, num_samps); + break; + } + + /* write in samples to all samps < me */ + new_val = c030_lo_val; + if(c030_state) { + new_val = c030_hi_val; + } + for(j = pos; j < sampnum; j++) { + outptr[0] = new_val; + outptr[1] = new_val; + outptr += 2; + pos++; + } + + /* now, calculate me */ + fpercent = (float)0.0; + if(c030_state) { + fpercent = (fsampnum - (float)sampnum); + } + + c030_state = !c030_state; + + while(next_sampnum == sampnum) { + if(c030_state) { + fpercent += (next_fsampnum - fsampnum); + } + i++; + fsampnum = next_fsampnum; + + next_fsampnum = c030_fsamps[i+1]; + next_sampnum = (int)next_fsampnum; + c030_state = !c030_state; + } + + if(c030_state) { + /* add in fractional time */ + ftmp = (int)(fsampnum + (float)1.0); + fpercent += (ftmp - fsampnum); + } + + if((fpercent < (float)0.0) || (fpercent > (float)1.0)) { + halt_printf("fpercent: %d = %f\n", i, fpercent); + show_c030_samps(outptr_start, num_samps); + break; + } + + val = (int)((fpercent * fc030_range) + fc030_base); + outptr[0] = val; + outptr[1] = val; + outptr += 2; + pos++; + i++; + + sampnum = next_sampnum; + fsampnum = next_fsampnum; + } + + samps_played += num_samps; + + /* since we pretended to get one extra sample, we will */ + /* have toggled the speaker one time too many. Fix it */ + g_c030_state = !c030_state; + + if(g_send_sound_to_file) { + show_c030_samps(outptr_start, num_samps); + } + } + + g_num_c030_fsamps = 0; + + GET_ITIMER(start_time2); + + num_running = 0; + + num_osc_en = g_doc_num_osc_en; + + done = 0; + while(!done) { + done = 1; + for(j = 0; j < num_osc_en; j++) { + osc = j; + rptr = &(g_doc_regs[osc]); + complete_dsamp = rptr->complete_dsamp; + samps_left = rptr->samps_left; + cur_acc = rptr->cur_acc; + cur_mask = rptr->cur_mask; + cur_inc = rptr->cur_inc; + cur_end = rptr->cur_end; + if(!rptr->running || cur_inc == 0 || + (complete_dsamp >= dsamp_now)) { + continue; + } + + done = 0; + ctl = rptr->ctl; + + samp_offset = 0; + if(complete_dsamp > last_dsamp) { + samp_offset = (int)(complete_dsamp- last_dsamp); + if(samp_offset > num_samps) { + rptr->complete_dsamp = dsamp_now; + continue; + } + } + outptr = outptr_start + 2 * samp_offset; + if(ctl & 0x10) { + /* other channel */ + outptr += 1; + } + + imul = (rptr->vol * g_doc_vol); + off = imul * 128; + + samps_to_do = MIN(samps_left, num_samps - samp_offset); + if(imul == 0 || samps_to_do == 0) { + /* produce no sound */ + samps_left = samps_left - samps_to_do; + cur_acc += cur_inc * samps_to_do; + rptr->samps_left = samps_left; + rptr->cur_acc = cur_acc; + cur_dsamp = last_dsamp + + (double)(samps_to_do + samp_offset); + DOC_LOG("nosnd", osc, cur_dsamp, samps_to_do); + rptr->complete_dsamp = dsamp_now; + cur_pos = rptr->cur_start+(cur_acc & cur_mask); + if(samps_left <= 0) { + doc_sound_end(osc, 1, cur_dsamp, + dsamp_now); + val = 0; + j--; + } else { + val = doc_ram[cur_pos >> SND_PTR_SHIFT]; + } + rptr->last_samp_val = val; + continue; + } + + if(snd_buf_init == 0) { + memset(outptr_start, 0, + 2*sizeof(outptr_start[0])*num_samps); + snd_buf_init++; + } + + val = 0; + rptr->complete_dsamp = dsamp_now; + cur_pos = rptr->cur_start + (cur_acc & cur_mask); + for(i = 0; i < samps_to_do; i++) { + pos = cur_pos >> SND_PTR_SHIFT; + cur_pos += cur_inc; + cur_acc += cur_inc; + val = doc_ram[pos]; + + val2 = (val * imul - off) >> 4; + if((val == 0) || (cur_pos >= cur_end)) { + cur_dsamp = last_dsamp + + (double)(samp_offset + i + 1); + rptr->cur_acc = cur_acc; + rptr->samps_left = 0; + DOC_LOG("end or 0", osc, cur_dsamp, + (pos << 16) + ((i &0xff) << 8) + + val); + doc_sound_end(osc, val, cur_dsamp, + dsamp_now); + val = 0; + break; + } + + val2 = outptr[0] + val2; + + samps_left--; + *outptr = val2; + outptr += 2; + } + + rptr->last_samp_val = val; + + if(val != 0) { + rptr->cur_acc = cur_acc; + rptr->samps_left = samps_left; + rptr->complete_dsamp = dsamp_now; + } + + samps_played += samps_to_do; + DOC_LOG("splayed", osc, dsamp_now, + (samps_to_do << 16) + (pos & 0xffff)); + } + } + + GET_ITIMER(end_time2); + + g_cycs_in_sound2 += (end_time2 - start_time2); + + g_last_sound_play_dsamp = dsamp_now; + + GET_ITIMER(start_time3); + + outptr = outptr_start; + + pos = g_sound_shm_pos; + sndptr = g_sound_shm_addr; + +#if 0 + printf("samps_left: %d, num_samps: %d\n", samps_left, num_samps); +#endif + + if(g_audio_enable != 0) { + + if(snd_buf_init) { + /* convert sound buf */ + + for(i = 0; i < num_samps; i++) { + val0 = outptr[0]; + val1 = outptr[1]; + val = val0; + if(val0 > 32767) { + val = 32767; + } + if(val0 < -32768) { + val = -32768; + } + + val0 = val; + val = val1; + if(val1 > 32767) { + val = 32767; + } + if(val1 < -32768) { + val = -32768; + } + + + outptr += 2; + +#if defined(__linux__) || defined(OSS) + /* Linux seems to expect little-endian */ + /* samples always, even on PowerPC */ +# ifdef KEGS_LITTLE_ENDIAN + sndptr[pos] = (val << 16) + (val0 & 0xffff); +# else + sndptr[pos] = ((val & 0xff) << 24) + + ((val & 0xff00) << 8) + + ((val0 & 0xff) << 8) + + ((val0 >> 8) & 0xff); +# endif +#else +# ifdef KEGS_LITTLE_ENDIAN + sndptr[pos] = (val << 16) + (val0 & 0xffff); +# else + sndptr[pos] = (val0 << 16) + (val & 0xffff); +# endif +#endif + pos++; + if(pos >= SOUND_SHM_SAMP_SIZE) { + pos = 0; + } + } + + if(g_queued_nonsamps) { + /* force out old 0 samps */ + send_sound(0, g_queued_nonsamps); + g_queued_nonsamps = 0; + } + + if(g_send_sound_to_file) { + send_sound_to_file(g_sound_shm_addr, + g_sound_shm_pos, num_samps); + } + + g_queued_samps += num_samps; + } else { + /* move pos */ + pos += num_samps; + while(pos >= SOUND_SHM_SAMP_SIZE) { + pos -= SOUND_SHM_SAMP_SIZE; + } + + if(g_send_sound_to_file) { + send_sound_to_file(zero_buf, g_sound_shm_pos, + num_samps); + } + + if(g_queued_samps) { + /* force out old non-0 samps */ + send_sound(1, g_queued_samps); + g_queued_samps = 0; + } + + g_queued_nonsamps += num_samps; + } + + } + + g_sound_shm_pos = pos; + + + GET_ITIMER(end_time3); + + g_fvoices += ((float)(samps_played) * (float)(g_drecip_audio_rate)); + + if(g_audio_enable != 0) { + if(g_queued_samps >= (g_audio_rate/32)) { + send_sound(1, g_queued_samps); + g_queued_samps = 0; + } + + if(g_queued_nonsamps >= (g_audio_rate/32)) { + send_sound(0, g_queued_nonsamps); + g_queued_nonsamps = 0; + } + } + + GET_ITIMER(end_time1); + + g_cycs_in_sound1 += (end_time1 - start_time1); + g_cycs_in_sound3 += (end_time3 - start_time3); + g_cycs_in_sound4 += (start_time2 - start_time4); + + g_last_sound_play_dsamp = dsamp_now; + + g_sound_play_depth--; +} + + +void +doc_handle_event(int osc, double dcycs) +{ + double dsamps; + + /* handle osc stopping and maybe interrupting */ + + g_num_doc_events++; + + dsamps = dcycs * g_dsamps_per_dcyc; + + DOC_LOG("doc_ev", osc, dcycs, 0); + + g_doc_regs[osc].event = 0; + + sound_play(dsamps); + +} + +void +doc_sound_end(int osc, int can_repeat, double eff_dsamps, double dsamps) +{ + Doc_reg *rptr, *orptr; + int mode, omode; + int other_osc; + int one_shot_stop; + int ctl; + + /* handle osc stopping and maybe interrupting */ + + if(osc < 0 || osc > 31) { + printf("doc_handle_event: osc: %d!\n", osc); + return; + } + + rptr = &(g_doc_regs[osc]); + ctl = rptr->ctl; + + if(rptr->event) { + remove_event_doc(osc); + } + rptr->event = 0; + rptr->cur_acc = 0; /* reset internal accumulator*/ + + /* check to make sure osc is running */ + if(ctl & 0x01) { + /* Oscillator already stopped. */ + halt_printf("Osc %d interrupt, but it was already stop!\n",osc); +#ifdef HPUX + U_STACK_TRACE(); +#endif + return; + } + + if(ctl & 0x08) { + if(rptr->has_irq_pending == 0) { + add_sound_irq(osc); + } + } + + if(!rptr->running) { + halt_printf("Doc event for osc %d, but ! running\n", osc); + } + + rptr->running = 0; + + mode = (ctl >> 1) & 3; + other_osc = osc ^ 1; + orptr = &(g_doc_regs[other_osc]); + omode = (orptr->ctl >> 1) & 3; + + /* If either this osc or it's partner is in swap mode, treat the */ + /* pair as being in swap mode. This Ensoniq feature pointed out */ + /* by Ian Schmidt */ + if(mode == 0 && can_repeat) { + /* free-running mode with no 0 byte! */ + /* start doing it again */ + + start_sound(osc, eff_dsamps, dsamps); + + return; + } else if((mode == 3) || (omode == 3)) { + /* swap mode (even if we're one_shot and partner is swap)! */ + /* unless we're one-shot and we hit a 0-byte--then */ + /* Olivier Goguel says just stop */ + rptr->ctl |= 1; + one_shot_stop = (mode == 1) && (!can_repeat); + if(!one_shot_stop && !orptr->running && + (orptr->ctl & 0x1)) { + orptr->ctl = orptr->ctl & (~1); + start_sound(other_osc, eff_dsamps, dsamps); + } + return; + } else { + /* stop the oscillator */ + rptr->ctl |= 1; + } + + return; +} + +void +add_sound_irq(int osc) +{ + int num_osc_interrupting; + + if(g_doc_regs[osc].has_irq_pending) { + halt_printf("Adding sound_irq for %02x, but irq_p: %d\n", osc, + g_doc_regs[osc].has_irq_pending); + } + + num_osc_interrupting = g_num_osc_interrupting + 1; + g_doc_regs[osc].has_irq_pending = num_osc_interrupting; + g_num_osc_interrupting = num_osc_interrupting; + + add_irq(); + if(num_osc_interrupting == 1) { + doc_reg_e0 = 0x00 + (osc << 1); + } + + DOC_LOG("add_irq", osc, g_cur_dcycs * g_dsamps_per_dcyc, 0); +} + +void +remove_sound_irq(int osc, int must) +{ + Doc_reg *rptr; + int num_osc_interrupt; + int has_irq_pending; + int first; + int i; + + doc_printf("remove irq for osc: %d, has_irq: %d\n", + osc, g_doc_regs[osc].has_irq_pending); + + num_osc_interrupt = g_doc_regs[osc].has_irq_pending; + first = 0; + if(num_osc_interrupt) { + g_num_osc_interrupting--; + g_doc_regs[osc].has_irq_pending = 0; + DOC_LOG("rem_irq", osc, g_cur_dcycs * g_dsamps_per_dcyc, 0); + remove_irq(); + + first = 0x40 | (doc_reg_e0 >> 1); + /* if none found, then def = no ints */ + for(i = 0; i < g_doc_num_osc_en; i++) { + rptr = &(g_doc_regs[i]); + has_irq_pending = rptr->has_irq_pending; + if(has_irq_pending > num_osc_interrupt) { + has_irq_pending--; + rptr->has_irq_pending = has_irq_pending; + } + if(has_irq_pending == 1) { + first = i; + } + } + if(num_osc_interrupt == 1) { + doc_reg_e0 = (first << 1); + } else { +#if 0 + halt_printf("remove_sound_irq[%02x]=%d, first:%d\n", + osc, num_osc_interrupt, first); +#endif + } + } else { +#if 0 + /* make sure no int pending */ + if(doc_reg_e0 != 0xff) { + halt_printf("remove_sound_irq[%02x]=0, but e0: %02x\n", + osc, doc_reg_e0); + } +#endif + if(must) { + halt_printf("REMOVE_sound_irq[%02x]=0, but e0: %02x\n", + osc, doc_reg_e0); + } + } + + if(doc_reg_e0 & 0x80) { + for(i = 0; i < 0x20; i++) { + has_irq_pending = g_doc_regs[i].has_irq_pending; + if(has_irq_pending) { + halt_printf("remove_sound_irq[%02x], but " + "[%02x]=%d!\n", osc,i,has_irq_pending); + printf("num_osc_int: %d, first: %02x\n", + num_osc_interrupt, first); + } + } + } +} + +void +start_sound(int osc, double eff_dsamps, double dsamps) +{ + register word32 start_time1; + register word32 end_time1; + Doc_reg *rptr; + int ctl; + int mode; + word32 sz; + word32 size; + word32 wave_size; + + if(osc < 0 || osc > 31) { + halt_printf("start_sound: osc: %02x!\n", osc); + } + + g_num_start_sounds++; + + rptr = &(g_doc_regs[osc]); + + if(osc >= g_doc_num_osc_en) { + rptr->ctl |= 1; + return; + } + + GET_ITIMER(start_time1); + + ctl = rptr->ctl; + + mode = (ctl >> 1) & 3; + + wave_size = rptr->wavesize; + + sz = ((wave_size >> 3) & 7) + 8; + size = 1 << sz; + + if(size < 0x100) { + halt_printf("size: %08x is too small, sz: %08x!\n", size, sz); + } + + if(rptr->running) { + halt_printf("start_sound osc: %d, already running!\n", osc); + } + + rptr->running = 1; + + rptr->complete_dsamp = eff_dsamps; + + doc_printf("Starting osc %02x, dsamp: %f\n", osc, dsamps); + doc_printf("size: %04x\n", size); + + if((mode == 2) && ((osc & 1) == 0)) { + printf("Sync mode osc %d starting!\n", osc); + /* set_halt(1); */ + + /* see if we should start our odd partner */ + if((rptr[1].ctl & 7) == 5) { + /* odd partner stopped in sync mode--start him */ + rptr[1].ctl &= (~1); + start_sound(osc + 1, eff_dsamps, dsamps); + } else { + printf("Osc %d starting sync, but osc %d ctl: %02x\n", + osc, osc+1, rptr[1].ctl); + } + } + + wave_end_estimate(osc, eff_dsamps, dsamps); + + DOC_LOG("st playing", osc, eff_dsamps, size); +#if 0 + if(rptr->cur_acc != 0) { + halt_printf("Start osc %02x, acc: %08x\n", osc, rptr->cur_acc); + } +#endif + + GET_ITIMER(end_time1); + + g_cycs_in_start_sound += (end_time1 - start_time1); +} + +void +wave_end_estimate(int osc, double eff_dsamps, double dsamps) +{ + register word32 start_time1; + register word32 end_time1; + Doc_reg *rptr; + byte *ptr1; + double event_dsamp; + double event_dcycs; + double dcycs_per_samp; + double dsamps_per_byte; + double num_dsamps; + double dcur_inc; + word32 tmp1; + word32 cur_inc; + word32 save_val; + int save_size; + int pos; + int size; + int estimate; + + GET_ITIMER(start_time1); + + dcycs_per_samp = g_dcycs_per_samp; + + rptr = &(g_doc_regs[osc]); + + cur_inc = rptr->cur_inc; + dcur_inc = (double)cur_inc; + dsamps_per_byte = 0.0; + if(cur_inc) { + dsamps_per_byte = SND_PTR_SHIFT_DBL / (double)dcur_inc; + } + + /* see if there's a zero byte */ + tmp1 = rptr->cur_start + (rptr->cur_acc & rptr->cur_mask); + pos = tmp1 >> SND_PTR_SHIFT; + size = ((rptr->cur_end) >> SND_PTR_SHIFT) - pos; + + ptr1 = &doc_ram[pos]; + + estimate = 0; + if(rptr->ctl & 0x08 || g_doc_regs[osc ^ 1].ctl & 0x08) { + estimate = 1; + } + +#if 0 + estimate = 1; +#endif + if(estimate) { + save_size = size; + save_val = ptr1[size]; + ptr1[size] = 0; + size = strlen((char *)ptr1); + ptr1[save_size] = save_val; + } + + /* calc samples to play */ + num_dsamps = (dsamps_per_byte * (double)size) + 1.0; + + rptr->samps_left = (int)num_dsamps; + + if(rptr->event) { + remove_event_doc(osc); + } + rptr->event = 0; + + event_dsamp = eff_dsamps + num_dsamps; + if(estimate) { + rptr->event = 1; + rptr->dsamp_ev = event_dsamp; + rptr->dsamp_ev2 = dsamps; + event_dcycs = (event_dsamp * dcycs_per_samp) + 1.0; + add_event_doc(event_dcycs, osc); + } + + GET_ITIMER(end_time1); + + g_cycs_in_est_sound += (end_time1 - start_time1); +} + + +void +remove_sound_event(int osc) +{ + if(g_doc_regs[osc].event) { + g_doc_regs[osc].event = 0; + remove_event_doc(osc); + } +} + + +void +doc_write_ctl_reg(int osc, int val, double dsamps) +{ + Doc_reg *rptr; + double eff_dsamps; + word32 old_halt; + word32 new_halt; + int old_val; + int mode; + + if(osc < 0 || osc >= 0x20) { + halt_printf("doc_write_ctl_reg: osc: %02x, val: %02x\n", + osc, val); + return; + } + + eff_dsamps = dsamps; + rptr = &(g_doc_regs[osc]); + old_val = rptr->ctl; + g_doc_saved_ctl = old_val; + + if(old_val == val) { + return; + } + + DOC_LOG("ctl_reg", osc, dsamps, (old_val << 16) + val); + + mode = (val >> 1) & 3; + + old_halt = (old_val & 1); + new_halt = (val & 1); + + /* bits are: 28: old int bit */ + /* 29: old halt bit */ + /* 30: new int bit */ + /* 31: new halt bit */ + +#if 0 + if(osc == 0x10) { + printf("osc %d new_ctl: %02x, old: %02x\n", osc, val, old_val); + } +#endif + + /* no matter what, remove any pending IRQs on this osc */ + remove_sound_irq(osc, 0); + +#if 0 + if(old_halt) { + printf("doc_write_ctl to osc %d, val: %02x, old: %02x\n", + osc, val, old_val); + } +#endif + + if(new_halt != 0) { + /* make sure sound is stopped */ + remove_sound_event(osc); + if(old_halt == 0) { + /* it was playing, finish it up */ +#if 0 + halt_printf("Aborted osc %d at eff_dsamps: %f, ctl: " + "%02x, oldctl: %02x\n", osc, eff_dsamps, + val, old_val); +#endif + sound_play(eff_dsamps); + } + if(((old_val >> 1) & 3) > 0) { + /* don't clear acc if free-running */ + g_doc_regs[osc].cur_acc = 0; + } + + g_doc_regs[osc].ctl = val; + g_doc_regs[osc].running = 0; + } else { + /* new halt == 0 = make sure sound is running */ + if(old_halt != 0) { + /* start sound */ + DOC_LOG("ctl_sound_play", osc, eff_dsamps, val); + sound_play(eff_dsamps); + g_doc_regs[osc].ctl = val; + + start_sound(osc, eff_dsamps, dsamps); + } else { + /* was running, and something changed */ + doc_printf("osc %d old ctl:%02x new:%02x!\n", + osc, old_val, val); +#if 0 + sound_play(eff_dsamps); +/* HACK: fix this??? */ +#endif + g_doc_regs[osc].ctl = val; + if((old_val ^ val) & val & 0x8) { + /* now has ints on */ + wave_end_estimate(osc, dsamps, dsamps); + } + } + } +} + +void +doc_recalc_sound_parms(int osc, double eff_dcycs, double dsamps) +{ + Doc_reg *rptr; + double dfreq; + double dtmp1; + double dacc, dacc_recip; + word32 res; + word32 sz; + word32 size; + word32 wave_size; + word32 cur_start; + word32 shifted_size; + + g_num_recalc_snd_parms++; + + rptr = &(g_doc_regs[osc]); + + wave_size = rptr->wavesize; + + dfreq = (double)rptr->freq; + + sz = ((wave_size >> 3) & 7) + 8; + size = 1 << sz; + rptr->size_bytes = size; + res = wave_size & 7; + + shifted_size = size << SND_PTR_SHIFT; + cur_start = (rptr->waveptr << (8 + SND_PTR_SHIFT)) & (-shifted_size); + + dtmp1 = dfreq * (DOC_SCAN_RATE * g_drecip_audio_rate); + dacc = (double)(1 << (20 - (17 - sz + res))); + dacc_recip = (SND_PTR_SHIFT_DBL) / ((double)(1 << 20)); + dtmp1 = dtmp1 * g_drecip_osc_en_plus_2 * dacc * dacc_recip; + + rptr->cur_inc = (int)(dtmp1); + rptr->cur_start = cur_start; + rptr->cur_end = cur_start + shifted_size; + rptr->cur_mask = (shifted_size - 1); + + DOC_LOG("recalc", osc, dsamps, (rptr->waveptr << 16) + wave_size); +} + +int +doc_read_c030(double dcycs) +{ + int num; + + num = g_num_c030_fsamps; + if(num >= MAX_C030_TIMES) { + halt_printf("Too many clicks per vbl: %d\n", num); + return 0; + } + + c030_fsamps[num] = (float)(dcycs * g_dsamps_per_dcyc - + g_last_sound_play_dsamp); + g_num_c030_fsamps = num + 1; + + doc_printf("read c030, num this vbl: %04x\n", num); + + return 0; +} + +int +doc_read_c03c(double dcycs) +{ + return doc_sound_ctl; +} + +int +doc_read_c03d(double dcycs) +{ + double dsamps; + Doc_reg *rptr; + int osc; + int type; + int ret; + + ret = doc_saved_val; + dsamps = dcycs * g_dsamps_per_dcyc; + + if(doc_sound_ctl & 0x40) { + /* Read RAM */ + doc_saved_val = doc_ram[doc_ptr]; + } else { + /* Read DOC */ + doc_saved_val = 0; + + osc = doc_ptr & 0x1f; + type = (doc_ptr >> 5) & 0x7; + rptr = &(g_doc_regs[osc]); + + switch(type) { + case 0x0: /* freq lo */ + doc_saved_val = rptr->freq & 0xff; + break; + case 0x1: /* freq hi */ + doc_saved_val = rptr->freq >> 8; + break; + case 0x2: /* vol */ + doc_saved_val = rptr->vol; + break; + case 0x3: /* data register */ + /* HACK: make this call sound_play sometimes */ + doc_saved_val = rptr->last_samp_val; + break; + case 0x4: /* wave ptr register */ + doc_saved_val = rptr->waveptr; + break; + case 0x5: /* control register */ + doc_saved_val = rptr->ctl; + break; + case 0x6: /* control register */ + doc_saved_val = rptr->wavesize; + break; + case 0x7: /* 0xe0-0xff */ + switch(osc) { + case 0x00: /* 0xe0 */ + doc_saved_val = doc_reg_e0; + doc_printf("Reading doc 0xe0, ret: %02x\n", + doc_saved_val); + + /* Clear IRQ on read of e0, if any irq pend */ + if((doc_reg_e0 & 0x80) == 0) { + remove_sound_irq(doc_reg_e0 >> 1, 1); + } + break; + case 0x01: /* 0xe1 */ + doc_saved_val = (g_doc_num_osc_en - 1) << 1; + break; + case 0x02: /* 0xe2 */ + doc_saved_val = 0x80; +#if 0 + halt_printf("Reading doc 0xe2, ret: %02x\n", + doc_saved_val); +#endif + break; + default: + doc_saved_val = 0; + halt_printf("Reading bad doc_reg[%04x]: %02x\n", + doc_ptr, doc_saved_val); + } + break; + default: + doc_saved_val = 0; + halt_printf("Reading bad doc_reg[%04x]: %02x\n", + doc_ptr, doc_saved_val); + } + } + + doc_printf("read c03d, doc_ptr: %04x, ret: %02x, saved: %02x\n", + doc_ptr, ret, doc_saved_val); + + DOC_LOG("read c03d", -1, dsamps, (doc_ptr << 16) + + (doc_saved_val << 8) + ret); + + if(doc_sound_ctl & 0x20) { + doc_ptr = (doc_ptr + 1) & 0xffff; + } + + + return ret; +} + +void +doc_write_c03c(int val, double dcycs) +{ + int vol; + + vol = val & 0xf; + if(g_doc_vol != vol) { + /* don't bother playing sound..wait till next update */ + /* sound_play(dcycs); */ + + g_doc_vol = vol; + doc_printf("Setting doc vol to 0x%x at %f\n", + vol, dcycs); + } + DOC_LOG("c03c write", -1, dcycs * g_dsamps_per_dcyc, val); + + doc_sound_ctl = val; +} + +void +doc_write_c03d(int val, double dcycs) +{ + double dsamps; + double eff_dsamps; + Doc_reg *rptr; + int osc; + int type; + int ctl; + int tmp; + int i; + + val = val & 0xff; + + dsamps = dcycs * g_dsamps_per_dcyc; + eff_dsamps = dsamps; + doc_printf("write c03d, doc_ptr: %04x, val: %02x\n", + doc_ptr, val); + + DOC_LOG("write c03d", -1, dsamps, (doc_ptr << 16) + val); + + if(doc_sound_ctl & 0x40) { + /* RAM */ + doc_ram[doc_ptr] = val; + } else { + /* DOC */ + osc = doc_ptr & 0x1f; + type = (doc_ptr >> 5) & 0x7; + + rptr = &(g_doc_regs[osc]); + ctl = rptr->ctl; +#if 0 + if((ctl & 1) == 0) { + if(type < 2 || type == 4 || type == 6) { + halt_printf("Osc %d is running, old ctl: %02x, " + "but write reg %02x=%02x\n", + osc, ctl, doc_ptr & 0xff, val); + } + } +#endif + + switch(type) { + case 0x0: /* freq lo */ + if((rptr->freq & 0xff) == (word32)val) { + break; + } + if((ctl & 1) == 0) { + /* play through current status */ + DOC_LOG("flo_sound_play", osc, dsamps, val); + sound_play(dsamps); + } + rptr->freq = (rptr->freq & 0xff00) + val; + doc_recalc_sound_parms(osc, eff_dsamps, dsamps); + break; + case 0x1: /* freq hi */ + if((rptr->freq >> 8) == (word32)val) { + break; + } + if((ctl & 1) == 0) { + /* play through current status */ + DOC_LOG("fhi_sound_play", osc, dsamps, val); + sound_play(dsamps); + } + rptr->freq = (rptr->freq & 0xff) + (val << 8); + doc_recalc_sound_parms(osc, eff_dsamps, dsamps); + break; + case 0x2: /* vol */ + if(rptr->vol == (word32)val) { + break; + } + if((ctl & 1) == 0) { + /* play through current status */ + DOC_LOG("vol_sound_play", osc, dsamps, val); + sound_play(dsamps); +#if 0 + halt_printf("vol_sound_play at %.1f osc:%d " + "val:%d\n", dsamps, osc, val); +#endif + } + rptr->vol = val; + break; + case 0x3: /* data register */ +#if 0 + printf("Writing %02x into doc_data_reg[%02x]!\n", + val, osc); +#endif + break; + case 0x4: /* wave ptr register */ + if(rptr->waveptr == (word32)val) { + break; + } + if((ctl & 1) == 0) { + /* play through current status */ + DOC_LOG("wptr_sound_play", osc, dsamps, val); + sound_play(dsamps); + } + rptr->waveptr = val; + doc_recalc_sound_parms(osc, eff_dsamps, dsamps); + break; + case 0x5: /* control register */ +#if 0 + printf("doc_write ctl osc %d, val: %02x\n", osc, val); +#endif + if(rptr->ctl == (word32)val) { + break; + } + doc_write_ctl_reg(osc, val, dsamps); + break; + case 0x6: /* wavesize register */ + if(rptr->wavesize == (word32)val) { + break; + } + if((ctl & 1) == 0) { + /* play through current status */ + DOC_LOG("wsz_sound_play", osc, dsamps, val); + sound_play(dsamps); + } + rptr->wavesize = val; + doc_recalc_sound_parms(osc, eff_dsamps, dsamps); + break; + case 0x7: /* 0xe0-0xff */ + switch(osc) { + case 0x00: /* 0xe0 */ + doc_printf("writing doc 0xe0 with %02x, " + "was:%02x\n", val, doc_reg_e0); +#if 0 + if(val != doc_reg_e0) { + halt_printf("writing doc 0xe0 with " + "%02x, was:%02x\n", val, + doc_reg_e0); + } +#endif + break; + case 0x01: /* 0xe1 */ + doc_printf("Writing doc 0xe1 with %02x\n", val); + tmp = val & 0x3e; + tmp = (tmp >> 1) + 1; + if(tmp < 1) { + tmp = 1; + } + if(tmp > 32) { + halt_printf("doc 0xe1: %02x!\n", val); + tmp = 32; + } + g_doc_num_osc_en = tmp; + UPDATE_G_DCYCS_PER_DOC_UPDATE(tmp); + + /* Stop any oscs that were running but now */ + /* are disabled */ + for(i = g_doc_num_osc_en; i < 0x20; i++) { + doc_write_ctl_reg(i, + g_doc_regs[i].ctl | 1, dsamps); + } + + break; + default: + /* this should be illegal, but Turkey Shoot */ + /* and apparently TaskForce, OOTW, etc */ + /* writes to e2-ff, for no apparent reason */ + doc_printf("Writing doc 0x%x with %02x\n", + doc_ptr, val); + break; + } + break; + default: + halt_printf("Writing %02x into bad doc_reg[%04x]\n", + val, doc_ptr); + } + } + + if(doc_sound_ctl & 0x20) { + doc_ptr = (doc_ptr + 1) & 0xffff; + } + + doc_saved_val = val; +} + +void +doc_write_c03e(int val) +{ + doc_ptr = (doc_ptr & 0xff00) + val; +} + +void +doc_write_c03f(int val) +{ + doc_ptr = (doc_ptr & 0xff) + (val << 8); +} + +void +doc_show_ensoniq_state(int osc) +{ + Doc_reg *rptr; + int i; + + printf("Ensoniq state\n"); + printf("c03c doc_sound_ctl: %02x, doc_saved_val: %02x\n", + doc_sound_ctl, doc_saved_val); + printf("doc_ptr: %04x, num_osc_en: %02x, e0: %02x\n", doc_ptr, + g_doc_num_osc_en, doc_reg_e0); + + for(i = 0; i < 32; i += 8) { + printf("irqp: %02x: %04x %04x %04x %04x %04x %04x %04x %04x\n", + i, + g_doc_regs[i].has_irq_pending, + g_doc_regs[i + 1].has_irq_pending, + g_doc_regs[i + 2].has_irq_pending, + g_doc_regs[i + 3].has_irq_pending, + g_doc_regs[i + 4].has_irq_pending, + g_doc_regs[i + 5].has_irq_pending, + g_doc_regs[i + 6].has_irq_pending, + g_doc_regs[i + 7].has_irq_pending); + } + + for(i = 0; i < 32; i += 8) { + printf("freq: %02x: %04x %04x %04x %04x %04x %04x %04x %04x\n", + i, + g_doc_regs[i].freq, g_doc_regs[i + 1].freq, + g_doc_regs[i + 2].freq, g_doc_regs[i + 3].freq, + g_doc_regs[i + 4].freq, g_doc_regs[i + 5].freq, + g_doc_regs[i + 6].freq, g_doc_regs[i + 7].freq); + } + + for(i = 0; i < 32; i += 8) { + printf("vol: %02x: %02x %02x %02x %02x %02x %02x %02x %02x\n", + i, + g_doc_regs[i].vol, g_doc_regs[i + 1].vol, + g_doc_regs[i + 2].vol, g_doc_regs[i + 3].vol, + g_doc_regs[i + 4].vol, g_doc_regs[i + 5].vol, + g_doc_regs[i + 6].vol, g_doc_regs[i + 6].vol); + } + + for(i = 0; i < 32; i += 8) { + printf("wptr: %02x: %02x %02x %02x %02x %02x %02x %02x %02x\n", + i, + g_doc_regs[i].waveptr, g_doc_regs[i + 1].waveptr, + g_doc_regs[i + 2].waveptr, g_doc_regs[i + 3].waveptr, + g_doc_regs[i + 4].waveptr, g_doc_regs[i + 5].waveptr, + g_doc_regs[i + 6].waveptr, g_doc_regs[i + 7].waveptr); + } + + for(i = 0; i < 32; i += 8) { + printf("ctl: %02x: %02x %02x %02x %02x %02x %02x %02x %02x\n", + i, + g_doc_regs[i].ctl, g_doc_regs[i + 1].ctl, + g_doc_regs[i + 2].ctl, g_doc_regs[i + 3].ctl, + g_doc_regs[i + 4].ctl, g_doc_regs[i + 5].ctl, + g_doc_regs[i + 6].ctl, g_doc_regs[i + 7].ctl); + } + + for(i = 0; i < 32; i += 8) { + printf("wsize: %02x: %02x %02x %02x %02x %02x %02x %02x %02x\n", + i, + g_doc_regs[i].wavesize, g_doc_regs[i + 1].wavesize, + g_doc_regs[i + 2].wavesize, g_doc_regs[i + 3].wavesize, + g_doc_regs[i + 4].wavesize, g_doc_regs[i + 5].wavesize, + g_doc_regs[i + 6].wavesize, g_doc_regs[i + 7].wavesize); + } + + show_doc_log(); + + for(i = 0; i < 32; i++) { + rptr = &(g_doc_regs[i]); + printf("%2d: ctl:%02x wp:%02x ws:%02x freq:%04x vol:%02x " + "ev:%d run:%d irq:%d sz:%04x\n", i, + rptr->ctl, rptr->waveptr, rptr->wavesize, rptr->freq, + rptr->vol, rptr->event, rptr->running, + rptr->has_irq_pending, rptr->size_bytes); + printf(" acc:%08x inc:%08x st:%08x end:%08x m:%08x\n", + rptr->cur_acc, rptr->cur_inc, rptr->cur_start, + rptr->cur_end, rptr->cur_mask); + printf(" compl_ds:%f samps_left:%d ev:%f ev2:%f\n", + rptr->complete_dsamp, rptr->samps_left, + rptr->dsamp_ev, rptr->dsamp_ev2); + } + +#if 0 + for(osc = 0; osc < 32; osc++) { + fmax = 0.0; + printf("osc %d has %d samps\n", osc, g_fsamp_num[osc]); + for(i = 0; i < g_fsamp_num[osc]; i++) { + printf("%4d: %f\n", i, g_fsamps[osc][i]); + fmax = MAX(fmax, g_fsamps[osc][i]); + } + printf("osc %d, fmax: %f\n", osc, fmax); + } +#endif +} diff --git a/src/sound.h b/src/sound.h new file mode 100644 index 0000000..552d5cf --- /dev/null +++ b/src/sound.h @@ -0,0 +1,60 @@ +/************************************************************************/ +/* KEGS: Apple //gs Emulator */ +/* Copyright 2002 by Kent Dickey */ +/* */ +/* This code is covered by the GNU GPL */ +/* */ +/* The KEGS web page is kegs.sourceforge.net */ +/* You may contact the author at: kadickey@alumni.princeton.edu */ +/************************************************************************/ + +#ifdef INCLUDE_RCSID_C +const char rcsid_sound_h[] = "@(#)$KmKId: sound.h,v 1.17 2003-11-21 15:15:57-05 kentd Exp $"; +#endif + +#if !defined(_WIN32) && !defined(__CYGWIN__) +# include +# include +#endif + +#define SOUND_SHM_SAMP_SIZE (32*1024) + +#define SAMPLE_SIZE 2 +#define NUM_CHANNELS 2 +#define SAMPLE_CHAN_SIZE (SAMPLE_SIZE * NUM_CHANNELS) + +STRUCT(Doc_reg) { + double dsamp_ev; + double dsamp_ev2; + double complete_dsamp; + int samps_left; + word32 cur_acc; + word32 cur_inc; + word32 cur_start; + word32 cur_end; + word32 cur_mask; + int size_bytes; + int event; + int running; + int has_irq_pending; + word32 freq; + word32 vol; + word32 waveptr; + word32 ctl; + word32 wavesize; + word32 last_samp_val; +}; + +/* prototypes for win32snd_driver.c functions */ +void win32snd_init(word32 *); +void win32snd_shutdown(); +void win32snd_shutdown(); +void child_sound_init_win32(); +int win32_send_audio(byte *ptr, int size); + + +/* Prototypes for macsnd_driver.c functions */ +int mac_send_audio(byte *ptr, int in_size); +void child_sound_init_mac(); +void macsnd_init(word32 *shmaddr); + diff --git a/src/sound_driver.c b/src/sound_driver.c new file mode 100644 index 0000000..30f9b61 --- /dev/null +++ b/src/sound_driver.c @@ -0,0 +1,469 @@ +/************************************************************************/ +/* KEGS: Apple //gs Emulator */ +/* Copyright 2002 by Kent Dickey */ +/* */ +/* This code is covered by the GNU GPL */ +/* */ +/* The KEGS web page is kegs.sourceforge.net */ +/* You may contact the author at: kadickey@alumni.princeton.edu */ +/************************************************************************/ + +const char rcsid_sound_driver_c[] = "@(#)$KmKId: sound_driver.c,v 1.16 2004-03-22 19:08:08-05 kentd Exp $"; + +#include "defc.h" +#include "sound.h" + +#ifdef HPUX +# include +#endif + +#ifdef SOLARIS +# include +#endif + +#if defined(__linux__) || defined(OSS) +# include +#endif + +#ifndef _WIN32 +# include +# include +#endif +#include + + +extern int Verbose; +extern int g_use_alib; + +extern int g_audio_rate; + +int g_preferred_rate = 48000; +int g_audio_socket = -1; +int g_bytes_written = 0; + +#define ZERO_BUF_SIZE 2048 + +word32 g_snd_zero_buf[ZERO_BUF_SIZE]; + +#define ZERO_PAUSE_SAFETY_SAMPS (g_audio_rate >> 5) +#define ZERO_PAUSE_NUM_SAMPS (4*g_audio_rate) + +int g_zeroes_buffered = 0; +int g_zeroes_seen = 0; +int g_sound_paused = 0; +int g_childsnd_vbl = 0; +int g_childsnd_pos = 0; +word32 *g_childsnd_shm_addr = 0; + +void child_sound_init_linux(); +void child_sound_init_hpdev(); +void child_sound_init_solaris(); +void child_sound_init_win32(); +void child_sound_init_mac(); + +void +reliable_buf_write(word32 *shm_addr, int pos, int size) +{ + byte *ptr; + int ret; + + if(size < 1 || pos < 0 || pos > SOUND_SHM_SAMP_SIZE || + size > SOUND_SHM_SAMP_SIZE || + (pos + size) > SOUND_SHM_SAMP_SIZE) { + printf("reliable_buf_write: pos: %04x, size: %04x\n", + pos, size); + exit(1); + } + + ptr = (byte *)&(shm_addr[pos]); + size = size * 4; + + while(size > 0) { +#ifdef _WIN32 + ret = win32_send_audio(ptr, size); +#else +# ifdef MAC + ret = mac_send_audio(ptr, size); +# else + ret = write(g_audio_socket, ptr, size); +# endif +#endif + + if(ret < 0) { + printf("audio write, errno: %d\n", errno); + exit(1); + } + size = size - ret; + ptr += ret; + g_bytes_written += ret; + } + +} + +void +reliable_zero_write(int amt) +{ + int len; + + while(amt > 0) { + len = MIN(amt, ZERO_BUF_SIZE); + reliable_buf_write(g_snd_zero_buf, 0, len); + amt -= len; + } +} + + +void +child_sound_loop(int read_fd, int write_fd, word32 *shm_addr) +{ +#ifdef HPUX + long status_return; +#endif + word32 tmp; + int ret; + + doc_printf("Child pipe fd: %d\n", read_fd); + + g_audio_rate = g_preferred_rate; + + g_zeroes_buffered = 0; + g_zeroes_seen = 0; + g_sound_paused = 0; + + g_childsnd_pos = 0; + g_childsnd_vbl = 0; + g_childsnd_shm_addr = shm_addr; + +#ifdef HPUX + child_sound_init_hpdev(); +#endif +#ifdef SOLARIS + child_sound_init_solaris(); +#endif +#if defined(__linux__) || defined(OSS) + child_sound_init_linux(); +#endif +#ifdef _WIN32 + child_sound_init_win32(); + return; +#endif +#ifdef MAC + child_sound_init_mac(); + return; +#endif + + tmp = g_audio_rate; + ret = write(write_fd, &tmp, 4); + if(ret != 4) { + printf("Unable to send back audio rate to parent\n"); + printf("ret: %d fd: %d, errno: %d\n", ret, write_fd, errno); + exit(1); + } + printf("Wrote to fd %d the audio rate\n", write_fd); + + close(write_fd); + + while(1) { + errno = 0; + ret = read(read_fd, &tmp, 4); + if(ret <= 0) { + printf("child dying from ret: %d, errno: %d\n", + ret, errno); + break; + } + + child_sound_playit(tmp); + } + +#ifdef HPUX + ioctl(g_audio_socket, AUDIO_DRAIN, 0); +#endif + close(g_audio_socket); + + exit(0); +} + +void +child_sound_playit(word32 tmp) +{ + int size; + + size = tmp & 0xffffff; + + //printf("child_sound_playit: %08x\n", tmp); + + if((tmp >> 24) == 0xa2) { + /* play sound here */ + + +#if 0 + g_childsnd_pos += g_zeroes_buffered; + while(g_childsnd_pos >= SOUND_SHM_SAMP_SIZE) { + g_childsnd_pos -= SOUND_SHM_SAMP_SIZE; + } +#endif + + if(g_zeroes_buffered) { + reliable_zero_write(g_zeroes_buffered); + } + + g_zeroes_buffered = 0; + g_zeroes_seen = 0; + + if((size + g_childsnd_pos) > SOUND_SHM_SAMP_SIZE) { + reliable_buf_write(g_childsnd_shm_addr, g_childsnd_pos, + SOUND_SHM_SAMP_SIZE - g_childsnd_pos); + size = (g_childsnd_pos + size) - SOUND_SHM_SAMP_SIZE; + g_childsnd_pos = 0; + } + + reliable_buf_write(g_childsnd_shm_addr, g_childsnd_pos, size); + + if(g_sound_paused) { + printf("Unpausing sound, zb: %d\n", g_zeroes_buffered); + g_sound_paused = 0; + } + + } else if((tmp >> 24) == 0xa1) { + if(g_sound_paused) { + if(g_zeroes_buffered < ZERO_PAUSE_SAFETY_SAMPS) { + g_zeroes_buffered += size; + } + } else { + /* not paused, send it through */ + g_zeroes_seen += size; + + reliable_zero_write(size); + + if(g_zeroes_seen >= ZERO_PAUSE_NUM_SAMPS) { + printf("Pausing sound\n"); + g_sound_paused = 1; + } + } + } else { + printf("tmp received bad: %08x\n", tmp); + exit(3); + } + + g_childsnd_pos += size; + while(g_childsnd_pos >= SOUND_SHM_SAMP_SIZE) { + g_childsnd_pos -= SOUND_SHM_SAMP_SIZE; + } + + g_childsnd_vbl++; + if(g_childsnd_vbl >= 60) { + g_childsnd_vbl = 0; +#if 0 + printf("sound bytes written: %06x\n", g_bytes_written); + printf("Sample samples[0]: %08x %08x %08x %08x\n", + g_childsnd_shm_addr[0], g_childsnd_shm_addr[1], + g_childsnd_shm_addr[2], g_childsnd_shm_addr[3]); + printf("Sample samples[100]: %08x %08x %08x %08x\n", + g_childsnd_shm_addr[100], g_childsnd_shm_addr[101], + g_childsnd_shm_addr[102], g_childsnd_shm_addr[103]); +#endif + g_bytes_written = 0; + } +} + + +#ifdef HPUX +void +child_sound_init_hpdev() +{ + struct audio_describe audio_descr; + int output_channel; + char *str; + int speaker; + int ret; + int i; + + g_audio_socket = open("/dev/audio", O_WRONLY, 0); + if(g_audio_socket < 0) { + printf("open /dev/audio failed, ret: %d, errno:%d\n", + g_audio_socket, errno); + exit(1); + } + + ret = ioctl(g_audio_socket, AUDIO_DESCRIBE, &audio_descr); + if(ret < 0) { + printf("ioctl AUDIO_DESCRIBE failed, ret:%d, errno:%d\n", + ret, errno); + exit(1); + } + + for(i = 0; i < audio_descr.nrates; i++) { + printf("Audio rate[%d] = %d\n", i, + audio_descr.sample_rate[i]); + } + + ret = ioctl(g_audio_socket, AUDIO_SET_DATA_FORMAT, + AUDIO_FORMAT_LINEAR16BIT); + if(ret < 0) { + printf("ioctl AUDIO_SET_DATA_FORMAT failed, ret:%d, errno:%d\n", + ret, errno); + exit(1); + } + + ret = ioctl(g_audio_socket, AUDIO_SET_CHANNELS, NUM_CHANNELS); + if(ret < 0) { + printf("ioctl AUDIO_SET_CHANNELS failed, ret:%d, errno:%d\n", + ret, errno); + exit(1); + } + + ret = ioctl(g_audio_socket, AUDIO_SET_TXBUFSIZE, 16*1024); + if(ret < 0) { + printf("ioctl AUDIO_SET_TXBUFSIZE failed, ret:%d, errno:%d\n", + ret, errno); + exit(1); + } + + ret = ioctl(g_audio_socket, AUDIO_SET_SAMPLE_RATE, g_audio_rate); + if(ret < 0) { + printf("ioctl AUDIO_SET_SAMPLE_RATE failed, ret:%d, errno:%d\n", + ret, errno); + exit(1); + } + + ret = ioctl(g_audio_socket, AUDIO_GET_OUTPUT, &output_channel); + if(ret < 0) { + printf("ioctl AUDIO_GET_OUTPUT failed, ret:%d, errno:%d\n", + ret, errno); + exit(1); + } + + + speaker = 1; + str = getenv("SPEAKER"); + if(str) { + if(str[0] != 'i' && str[0] != 'I') { + speaker = 0; + } + } + + if(speaker) { + printf("Sending sound to internal speaker\n"); + output_channel |= AUDIO_OUT_SPEAKER; + } else { + printf("Sending sound to external jack\n"); + output_channel &= (~AUDIO_OUT_SPEAKER); + output_channel |= AUDIO_OUT_HEADPHONE; + } + + ret = ioctl(g_audio_socket, AUDIO_SET_OUTPUT, output_channel); + if(ret < 0) { + printf("ioctl AUDIO_SET_OUTPUT failed, ret:%d, errno:%d\n", + ret, errno); + exit(1); + } +} +#endif /* HPUX */ + +#ifdef SOLARIS +void +child_sound_init_solaris() +{ + struct audio_info audioi; + int ret; + + g_audio_socket = open("/dev/audio", O_WRONLY, 0); + if(g_audio_socket < 0) { + printf("open /dev/audio failed, ret: %d, errno:%d\n", + g_audio_socket, errno); + exit(1); + } + + ret = ioctl(g_audio_socket, AUDIO_GETINFO, &audioi); + if(ret < 0) { + printf("ioctl audio getinfo ret: %d, errno:%d\n", ret, errno); + exit(1); + } + audioi.play.sample_rate = g_preferred_rate; + audioi.play.encoding = AUDIO_ENCODING_LINEAR; + audioi.play.precision = 16; + audioi.play.channels = 2; + ret = ioctl(g_audio_socket, AUDIO_SETINFO, &audioi); + if(ret < 0) { + printf("ioctl audio setinfo ret: %d, errno:%d\n", ret, errno); + exit(1); + } +} +#endif /* SOLARIS */ + +#if defined(__linux__) || defined(OSS) +void +child_sound_init_linux() +{ + int stereo; + int sample_size; + int rate; + int fragment; + int fmt; + int ret; + + g_audio_socket = open("/dev/dsp", O_WRONLY, 0); + if(g_audio_socket < 0) { + printf("open /dev/dsp failed, ret: %d, errno:%d\n", + g_audio_socket, errno); + exit(1); + } + + fragment = 0x00200009; +#if 0 + ret = ioctl(g_audio_socket, SNDCTL_DSP_SETFRAGMENT, &fragment); + if(ret < 0) { + printf("ioctl SETFRAGEMNT failed, ret:%d, errno:%d\n", + ret, errno); + exit(1); + } +#endif + + sample_size = 16; + ret = ioctl(g_audio_socket, SNDCTL_DSP_SAMPLESIZE, &sample_size); + if(ret < 0) { + printf("ioctl SNDCTL_DSP_SAMPLESIZE failed, ret:%d, errno:%d\n", + ret, errno); + exit(1); + } + +#ifdef KEGS_LITTLE_ENDIAN + fmt = AFMT_S16_LE; +#else + fmt = AFMT_S16_BE; +#endif + ret = ioctl(g_audio_socket, SNDCTL_DSP_SETFMT, &fmt); + if(ret < 0) { + printf("ioctl SNDCTL_DSP_SETFMT failed, ret:%d, errno:%d\n", + ret, errno); + exit(1); + } + + stereo = 1; + ret = ioctl(g_audio_socket, SNDCTL_DSP_STEREO, &stereo); + if(ret < 0) { + printf("ioctl SNDCTL_DSP_STEREO failed, ret:%d, errno:%d\n", + ret, errno); + exit(1); + } + + rate = g_audio_rate; + ret = ioctl(g_audio_socket, SNDCTL_DSP_SPEED, &rate); + if(ret < 0) { + printf("ioctl SNDCTL_DSP_SPEED failed, ret:%d, errno:%d\n", + ret, errno); + exit(1); + } + if(ret > 0) { + rate = ret; /* rate is returned value */ + } + if(rate < 8000) { + printf("Audio rate of %d which is < 8000!\n", rate); + exit(1); + } + + g_audio_rate = rate; + + printf("Sound initialized\n"); +} +#endif diff --git a/src/superhires.h b/src/superhires.h new file mode 100644 index 0000000..744010b --- /dev/null +++ b/src/superhires.h @@ -0,0 +1,204 @@ + +/* This file is included by video.c */ + +#ifndef SUPERHIRES_INCLUDED +const char rcsid_superhires_h[] = "@(#)$KmKId: superhires.h,v 1.9 2003-10-29 02:02:59-05 kentd Exp $"; +# define SUPERHIRES_INCLUDED +#endif + +void +SUPER_TYPE(byte *screen_data, int pixels_per_line, int y, int scan, + word32 ch_mask, int use_a2vid_palette, int mode_640) +{ + word32 *palptr; + word32 mem_ptr; + byte val0; + int x1, x2; + byte *b_ptr; + word32 *img_ptr, *img_ptr2; + word32 tmp, tmp2; + word32 ch_tmp; + byte *slow_mem_ptr; + int shift_per; + word32 pal; + word32 pal_word; + word32 pix0, pix1, pix2, pix3; + word32 save_pix; + int offset, next_line_offset; + int dopr; + + mem_ptr = 0xa0 * y + 0x12000; + tmp2 = 0; + tmp = 0; + + shift_per = (1 << SHIFT_PER_CHANGE); + if(use_a2vid_palette) { + pal = (g_a2vid_palette & 0xf); + } else { + pal = (scan & 0xf); + } + + if(SUPER_FILL) { + ch_mask = -1; + save_pix = 0; + } + + if(use_a2vid_palette) { + palptr = &(g_a2vid_palette_remap[0]); + } else { + palptr = &(g_palette_8to1624[pal * 16]); + } + + dopr = 0; +#if 0 + if(y == 1) { + dopr = 1; + printf("superhires line %d has ch_mask: %08x\n", y, ch_mask); + } +#endif + for(x1 = 0; x1 < 0xa0; x1 += shift_per) { + + CH_LOOP_A2_VID(ch_mask, ch_tmp); + + pal_word = (pal << 28) + (pal << 20) + (pal << 12) + + (pal << 4); + + if(mode_640 && !use_a2vid_palette) { +#ifdef KEGS_LITTLE_ENDIAN + pal_word += 0x04000c08; +#else + pal_word += 0x080c0004; +#endif + } + + + slow_mem_ptr = &(g_slow_memory_ptr[mem_ptr + x1]); + offset = y*2*pixels_per_line + x1*4; + next_line_offset = pixels_per_line; +#if SUPER_PIXEL_SIZE == 16 + offset *= 2; + next_line_offset *= 2; +#elif SUPER_PIXEL_SIZE == 32 + offset *= 4; + next_line_offset *= 4; +#endif + b_ptr = &screen_data[offset]; + img_ptr = (word32 *)b_ptr; + img_ptr2 = (word32 *)(b_ptr + next_line_offset); + + if(mode_640) { + for(x2 = 0; x2 < shift_per; x2++) { + val0 = *slow_mem_ptr++; + + pix0 = (val0 >> 6) & 0x3; + pix1 = (val0 >> 4) & 0x3; + pix2 = (val0 >> 2) & 0x3; + pix3 = val0 & 0x3; + if(use_a2vid_palette || (SUPER_PIXEL_SIZE > 8)){ + pix0 = palptr[pix0+8]; + pix1 = palptr[pix1+12]; + pix2 = palptr[pix2+0]; + pix3 = palptr[pix3+4]; + } +#if SUPER_PIXEL_SIZE == 8 +# ifdef KEGS_LITTLE_ENDIAN + tmp = (pix3 << 24) + (pix2 << 16) + + (pix1 << 8) + pix0 + pal_word; +# else + tmp = (pix0 << 24) + (pix1 << 16) + + (pix2 << 8) + pix3 + pal_word; +# endif + *img_ptr++ = tmp; *img_ptr2++ = tmp; +#elif SUPER_PIXEL_SIZE == 16 +# ifdef KEGS_LITTLE_ENDIAN + tmp = (pix1 << 16) + pix0; + tmp2 = (pix3 << 16) + pix2; +# else + tmp = (pix0 << 16) + pix1; + tmp2 = (pix2 << 16) + pix3; +# endif + *img_ptr++ = tmp; + *img_ptr++ = tmp2; + *img_ptr2++ = tmp; + *img_ptr2++ = tmp2; +#else /* SUPER_PIXEL_SIZE == 32 */ + *img_ptr++ = pix0; + *img_ptr++ = pix1; + *img_ptr++ = pix2; + *img_ptr++ = pix3; + *img_ptr2++ = pix0; + *img_ptr2++ = pix1; + *img_ptr2++ = pix2; + *img_ptr2++ = pix3; +#endif + +#if 0 + if(y == 1 && x1 == 0 && x2 == 0) { + printf("y==1,x1,x2=0, %02x = %08x %08x " + "%08x %08x, pal: %04x\n", val0, + pix0, pix1, pix2, pix3, pal); + printf("offset: %04x, nlo:%04x, ppl:" + "%04x, %d\n", offset, + next_line_offset, + pixels_per_line, SUPER_PIXEL_SIZE); + } +#endif + } + + } else { /* 320 mode */ + for(x2 = 0; x2 < shift_per; x2++) { + val0 = *slow_mem_ptr++; + pix0 = (val0 >> 4); + if(SUPER_FILL) { + if(pix0) { + save_pix = pix0; + } else { + pix0 = save_pix; + } + } + pix1 = (val0 & 0xf); + if(SUPER_FILL) { + if(pix1) { + save_pix = pix1; + } else { + pix1 = save_pix; + } + } + if(use_a2vid_palette || (SUPER_PIXEL_SIZE > 8)){ + pix0 = palptr[pix0]; + pix1 = palptr[pix1]; + } + if(dopr && x1 == 0) { + printf("y:%d, x2:%d, val:%02x = %08x %08x\n", y, x2, val0, pix0, pix1); + } +#if SUPER_PIXEL_SIZE == 8 +# ifdef KEGS_LITTLE_ENDIAN + tmp = (pix1 << 24) + (pix1 << 16) + + (pix0 << 8) + pix0 + pal_word; +# else + tmp = (pix0 << 24) + (pix0 << 16) + + (pix1 << 8) + pix1 + pal_word; +# endif + *img_ptr++ = tmp; *img_ptr2++ = tmp; +#elif SUPER_PIXEL_SIZE == 16 + tmp = (pix0 << 16) + pix0; + tmp2 = (pix1 << 16) + pix1; + *img_ptr++ = tmp; + *img_ptr++ = tmp2; + *img_ptr2++ = tmp; + *img_ptr2++ = tmp2; +#else /* SUPER_PIXEL_SIZE == 32 */ + *img_ptr++ = pix0; + *img_ptr++ = pix0; + *img_ptr++ = pix1; + *img_ptr++ = pix1; + *img_ptr2++ = pix0; + *img_ptr2++ = pix0; + *img_ptr2++ = pix1; + *img_ptr2++ = pix1; +#endif + } + } + } +} + diff --git a/src/to_pro.c b/src/to_pro.c new file mode 100755 index 0000000..722c333 --- /dev/null +++ b/src/to_pro.c @@ -0,0 +1,897 @@ +/****************************************************************/ +/* Apple //gs emulator */ +/* Copyright 1996 Kent Dickey */ +/* */ +/* This code may not be used in a commercial product */ +/* without prior written permission of the author. */ +/* */ +/* You may freely distribute this code. */ +/* */ +/* You can contact the author at kentd@cup.hp.com. */ +/* HP has nothing to do with this software. */ +/****************************************************************/ + +#include "defc.h" +#include + +#include "prodos.h" + +#include + + +#define DEF_DISK_SIZE (800*1024) +#define MAX_FILE_NAMES 51 + +char out_name[] = "POOF1"; + +int g_def_file_type = -1; +int g_def_aux_type = -1; + +void make_legal_prodos_name(char *new_name, char *old_name); + +int +main(int argc, char **argv) +{ + char *files[MAX_FILE_NAMES]; + char *name; + char *new_name_end; + int new_name_len; + struct stat stat_buf; + int in; + int in_size; + int ret; + ProDisk *disk; + char new_name[128]; + byte in_buf[1024]; + int disk_size; + int i; + int done; + int pos; + int size; + int num_files; + int file_type; + int aux_type; + + disk_size = DEF_DISK_SIZE; + if(argc < 2) { + fprintf(stderr, "%s: {-[size in K]} {unix_files}\n", + argv[0]); + exit(1); + } + + num_files = 0; + for(i = 1; i < argc; i++) { + if(argv[i][0] == '-') { + /* I smell a -size_in_K */ + disk_size = strtoul(&(argv[i][1]), 0, 10) * 1024; + printf("disk_size: %d, 0x%x\n", disk_size, disk_size); + if(disk_size < 40*1024) { + printf("Too small!\n"); + exit(1); + } + } else { + files[num_files] = argv[i]; + num_files++; + if(num_files >= MAX_FILE_NAMES) { + printf("Too many filenames: %d\n", num_files); + exit(2); + } + } + } + + disk = allocate_memdisk(out_name, disk_size); + format_memdisk(disk, out_name); + + for(i = 0; i < num_files; i++) { + name = files[i]; + in = open(name, O_RDONLY | O_BINARY); + if(in < 0) { + fprintf(stderr, "opening %s returned %d, errno: %d\n", + name, in, errno); + exit(1); + } + + ret = fstat(in, &stat_buf); + if(ret != 0) { + fprintf(stderr, "fstat returned %d, errno: %d\n", + ret, errno); + } + + in_size = stat_buf.st_size; + printf("in size: %d\n", in_size); + + if(in_size > disk->disk_bytes_left) { + printf("File bigger than %d, too big!\n", disk_size); + exit(2); + } + + make_legal_prodos_name(new_name, name); + + new_name_len = strlen(new_name); + new_name_end = new_name + new_name_len; + + file_type = g_def_file_type; + aux_type = g_def_aux_type; + while(g_def_file_type < 0) { + /* choose file type */ + if(new_name_len >= 5) { + if(strcmp(new_name_end - 4, ".SHK") == 0) { + file_type = 0xe0; + aux_type = 0x8002; + break; + } + if(strcmp(new_name_end - 4, ".SDK") == 0) { + file_type = 0xe0; + aux_type = 0x8002; + break; + } + } + file_type = 0x04; /* TXT */ + aux_type = 0; + break; + } + + create_new_file(disk, 2, 1, new_name, file_type, + 0, 0, 0, 0xc3, aux_type, 0, in_size); + + + done = 0; + pos = 0; + while(pos < in_size) { + size = 512; + if(pos + size > in_size) { + size = in_size - pos; + } + ret = read(in, in_buf, size); + if(ret != size || ret <= 0) { + fprintf(stderr, "read returned %d, errno: %d\n", + ret, errno); + exit(2); + } + ret = pro_write_file(disk, in_buf, pos, size); + if(ret != 0) { + printf("pro_write returned %d!\n", ret); + exit(3); + } + pos += size; + } + + close_file(disk); + + close(in); + } + + flush_disk(disk); + return 0; +} + +void +make_legal_prodos_name(char *new_name, char *old_name) +{ + char *ptr; + int start_len, start_char; + int len; + int pos; + int c; + int j; + + for(j = 0; j < 16; j++) { + /* make sure it ends with null == 15 + 1 */ + new_name[j] = 0; + } + + start_char = 0; + start_len = strlen(old_name); + len = 0; + ptr = &old_name[start_len - 1]; + for(j = start_len - 1; j >= 0; j--) { + if(*ptr == '/' || *ptr == ':') { + break; + } + ptr--; + len++; + } + ptr++; + + if(len <= 0) { + printf("Filename: %s has len: %d!\n", old_name, len); + exit(1); + } + + printf("mid_name: %s, len:%d\n", ptr, len); + + pos = 0; + for(j = 0; j < 15; j++) { + c = ptr[pos]; + if(isalnum(c)) { + c = toupper(c); + } else if(c != 0) { + c = '.'; + } + + if(j == 0 && !isalpha(c)) { + c = 'A'; + } + + new_name[j] = c; + + pos++; + if(pos == 7 && len > 15) { + pos = len - 8; + } + } + + printf("new_name: %s\n", new_name); +} + +void +flush_disk(ProDisk *disk) +{ + disk_write_data(disk, 6, disk->bitmap_ptr, + disk->size_bitmap_blocks * 512); + close(disk->fd); + disk->fd = -1; +} + +void +close_file(ProDisk *disk) +{ + write_ind_block(disk); + write_master_ind_block(disk); + disk_write_data(disk, disk->dir_blk_num, &(disk->dir_blk_data[0]), 512); + disk->file_ptr = 0; +} + +ProDisk * +allocate_memdisk(char *out_name, int size) +{ + ProDisk *disk; + int out; + + out = open(out_name, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, 0x1b6); + if(out < 0) { + fprintf(stderr, "opening %s returned %d, errno: %d\n", + out_name, out, errno); + exit(1); + } + + disk = (ProDisk *)malloc(sizeof(ProDisk)); + if(disk == 0) { + printf("allocate_memdisk failed, errno: %d\n", errno); + } + + disk->fd = out; + disk->total_blocks = (size + 511) / 512; + disk->bitmap_ptr = 0; + disk->disk_bytes_left = 0; + disk->size_bitmap_blocks = 0; + disk->dir_blk_num = -1; + disk->ind_blk_num = -1; + disk->master_ind_blk_num = -1; + + return disk; +} + +void +format_memdisk(ProDisk *disk, char *name) +{ + byte zero_buf[1024]; + int total_blocks; + byte *bitmap_ptr; + Vol_hdr *vol_hdr; + Directory *dir; + int size_bitmap_bytes; + int size_bitmap_blocks; + int disk_blocks_left; + int i, j; + + total_blocks = disk->total_blocks; + + /* Zero out blocks 0 and 1 */ + for(i = 0; i < 2*512; i++) { + zero_buf[i] = 0; + } + disk_write_data(disk, 0x00000, zero_buf, 2*512); + + /* and make the image the right size */ + disk_write_data(disk, total_blocks - 1, zero_buf, 512); + + dir = disk_read_dir(disk, 2); + set_l2byte(&(dir->prev_blk), 0); + set_l2byte(&(dir->next_blk), 3); + vol_hdr = (Vol_hdr *)&(dir->file_entries[0]); + vol_hdr->storage_type_name_len = 0xf0 + strlen(name); + strncpy((char *)vol_hdr->vol_name, name, strlen(name)); + vol_hdr->version = 0; + vol_hdr->min_version = 0; + vol_hdr->access = 0xc3; + vol_hdr->entry_length = 0x27; + vol_hdr->entries_per_block = 0x0d; + set_l2byte(&(vol_hdr->file_count), 0); + vol_hdr->entries_per_block = 0x0d; + set_l2byte(&(vol_hdr->bit_map), 6); + set_l2byte(&(vol_hdr->total_blocks), total_blocks); + for(i = 1; i < 13; i++) { + set_file_entry(&(dir->file_entries[i]), 0, "", 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0); + } + disk_write_dir(disk, 2); + + + for(i = 3; i < 6; i++) { + dir = disk_read_dir(disk, i); + set_l2byte(&(dir->prev_blk), i - 1); + set_l2byte(&(dir->next_blk), i + 1); + if(i == 5) { + set_l2byte(&(dir->next_blk), 0); + } + for(j = 0; j < 13; j++) { + set_file_entry(&(dir->file_entries[j]), 0, "", 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0); + } + + disk_write_dir(disk, i); + } + + size_bitmap_bytes = (total_blocks + 7)/ 8; + size_bitmap_blocks = (size_bitmap_bytes + 511)/ 512; + bitmap_ptr = (byte *)malloc(size_bitmap_blocks * 512); + for(i = 0; i < 6+size_bitmap_blocks; i++) { + set_bitmap_used(bitmap_ptr, i); + } + for(i = 6+size_bitmap_blocks; i < total_blocks; i++) { + set_bitmap_free(bitmap_ptr, i); + } + for(i = total_blocks; i < size_bitmap_blocks*512*8; i++) { + set_bitmap_used(bitmap_ptr, i); + } + + disk_write_data(disk, 6, bitmap_ptr, size_bitmap_blocks * 512); + disk->bitmap_ptr = bitmap_ptr; + disk->size_bitmap_blocks = size_bitmap_blocks; + disk->bitmap_bytes = size_bitmap_blocks * 512; + disk->bitmap_cur_pos = 0; + + disk_blocks_left = total_blocks - 6 - size_bitmap_blocks; + disk->disk_bytes_left = disk_blocks_left * 512; +} + +void +disk_write_data(ProDisk *disk, int blk_num, byte *buf, int size) +{ + int size_in_blocks; + int ret; + +#if 0 + printf("Writing blk %04x from buf: %08x, %03x\n", blk_num, buf, size); +#endif + + size_in_blocks = size >> 9; + if(size_in_blocks * 512 != size) { + printf("disk_write: blk: %04x, buf: %08x, size: %08x\n", + blk_num, (word32)buf, size); + exit(1); + } + + ret = lseek(disk->fd, 512*blk_num, SEEK_SET); + if(ret != 512*blk_num) { + printf("disk_write: seek: %d, errno: %d, blk: %04x, buf: " + "%08x, sz: %08x\n", ret, errno, blk_num, + (word32)buf, size); + exit(1); + } + + ret = write(disk->fd, buf, size); + if(ret != size) { + printf("disk_write: write: %d, errno: %d, blk: %04x, buf: " + "%08x, sz: %08x\n", ret, errno, blk_num, + (word32)buf, size); + exit(1); + } +} + +void +disk_read_data(ProDisk *disk, int blk_num, byte *buf, int size) +{ + int size_in_blocks; + int ret; + int i; + + size_in_blocks = size >> 9; + if(size_in_blocks * 512 != size) { + printf("disk_read: blk: %04x, buf: %08x, size: %08x\n", + blk_num, (word32)buf, size); + exit(1); + } + + ret = lseek(disk->fd, 512*blk_num, SEEK_SET); + if(ret != 512*blk_num) { + printf("disk_read: seek: %d, errno: %d, blk: %04x, buf: " + "%08x, sz: %08x\n", ret, errno, blk_num, + (word32)buf, size); + exit(1); + } + + ret = read(disk->fd, buf, size); + if(ret != size) { + printf("disk_read: read: %d, errno: %d, blk: %04x, buf: " + "%08x, sz: %08x\n", ret, errno, blk_num, + (word32)buf, size); + for(i = 0; i < size; i++) { + buf[i] = 0; + } + } +} + +Directory * +disk_read_dir(ProDisk *disk, int blk_num) +{ + disk_write_dir(disk, blk_num); + + disk->dir_blk_num = blk_num; + disk_read_data(disk, blk_num, &(disk->dir_blk_data[0]), 512); + + return (Directory *)&(disk->dir_blk_data[0]); +} + +void +disk_write_dir(ProDisk *disk, int blk_num) +{ + if(disk->dir_blk_num >= 0) { + if(disk->dir_blk_num != blk_num) { + printf("disk_write_dir: %04x != %04x\n", + disk->dir_blk_num, blk_num); + } + disk_write_data(disk, disk->dir_blk_num, + &(disk->dir_blk_data[0]), 512); + disk->dir_blk_num = -1; + } +} + +void +create_new_file(ProDisk *disk, int dir_block, int storage_type, char *name, + int file_type, word32 creation_time, int version, int min_version, + int access, int aux_type, word32 last_mod, word32 eof) +{ + Vol_hdr *vol_ptr; + int val; + Directory *dir_ptr; + File_entry *file_ptr; + int file_count; + int pos; + int last_pos; + int done; + int next_blk; + int name_len; + + name_len = strlen(name); + dir_ptr = disk_read_dir(disk, dir_block); + next_blk = dir_block; + val = dir_ptr->file_entries[0].storage_type_name_len; + last_pos = 13; + pos = 0; + if(((val & 0xf0) == 0xf0) || ((val & 0xf0) == 0xe0)) { + /* vol dir or subdir header */ + vol_ptr = (Vol_hdr *)&(dir_ptr->file_entries[0]); + file_count = get_l2byte(&(vol_ptr->file_count)); + pos = 1; + last_pos = vol_ptr->entries_per_block; + } else { + printf("dir_block: %04x not subdir or voldir\n", dir_block); + exit(6); + } + + vol_ptr = 0; + + done = 0; + while(!done) { + file_ptr = &(dir_ptr->file_entries[pos]); + if(((file_ptr->storage_type_name_len) & 0xf0) == 0) { + /* Got it! */ + file_ptr->storage_type_name_len = + (storage_type << 4) | name_len; + strncpy((char *)file_ptr->file_name, name, 15); + file_ptr->file_type = file_type; + set_l2byte(&(file_ptr->key_pointer), + find_next_free_block(disk)); + set_l2byte(&(file_ptr->blocks_used), 0); + set_pro_time(&(file_ptr->creation_time), + creation_time); + file_ptr->version = version; + file_ptr->min_version = min_version; + file_ptr->access = access; + set_l2byte(&(file_ptr->aux_type), aux_type); + set_pro_time(&(file_ptr->last_mod), last_mod); + set_l2byte(&(file_ptr->header_pointer), + dir_block); + set_l3byte(&(file_ptr->eof), eof); + disk_write_dir(disk, next_blk); + + dir_ptr = disk_read_dir(disk, dir_block); + vol_ptr = (Vol_hdr *)&(dir_ptr->file_entries[0]); + set_l2byte(&(vol_ptr->file_count), file_count+1); + disk_write_dir(disk, dir_block); + + disk_read_dir(disk, next_blk); + /* re-read dir so that ptrs are set up right */ + disk->file_ptr = file_ptr; + disk->file_open = 1; + disk->ind_blk_num = -1; + disk->master_ind_blk_num = -1; + done = 1; + break; + } else { + /* check to make sure name is unique */ + if((file_ptr->storage_type_name_len & 0x0f)== name_len){ + if(!memcmp(file_ptr->file_name, name,name_len)){ + printf("Name %s already on disk!\n", + name); + exit(8); + } + } + pos++; + if(pos >= last_pos) { + /* Go to next block */ + next_blk = get_l2byte(&(dir_ptr->next_blk)); + if(next_blk) { + dir_ptr = disk_read_dir(disk, next_blk); + pos = 0; + } else { + printf("Top directory full!\n"); + exit(2); + } + } + } + } +} + +int +pro_write_file(ProDisk *disk, byte *in_buf, int pos, int size) +{ + int block; + int i; + + block = get_disk_block(disk, pos, 1); + if(block < 7) { + printf("pro_write_file, get_disk_block: %d\n", block); + exit(3); + } + if(size < 512) { + for(i = size; i < 512; i++) { + in_buf[i] = 0; + } + } else if(size > 512) { + printf("error, pro_write_file size: %d too big\n", size); + exit(4); + } + + disk_write_data(disk, block, in_buf, 512); + + return 0; +} + + + +int +get_disk_block(ProDisk *disk, int pos, int create) +{ + File_entry *file_ptr; + int storage_type; + word32 eof; + int lo, hi; + int offset; + int master_ind_block, ind_block; + int ret_block; + int key_block; + + if(pos >= 128*256*512) { + printf("offset too big\n"); + exit(3); + } + + file_ptr = disk->file_ptr; + + eof = get_l3byte(&(file_ptr->eof)); + storage_type = (file_ptr->storage_type_name_len) >> 4; + + key_block = get_l2byte(&(file_ptr->key_pointer)); + + if(storage_type == 1 && pos >= 512) { + /* make it sapling */ + get_new_ind_block(disk); + inc_l2byte(&(file_ptr->blocks_used)); + disk->ind_blk_data[0] = key_block & 0xff; + disk->ind_blk_data[0x100] = key_block >> 8; + key_block = disk->ind_blk_num; + set_l2byte(&(file_ptr->key_pointer), key_block); + file_ptr->storage_type_name_len += 0x10; + storage_type++; + } + if(storage_type == 2 && pos >= 256*512) { + /* make it tree */ + get_new_master_ind_block(disk); + inc_l2byte(&(file_ptr->blocks_used)); + disk->master_ind_blk_data[0] = key_block & 0xff; + disk->master_ind_blk_data[0x100] = key_block >> 8; + key_block = disk->master_ind_blk_num; + set_l2byte(&(file_ptr->key_pointer), key_block); + file_ptr->storage_type_name_len += 0x10; + storage_type++; + } + + switch(storage_type) { + case 1: + if(pos >= 512) { + printf("Error1!\n"); + exit(3); + } + ret_block = key_block; + if(ret_block == 0) { + ret_block = find_next_free_block(disk); + inc_l2byte(&(file_ptr->blocks_used)); + set_l2byte(&(file_ptr->key_pointer), ret_block); + } + return ret_block; + case 2: + ind_block = key_block; + if(ind_block <= 0) { + printf("write failure, ind_block: %d!\n", ind_block); + exit(3); + } + offset = pos >> 9; + if(offset >= 256) { + printf("pos too big!\n"); + exit(3); + } + + lo = disk->ind_blk_data[offset]; + hi = disk->ind_blk_data[offset + 0x100]; + ret_block = hi*256 + lo; + if(ret_block == 0) { + /* Need to alloc a block for this guy */ + ret_block = find_next_free_block(disk); + inc_l2byte(&(file_ptr->blocks_used)); + disk->ind_blk_data[offset] = ret_block & 0xff; + disk->ind_blk_data[offset + 0x100] = + ((ret_block >> 8) & 0xff); + } + return ret_block; + case 3: + /* tree */ + master_ind_block = key_block; + if(master_ind_block <= 0) { + printf("write failure, master_ind_block: %d!\n", + master_ind_block); + exit(3); + } + offset = pos >> 17; + if(offset >= 128) { + printf("master too big!\n"); + exit(4); + } + lo = disk->master_ind_blk_data[offset]; + hi = disk->master_ind_blk_data[offset + 0x100]; + ind_block = hi*256 + lo; + if(ind_block == 0) { + /* Need to alloc an ind block */ + get_new_ind_block(disk); + ind_block = disk->ind_blk_num; + inc_l2byte(&(file_ptr->blocks_used)); + disk->master_ind_blk_data[offset] = ind_block & 0xff; + disk->master_ind_blk_data[offset + 0x100] = + ((ind_block >> 8) & 0xff); + } + + offset = (pos >> 9) & 0xff; + lo = disk->ind_blk_data[offset]; + hi = disk->ind_blk_data[offset + 0x100]; + ret_block = hi*256 + lo; + + if(ret_block == 0) { + /* Need to alloc a block for this guy */ + ret_block = find_next_free_block(disk); + inc_l2byte(&(file_ptr->blocks_used)); + disk->ind_blk_data[offset] = ret_block & 0xff; + disk->ind_blk_data[offset + 0x100] = + ((ret_block >> 8) & 0xff); + } + return ret_block; + default: + printf("unknown storage type: %d\n", storage_type); + exit(4); + } + + printf("Can't get here!\n"); + exit(5); +} + +void +get_new_ind_block(ProDisk *disk) +{ + int ind_blk_num; + int i; + + write_ind_block(disk); + + ind_blk_num = find_next_free_block(disk); + for(i = 0; i < 512; i++) { + disk->ind_blk_data[i] = 0; + } + + disk->ind_blk_num = ind_blk_num; +} + +void +write_ind_block(ProDisk *disk) +{ + int ind_blk_num; + + ind_blk_num = disk->ind_blk_num; + if(ind_blk_num > 0) { + printf("Write ind block: %04x\n", ind_blk_num); + disk_write_data(disk, ind_blk_num, &(disk->ind_blk_data[0]), + 512); + disk->ind_blk_num = -1; + } +} + +void +get_new_master_ind_block(ProDisk *disk) +{ + int master_ind_blk_num; + int i; + + write_master_ind_block(disk); + + master_ind_blk_num = find_next_free_block(disk); + for(i = 0; i < 512; i++) { + disk->master_ind_blk_data[i] = 0; + } + + disk->master_ind_blk_num = master_ind_blk_num; +} + +void +write_master_ind_block(ProDisk *disk) +{ + int master_ind_blk_num; + + master_ind_blk_num = disk->master_ind_blk_num; + if(master_ind_blk_num > 0) { + printf("Write master_ind block: %04x\n", master_ind_blk_num); + disk_write_data(disk, master_ind_blk_num, + &(disk->master_ind_blk_data[0]), 512); + disk->master_ind_blk_num = -1; + } +} + +int +find_next_free_block(ProDisk *disk) +{ + byte *bitmap_ptr; + int pos; + int bitmap_bytes; + int i, j; + word32 val; + + bitmap_ptr = disk->bitmap_ptr; + bitmap_bytes = disk->bitmap_bytes; + pos = disk->bitmap_cur_pos; + + for(i = pos; i < bitmap_bytes; i++) { + val = bitmap_ptr[i]; + if(val == 0) { + continue; + } + for(j = 0; j < 8; j++) { + if(val & (0x80 >> j)) { + set_bitmap_used(bitmap_ptr, 8*i+j); + disk->bitmap_cur_pos = i; + return 8*i + j; + } + } + return -1; + } + return -1; +} + + + +void +set_bitmap_used(byte *ptr, int i) +{ + word32 offset, bit; + word32 val; + + offset = i >> 3; + bit = i & 7; + + val = ~(0x80 >> bit); + ptr[offset] &= val; +} + +void +set_bitmap_free(byte *ptr, int i) +{ + int offset, bit; + int val; + + offset = i >> 3; + bit = i & 7; + + val = (0x80 >> bit); + ptr[offset] |= val; +} + +void +set_file_entry(File_entry *entry, int storage_type_name_len, char *file_name, + int file_type, int key_pointer, int blocks_used, int eof, + word32 creation_time, int version, int min_version, int access, + int aux_type, word32 last_mod, int header_pointer) +{ + + entry->storage_type_name_len = storage_type_name_len; + strncpy((char *)entry->file_name, file_name, 15); + entry->file_type = file_type; + set_l2byte(&(entry->key_pointer), key_pointer); + set_l2byte(&(entry->blocks_used), blocks_used); + set_l3byte(&(entry->eof), eof); + set_pro_time(&(entry->creation_time), creation_time); + entry->version = version; + entry->min_version = min_version; + entry->access = access; + set_l2byte(&(entry->aux_type), aux_type); + set_pro_time(&(entry->last_mod), last_mod); + set_l2byte(&(entry->aux_type), header_pointer); + +} + +void +set_l2byte(L2byte *ptr, int val) +{ + ptr->low = (val & 0xff); + ptr->hi = ((val >> 8) & 0xff); + +} + +void +set_l3byte(L3byte *ptr, int val) +{ + ptr->low = (val & 0xff); + ptr->hi = ((val >> 8) & 0xff); + ptr->higher = ((val >> 16) & 0xff); + +} + +void +set_pro_time(Pro_time *ptr, word32 val) +{ + ptr->times[0] = ((val >> 24) & 0xff); + ptr->times[1] = ((val >> 16) & 0xff); + ptr->times[2] = ((val >> 8) & 0xff); + ptr->times[3] = ((val) & 0xff); +} + +int +get_l2byte(L2byte *ptr) +{ + int val; + + val = ((ptr->hi) * 256) + ptr->low; + return val; +} + +int +get_l3byte(L3byte *ptr) +{ + int val; + + val = ((ptr->higher) * 65536) + ((ptr->hi) * 256) + ptr->low; + return val; +} + +void +inc_l2byte(L2byte *ptr) +{ + set_l2byte(ptr, get_l2byte(ptr) + 1); +} diff --git a/src/vars b/src/vars new file mode 120000 index 0000000..aa40ff6 --- /dev/null +++ b/src/vars @@ -0,0 +1 @@ +vars_mac \ No newline at end of file diff --git a/src/vars_c b/src/vars_c new file mode 100644 index 0000000..a896f13 --- /dev/null +++ b/src/vars_c @@ -0,0 +1,20 @@ + +TARGET = xkegs +OBJECTS = $(OBJECTS1) xdriver.o +CC = gcc +CCOPTS = -O +OPTS = -DNDEBUG +SUFFIX = +NAME = xkegs +LDFLAGS = +LDOPTS = -z +LD = $(CC) +EXTRA_LIBS = -lXext -lX11 -lcl -lc +EXTRA_SPECIALS = Alib.h + +AS = cc -Ae +PERL = perl + +XOPTS = -DHPUX -I/usr/include/X11R5 +XLIBS = -L/usr/lib/X11R5 -L/opt/audio/lib + diff --git a/src/vars_hp b/src/vars_hp new file mode 100644 index 0000000..a1c5d84 --- /dev/null +++ b/src/vars_hp @@ -0,0 +1,20 @@ + +TARGET = xkegs +OBJECTS = engine_s.o $(OBJECTS1) sound_driver.o xdriver.o +CC = cc -Ae +DA1.1 +CCOPTS = -O +OPTS = -DNDEBUG +SUFFIX = +NAME = xkegs +LDFLAGS = +LDOPTS = -z +LD = $(CC) +EXTRA_LIBS = -lXext -lX11 -lcl -lc +EXTRA_SPECIALS = 8inst_s 16inst_s 8size 16size size_s Alib.h + +AS = cc -Ae +PERL = perl + +XOPTS = -DHPUX -I/usr/include/X11R5 +XLIBS = -L/usr/lib/X11R5 -L/opt/audio/lib + diff --git a/src/vars_linuxppc b/src/vars_linuxppc new file mode 100644 index 0000000..996274c --- /dev/null +++ b/src/vars_linuxppc @@ -0,0 +1,20 @@ + +TARGET = xkegs +OBJECTS = $(OBJECTS1) xdriver.o +CC = cc +CCOPTS = -O +OPTS = -DNDEBUG +SUFFIX = +NAME = xkegs +LDFLAGS = +LDOPTS = +LD = $(CC) +EXTRA_LIBS = -lXext -lX11 -lc +EXTRA_SPECIALS = + +AS = cc +PERL = perl + +XOPTS = -I/usr/X11R6/include +XLIBS = -L/usr/X11R6/lib + diff --git a/src/vars_mac b/src/vars_mac new file mode 100644 index 0000000..d59fed2 --- /dev/null +++ b/src/vars_mac @@ -0,0 +1,10 @@ + +TARGET = kegsmac +OBJECTS = $(OBJECTS1) macsnd_driver.o macdriver.o +CCOPTS = -O2 -DMAC +SUFFIX = +NAME = kegsmac + +XOPTS = -Wall -fpascal-strings -mdynamic-no-pic -arch ppc +XLIBS = + diff --git a/src/vars_solaris b/src/vars_solaris new file mode 100644 index 0000000..4b77f20 --- /dev/null +++ b/src/vars_solaris @@ -0,0 +1,20 @@ + +TARGET = xkegs +OBJECTS = $(OBJECTS1) xdriver.o +CC = cc +CCOPTS = -O +OPTS = -DNDEBUG -DSOLARIS +SUFFIX = +NAME = xkegs +LDFLAGS = +LDOPTS = +LD = $(CC) +EXTRA_LIBS = -lXext -lX11 -lsocket -lnsl +EXTRA_SPECIALS = + +AS = cc +PERL = perl + +XOPTS = -I/usr/X/include +XLIBS = -L/usr/X/lib + diff --git a/src/vars_win32 b/src/vars_win32 new file mode 100644 index 0000000..ac61f66 --- /dev/null +++ b/src/vars_win32 @@ -0,0 +1,10 @@ + +TARGET = kegswin.exe +OBJECTS = $(OBJECTS1) win32snd_driver.o windriver.o +CCOPTS = -O2 -DKEGS_LITTLE_ENDIAN +SUFFIX = ".exe" +NAME = kegswin + +XOPTS = -Wall -fomit-frame-pointer -march=pentium +XLIBS = + diff --git a/src/vars_x86linux b/src/vars_x86linux new file mode 100644 index 0000000..c99e999 --- /dev/null +++ b/src/vars_x86linux @@ -0,0 +1,19 @@ + +TARGET = xkegs +OBJECTS = $(OBJECTS1) xdriver.o +CC = cc +CCOPTS = -O2 -Wall -fomit-frame-pointer -march=pentium +OPTS = -DKEGS_LITTLE_ENDIAN +SUFFIX = +NAME = xkegs +LDFLAGS = +LDOPTS = +LD = $(CC) +EXTRA_LIBS = -lXext +EXTRA_SPECIALS = + +AS = cc +PERL = perl + +XOPTS = -I/usr/X11R6/include + diff --git a/src/vars_x86solaris b/src/vars_x86solaris new file mode 100644 index 0000000..69d8671 --- /dev/null +++ b/src/vars_x86solaris @@ -0,0 +1,20 @@ + +TARGET = xkegs +OBJECTS = $(OBJECTS1) xdriver.o +CC = gcc +CCOPTS = -O +OPTS = -DNDEBUG -DSOLARIS -DKEGS_LITTLE_ENDIAN -DOSS +SUFFIX = +NAME = xkegs +LDFLAGS = +LDOPTS = +LD = $(CC) +EXTRA_LIBS = -lXext -lX11 -lsocket -lnsl +EXTRA_SPECIALS = + +AS = cc +PERL = perl + +XOPTS = -I/usr/X/include +XLIBS = -L/usr/X/lib + diff --git a/src/video.c b/src/video.c new file mode 100644 index 0000000..b7a4055 --- /dev/null +++ b/src/video.c @@ -0,0 +1,3354 @@ +/************************************************************************/ +/* KEGS: Apple //gs Emulator */ +/* Copyright 2002 by Kent Dickey */ +/* */ +/* This code is covered by the GNU GPL */ +/* */ +/* The KEGS web page is kegs.sourceforge.net */ +/* You may contact the author at: kadickey@alumni.princeton.edu */ +/************************************************************************/ + +const char rcsid_video_c[] = "@(#)$KmKId: video.c,v 1.125 2004-03-23 17:25:50-05 kentd Exp $"; + +#include + +#include "defc.h" + +extern int Verbose; + +int g_a2_line_stat[200]; +int g_a2_line_left_edge[200]; +int g_a2_line_right_edge[200]; +Kimage *g_a2_line_kimage[200]; + +int g_mode_text[2][200]; +int g_mode_hires[2][200]; +int g_mode_superhires[200]; +int g_mode_border[200]; + +byte g_cur_border_colors[270]; +byte g_new_special_border[64][64]; +byte g_cur_special_border[64][64]; + +word32 g_a2_screen_buffer_changed = (word32)-1; +word32 g_full_refresh_needed = (word32)-1; + +word32 g_cycs_in_40col = 0; +word32 g_cycs_in_xredraw = 0; +word32 g_refresh_bytes_xfer = 0; + +extern byte *g_slow_memory_ptr; +extern int g_screen_depth; +extern int g_screen_mdepth; + +extern int statereg; +extern double g_cur_dcycs; + +extern int g_line_ref_amt; + +extern int g_border_color; +extern int g_config_control_panel; + +typedef byte Change; + +word32 slow_mem_changed[SLOW_MEM_CH_SIZE]; + +word32 g_font40_even_bits[0x100][8][16/4]; +word32 g_font40_odd_bits[0x100][8][16/4]; +word32 g_font80_off0_bits[0x100][8][12/4]; +word32 g_font80_off1_bits[0x100][8][12/4]; +word32 g_font80_off2_bits[0x100][8][12/4]; +word32 g_font80_off3_bits[0x100][8][12/4]; + +word32 g_superhires_scan_save[256]; + +Kimage g_kimage_text[2]; +Kimage g_kimage_hires[2]; +Kimage g_kimage_superhires; +Kimage g_kimage_border_special; +Kimage g_kimage_border_sides; + +Kimage g_mainwin_kimage; + +extern double g_last_vbl_dcycs; + +int g_need_redraw = 1; +int g_palette_change_summary = 0; +word32 g_palette_change_cnt[16]; +int g_border_sides_refresh_needed = 1; +int g_border_special_refresh_needed = 1; +int g_border_line24_refresh_needed = 1; +int g_status_refresh_needed = 1; + +int g_vbl_border_color = 0; +int g_border_last_vbl_changes = 0; + +int g_use_dhr140 = 0; +int g_use_bw_hires = 0; + +int g_a2_new_all_stat[200]; +int g_a2_cur_all_stat[200]; +int g_new_a2_stat_cur_line = 0; +int g_vid_update_last_line = 0; + +int g_expanded_col_0[16]; +int g_expanded_col_1[16]; +int g_expanded_col_2[16]; + + +int g_cur_a2_stat = ALL_STAT_TEXT | ALL_STAT_ANNUNC3 | + (0xf << BIT_ALL_STAT_TEXT_COLOR); + +int g_a2vid_palette = 0xe; +int g_installed_full_superhires_colormap = 0; + +int Max_color_size = 256; + +word32 g_palette_8to1624[256]; +word32 g_a2palette_8to1624[256]; + +word32 g_saved_line_palettes[200][8]; +int g_saved_a2vid_palette = -1; +word32 g_a2vid_palette_remap[16]; + +word32 g_cycs_in_refresh_line = 0; +word32 g_cycs_in_refresh_ximage = 0; + +int g_num_lines_superhires = 0; +int g_num_lines_superhires640 = 0; +int g_num_lines_prev_superhires = 0; +int g_num_lines_prev_superhires640 = 0; + +word32 g_red_mask = 0xff; +word32 g_green_mask = 0xff; +word32 g_blue_mask = 0xff; +int g_red_left_shift = 16; +int g_green_left_shift = 8; +int g_blue_left_shift = 0; +int g_red_right_shift = 0; +int g_green_right_shift = 0; +int g_blue_right_shift = 0; + +char g_status_buf[MAX_STATUS_LINES][STATUS_LINE_LENGTH + 1]; +char *g_status_ptrs[MAX_STATUS_LINES] = { 0 }; + +const int g_dbhires_colors[] = { + /* rgb */ + 0x000, /* 0x0 black */ + 0xd03, /* 0x1 deep red */ + 0x852, /* 0x2 brown */ + 0xf60, /* 0x3 orange */ + 0x070, /* 0x4 dark green */ + 0x555, /* 0x5 dark gray */ + 0x0d0, /* 0x6 green */ + 0xff0, /* 0x7 yellow */ + 0x009, /* 0x8 dark blue */ + 0xd0d, /* 0x9 purple */ + 0xaaa, /* 0xa light gray */ + 0xf98, /* 0xb pink */ + 0x22f, /* 0xc medium blue */ + 0x6af, /* 0xd light blue */ + 0x0f9, /* 0xe aquamarine */ + 0xfff /* 0xf white */ +}; + +word32 g_dhires_convert[4096]; /* look up table of 7 bits (concat): */ + /* { 4 bits, |3 prev bits| } */ + +const byte g_dhires_colors_16[] = { + 0x00, /* 0x0 black */ + 0x02, /* 0x1 dark blue */ + 0x04, /* 0x2 dark green */ + 0x06, /* 0x3 medium blue */ + 0x08, /* 0x4 brown */ + 0x0a, /* 0x5 light gray */ + 0x0c, /* 0x6 green */ + 0x0e, /* 0x7 aquamarine */ + 0x01, /* 0x8 deep red */ + 0x03, /* 0x9 purple */ + 0x05, /* 0xa dark gray */ + 0x07, /* 0xb light blue */ + 0x09, /* 0xc orange */ + 0x0b, /* 0xd pink */ + 0x0d, /* 0xe yellow */ + 0x0f/* 0xf white */ +}; + +const int g_lores_colors[] = { + /* rgb */ + 0x000, /* 0x0 black */ + 0xd03, /* 0x1 deep red */ + 0x009, /* 0x2 dark blue */ + 0xd0d, /* 0x3 purple */ + 0x070, /* 0x4 dark green */ + 0x555, /* 0x5 dark gray */ + 0x22f, /* 0x6 medium blue */ + 0x6af, /* 0x7 light blue */ + 0x852, /* 0x8 brown */ + 0xf60, /* 0x9 orange */ + 0xaaa, /* 0xa light gray */ + 0xf98, /* 0xb pink */ + 0x0d0, /* 0xc green */ + 0xff0, /* 0xd yellow */ + 0x0f9, /* 0xe aquamarine */ + 0xfff /* 0xf white */ +}; + +const word32 g_bw_hires_convert[4] = { + BIGEND(0x00000000), + BIGEND(0x0f0f0000), + BIGEND(0x00000f0f), + BIGEND(0x0f0f0f0f) +}; + +const word32 g_bw_dhires_convert[16] = { + BIGEND(0x00000000), + BIGEND(0x0f000000), + BIGEND(0x000f0000), + BIGEND(0x0f0f0000), + + BIGEND(0x00000f00), + BIGEND(0x0f000f00), + BIGEND(0x000f0f00), + BIGEND(0x0f0f0f00), + + BIGEND(0x0000000f), + BIGEND(0x0f00000f), + BIGEND(0x000f000f), + BIGEND(0x0f0f000f), + + BIGEND(0x00000f0f), + BIGEND(0x0f000f0f), + BIGEND(0x000f0f0f), + BIGEND(0x0f0f0f0f), +}; + +const word32 g_hires_convert[64] = { + BIGEND(0x00000000), /* 00,0000 = black, black, black, black */ + BIGEND(0x00000000), /* 00,0001 = black, black, black, black */ + BIGEND(0x03030000), /* 00,0010 = purp , purp , black, black */ + BIGEND(0x0f0f0000), /* 00,0011 = white, white, black, black */ + BIGEND(0x00000c0c), /* 00,0100 = black, black, green, green */ + BIGEND(0x0c0c0c0c), /* 00,0101 = green, green, green, green */ + BIGEND(0x0f0f0f0f), /* 00,0110 = white, white, white, white */ + BIGEND(0x0f0f0f0f), /* 00,0111 = white, white, white, white */ + BIGEND(0x00000000), /* 00,1000 = black, black, black, black */ + BIGEND(0x00000000), /* 00,1001 = black, black, black, black */ + BIGEND(0x03030303), /* 00,1010 = purp , purp , purp , purp */ + BIGEND(0x0f0f0303), /* 00,1011 = white ,white, purp , purp */ + BIGEND(0x00000f0f), /* 00,1100 = black ,black, white, white */ + BIGEND(0x0c0c0f0f), /* 00,1101 = green ,green, white, white */ + BIGEND(0x0f0f0f0f), /* 00,1110 = white ,white, white, white */ + BIGEND(0x0f0f0f0f), /* 00,1111 = white ,white, white, white */ + + BIGEND(0x00000000), /* 01,0000 = black, black, black, black */ + BIGEND(0x00000000), /* 01,0001 = black, black, black, black */ + BIGEND(0x06060000), /* 01,0010 = blue , blue , black, black */ + BIGEND(0x0f0f0000), /* 01,0011 = white, white, black, black */ + BIGEND(0x00000c0c), /* 01,0100 = black, black, green, green */ + BIGEND(0x09090c0c), /* 01,0101 = orang, orang, green, green */ + BIGEND(0x0f0f0f0f), /* 01,0110 = white, white, white, white */ + BIGEND(0x0f0f0f0f), /* 01,0111 = white, white, white, white */ + BIGEND(0x00000000), /* 01,1000 = black, black, black, black */ + BIGEND(0x00000000), /* 01,1001 = black, black, black, black */ + BIGEND(0x06060303), /* 01,1010 = blue , blue , purp , purp */ + BIGEND(0x0f0f0303), /* 01,1011 = white ,white, purp , purp */ + BIGEND(0x00000f0f), /* 01,1100 = black ,black, white, white */ + BIGEND(0x09090f0f), /* 01,1101 = orang ,orang, white, white */ + BIGEND(0x0f0f0f0f), /* 01,1110 = white ,white, white, white */ + BIGEND(0x0f0f0f0f), /* 01,1111 = white ,white, white, white */ + + BIGEND(0x00000000), /* 10,0000 = black, black, black, black */ + BIGEND(0x00000000), /* 10,0001 = black, black, black, black */ + BIGEND(0x03030000), /* 10,0010 = purp , purp , black, black */ + BIGEND(0x0f0f0000), /* 10,0011 = white, white, black, black */ + BIGEND(0x00000909), /* 10,0100 = black, black, orang, orang */ + BIGEND(0x0c0c0909), /* 10,0101 = green, green, orang, orang */ + BIGEND(0x0f0f0f0f), /* 10,0110 = white, white, white, white */ + BIGEND(0x0f0f0f0f), /* 10,0111 = white, white, white, white */ + BIGEND(0x00000000), /* 10,1000 = black, black, black, black */ + BIGEND(0x00000000), /* 10,1001 = black, black, black, black */ + BIGEND(0x03030606), /* 10,1010 = purp , purp , blue , blue */ + BIGEND(0x0f0f0606), /* 10,1011 = white ,white, blue , blue */ + BIGEND(0x00000f0f), /* 10,1100 = black ,black, white, white */ + BIGEND(0x0c0c0f0f), /* 10,1101 = green ,green, white, white */ + BIGEND(0x0f0f0f0f), /* 10,1110 = white ,white, white, white */ + BIGEND(0x0f0f0f0f), /* 10,1111 = white ,white, white, white */ + + BIGEND(0x00000000), /* 11,0000 = black, black, black, black */ + BIGEND(0x00000000), /* 11,0001 = black, black, black, black */ + BIGEND(0x06060000), /* 11,0010 = blue , blue , black, black */ + BIGEND(0x0f0f0000), /* 11,0011 = white, white, black, black */ + BIGEND(0x00000909), /* 11,0100 = black, black, orang, orang */ + BIGEND(0x09090909), /* 11,0101 = orang, orang, orang, orang */ + BIGEND(0x0f0f0f0f), /* 11,0110 = white, white, white, white */ + BIGEND(0x0f0f0f0f), /* 11,0111 = white, white, white, white */ + BIGEND(0x00000000), /* 11,1000 = black, black, black, black */ + BIGEND(0x00000000), /* 11,1001 = black, black, black, black */ + BIGEND(0x06060606), /* 11,1010 = blue , blue , blue , blue */ + BIGEND(0x0f0f0606), /* 11,1011 = white ,white, blue , blue */ + BIGEND(0x00000f0f), /* 11,1100 = black ,black, white, white */ + BIGEND(0x09090f0f), /* 11,1101 = orang ,orang, white, white */ + BIGEND(0x0f0f0f0f), /* 11,1110 = white ,white, white, white */ + BIGEND(0x0f0f0f0f), /* 11,1111 = white ,white, white, white */ +}; + +const int g_screen_index[] = { + 0x000, 0x080, 0x100, 0x180, 0x200, 0x280, 0x300, 0x380, + 0x028, 0x0a8, 0x128, 0x1a8, 0x228, 0x2a8, 0x328, 0x3a8, + 0x050, 0x0d0, 0x150, 0x1d0, 0x250, 0x2d0, 0x350, 0x3d0 +}; + + +void +video_init() +{ + word32 col[4]; + Kimage *kimage_ptr; + word32 *ptr; + word32 val0, val1, val2, val3; + word32 match_col; + word32 next_col, next2_col, next3_col; + word32 val; + word32 cur_col; + int width, height; + int total_bytes; + int i, j; +/* Initialize video system */ + + for(i = 0; i < 200; i++) { + g_a2_line_kimage[i] = (void *)0; + g_a2_line_stat[i] = -1; + g_a2_line_left_edge[i] = 0; + g_a2_line_right_edge[i] = 0; + } + for(i = 0; i < 200; i++) { + g_a2_new_all_stat[i] = 0; + g_a2_cur_all_stat[i] = 1; + for(j = 0; j < 8; j++) { + g_saved_line_palettes[i][j] = (word32)-1; + } + } + for(i = 0; i < 262; i++) { + g_cur_border_colors[i] = -1; + } + + g_new_a2_stat_cur_line = 0; + + dev_video_init(); + + read_a2_font(); + + vid_printf("Zeroing out video memory\n"); + + for(i = 0; i < 7; i++) { + switch(i) { + case 0: + kimage_ptr = &(g_kimage_text[0]); + break; + case 1: + kimage_ptr = &(g_kimage_text[1]); + break; + case 2: + kimage_ptr = &(g_kimage_hires[0]); + break; + case 3: + kimage_ptr = &(g_kimage_hires[1]); + break; + case 4: + kimage_ptr = &g_kimage_superhires; + break; + case 5: + kimage_ptr = &g_kimage_border_sides; + break; + case 6: + kimage_ptr = &g_kimage_border_special; + break; + default: + printf("i: %d, unknown\n", i); + exit(3); + } + + ptr = (word32 *)kimage_ptr->data_ptr; + width = kimage_ptr->width_act; + height = kimage_ptr->height; + total_bytes = (kimage_ptr->mdepth >> 3) * width * height; + + for(j = 0; j < total_bytes >> 2; j++) { + *ptr++ = 0; + } + } + + for(i = 0; i < SLOW_MEM_CH_SIZE; i++) { + slow_mem_changed[i] = (word32)-1; + } + + /* create g_expanded_col_* */ + for(i = 0; i < 16; i++) { + val = (g_lores_colors[i] >> 0) & 0xf; + g_expanded_col_0[i] = val; + + val = (g_lores_colors[i] >> 4) & 0xf; + g_expanded_col_1[i] = val; + + val = (g_lores_colors[i] >> 8) & 0xf; + g_expanded_col_2[i] = val; + } + + /* create g_dhires_convert[] array */ + for(i = 0; i < 4096; i++) { + /* Convert index bits 11:0 where 3:0 is the previous color */ + /* and 7:4 is the current color to translate */ + /* Bit 4 will be the first pixel displayed on the screen */ + match_col = i & 0xf; + for(j = 0; j < 4; j++) { + cur_col = (i >> (1 + j)) & 0xf; + next_col = (i >> (2 + j)) & 0xf; + next2_col = (i >> (3 + j)) & 0xf; + next3_col = (i >> (4 + j)) & 0xf; + cur_col = (((cur_col << 4) + cur_col) >> (3 - j)) & 0xf; + + if((cur_col == 0xf) || (next_col == 0xf) || + (next2_col == 0xf) || + (next3_col == 0xf)) { + cur_col = 0xf; + col[j] = cur_col; + match_col = cur_col; + } else if((cur_col == 0) || (next_col == 0) || + (next2_col == 0) || (next3_col == 0)) { + cur_col = 0; + col[j] = cur_col; + match_col = cur_col; + } else { + col[j] = cur_col; + match_col = cur_col; + } + } + if(g_use_dhr140) { + for(j = 0; j < 4; j++) { + col[j] = (i >> 4) & 0xf; + } + } + val0 = g_dhires_colors_16[col[0] & 0xf]; + val1 = g_dhires_colors_16[col[1] & 0xf]; + val2 = g_dhires_colors_16[col[2] & 0xf]; + val3 = g_dhires_colors_16[col[3] & 0xf]; +#ifdef KEGS_LITTLE_ENDIAN + val = (val3 << 24) + (val2 << 16) + (val1 << 8) + val0; +#else + val = (val0 << 24) + (val1 << 16) + (val2 << 8) + val3; +#endif + g_dhires_convert[i] = val; + } + + change_display_mode(g_cur_dcycs); + video_reset(); + display_screen(); + + vid_printf("Done with display_screen\n"); + + fflush(stdout); +} + +void +show_a2_line_stuff() +{ + int i; + + for(i = 0; i < 200; i++) { + printf("line: %d: stat: %04x, ptr: %p, " + "left_edge:%d, right_edge:%d\n", + i, g_a2_line_stat[i], g_a2_line_kimage[i], + g_a2_line_left_edge[i], + g_a2_line_right_edge[i]); + } + + printf("new_a2_stat_cur_line: %d\n", g_new_a2_stat_cur_line); + for(i = 0; i < 200; i++) { + printf("cur_all[%d]: %03x new_all: %03x\n", i, + g_a2_cur_all_stat[i], g_a2_new_all_stat[i]); + } + +} + +int g_flash_count = 0; + +void +video_reset() +{ + int i; + + g_installed_full_superhires_colormap = (g_screen_depth != 8); + g_cur_a2_stat = ALL_STAT_TEXT | ALL_STAT_ANNUNC3 | + (0xf << BIT_ALL_STAT_TEXT_COLOR); + if(g_use_bw_hires) { + g_cur_a2_stat |= ALL_STAT_COLOR_C021; + } + + g_palette_change_summary = 0; + for(i = 0; i < 16; i++) { + g_palette_change_cnt[i] = 0; + } + + /* install_a2vid_colormap(); */ + video_update_colormap(); +} + +int g_screen_redraw_skip_count = 0; +int g_screen_redraw_skip_amt = -1; + +word32 g_cycs_in_check_input = 0; + +void +video_update() +{ + register word32 start_time; + register word32 end_time; + int did_video; + + update_border_info(); + + GET_ITIMER(start_time); + check_input_events(); + GET_ITIMER(end_time); + + g_cycs_in_check_input += (end_time - start_time); + + g_screen_redraw_skip_count--; + did_video = 0; + if(g_screen_redraw_skip_count < 0) { + did_video = 1; + video_update_event_line(262); + g_screen_redraw_skip_count = g_screen_redraw_skip_amt; + } + + /* update flash */ + g_flash_count++; + if(g_flash_count >= 30) { + g_flash_count = 0; + g_cur_a2_stat ^= ALL_STAT_FLASH_STATE; + change_display_mode(g_cur_dcycs); + } + + + check_a2vid_palette(); + + + if(did_video) { + g_new_a2_stat_cur_line = 0; + g_a2_new_all_stat[0] = (g_cur_a2_stat & (~ALL_STAT_PAGE2)) + + PAGE2; + g_vid_update_last_line = 0; + video_update_through_line(0); + } +} + + +int +video_all_stat_to_line_stat(int line, int new_all_stat) +{ + int page, color, dbl; + int st80, hires, annunc3, mix_t_gr; + int altchar, text_color, bg_color, flash_state; + int mode; + + st80 = new_all_stat & ALL_STAT_ST80; + hires = new_all_stat & ALL_STAT_HIRES; + annunc3 = new_all_stat & ALL_STAT_ANNUNC3; + mix_t_gr = new_all_stat & ALL_STAT_MIX_T_GR; + + page = EXTRU(new_all_stat, 31 - BIT_ALL_STAT_PAGE2, 1) && !st80; + color = EXTRU(new_all_stat, 31 - BIT_ALL_STAT_COLOR_C021, 1); + dbl = EXTRU(new_all_stat, 31 - BIT_ALL_STAT_VID80, 1); + + altchar = 0; text_color = 0; bg_color = 0; flash_state = 0; + + if(new_all_stat & ALL_STAT_SUPER_HIRES) { + mode = MODE_SUPER_HIRES; + page = 0; dbl = 0; color = 0; + } else { + if(line >= 192) { + mode = MODE_BORDER; + page = 0; dbl = 0; color = 0; + } else if((new_all_stat & ALL_STAT_TEXT) || + (line >= 160 && mix_t_gr)) { + mode = MODE_TEXT; + color = 0; + altchar = EXTRU(new_all_stat, + 31 - BIT_ALL_STAT_ALTCHARSET, 1); + text_color = EXTRU(new_all_stat, + 31 - BIT_ALL_STAT_TEXT_COLOR, 4); + bg_color = EXTRU(new_all_stat, + 31 - BIT_ALL_STAT_BG_COLOR, 4); + flash_state = EXTRU(new_all_stat, + 31 - BIT_ALL_STAT_FLASH_STATE, 1); + if(altchar) { + /* don't bother flashing if altchar on */ + flash_state = 0; + } + } else { + /* obey the graphics mode */ + dbl = dbl && !annunc3; + if(hires) { + color = color | EXTRU(new_all_stat, + 31 - BIT_ALL_STAT_DIS_COLOR_DHIRES, 1); + mode = MODE_HGR; + } else { + mode = MODE_GR; + } + } + } + + return((text_color << 12) + (bg_color << 8) + (altchar << 7) + + (mode << 4) + (flash_state << 3) + (page << 2) + + (color << 1) + dbl); +} + +int * +video_update_kimage_ptr(int line, int new_stat) +{ + Kimage *kimage_ptr; + int *mode_ptr; + int page; + int mode; + + page = (new_stat >> 2) & 1; + mode = (new_stat >> 4) & 7; + + switch(mode) { + case MODE_TEXT: + case MODE_GR: + kimage_ptr = &(g_kimage_text[page]); + mode_ptr = &(g_mode_text[page][0]); + break; + case MODE_HGR: + kimage_ptr = &(g_kimage_hires[page]); + mode_ptr = &(g_mode_hires[page][0]); + /* arrange to force superhires reparse since we use the */ + /* same memory */ + g_mode_superhires[line] = -1; + break; + case MODE_SUPER_HIRES: + kimage_ptr = &g_kimage_superhires; + mode_ptr = &(g_mode_superhires[0]); + /* arrange to force hires reparse since we use the */ + /* same memory */ + g_mode_hires[0][line] = -1; + g_mode_hires[1][line] = -1; + break; + case MODE_BORDER: + /* Hack: reuse text page last line as the special border */ + kimage_ptr = &(g_kimage_text[0]); + mode_ptr = &(g_mode_border[0]); + break; + default: + halt_printf("update_a2_ptrs: mode: %d unknown!\n", mode); + return &(g_mode_superhires[0]); + } + + g_a2_line_kimage[line] = kimage_ptr; + return mode_ptr; +} + +void +change_a2vid_palette(int new_palette) +{ + int i; + + for(i = 0; i < 200; i++) { + g_mode_text[0][i] = -1; + g_mode_text[1][i] = -1; + g_mode_hires[0][i] = -1; + g_mode_hires[1][i] = -1; + g_mode_superhires[i] = -1; + g_mode_border[i] = -1; + } + + printf("Changed a2vid_palette to %x\n", new_palette); + + g_a2vid_palette = new_palette; + g_cur_a2_stat = (g_cur_a2_stat & (~ALL_STAT_A2VID_PALETTE)) + + (new_palette << BIT_ALL_STAT_A2VID_PALETTE); + change_display_mode(g_cur_dcycs); + + g_border_sides_refresh_needed = 1; + g_border_special_refresh_needed = 1; + g_status_refresh_needed = 1; + g_palette_change_cnt[new_palette]++; + g_border_last_vbl_changes = 1; + for(i = 0; i < 262; i++) { + g_cur_border_colors[i] = -1; + } +} + +int g_num_a2vid_palette_checks = 1; +int g_shr_palette_used[16]; + +void +check_a2vid_palette() +{ + int sum; + int min; + int val; + int min_pos; + int count_cur; + int i; + + /* determine if g_a2vid_palette should change */ + /* This is the palette of least use on superhires so that the */ + /* borders don't change when all 256 superhires colors are used */ + + g_num_a2vid_palette_checks--; + if(g_num_a2vid_palette_checks || g_installed_full_superhires_colormap){ + return; + } + + g_num_a2vid_palette_checks = 60; + + sum = 0; + min = 0x100000; + min_pos = -1; + count_cur = g_shr_palette_used[g_a2vid_palette]; + + for(i = 0; i < 16; i++) { + val = g_shr_palette_used[i]; + g_shr_palette_used[i] = 0; + if(val < min) { + min = val; + min_pos = i; + } + sum += val; + } + + if(g_a2vid_palette != min_pos && (count_cur > min)) { + change_a2vid_palette(min_pos); + } +} + +void +change_display_mode(double dcycs) +{ + int line, tmp_line; + + line = ((get_lines_since_vbl(dcycs) + 0xff) >> 8); + if(line < 0) { + line = 0; + halt_printf("Line < 0!\n"); + } + tmp_line = MIN(199, line); + + video_update_all_stat_through_line(tmp_line); + + if(line < 200) { + g_a2_new_all_stat[line] = + (g_cur_a2_stat & (~ALL_STAT_PAGE2)) + PAGE2; + } + /* otherwise, g_cur_a2_stat is covered at the end of vbl */ +} + +void +video_update_all_stat_through_line(int line) +{ + int start_line; + int prev_stat; + int max_line; + int i; + + start_line = g_new_a2_stat_cur_line; + prev_stat = g_a2_new_all_stat[start_line]; + + max_line = MIN(199, line); + + for(i = start_line + 1; i <= max_line; i++) { + g_a2_new_all_stat[i] = prev_stat; + } + g_new_a2_stat_cur_line = max_line; +} + + +#define MAX_BORDER_CHANGES 16384 + +STRUCT(Border_changes) { + float fcycs; + int val; +}; + +Border_changes g_border_changes[MAX_BORDER_CHANGES]; +int g_num_border_changes = 0; + +void +change_border_color(double dcycs, int val) +{ + int pos; + + pos = g_num_border_changes; + g_border_changes[pos].fcycs = dcycs - g_last_vbl_dcycs; + g_border_changes[pos].val = val; + + pos++; + g_num_border_changes = pos; + + if(pos >= MAX_BORDER_CHANGES) { + halt_printf("num border changes: %d\n", pos); + g_num_border_changes = 0; + } +} + +void +update_border_info() +{ + double dlines_per_dcyc; + double dcycs, dline, dcyc_line_start; + int offset; + int new_line_offset, last_line_offset; + int new_line; + int new_val; + int limit; + int color_now; + int i; + + /* to get this routine to redraw the border, change */ + /* g_vbl_border_color, set g_border_last_vbl_changes = 1 */ + /* and change the cur_border_colors[] array */ + + color_now = g_vbl_border_color; + + dlines_per_dcyc = (double)(262.0 / DCYCS_IN_16MS); + limit = g_num_border_changes; + if(g_border_last_vbl_changes || limit) { + /* add a dummy entry */ + g_border_changes[limit].fcycs = DCYCS_IN_16MS + 21.0; + g_border_changes[limit].val = g_border_color; + limit++; + } + last_line_offset = (-1 << 8) + 44; + for(i = 0; i < limit; i++) { + dcycs = g_border_changes[i].fcycs; + dline = dcycs * dlines_per_dcyc; + new_line = (int)dline; + dcyc_line_start = (double)new_line * (DCYCS_IN_16MS/262.0); + offset = ((int)(dcycs - dcyc_line_start)) & 0xff; + + /* here comes the tricky part */ + /* offset is from 0 to 65, where 0-3 is the right border of */ + /* the previous line, 4-20 is horiz blanking, 21-24 is the */ + /* left border and 25-64 is the main window */ + /* Convert this to a new notation which is 0-3 is the left */ + /* border, 4-43 is the main window, and 44-47 is the right */ + /* basically, add -21 to offset, and wrap < 0 to previous ln */ + /* note this makes line -1 offset 44-47 the left hand border */ + /* for true line 261 on the screen */ + offset -= 21; + if(offset < 0) { + new_line--; + offset += 64; + } + new_val = g_border_changes[i].val; + new_line_offset = (new_line << 8) + offset; + + if(new_line_offset < -256 || new_line_offset >(262*256 + 0x80)){ + printf("new_line_offset: %05x\n", new_line_offset); + new_line_offset = last_line_offset; + } + while(last_line_offset < new_line_offset) { + /* see if this will finish it */ + if((last_line_offset & -256)==(new_line_offset & -256)){ + update_border_line(last_line_offset, + new_line_offset, color_now); + last_line_offset = new_line_offset; + } else { + update_border_line(last_line_offset, + (last_line_offset & -256) + 65, + color_now); + last_line_offset =(last_line_offset & -256)+256; + } + } + + color_now = new_val; + } + +#if 0 + if(g_num_border_changes) { + printf("Border changes: %d\n", g_num_border_changes); + } +#endif + + if(limit > 1) { + g_border_last_vbl_changes = 1; + } else { + g_border_last_vbl_changes = 0; + } + + g_num_border_changes = 0; + g_vbl_border_color = g_border_color; +} + +void +update_border_line(int st_line_offset, int end_line_offset, int color) +{ + word32 val; + int st_offset, end_offset; + int left, right; + int line; + + line = st_line_offset >> 8; + if(line != (end_line_offset >> 8)) { + halt_printf("ubl, %04x %04x %02x!\n", st_line_offset, + end_line_offset, color); + } + if(line < -1 || line >= 262) { + halt_printf("ubl-b, mod line is %d\n", line); + line = 0; + } + if(line < 0 || line >= 262) { + line = 0; + } + + st_offset = st_line_offset & 0xff; + end_offset = end_line_offset & 0xff; + + if((st_offset == 0) && (end_offset >= 0x41)) { + /* might be the same as last time, save some work */ + if(g_cur_border_colors[line] == color) { + return; + } + g_cur_border_colors[line] = color; + } else { + g_cur_border_colors[line] = -1; + } + + val = (color + (g_a2vid_palette << 4)); + val = (val << 24) + (val << 16) + (val << 8) + val; + + /* 0-3: left border, 4-43: main window, 44-47: right border */ + /* 48-65: horiz blanking */ + /* first, do the sides from line 0 to line 199 */ + if((line < 200) || (line >= 262)) { + if(line >= 262) { + line = 0; + } + if(st_offset < 4) { + /* left side */ + left = st_offset; + right = MIN(4, end_offset); + video_border_pixel_write(&g_kimage_border_sides, + 2*line, 2, val, (left * BORDER_WIDTH)/4, + (right * BORDER_WIDTH) / 4); + + g_border_sides_refresh_needed = 1; + } + if((st_offset < 48) && (end_offset >= 44)) { + /* right side */ + left = MAX(0, st_offset - 44); + right = MIN(4, end_offset - 44); + video_border_pixel_write(&g_kimage_border_sides, + 2*line, 2, val, + 32 + (left * EFF_BORDER_WIDTH/4), + 32 + (right * EFF_BORDER_WIDTH/4)); + g_border_sides_refresh_needed = 1; + } + } + + if((line >= 192) && (line < 200)) { + if(st_offset < 44 && end_offset > 4) { + left = MAX(0, st_offset - 4); + right = MIN(40, end_offset - 4); + video_border_pixel_write(&g_kimage_text[0], + 2*line, 2, val, left * 640 / 40, + right * 640 / 40); + g_border_line24_refresh_needed = 1; + } + } + + /* now do the bottom, lines 200 to 215 */ + if((line >= 200) && (line < (200 + BASE_MARGIN_BOTTOM/2)) ) { + line -= 200; + left = st_offset; + right = MIN(48, end_offset); + video_border_pixel_write(&g_kimage_border_special, 2*line, 2, + val, (left * X_A2_WINDOW_WIDTH / 48), + (right * X_A2_WINDOW_WIDTH / 48)); + g_border_special_refresh_needed = 1; + } + + /* and top, lines 236 to 262 */ + if((line >= (262 - BASE_MARGIN_TOP/2)) && (line < 262)) { + line -= (262 - BASE_MARGIN_TOP/2); + left = st_offset; + right = MIN(48, end_offset); + video_border_pixel_write(&g_kimage_border_special, + BASE_MARGIN_BOTTOM + 2*line, 2, val, + (left * X_A2_WINDOW_WIDTH / 48), + (right * X_A2_WINDOW_WIDTH / 48)); + g_border_special_refresh_needed = 1; + } +} + +void +video_border_pixel_write(Kimage *kimage_ptr, int starty, int num_lines, + word32 val, int st_off, int end_off) +{ + word32 *ptr; + int width; + int width_act; + int mdepth; + int num_words, num_bytes; + int bytes_per_pix; + int i, j; + + if(end_off <= st_off) { + return; + } + + width = end_off - st_off; + width_act = kimage_ptr->width_act; + mdepth = kimage_ptr->mdepth; + bytes_per_pix = mdepth >> 3; + num_bytes = width * bytes_per_pix; + num_words = num_bytes >> 2; + + if(width > width_act) { + halt_printf("border write but width %d > act %d\n", width, + width_act); + } + + if(mdepth == 16) { + val = g_a2palette_8to1624[val & 0xff]; + val = (val << 16) + val; + } else if(mdepth == 32) { + /* 32-bit pixels */ + val = g_a2palette_8to1624[val & 0xff]; + } + + for(i = 0; i < num_lines; i++) { + ptr = (word32 *)&(kimage_ptr->data_ptr[ + (starty + i)*width_act*bytes_per_pix]); + ptr += ((st_off * bytes_per_pix) / 4); + /* HACK: the above isn't really right when bytes_per_pix is */ + /* less than four... */ + for(j = 0; j < num_words; j++) { + *ptr++ = val; + } + } +} + + +#define CH_SETUP_A2_VID(mem_ptr, ch_mask, reparse, do_clear) \ + ch_ptr = &(slow_mem_changed[mem_ptr >> CHANGE_SHIFT]); \ + ch_bitpos = 0; \ + bits_per_line = 40 >> SHIFT_PER_CHANGE; \ + ch_shift_amount = (mem_ptr >> SHIFT_PER_CHANGE) & 0x1f; \ + mask_per_line = (-(1 << (32 - bits_per_line))); \ + mask_per_line = mask_per_line >> ch_shift_amount; \ + ch_mask = *ch_ptr & mask_per_line; \ + if(do_clear) { \ + *ch_ptr = *ch_ptr & (~ch_mask); \ + } \ + ch_mask = ch_mask << ch_shift_amount; \ + \ + if(reparse) { \ + ch_mask = - (1 << (32 - bits_per_line)); \ + } + +#define CH_LOOP_A2_VID(ch_mask, ch_tmp) \ + ch_tmp = ch_mask & 0x80000000; \ + ch_mask = ch_mask << 1; \ + \ + if(!ch_tmp) { \ + continue; \ + } + +void +redraw_changed_text_40(int start_offset, int start_line, int num_lines, + int reparse, byte *screen_data, int altcharset, int bg_val, int fg_val, + int pixels_per_line) +{ + register word32 start_time, end_time; + word32 *img_ptr, *img_ptr2; + word32 *save_img_ptr, *save_img_ptr2; + word32 *ch_ptr; + const word32 *font_ptr1; + const word32 *font_ptr2; + byte *slow_mem_ptr; + byte *b_ptr; + word32 ch_mask; + word32 ch_tmp; + word32 line_mask; + word32 mask_per_line; + word32 mem_ptr; + word32 tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6; + word32 palette_add; + word32 diff_val; + word32 and_val; + word32 add_val; + word32 ff_val; + word32 val0, val1; + int flash_state; + int y; + int x1, x2; + int ch_bitpos; + int bits_per_line; + int ch_shift_amount; + int shift_per; + int left, right; + int st_line_mod8, st_line; + int i; + + /* always redraws to the next multiple of 8 lines due to redraw */ + /* issues: char changed on one screen redraw at line 0 with */ + /* num_lines=1. We need to have drawn lines 1-7 also since line 1 */ + /* will not see any changed bytes */ + st_line_mod8 = start_line & 7; + st_line = start_line; + + start_line = start_line >> 3; + + y = start_line; + line_mask = 1 << (y); + mem_ptr = 0x400 + g_screen_index[y] + start_offset; + if(mem_ptr < 0x400 || mem_ptr >= 0xc00) { + halt_printf("redraw_changed_text: mem_ptr: %08x\n", mem_ptr); + } + + CH_SETUP_A2_VID(mem_ptr, ch_mask, reparse, (st_line_mod8 == 0)); + /* avoid clearing changed bits unless we are line 0 (mod 8) */ + + if(ch_mask == 0) { + return; + } + + GET_ITIMER(start_time); + + shift_per = (1 << SHIFT_PER_CHANGE); + + g_a2_screen_buffer_changed |= line_mask; + + palette_add = (g_a2vid_palette << 4); + palette_add = palette_add + (palette_add << 8) + (palette_add << 16) + + (palette_add << 24); + + left = 40; + right = 0; + + diff_val = (fg_val - bg_val) & 0xf; + and_val = diff_val + (diff_val << 8) + (diff_val << 16) +(diff_val<<24); + add_val = bg_val + (bg_val << 8) + (bg_val << 16) + (bg_val << 24); + ff_val = 0x0f0f0f0f; + + + flash_state = (g_cur_a2_stat & ALL_STAT_FLASH_STATE); + + for(x1 = 0; x1 < 40; x1 += shift_per) { + + CH_LOOP_A2_VID(ch_mask, ch_tmp); + + left = MIN(x1, left); + right = MAX(x1 + shift_per, right); + slow_mem_ptr = &(g_slow_memory_ptr[mem_ptr + x1]); + b_ptr = &screen_data[(8*y + st_line_mod8)*2*pixels_per_line + + x1*14]; + img_ptr = (word32 *)b_ptr; + img_ptr2 = (word32 *)(b_ptr + pixels_per_line); + + + for(x2 = 0; x2 < shift_per; x2 += 2) { + val0 = *slow_mem_ptr++; + val1 = *slow_mem_ptr++; + + if(!altcharset) { + if(val0 >= 0x40 && val0 < 0x80) { + if(flash_state) { + val0 += 0x40; + } else { + val0 -= 0x40; + } + } + if(val1 >= 0x40 && val1 < 0x80) { + if(flash_state) { + val1 += 0x40; + } else { + val1 -= 0x40; + } + } + } + save_img_ptr = img_ptr; + save_img_ptr2 = img_ptr2; + + for(i = st_line_mod8; i < 8; i++) { + font_ptr1 = &(g_font40_even_bits[val0][i][0]); + tmp0 = (font_ptr1[0] & and_val) + add_val; + tmp1 = (font_ptr1[1] & and_val) + add_val; + tmp2 = (font_ptr1[2] & and_val) + add_val; + + font_ptr2 = &(g_font40_odd_bits[val1][i][0]); + tmp3 = ((font_ptr1[3]+font_ptr2[0]) & and_val)+ + add_val; + + tmp4 = (font_ptr2[1] & and_val) + add_val; + tmp5 = (font_ptr2[2] & and_val) + add_val; + tmp6 = (font_ptr2[3] & and_val) + add_val; + + tmp0 = (tmp0 & ff_val) + palette_add; + tmp1 = (tmp1 & ff_val) + palette_add; + tmp2 = (tmp2 & ff_val) + palette_add; + tmp3 = (tmp3 & ff_val) + palette_add; + tmp4 = (tmp4 & ff_val) + palette_add; + tmp5 = (tmp5 & ff_val) + palette_add; + tmp6 = (tmp6 & ff_val) + palette_add; + + img_ptr[0] = tmp0; + img_ptr[1] = tmp1; + img_ptr[2] = tmp2; + img_ptr[3] = tmp3; + img_ptr[4] = tmp4; + img_ptr[5] = tmp5; + img_ptr[6] = tmp6; + + img_ptr2[0] = tmp0; + img_ptr2[1] = tmp1; + img_ptr2[2] = tmp2; + img_ptr2[3] = tmp3; + img_ptr2[4] = tmp4; + img_ptr2[5] = tmp5; + img_ptr2[6] = tmp6; + + img_ptr += (2*pixels_per_line)/4; + img_ptr2 += (2*pixels_per_line)/4; + } + + img_ptr = save_img_ptr + 7; + img_ptr2 = save_img_ptr2 + 7; + } + } + GET_ITIMER(end_time); + + for(i = 0; i < (8 - st_line_mod8); i++) { + g_a2_line_left_edge[st_line + i] = (left*14); + g_a2_line_right_edge[st_line + i] = (right*14); + } + + if(left >= right || left < 0 || right < 0) { + printf("line %d, 40: left >= right: %d >= %d\n", + start_line, left, right); + } + + g_cycs_in_40col += (end_time - start_time); + + g_need_redraw = 0; +} + +void +redraw_changed_text_80(int start_offset, int start_line, int num_lines, + int reparse, byte *screen_data, int altcharset, int bg_val, int fg_val, + int pixels_per_line) +{ + const word32 *font_ptr0, *font_ptr1, *font_ptr2, *font_ptr3; + word32 *ch_ptr; + word32 *img_ptr, *img_ptr2; + word32 *save_img_ptr, *save_img_ptr2; + byte *b_ptr; + byte *slow_mem_ptr; + word32 ch_mask; + word32 ch_tmp; + word32 mask_per_line; + word32 mem_ptr; + word32 tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6; + word32 diff_val; + word32 add_val, and_val, ff_val; + word32 palette_add; + word32 line_mask; + word32 val0, val1, val2, val3; + int st_line_mod8, st_line; + int flash_state; + int y; + int x1, x2; + int i; + int ch_bitpos; + int bits_per_line; + int ch_shift_amount; + int shift_per; + int left, right; + + st_line_mod8 = start_line & 7; + st_line = start_line; + + start_line = start_line >> 3; + + y = start_line; + line_mask = 1 << (y); + mem_ptr = 0x400 + g_screen_index[y] + start_offset; + if(mem_ptr < 0x400 || mem_ptr >= 0xc00) { + halt_printf("redraw_changed_text: mem_ptr: %08x\n", mem_ptr); + } + + CH_SETUP_A2_VID(mem_ptr, ch_mask, reparse, (st_line_mod8 == 0)); + + if(ch_mask == 0) { + return; + } + + shift_per = (1 << SHIFT_PER_CHANGE); + + g_a2_screen_buffer_changed |= line_mask; + + palette_add = (g_a2vid_palette << 4); + palette_add = palette_add + (palette_add << 8) + (palette_add << 16) + + (palette_add << 24); + + left = 40; + right = 0; + + diff_val = (fg_val - bg_val) & 0xf; + add_val = bg_val + (bg_val << 8) + (bg_val << 16) + (bg_val << 24); + and_val = diff_val + (diff_val << 8) + (diff_val << 16) +(diff_val<<24); + ff_val = 0x0f0f0f0f; + + flash_state = (g_cur_a2_stat & ALL_STAT_FLASH_STATE); + + for(x1 = 0; x1 < 40; x1 += shift_per) { + CH_LOOP_A2_VID(ch_mask, ch_tmp); + + left = MIN(x1, left); + right = MAX(x1 + shift_per, right); + + slow_mem_ptr = &(g_slow_memory_ptr[mem_ptr + x1]); + b_ptr = &screen_data[(y*8 + st_line_mod8)*2*pixels_per_line + + x1*14]; + img_ptr = (word32 *)b_ptr; + img_ptr2 = (word32 *)(b_ptr + pixels_per_line); + + for(x2 = 0; x2 < shift_per; x2 += 2) { + /* do 4 chars at once! */ + + val1 = slow_mem_ptr[0]; + val3 = slow_mem_ptr[1]; + val0 = slow_mem_ptr[0x10000]; + val2 = slow_mem_ptr[0x10001]; + slow_mem_ptr += 2; + + if(!altcharset) { + if(val0 >= 0x40 && val0 < 0x80) { + if(flash_state) { + val0 += 0x40; + } else { + val0 -= 0x40; + } + } + if(val1 >= 0x40 && val1 < 0x80) { + if(flash_state) { + val1 += 0x40; + } else { + val1 -= 0x40; + } + } + if(val2 >= 0x40 && val2 < 0x80) { + if(flash_state) { + val2 += 0x40; + } else { + val2 -= 0x40; + } + } + if(val3 >= 0x40 && val3 < 0x80) { + if(flash_state) { + val3 += 0x40; + } else { + val3 -= 0x40; + } + } + } + save_img_ptr = img_ptr; + save_img_ptr2 = img_ptr2; + + for(i = st_line_mod8; i < 8; i++) { + font_ptr0 = &(g_font80_off0_bits[val0][i][0]); + tmp0 = (font_ptr0[0] & and_val) + add_val; + + font_ptr3 = &(g_font80_off3_bits[val1][i][0]); + tmp1 = ((font_ptr0[1]+font_ptr3[0]) & and_val)+ + add_val; + /* 3 bytes from ptr0, 1 from ptr3 */ + tmp2 = (font_ptr3[1] & and_val) + add_val; + + font_ptr2 = &(g_font80_off2_bits[val2][i][0]); + tmp3 = ((font_ptr3[2]+font_ptr2[0]) & and_val)+ + add_val; + /* 2 bytes from ptr3, 2 from ptr2*/ + tmp4 = (font_ptr2[1] & and_val) + add_val; + + font_ptr1 = &(g_font80_off1_bits[val3][i][0]); + tmp5 = ((font_ptr2[2]+font_ptr1[0]) & and_val)+ + add_val; + /* 1 byte from ptr2, 3 from ptr1 */ + tmp6 = (font_ptr1[1] & and_val) + add_val; + + tmp0 = (tmp0 & ff_val) + palette_add; + tmp1 = (tmp1 & ff_val) + palette_add; + tmp2 = (tmp2 & ff_val) + palette_add; + tmp3 = (tmp3 & ff_val) + palette_add; + tmp4 = (tmp4 & ff_val) + palette_add; + tmp5 = (tmp5 & ff_val) + palette_add; + tmp6 = (tmp6 & ff_val) + palette_add; + + img_ptr[0] = tmp0; + img_ptr[1] = tmp1; + img_ptr[2] = tmp2; + img_ptr[3] = tmp3; + img_ptr[4] = tmp4; + img_ptr[5] = tmp5; + img_ptr[6] = tmp6; + + img_ptr2[0] = tmp0; + img_ptr2[1] = tmp1; + img_ptr2[2] = tmp2; + img_ptr2[3] = tmp3; + img_ptr2[4] = tmp4; + img_ptr2[5] = tmp5; + img_ptr2[6] = tmp6; + + img_ptr += (2*pixels_per_line)/4; + img_ptr2 += (2*pixels_per_line)/4; + } + + img_ptr = save_img_ptr + 7; + img_ptr2 = save_img_ptr2 + 7; + + } + } + + for(i = 0; i < (8 - st_line_mod8); i++) { + g_a2_line_left_edge[st_line + i] = (left*14); + g_a2_line_right_edge[st_line + i] = (right*14); + } + + if(left >= right || left < 0 || right < 0) { + printf("line %d, 80: left >= right: %d >= %d\n", + start_line, left, right); + } + + g_need_redraw = 0; +} + +void +redraw_changed_gr(int start_offset, int start_line, int num_lines, int reparse, + byte *screen_data, int pixels_per_line) +{ + word32 *img_ptr; + word32 *save_img_ptr; + word32 *ch_ptr; + byte *b_ptr; + byte *slow_mem_ptr; + word32 mask_per_line; + word32 ch_mask; + word32 ch_tmp; + word32 mem_ptr; + word32 line_mask; + word32 val0, val1; + word32 val0_wd, val1_wd; + word32 val01_wd; + word32 val_even, val_odd; + word32 palette_add; + int half; + int x1, x2; + int y; + int y2; + int ch_bitpos; + int bits_per_line; + int ch_shift_amount; + int shift_per; + int left, right; + int st_line_mod8, st_line, eff_line, end_line; + int i; + + st_line_mod8 = start_line & 7; + st_line = start_line; + end_line = 8; // st_line_mod8 + num_lines; + + start_line = start_line >> 3; + + y = start_line; + line_mask = 1 << y; + mem_ptr = 0x400 + g_screen_index[y] + start_offset; + if(mem_ptr < 0x400 || mem_ptr >= 0xc00) { + printf("redraw_changed_gr: mem_ptr: %08x\n", mem_ptr); + } + + CH_SETUP_A2_VID(mem_ptr, ch_mask, reparse, (st_line_mod8 == 0)); + + if(ch_mask == 0) { + return; + } + + shift_per = (1 << SHIFT_PER_CHANGE); + + g_a2_screen_buffer_changed |= line_mask; + + palette_add = (g_a2vid_palette << 4); + palette_add = palette_add + (palette_add << 8) + (palette_add << 16) + + (palette_add << 24); + + left = 40; + right = 0; + + for(x1 = 0; x1 < 40; x1 += shift_per) { + CH_LOOP_A2_VID(ch_mask, ch_tmp); + + left = MIN(x1, left); + right = MAX(x1 + shift_per, right); + + slow_mem_ptr = &(g_slow_memory_ptr[mem_ptr + x1]); + b_ptr = &screen_data[(y*8 + st_line_mod8)*2*pixels_per_line + + x1*14]; + img_ptr = (word32 *)b_ptr; + + for(x2 = 0; x2 < shift_per; x2 += 2) { + val_even = *slow_mem_ptr++; + val_odd = *slow_mem_ptr++; + + save_img_ptr = img_ptr; + + for(half = 0; half < 2; half++) { + val0 = val_even & 0xf; + val1 = val_odd & 0xf; + val0_wd = (val0 << 24) + (val0 << 16) + + (val0 << 8) + val0; + val1_wd = (val1 << 24) + (val1 << 16) + + (val1 << 8) + val1; +#ifdef KEGS_LITTLE_ENDIAN + val01_wd = (val1_wd << 16) + (val0_wd & 0xffff); +#else + val01_wd = (val0_wd << 16) + (val1_wd & 0xffff); +#endif + + for(y2 = 0; y2 < 8; y2++) { + eff_line = half*4 + (y2 >> 1); + if((eff_line < st_line_mod8) || + (eff_line > end_line)) { + continue; + } + + img_ptr[0] = val0_wd + palette_add; + img_ptr[1] = val0_wd + palette_add; + img_ptr[2] = val0_wd + palette_add; + img_ptr[3] = val01_wd + palette_add; + img_ptr[4] = val1_wd + palette_add; + img_ptr[5] = val1_wd + palette_add; + img_ptr[6] = val1_wd + palette_add; + img_ptr += (pixels_per_line)/4; + } + + + val_even = val_even >> 4; + val_odd = val_odd >> 4; + } + + img_ptr = save_img_ptr + 7; + } + } + + for(i = 0; i < (8 - st_line_mod8); i++) { + g_a2_line_left_edge[st_line + i] = (left*14); + g_a2_line_right_edge[st_line + i] = (right*14); + } + + g_need_redraw = 0; +} + +void +redraw_changed_dbl_gr(int start_offset, int start_line, int num_lines, + int reparse, byte *screen_data, int pixels_per_line) +{ + word32 *img_ptr; + word32 *save_img_ptr; + word32 *ch_ptr; + byte *b_ptr; + byte *slow_mem_ptr; + word32 mask_per_line; + word32 ch_mask; + word32 ch_tmp; + word32 mem_ptr; + word32 line_mask; + word32 val0, val1, val2, val3; + word32 val0_wd, val1_wd, val2_wd, val3_wd; + word32 val01_wd, val12_wd, val23_wd; + word32 val_even_main, val_odd_main; + word32 val_even_aux, val_odd_aux; + word32 palette_add; + int half; + int x1, x2; + int y; + int y2; + int ch_bitpos; + int bits_per_line; + int ch_shift_amount; + int shift_per; + int left, right; + int st_line_mod8, st_line, eff_line, end_line; + int i; + + st_line_mod8 = start_line & 7; + end_line = 8; // st_line_mod8 + num_lines + st_line = start_line; + + start_line = start_line >> 3; + + y = start_line; + line_mask = 1 << y; + mem_ptr = 0x400 + g_screen_index[y] + start_offset; + if(mem_ptr < 0x400 || mem_ptr >= 0xc00) { + printf("redraw_changed_dbl_gr: mem_ptr: %08x\n", mem_ptr); + } + + CH_SETUP_A2_VID(mem_ptr, ch_mask, reparse, (st_line_mod8 == 0)); + + if(ch_mask == 0) { + return; + } + + shift_per = (1 << SHIFT_PER_CHANGE); + + g_a2_screen_buffer_changed |= line_mask; + + palette_add = (g_a2vid_palette << 4); + palette_add = palette_add + (palette_add << 8) + (palette_add << 16) + + (palette_add << 24); + + left = 40; + right = 0; + + for(x1 = 0; x1 < 40; x1 += shift_per) { + CH_LOOP_A2_VID(ch_mask, ch_tmp); + + left = MIN(x1, left); + right = MAX(x1 + shift_per, right); + + slow_mem_ptr = &(g_slow_memory_ptr[mem_ptr + x1]); + b_ptr = &screen_data[(y*8 + st_line_mod8)*2*pixels_per_line + + x1*14]; + img_ptr = (word32 *)b_ptr; + + for(x2 = 0; x2 < shift_per; x2 += 2) { + val_even_main = slow_mem_ptr[0]; + val_odd_main = slow_mem_ptr[1]; + val_even_aux = slow_mem_ptr[0x10000]; + val_odd_aux = slow_mem_ptr[0x10001]; + slow_mem_ptr += 2; + + save_img_ptr = img_ptr; + + for(half = 0; half < 2; half++) { + val0 = val_even_aux & 0xf; + val1 = val_even_main & 0xf; + val2 = val_odd_aux & 0xf; + val3 = val_odd_main & 0xf; + + /* Handle funny pattern of dbl gr aux mem */ + val0 = ((val0 << 1) & 0xf) + (val0 >> 3); + val2 = ((val2 << 1) & 0xf) + (val2 >> 3); + + val0_wd = (val0 << 24) + (val0 << 16) + + (val0 << 8) + val0; + val1_wd = (val1 << 24) + (val1 << 16) + + (val1 << 8) + val1; + val2_wd = (val2 << 24) + (val2 << 16) + + (val2 << 8) + val2; + val3_wd = (val3 << 24) + (val3 << 16) + + (val3 << 8) + val3; +#ifdef KEGS_LITTLE_ENDIAN + val01_wd = (val1_wd << 24) + (val0_wd&0xffffff); + val12_wd = (val2_wd << 16) + (val1_wd & 0xffff); + val23_wd = (val3_wd << 8) + (val2_wd & 0xff); +#else + val01_wd = (val0_wd << 8) + (val1_wd & 0xff); + val12_wd = (val1_wd << 16) + (val2_wd & 0xffff); + val23_wd = (val2_wd << 24) + (val3_wd&0xffffff); +#endif + + for(y2 = 0; y2 < 8; y2++) { + eff_line = half*4 + (y2 >> 1); + if((eff_line < st_line_mod8) || + (eff_line > end_line)) { + continue; + } + img_ptr[0] = val0_wd + palette_add; + img_ptr[1] = val01_wd + palette_add; + img_ptr[2] = val1_wd + palette_add; + img_ptr[3] = val12_wd + palette_add; + img_ptr[4] = val2_wd + palette_add; + img_ptr[5] = val23_wd + palette_add; + img_ptr[6] = val3_wd + palette_add; + img_ptr += (pixels_per_line)/4; + } + + val_even_aux = val_even_aux >> 4; + val_even_main = val_even_main >> 4; + val_odd_aux = val_odd_aux >> 4; + val_odd_main = val_odd_main >> 4; + } + + img_ptr = save_img_ptr + 7; + } + } + + for(i = 0; i < (8 - st_line_mod8); i++) { + g_a2_line_left_edge[st_line + i] = (left*14); + g_a2_line_right_edge[st_line + i] = (right*14); + } + + g_need_redraw = 0; +} + +void +redraw_changed_hires(int start_offset, int start_line, int num_lines, + int color, int reparse, byte *screen_data, int pixels_per_line) +{ + if(!color) { + redraw_changed_hires_color(start_offset, start_line, num_lines, + reparse, screen_data, pixels_per_line); + } else { + redraw_changed_hires_bw(start_offset, start_line, num_lines, + reparse, screen_data, pixels_per_line); + } +} + +void +redraw_changed_hires_bw(int start_offset, int start_line, int num_lines, + int reparse, byte *screen_data, int pixels_per_line) +{ + word32 *img_ptr, *img_ptr2; + word32 *ch_ptr; + byte *b_ptr; + byte *slow_mem_ptr; + word32 mask_per_line; + word32 ch_mask; + word32 ch_tmp; + word32 tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6; + word32 mem_ptr; + word32 val0, val1; + word32 val_whole; + word32 line_mask; + word32 palette_add; + int y; + int x1, x2; + int ch_bitpos; + int bits_per_line; + int ch_shift_amount; + int shift_per; + int left, right; + int st_line; + int i; + + st_line = start_line; + start_line = start_line >> 3; + + palette_add = (g_a2vid_palette << 4); + palette_add = palette_add + (palette_add << 8) + (palette_add << 16) + + (palette_add << 24); + + left = 40; + right = 0; + + for(y = st_line; y < (st_line + num_lines); y++) { + line_mask = 1 << (y >> 3); + mem_ptr = 0x2000 + (((y & 7) * 0x400) + + g_screen_index[y >> 3]) + start_offset; + + CH_SETUP_A2_VID(mem_ptr, ch_mask, reparse, 1); + + if(ch_mask == 0) { + continue; + } + + /* Hires depends on adjacent bits, so also reparse adjacent */ + /* regions so that if bits on the edge change, redrawing is */ + /* correct */ + ch_mask = ch_mask | (ch_mask >> 1) | (ch_mask << 1); + + shift_per = (1 << SHIFT_PER_CHANGE); + + g_a2_screen_buffer_changed |= line_mask; + + for(x1 = 0; x1 < 40; x1 += shift_per) { + CH_LOOP_A2_VID(ch_mask, ch_tmp); + + left = MIN(x1, left); + right = MAX(x1 + shift_per, right); + + slow_mem_ptr = &(g_slow_memory_ptr[mem_ptr + x1]); + b_ptr = &screen_data[(y*2)*pixels_per_line + x1*14]; + img_ptr = (word32 *)b_ptr; + img_ptr2 = (word32 *)(b_ptr + pixels_per_line); + + for(x2 = 0; x2 < shift_per; x2 += 2) { + val0 = *slow_mem_ptr++; + val1 = *slow_mem_ptr++; + + val_whole = ((val1 & 0x7f) << 7) +(val0 & 0x7f); + + tmp0 = g_bw_hires_convert[val_whole & 3]; + val_whole = val_whole >> 2; + tmp1 = g_bw_hires_convert[val_whole & 3]; + val_whole = val_whole >> 2; + tmp2 = g_bw_hires_convert[val_whole & 3]; + val_whole = val_whole >> 2; + tmp3 = g_bw_hires_convert[val_whole & 3]; + val_whole = val_whole >> 2; + tmp4 = g_bw_hires_convert[val_whole & 3]; + val_whole = val_whole >> 2; + tmp5 = g_bw_hires_convert[val_whole & 3]; + val_whole = val_whole >> 2; + tmp6 = g_bw_hires_convert[val_whole & 3]; + + img_ptr[0] = tmp0 + palette_add; + img_ptr[1] = tmp1 + palette_add; + img_ptr[2] = tmp2 + palette_add; + img_ptr[3] = tmp3 + palette_add; + img_ptr[4] = tmp4 + palette_add; + img_ptr[5] = tmp5 + palette_add; + img_ptr[6] = tmp6 + palette_add; + + img_ptr2[0] = tmp0 + palette_add; + img_ptr2[1] = tmp1 + palette_add; + img_ptr2[2] = tmp2 + palette_add; + img_ptr2[3] = tmp3 + palette_add; + img_ptr2[4] = tmp4 + palette_add; + img_ptr2[5] = tmp5 + palette_add; + img_ptr2[6] = tmp6 + palette_add; + + img_ptr += 7; + img_ptr2 += 7; + } + } + } + + for(i = 0; i < num_lines; i++) { + g_a2_line_left_edge[st_line + i] = (left*14); + g_a2_line_right_edge[st_line + i] = (right*14); + } + + g_need_redraw = 0; +} + +void +redraw_changed_hires_color(int start_offset, int start_line, int num_lines, + int reparse, byte *screen_data, int pixels_per_line) +{ + word32 *img_ptr, *img_ptr2; + word32 *ch_ptr; + byte *b_ptr; + byte *slow_mem_ptr; + word32 mask_per_line; + word32 ch_mask; + word32 ch_tmp; + word32 mem_ptr; + word32 val0, val1; + word32 val_whole; + word32 pix_val; + word32 line_mask; + word32 prev_pixel; + word32 prev_hi; + word32 loc_hi; + word32 val_hi; + word32 tmp_val; + word32 palette_add; + int y; + int x1, x2; + int ch_bitpos; + int bits_per_line; + int ch_shift_amount; + int shift_per; + int left, right; + int st_line; + int i, j; + + st_line = start_line; + + start_line = start_line >> 3; + + palette_add = (g_a2vid_palette << 4); + palette_add = palette_add + (palette_add << 8) + (palette_add << 16) + + (palette_add << 24); + + left = 40; + right = 0; + + for(y = st_line; y < (st_line + num_lines); y++) { + line_mask = 1 << (y >> 3); + mem_ptr = 0x2000 + (((y & 7) * 0x400) + + g_screen_index[y >> 3]) + start_offset; + + CH_SETUP_A2_VID(mem_ptr, ch_mask, reparse, 1); + + if(ch_mask == 0) { + continue; + } + + /* Hires depends on adjacent bits, so also reparse adjacent */ + /* regions so that if bits on the edge change, redrawing is */ + /* correct */ + ch_mask = ch_mask | (ch_mask >> 1) | (ch_mask << 1); + + shift_per = (1 << SHIFT_PER_CHANGE); + + g_a2_screen_buffer_changed |= line_mask; + + for(x1 = 0; x1 < 40; x1 += shift_per) { + + CH_LOOP_A2_VID(ch_mask, ch_tmp); + + left = MIN(x1, left); + right = MAX(x1 + shift_per, right); + + slow_mem_ptr = &(g_slow_memory_ptr[mem_ptr + x1]); + b_ptr = &screen_data[(y*2)*pixels_per_line + x1*14]; + img_ptr = (word32 *)b_ptr; + img_ptr2 = (word32 *)(b_ptr + pixels_per_line); + + prev_pixel = 0; + prev_hi = 0; + + if(x1 > 0) { + tmp_val = slow_mem_ptr[-1]; + prev_pixel = (tmp_val >> 6) & 1; + prev_hi = (tmp_val >> 7) & 0x1; + } + + for(x2 = 0; x2 < shift_per; x2 += 2) { + val0 = *slow_mem_ptr++; + val1 = *slow_mem_ptr++; + + val_whole = ((val1 & 0x7f) << 8) + + ((val0 & 0x7f) << 1) + + prev_pixel; + + loc_hi = prev_hi; + if(((val1 >> 7) & 1) != 0) { + loc_hi += 0x7f00; + } + if(((val0 >> 7) & 1) != 0) { + loc_hi += 0xfe; + } + + prev_pixel = (val1 >> 6) & 1; + prev_hi = (val1 >> 7) & 1; + if((x1 + x2 + 2) < 40) { + tmp_val = slow_mem_ptr[0]; + if(tmp_val & 1) { + val_whole |= 0x8000; + } + if(tmp_val & 0x80) { + loc_hi |= 0x8000; + } + } + + loc_hi = loc_hi >> 1; + + for(j = 0; j < 7; j++) { + tmp_val = val_whole & 0xf; + val_hi = loc_hi & 0x3; + + pix_val = g_hires_convert[(val_hi<<4) + + tmp_val]; + *img_ptr++ = pix_val + palette_add; + *img_ptr2++ = pix_val + palette_add; + val_whole = val_whole >> 2; + loc_hi = loc_hi >> 2; + } + } + } + } + + for(i = 0; i < num_lines; i++) { + g_a2_line_left_edge[st_line + i] = (left*14); + g_a2_line_right_edge[st_line + i] = (right*14); + } + + g_need_redraw = 0; +} + + +void +redraw_changed_dbl_hires(int start_offset, int start_line, int num_lines, + int color, int reparse, byte *screen_data, int pixels_per_line) +{ + if(!color) { + redraw_changed_dbl_hires_color(start_offset, start_line, + num_lines, reparse, screen_data, pixels_per_line); + } else { + redraw_changed_dbl_hires_bw(start_offset, start_line, + num_lines, reparse, screen_data, pixels_per_line); + } +} + + +void +redraw_changed_dbl_hires_bw(int start_offset, int start_line, int num_lines, + int reparse, byte *screen_data, int pixels_per_line) +{ + word32 *img_ptr, *img_ptr2; + word32 *ch_ptr; + byte *b_ptr; + byte *slow_mem_ptr; + word32 mask_per_line; + word32 ch_mask; + word32 ch_tmp; + word32 mem_ptr; + word32 val0, val1, val2, val3; + word32 tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6; + word32 val_whole; + word32 line_mask; + word32 palette_add; + int y; + int x1, x2; + int ch_bitpos; + int bits_per_line; + int ch_shift_amount; + int shift_per; + int left, right; + int st_line; + int i; + + st_line = start_line; + start_line = start_line >> 3; + + palette_add = (g_a2vid_palette << 4); + palette_add = palette_add + (palette_add << 8) + (palette_add << 16) + + (palette_add << 24); + + left = 40; + right = 0; + + for(y = st_line; y < (st_line + num_lines); y++) { + line_mask = 1 << (y >> 3); + mem_ptr = 0x2000 + (((y & 7) * 0x400) + g_screen_index[y >> 3] + + start_offset); + + CH_SETUP_A2_VID(mem_ptr, ch_mask, reparse, 1); + + if(ch_mask == 0) { + continue; + } + + shift_per = (1 << SHIFT_PER_CHANGE); + + g_a2_screen_buffer_changed |= line_mask; + + for(x1 = 0; x1 < 40; x1 += shift_per) { + + CH_LOOP_A2_VID(ch_mask, ch_tmp); + + left = MIN(x1, left); + right = MAX(x1 + shift_per, right); + + slow_mem_ptr = &(g_slow_memory_ptr[mem_ptr + x1]); + b_ptr = &screen_data[(y*2)*pixels_per_line + x1*14]; + img_ptr = (word32 *)b_ptr; + img_ptr2 = (word32 *)(b_ptr + pixels_per_line); + + for(x2 = 0; x2 < shift_per; x2 += 2) { + val0 = slow_mem_ptr[0x10000]; + val1 = slow_mem_ptr[0]; + val2 = slow_mem_ptr[0x10001]; + val3 = slow_mem_ptr[1]; + slow_mem_ptr += 2; + + val_whole = ((val3 & 0x7f) << 21) + + ((val2 & 0x7f) << 14) + + ((val1 & 0x7f) << 7) + + (val0 & 0x7f); + + tmp0 = g_bw_dhires_convert[val_whole & 0xf]; + val_whole = val_whole >> 4; + tmp1 = g_bw_dhires_convert[val_whole & 0xf]; + val_whole = val_whole >> 4; + tmp2 = g_bw_dhires_convert[val_whole & 0xf]; + val_whole = val_whole >> 4; + tmp3 = g_bw_dhires_convert[val_whole & 0xf]; + val_whole = val_whole >> 4; + tmp4 = g_bw_dhires_convert[val_whole & 0xf]; + val_whole = val_whole >> 4; + tmp5 = g_bw_dhires_convert[val_whole & 0xf]; + val_whole = val_whole >> 4; + tmp6 = g_bw_dhires_convert[val_whole & 0xf]; + + img_ptr[0] = tmp0 + palette_add; + img_ptr[1] = tmp1 + palette_add; + img_ptr[2] = tmp2 + palette_add; + img_ptr[3] = tmp3 + palette_add; + img_ptr[4] = tmp4 + palette_add; + img_ptr[5] = tmp5 + palette_add; + img_ptr[6] = tmp6 + palette_add; + + img_ptr2[0] = tmp0 + palette_add; + img_ptr2[1] = tmp1 + palette_add; + img_ptr2[2] = tmp2 + palette_add; + img_ptr2[3] = tmp3 + palette_add; + img_ptr2[4] = tmp4 + palette_add; + img_ptr2[5] = tmp5 + palette_add; + img_ptr2[6] = tmp6 + palette_add; + + img_ptr += 7; + img_ptr2 += 7; + } + } + } + + for(i = 0; i < num_lines; i++) { + g_a2_line_left_edge[st_line + i] = (left*14); + g_a2_line_right_edge[st_line + i] = (right*14); + } + + g_need_redraw = 0; +} + +void +redraw_changed_dbl_hires_color(int start_offset, int start_line, int num_lines, + int reparse, byte *screen_data, int pixels_per_line) +{ + word32 *ch_ptr; + word32 *img_ptr, *img_ptr2; + byte *slow_mem_ptr; + byte *b_ptr; + word32 mask_per_line; + word32 ch_mask; + word32 ch_tmp; + word32 mem_ptr; + word32 val0, val1, val2, val3; + word32 tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6; + word32 val_whole; + word32 prev_val; + word32 line_mask; + word32 palette_add; + int y; + int x1, x2; + int ch_bitpos; + int bits_per_line; + int ch_shift_amount; + int shift_per; + int left, right; + int st_line; + int i; + + st_line = start_line; + start_line = start_line >> 3; + + palette_add = (g_a2vid_palette << 4); + palette_add = palette_add + (palette_add << 8) + (palette_add << 16) + + (palette_add << 24); + + left = 40; + right = 0; + + for(y = st_line; y < (st_line + num_lines); y++) { + line_mask = 1 << (y >> 3); + mem_ptr = 0x2000 + (((y & 7) * 0x400) + g_screen_index[y >> 3] + + start_offset); + + CH_SETUP_A2_VID(mem_ptr, ch_mask, reparse, 1); + + if(ch_mask == 0) { + continue; + } + + /* dbl-hires also depends on adjacent bits, so reparse */ + /* adjacent regions so that if bits on the edge change, */ + /* redrawing is correct */ + ch_mask = ch_mask | (ch_mask >> 1) | (ch_mask << 1); + ch_mask = -1; + + shift_per = (1 << SHIFT_PER_CHANGE); + + g_a2_screen_buffer_changed |= line_mask; + + for(x1 = 0; x1 < 40; x1 += shift_per) { + + CH_LOOP_A2_VID(ch_mask, ch_tmp); + + left = MIN(x1, left); + right = MAX(x1 + shift_per, right); + + slow_mem_ptr = &(g_slow_memory_ptr[mem_ptr + x1]); + b_ptr = &screen_data[(y*2)*pixels_per_line + x1*14]; + img_ptr = (word32 *)b_ptr; + img_ptr2 = (word32 *)(b_ptr + pixels_per_line); + + for(x2 = 0; x2 < shift_per; x2 += 2) { + val0 = slow_mem_ptr[0x10000]; + val1 = slow_mem_ptr[0]; + val2 = slow_mem_ptr[0x10001]; + val3 = slow_mem_ptr[1]; + + prev_val = 0; + if((x1 + x2) > 0) { + prev_val = (slow_mem_ptr[-1] >> 3) &0xf; + } + val_whole = ((val3 & 0x7f) << 25) + + ((val2 & 0x7f) << 18) + + ((val1 & 0x7f) << 11) + + ((val0 & 0x7f) << 4) + prev_val; + + tmp0 = g_dhires_convert[val_whole & 0xfff]; + val_whole = val_whole >> 4; + tmp1 = g_dhires_convert[val_whole & 0xfff]; + val_whole = val_whole >> 4; + tmp2 = g_dhires_convert[val_whole & 0xfff]; + val_whole = val_whole >> 4; + tmp3 = g_dhires_convert[val_whole & 0xfff]; + val_whole = val_whole >> 4; + tmp4 = g_dhires_convert[val_whole & 0xfff]; + val_whole = val_whole >> 4; + tmp5 = g_dhires_convert[val_whole & 0xfff]; + val_whole = val_whole >> 4; + if((x1 + x2 + 2) < 40) { + val_whole += (slow_mem_ptr[0x10002]<<8); + } + tmp6 = g_dhires_convert[val_whole & 0xfff]; + + img_ptr[0] = tmp0 + palette_add; + img_ptr[1] = tmp1 + palette_add; + img_ptr[2] = tmp2 + palette_add; + img_ptr[3] = tmp3 + palette_add; + img_ptr[4] = tmp4 + palette_add; + img_ptr[5] = tmp5 + palette_add; + img_ptr[6] = tmp6 + palette_add; + + img_ptr2[0] = tmp0 + palette_add; + img_ptr2[1] = tmp1 + palette_add; + img_ptr2[2] = tmp2 + palette_add; + img_ptr2[3] = tmp3 + palette_add; + img_ptr2[4] = tmp4 + palette_add; + img_ptr2[5] = tmp5 + palette_add; + img_ptr2[6] = tmp6 + palette_add; + + slow_mem_ptr += 2; + img_ptr += 7; + img_ptr2 += 7; + } + } + } + + for(i = 0; i < num_lines; i++) { + g_a2_line_left_edge[st_line + i] = (left*14); + g_a2_line_right_edge[st_line + i] = (right*14); + } + + g_need_redraw = 0; +} + +int +video_rebuild_super_hires_palette(word32 scan_info, int line, int reparse) +{ + word32 *word_ptr; + word32 *ch_ptr; + byte *byte_ptr; + word32 ch_mask, mask_per_line; + word32 tmp; + word32 scan, old_scan; + int palette_changed; + int diff0, diff1, diff2; + int val0, val1, val2; + int diffs; + int low_delta, low_color; + int delta; + int full; + int ch_bit_offset, ch_word_offset; + int bits_per_line; + int palette; + int j, k; + + palette_changed = 0; + palette = scan_info & 0xf; + + ch_ptr = &(slow_mem_changed[0x9e00 >> CHANGE_SHIFT]); + ch_bit_offset = (palette << 5) >> SHIFT_PER_CHANGE; + ch_word_offset = ch_bit_offset >> 5; + ch_bit_offset = ch_bit_offset & 0x1f; + bits_per_line = (0x20 >> SHIFT_PER_CHANGE); + mask_per_line = -(1 << (32 - bits_per_line)); + mask_per_line = mask_per_line >> ch_bit_offset; + + ch_mask = ch_ptr[ch_word_offset] & mask_per_line; + ch_ptr[ch_word_offset] &= ~mask_per_line; /* clear the bits */ + + old_scan = g_superhires_scan_save[line]; + scan = (scan_info & 0xfaf) + (g_palette_change_cnt[palette] << 12); + g_superhires_scan_save[line] = scan; + +#if 0 + if(line == 1) { + word_ptr = (word32 *)&(g_slow_memory_ptr[0x19e00+palette*0x20]); + printf("y1vrshp, ch:%08x, s:%08x,os:%08x %d = %08x %08x %08x %08x %08x %08x %08x %08x\n", + ch_mask, scan, old_scan, reparse, + word_ptr[0], word_ptr[1], word_ptr[2], word_ptr[3], + word_ptr[4], word_ptr[5], word_ptr[6], word_ptr[7]); + } +#endif + + diffs = reparse | ((scan ^ old_scan) & 0xf0f); + /* we must do full reparse if palette changed for this line */ + + if(!diffs && (ch_mask == 0) && (((scan ^ old_scan) & (~0xf0)) == 0)) { + /* nothing changed, get out fast */ + return 0; + } + + if(ch_mask) { + /* indicates the palette has changed, and other scan lines */ + /* using this palette need to do a full 32-byte compare to */ + /* decide if they need to update or not */ + g_palette_change_cnt[palette]++; + } + + word_ptr = (word32 *)&(g_slow_memory_ptr[0x19e00 + palette*0x20]); + for(j = 0; j < 8; j++) { + if(word_ptr[j] != g_saved_line_palettes[line][j]) { + diffs = 1; + break; + } + } + + if(diffs == 0) { + return 0; + } + + /* first, save this word_ptr into saved_line_palettes */ + byte_ptr = (byte *)word_ptr; + for(j = 0; j < 8; j++) { + g_saved_line_palettes[line][j] = word_ptr[j]; + } + + full = g_installed_full_superhires_colormap; + + if(!full && palette == g_a2vid_palette) { + /* construct new color approximations from lores */ + for(j = 0; j < 16; j++) { + tmp = *byte_ptr++; + val2 = (*byte_ptr++) & 0xf; + val0 = tmp & 0xf; + val1 = (tmp >> 4) & 0xf; + low_delta = 0x1000; + low_color = 0x0; + for(k = 0; k < 16; k++) { + diff0 = g_expanded_col_0[k] - val0; + diff1 = g_expanded_col_1[k] - val1; + diff2 = g_expanded_col_2[k] - val2; + if(diff0 < 0) { + diff0 = -diff0; + } + if(diff1 < 0) { + diff1 = -diff1; + } + if(diff2 < 0) { + diff2 = -diff2; + } + delta = diff0 + diff1 + diff2; + if(delta < low_delta) { + low_delta = delta; + low_color = k; + } + } + + g_a2vid_palette_remap[j] = low_color; + } + } + + byte_ptr = (byte *)word_ptr; + /* this palette has changed */ + for(j = 0; j < 16; j++) { + val0 = *byte_ptr++; + val1 = *byte_ptr++; + video_update_color_array(palette*16 + j, (val1<<8) + val0); + } + + g_palette_change_summary = 1; + + return 1; +} + +#define SUPER_TYPE redraw_changed_super_hires_oneline_nofill_8 +#define SUPER_FILL 0 +#define SUPER_PIXEL_SIZE 8 +#include "superhires.h" +#undef SUPER_TYPE +#undef SUPER_FILL +#undef SUPER_PIXEL_SIZE + +#define SUPER_TYPE redraw_changed_super_hires_oneline_nofill_16 +#define SUPER_FILL 0 +#define SUPER_PIXEL_SIZE 16 +#include "superhires.h" +#undef SUPER_TYPE +#undef SUPER_FILL +#undef SUPER_PIXEL_SIZE + +#define SUPER_TYPE redraw_changed_super_hires_oneline_nofill_32 +#define SUPER_FILL 0 +#define SUPER_PIXEL_SIZE 32 +#include "superhires.h" +#undef SUPER_TYPE +#undef SUPER_FILL +#undef SUPER_PIXEL_SIZE + +#define SUPER_TYPE redraw_changed_super_hires_oneline_fill_8 +#define SUPER_FILL 1 +#define SUPER_PIXEL_SIZE 8 +#include "superhires.h" +#undef SUPER_TYPE +#undef SUPER_FILL +#undef SUPER_PIXEL_SIZE + +#define SUPER_TYPE redraw_changed_super_hires_oneline_fill_16 +#define SUPER_FILL 1 +#define SUPER_PIXEL_SIZE 16 +#include "superhires.h" +#undef SUPER_TYPE +#undef SUPER_FILL +#undef SUPER_PIXEL_SIZE + +#define SUPER_TYPE redraw_changed_super_hires_oneline_fill_32 +#define SUPER_FILL 1 +#define SUPER_PIXEL_SIZE 32 +#include "superhires.h" +#undef SUPER_TYPE +#undef SUPER_FILL +#undef SUPER_PIXEL_SIZE + + + +void +redraw_changed_super_hires(int start_offset, int start_line, int num_lines, + int in_reparse, byte *screen_data) +{ + word32 *ch_ptr; + word32 mask_per_line; + word32 all_checks; + word32 check0, check1, mask0, mask1; + word32 this_check; + word32 tmp; + word32 line_mask; + word32 pal; + word32 scan, old_scan; + word32 kd_tmp_debug; + int y; + int bits_per_line; + int a2vid_palette; + int type; + int left, right; + int st_line; + int check_bit_pos, check_word_off; + int pixel_size, pixel_size_type; + int use_a2vid_palette, mode_640; + int pixels_per_line; + int ret; + int i; + + st_line = start_line; + start_line = start_line >> 3; + + pixel_size = g_kimage_superhires.mdepth; + pixels_per_line = g_kimage_superhires.width_act; + + pixel_size_type = (pixel_size >> 3) - 1; + /* pixel_size_type is now: 0=8bit, 1=16bit, 3=32bit */ + if(pixel_size_type >= 3) { + pixel_size_type = 2; + } + + kd_tmp_debug = g_a2_screen_buffer_changed; + + line_mask = 1 << (start_line); + + ch_ptr = &(slow_mem_changed[(0x2000) >> CHANGE_SHIFT]); + bits_per_line = 160 >> SHIFT_PER_CHANGE; + mask_per_line = -(1 << (32 - bits_per_line)); + + if(SHIFT_PER_CHANGE != 3) { + halt_printf("SHIFT_PER_CHANGE must be 3!\n"); + return; + } + + a2vid_palette = g_a2vid_palette; + if(g_installed_full_superhires_colormap) { + a2vid_palette = -1; + } else { + /* handle palette counting for finding least-used palette */ + if(pixel_size == 8) { + for(y = 8*start_line; y < 8*(start_line + 1); y++) { + scan = g_slow_memory_ptr[0x19d00 + y]; + pal = scan & 0xf; + g_shr_palette_used[pal]++; + } + } + } + + all_checks = 0; + check0 = 0; + check1 = 0; + for(y = st_line; y < (st_line + num_lines); y++) { + scan = g_slow_memory_ptr[0x19d00 + y]; + check_bit_pos = bits_per_line * y; + check_word_off = check_bit_pos >> 5; /* 32 bits per word */ + check_bit_pos = check_bit_pos & 0x1f; /* 5-bit bit_pos */ + check0 = ch_ptr[check_word_off]; + check1 = ch_ptr[check_word_off+1]; + mask0 = mask_per_line >> check_bit_pos; + mask1 = 0; + this_check = check0 << check_bit_pos; + /* move indicated bit to MSbit position */ + if((check_bit_pos + bits_per_line) > 32) { + this_check |= (check1 >> (32 - check_bit_pos)); + mask1 = mask_per_line << (32 - check_bit_pos); + } + + ch_ptr[check_word_off] = check0 & ~mask0; + ch_ptr[check_word_off+1] = check1 & ~mask1; + + this_check = this_check & mask_per_line; + old_scan = g_superhires_scan_save[y]; + use_a2vid_palette = ((scan & 0xf) == (word32)a2vid_palette); + scan = (scan + (a2vid_palette << 8)) & 0xfff; + + ret = video_rebuild_super_hires_palette(scan, y, in_reparse); +#if 0 + if(y == 1) { + printf("y1, ch:%08x, ret:%d, scan:%03x, os:%03x\n", + this_check, ret, scan, old_scan); + } +#endif + if(ret || in_reparse || ((scan ^ old_scan) & 0xa0)) { + /* 0x80 == mode640, 0x20 = fill */ + this_check = -1; + } + + if(this_check == 0) { + continue; + } + + mode_640 = (scan & 0x80); + if(mode_640) { + g_num_lines_superhires640++; + } + + type = ((scan >> 5) & 1) + (pixel_size_type << 1); + if(type & 1) { + /* fill mode--redraw whole line */ + this_check = -1; + } + + all_checks |= this_check; + + g_a2_screen_buffer_changed |= line_mask; + + + switch(type) { + case 0: /* nofill, 8 bit pixels */ + redraw_changed_super_hires_oneline_nofill_8( + screen_data, pixels_per_line, y, scan, + this_check, use_a2vid_palette, mode_640); + break; + case 1: /* fill, 8 bit pixels */ + redraw_changed_super_hires_oneline_fill_8( + screen_data, pixels_per_line, y, scan, + this_check, use_a2vid_palette, mode_640); + break; + case 2: /* nofill, 16 bit pixels */ + redraw_changed_super_hires_oneline_nofill_16( + screen_data, pixels_per_line, y, scan, + this_check, use_a2vid_palette, mode_640); + break; + case 3: /* fill, 16 bit pixels */ + redraw_changed_super_hires_oneline_fill_16( + screen_data, pixels_per_line, y, scan, + this_check, use_a2vid_palette, mode_640); + break; + case 4: /* nofill, 32 bit pixels */ + redraw_changed_super_hires_oneline_nofill_32( + screen_data, pixels_per_line, y, scan, + this_check, use_a2vid_palette, mode_640); + break; + case 5: /* fill, 32 byte pixels */ + redraw_changed_super_hires_oneline_fill_32( + screen_data, pixels_per_line, y, scan, + this_check, use_a2vid_palette, mode_640); + break; + default: + halt_printf("type: %d bad!\n", type); + } + } + + left = 4*40; + right = 0; + + tmp = all_checks; + if(all_checks) { + for(i = 0; i < 160; i += 8) { + if(tmp & 0x80000000) { + left = MIN(i, left); + right = MAX(i + 8, right); + } + tmp = tmp << 1; + } + } + + for(i = 0; i < num_lines; i++) { + g_a2_line_left_edge[st_line + i] = 4*left; + g_a2_line_right_edge[st_line + i] = 4*right; + } + +#if 0 + if((g_a2_screen_buffer_changed & (1 << start_line)) != 0) { + if(((g_full_refresh_needed & (1 << start_line)) == 0) && + left >= right) { + halt_printf("shr: line: %d, left: %d, right:%d\n", + start_line, left, right); + printf("mask_per_line: %08x, all_checks: %08x\n", + mask_per_line, all_checks); + printf("check0,1 = %08x,%08x\n", check0, check1); + printf("a2_screen_chang: %08x\n", kd_tmp_debug); +#ifdef HPUX + U_STACK_TRACE(); +#endif + } + } +#endif + + g_need_redraw = 0; +} + +void +display_screen() +{ + video_update_through_line(262); +} + +void +video_update_event_line(int line) +{ + int new_line; + + video_update_through_line(line); + + new_line = line + g_line_ref_amt; + if(new_line < 200) { + if(!g_config_control_panel) { + add_event_vid_upd(new_line); + } + } else if(line >= 262) { + video_update_through_line(0); + if(!g_config_control_panel) { + add_event_vid_upd(1); /* add event for new screen */ + } + } +} + +void +video_update_through_line(int line) +{ + register word32 start_time; + register word32 end_time; + int *mode_ptr; + word32 mask; + int last_line, num_lines; + int must_reparse; + int new_all_stat, prev_all_stat; + int new_stat, prev_stat; + int i; + +#if 0 + vid_printf("\nvideo_upd for line %d, lines: %06x\n\n", line, + get_lines_since_vbl(g_cur_dcycs)); +#endif + + GET_ITIMER(start_time); + + video_update_all_stat_through_line(line); + + i = g_vid_update_last_line; + + last_line = MIN(200, line+1); /* go through line, but not past 200 */ + + prev_stat = -2; + prev_all_stat = -2; + num_lines = 0; + must_reparse = 0; + for(i = g_vid_update_last_line; i < last_line; i++) { + new_all_stat = g_a2_new_all_stat[i]; + if(new_all_stat != g_a2_cur_all_stat[i]) { + /* regen line_stat for this line */ + g_a2_cur_all_stat[i] = new_all_stat; + if(new_all_stat == prev_all_stat) { + /* save a lookup */ + new_stat = prev_stat; + } else { + new_stat = video_all_stat_to_line_stat(i, + new_all_stat); + } + if(new_stat != g_a2_line_stat[i]) { + /* status changed */ + g_a2_line_stat[i] = new_stat; + mode_ptr = video_update_kimage_ptr(i, new_stat); + if(mode_ptr[i] != new_stat) { + must_reparse = 1; + mode_ptr[i] = new_stat; + } + mask = 1 << (line >> 3); + g_full_refresh_needed |= mask; + g_a2_screen_buffer_changed |= mask; + } + } + + new_stat = g_a2_line_stat[i]; + + if( ((new_stat == prev_stat) && ((i & 7) != 0)) || + (num_lines == 0) ) { + /* merge prev and this together */ + prev_stat = new_stat; + num_lines++; + continue; + } + + /* else, we must call refresh */ + video_refresh_lines(i - num_lines, num_lines, must_reparse); + num_lines = 1; + prev_all_stat = -1; + prev_stat = new_stat; + must_reparse = 0; + } + if(num_lines > 0) { + video_refresh_lines(i - num_lines, num_lines, must_reparse); + } + + g_vid_update_last_line = last_line; + + /* deal with border */ + if(line >= 262) { + if(g_num_lines_prev_superhires != g_num_lines_superhires) { + /* switched in/out from superhires--refresh borders */ + g_border_sides_refresh_needed = 1; + } + refresh_border(); + + if(g_status_refresh_needed) { + g_status_refresh_needed = 0; + x_redraw_status_lines(); + } + } + GET_ITIMER(end_time); + + g_cycs_in_refresh_line += (end_time - start_time); + + if(line >= 262) { + GET_ITIMER(start_time); + if(g_palette_change_summary) { + g_palette_change_summary = 0; + video_update_colormap(); + } + + video_push_kimages(); + GET_ITIMER(end_time); + g_cycs_in_refresh_ximage += (end_time - start_time); + + g_num_lines_prev_superhires = g_num_lines_superhires; + g_num_lines_prev_superhires640 = g_num_lines_superhires640; + g_num_lines_superhires = 0; + g_num_lines_superhires640 = 0; + } +} + +void +video_refresh_lines(int st_line, int num_lines, int must_reparse) +{ + byte *ptr; + int line; + int stat; + int mode; + int dbl, page, color; + int altchar, bg_color, text_color; + int pixels_per_line; + int i; + + line = st_line; + + /* do some basic checking, num_lines should be 1-8, and */ + /* st_line+num_lines-1 cannot roll over 8 */ + if((num_lines < 1) || (num_lines > 8) || + (((st_line & 7) + num_lines) > 8) ) { + halt_printf("video_refresh_lines called with %d, %d\n", + st_line, num_lines); + return; + } + + stat = g_a2_line_stat[line]; + ptr = g_a2_line_kimage[line]->data_ptr; + pixels_per_line = g_a2_line_kimage[line]->width_act; + + /* do not zero g_a2_line_left/right_edge here since text/gr routs */ + /* need to leave stale values around for drawing to work correctly */ + /* all routs force in new left/right when there are screen changes */ + + dbl = stat & 1; + color = (stat >> 1) & 1; + page = (stat >> 2) & 1; + mode = (stat >> 4) & 7; + +#if 0 + printf("refresh line: %d, stat: %04x\n", line, stat); +#endif + + switch(mode) { + case MODE_TEXT: + altchar = (stat >> 7) & 1; + bg_color = (stat >> 8) & 0xf; + text_color = (stat >> 12) & 0xf; + if(dbl) { + redraw_changed_text_80(0x000 + page*0x400, st_line, + num_lines, must_reparse, ptr, altchar, bg_color, + text_color, pixels_per_line); + } else { + redraw_changed_text_40(0x000 + page*0x400, st_line, + num_lines, must_reparse, ptr, altchar, bg_color, + text_color, pixels_per_line); + } + break; + case MODE_GR: + if(dbl) { + redraw_changed_dbl_gr(0x000 + page*0x400, st_line, + num_lines, must_reparse, ptr, pixels_per_line); + } else { + redraw_changed_gr(0x000 + page*0x400, st_line, + num_lines, must_reparse, ptr, pixels_per_line); + } + break; + case MODE_HGR: + if(dbl) { + redraw_changed_dbl_hires(0x000 + page*0x2000, st_line, + num_lines, color, must_reparse, ptr, + pixels_per_line); + } else { + redraw_changed_hires(0x000 + page*0x2000, st_line, + num_lines, color, must_reparse, ptr, + pixels_per_line); + } + break; + case MODE_SUPER_HIRES: + g_num_lines_superhires++; + redraw_changed_super_hires(0, st_line, num_lines, + must_reparse, ptr); + break; + case MODE_BORDER: + if(line < 192) { + halt_printf("Border line not 192: %d\n", line); + } + for(i = 0; i < num_lines; i++) { + g_a2_line_left_edge[line + i] = 0; + g_a2_line_right_edge[line + i] = 560; + } + if(g_border_line24_refresh_needed) { + g_border_line24_refresh_needed = 0; + g_a2_screen_buffer_changed |= (1 << 24); + } + break; + default: + halt_printf("refresh screen: mode: 0x%02x unknown!\n", mode); + exit(7); + } +} + +void +refresh_border() +{ + /**ZZZZ***/ +} + +void +end_screen() +{ + printf("In end_screen\n"); + xdriver_end(); +} + +byte g_font_array[256][8] = { +#include "kegsfont.h" +}; + +void +read_a2_font() +{ + byte *f40_e_ptr; + byte *f40_o_ptr; + byte *f80_0_ptr, *f80_1_ptr, *f80_2_ptr, *f80_3_ptr; + int char_num; + int j, k; + int val0; + int mask; + int pix; + + for(char_num = 0; char_num < 0x100; char_num++) { + for(j = 0; j < 8; j++) { + val0 = g_font_array[char_num][j]; + + mask = 0x80; + + for(k = 0; k < 3; k++) { + g_font80_off0_bits[char_num][j][k] = 0; + g_font80_off1_bits[char_num][j][k] = 0; + g_font80_off2_bits[char_num][j][k] = 0; + g_font80_off3_bits[char_num][j][k] = 0; + g_font40_even_bits[char_num][j][k] = 0; + g_font40_odd_bits[char_num][j][k] = 0; + } + g_font40_even_bits[char_num][j][3] = 0; + g_font40_odd_bits[char_num][j][3] = 0; + + f40_e_ptr = (byte *)&g_font40_even_bits[char_num][j][0]; + f40_o_ptr = (byte *)&g_font40_odd_bits[char_num][j][0]; + + f80_0_ptr = (byte *)&g_font80_off0_bits[char_num][j][0]; + f80_1_ptr = (byte *)&g_font80_off1_bits[char_num][j][0]; + f80_2_ptr = (byte *)&g_font80_off2_bits[char_num][j][0]; + f80_3_ptr = (byte *)&g_font80_off3_bits[char_num][j][0]; + + for(k = 0; k < 7; k++) { + pix = 0; + if(val0 & mask) { + pix = 0xf; + } + + f40_e_ptr[2*k] = pix; + f40_e_ptr[2*k+1] = pix; + + f40_o_ptr[2*k+2] = pix; + f40_o_ptr[2*k+3] = pix; + + f80_0_ptr[k] = pix; + f80_1_ptr[k+1] = pix; + f80_2_ptr[k+2] = pix; + f80_3_ptr[k+3] = pix; + + mask = mask >> 1; + } + } + } +} + + +/* Helper routine for the *driver.c files */ + +void +video_get_kimage(Kimage *kimage_ptr, int extend_info, int depth, int mdepth) +{ + int width; + int height; + + width = A2_WINDOW_WIDTH; + height = A2_WINDOW_HEIGHT; + if(extend_info & 1) { + /* Border at top and bottom of screen */ + width = X_A2_WINDOW_WIDTH; + height = X_A2_WINDOW_HEIGHT - A2_WINDOW_HEIGHT + 2*8; + } + if(extend_info & 2) { + /* Border at sides of screen */ + width = BORDER_WIDTH + EFF_BORDER_WIDTH; + height = A2_WINDOW_HEIGHT; + } + + kimage_ptr->dev_handle = 0; + kimage_ptr->dev_handle2 = 0; + kimage_ptr->data_ptr = 0; + kimage_ptr->width_req = width; + kimage_ptr->width_act = width; + kimage_ptr->height = height; + kimage_ptr->depth = depth; + kimage_ptr->mdepth = mdepth; + kimage_ptr->aux_info = 0; + + x_get_kimage(kimage_ptr); +} + +void +video_get_kimages() +{ + video_get_kimage(&g_kimage_text[0], 0, 8, 8); + video_get_kimage(&g_kimage_text[1], 0, 8, 8); + video_get_kimage(&g_kimage_hires[0], 0, 8, 8); + video_get_kimage(&g_kimage_hires[1], 0, 8, 8); + video_get_kimage(&g_kimage_superhires, 0, g_screen_depth, + g_screen_mdepth); + video_get_kimage(&g_kimage_border_special, 1, g_screen_depth, + g_screen_mdepth); + video_get_kimage(&g_kimage_border_sides, 2, g_screen_depth, + g_screen_mdepth); +} + +void +video_convert_kimage_depth(Kimage *kim_in, Kimage *kim_out, int startx, + int starty, int width, int height) +{ + byte *indata, *inptr; + word32 *outdata32, *outptr32; + word16 *outdata16, *outptr16; + word32 *palptr; + int out_width, in_width; + int x, y; + + indata = (byte *)kim_in->data_ptr; + outdata32 = (word32 *)kim_out->data_ptr; + outdata16 = (word16 *)kim_out->data_ptr; + + if(kim_in == &g_kimage_superhires) { + palptr = &(g_palette_8to1624[0]); + } else { + palptr = &(g_a2palette_8to1624[0]); + } + if(kim_in->depth != 8) { + printf("x_convert_kimage_depth from non-8 bit depth: %p\n", + kim_in); + exit(1); + } + + out_width = kim_out->width_act; + in_width = kim_in->width_act; + indata += (starty * in_width + startx); + outdata32 += (starty * out_width + startx); + outdata16 += (starty * out_width + startx); + if(kim_out->mdepth == 16) { + for(y = 0; y < height; y++) { + outptr16 = outdata16; + inptr = indata; + for(x = 0; x < width; x++) { + *outptr16++ = palptr[*inptr++]; + } + outdata16 += out_width; + indata += in_width; + } + } else { + /* 32-bit depth */ + for(y = 0; y < height; y++) { + outptr32 = outdata32; + inptr = indata; + for(x = 0; x < width; x++) { + *outptr32++ = palptr[*inptr++]; + } + outdata32 += out_width; + indata += in_width; + } + } +} + +void +video_push_lines(Kimage *kimage_ptr, int start_line, int end_line, int left_pix, + int right_pix) +{ + int mdepth_mismatch; + int srcy; + + if(left_pix >= right_pix || left_pix < 0 || right_pix <= 0) { + halt_printf("video_push_lines: lines %d to %d, pix %d to %d\n", + start_line, end_line, left_pix, right_pix); + printf("a2_screen_buf_ch:%08x, g_full_refr:%08x\n", + g_a2_screen_buffer_changed, g_full_refresh_needed); + } + + srcy = 2*start_line; + + mdepth_mismatch = (kimage_ptr->mdepth != g_screen_mdepth); + if(mdepth_mismatch) { + /* translate from 8-bit pseudo to correct visual */ + video_convert_kimage_depth(kimage_ptr, &g_mainwin_kimage, + left_pix, srcy, (right_pix - left_pix), + 2*(end_line - start_line)); + kimage_ptr = &g_mainwin_kimage; + } + g_refresh_bytes_xfer += 2*(end_line - start_line) * + (right_pix - left_pix); + + + x_push_kimage(kimage_ptr, BASE_MARGIN_LEFT + left_pix, + BASE_MARGIN_TOP + srcy, left_pix, srcy, + (right_pix - left_pix), 2*(end_line - start_line)); +} + +void +video_push_border_sides_lines(int src_x, int dest_x, int width, int start_line, + int end_line) +{ + Kimage *kimage_ptr; + int srcy; + + if(start_line < 0 || width < 0) { + return; + } + +#if 0 + printf("push_border_sides lines:%d-%d from %d to %d\n", + start_line, end_line, end_x - width, end_x); +#endif + kimage_ptr = &g_kimage_border_sides; + g_refresh_bytes_xfer += 2 * (end_line - start_line) * width; + + srcy = 2 * start_line; + x_push_kimage(kimage_ptr, dest_x, BASE_MARGIN_TOP + srcy, + src_x, srcy, width, 2*(end_line - start_line)); +} + +void +video_push_border_sides() +{ + int old_width; + int prev_line; + int width; + int mode; + int i; + +#if 0 + printf("refresh border sides!\n"); +#endif + + /* redraw left sides */ + video_push_border_sides_lines(0, 0, BORDER_WIDTH, 0, 200); + + /* right side--can be "jagged" */ + prev_line = -1; + old_width = -1; + for(i = 0; i < 200; i++) { + mode = (g_a2_line_stat[i] >> 4) & 7; + width = EFF_BORDER_WIDTH; + if(mode == MODE_SUPER_HIRES) { + width = BORDER_WIDTH; + } + if(width != old_width) { + video_push_border_sides_lines(BORDER_WIDTH, + X_A2_WINDOW_WIDTH - old_width, old_width, + prev_line, i); + prev_line = i; + old_width = width; + } + } + + video_push_border_sides_lines(BORDER_WIDTH, + X_A2_WINDOW_WIDTH - old_width, old_width, prev_line, 200); +} + +void +video_push_border_special() +{ + Kimage *kimage_ptr; + int width, height; + + width = X_A2_WINDOW_WIDTH; + height = BASE_MARGIN_TOP; + + kimage_ptr = &g_kimage_border_special; + g_refresh_bytes_xfer += width * (BASE_MARGIN_TOP + BASE_MARGIN_BOTTOM); + + x_push_kimage(kimage_ptr, 0, BASE_MARGIN_TOP + A2_WINDOW_HEIGHT, + 0, 0, width, BASE_MARGIN_BOTTOM); + x_push_kimage(kimage_ptr, 0, 0, + 0, BASE_MARGIN_BOTTOM, width, BASE_MARGIN_TOP); + +} + +void +video_push_kimages() +{ + register word32 start_time; + register word32 end_time; + Kimage *last_kim, *cur_kim; + word32 mask; + int start; + int line; + int left_pix, right_pix; + int left, right; + int line_div8; + + if(g_border_sides_refresh_needed) { + g_border_sides_refresh_needed = 0; + video_push_border_sides(); + } + if(g_border_special_refresh_needed) { + g_border_special_refresh_needed = 0; + video_push_border_special(); + } + + if(g_a2_screen_buffer_changed == 0) { + return; + } + + GET_ITIMER(start_time); + + start = -1; + last_kim = (Kimage *)-1; + cur_kim = (Kimage *)0; + + left_pix = 640; + right_pix = 0; + + for(line = 0; line < 200; line++) { + line_div8 = line >> 3; + mask = 1 << (line_div8); + cur_kim = g_a2_line_kimage[line]; + if((g_full_refresh_needed & mask) != 0) { + left = 0; + right = 560; + if(cur_kim == &g_kimage_superhires) { + right = 640; + } + } else { + left = g_a2_line_left_edge[line]; + right = g_a2_line_right_edge[line]; + } + + if(!(g_a2_screen_buffer_changed & mask) || (left > right)) { + /* No need to update this line */ + /* Refresh previous chunks of lines, if any */ + if(start >= 0) { + video_push_lines(last_kim, start, line, + left_pix, right_pix); + start = -1; + left_pix = 640; + right_pix = 0; + } + } else { + /* Need to update this line */ + if(start < 0) { + start = line; + last_kim = cur_kim; + } + if(cur_kim != last_kim) { + /* do the refresh */ + video_push_lines(last_kim, start, line, + left_pix, right_pix); + last_kim = cur_kim; + start = line; + left_pix = left; + right_pix = right; + } + left_pix = MIN(left, left_pix); + right_pix = MAX(right, right_pix); + } + } + + if(start >= 0) { + video_push_lines(last_kim, start, 200, left_pix, right_pix); + } + + g_a2_screen_buffer_changed = 0; + g_full_refresh_needed = 0; + + x_push_done(); + + GET_ITIMER(end_time); + + g_cycs_in_xredraw += (end_time - start_time); +} + + +void +video_update_color_raw(int col_num, int a2_color) +{ + word32 tmp; + int red, green, blue; + int newred, newgreen, newblue; + + red = (a2_color >> 8) & 0xf; + green = (a2_color >> 4) & 0xf; + blue = (a2_color) & 0xf; + red = ((red << 4) + red); + green = ((green << 4) + green); + blue = ((blue << 4) + blue); + + newred = red >> g_red_right_shift; + newgreen = green >> g_green_right_shift; + newblue = blue >> g_blue_right_shift; + + tmp = ((newred & g_red_mask) << g_red_left_shift) + + ((newgreen & g_green_mask) << g_green_left_shift) + + ((newblue & g_blue_mask) << g_blue_left_shift); + g_palette_8to1624[col_num] = tmp; + + x_update_color(col_num, red, green, blue, tmp); +} + +void +video_update_color_array(int col_num, int a2_color) +{ + int palette; + int full; + + if(col_num >= 256 || col_num < 0) { + halt_printf("video_update_color_array: col: %03x\n", col_num); + return; + } + + full = g_installed_full_superhires_colormap; + + palette = col_num >> 4; + if(!full && palette == g_a2vid_palette) { + return; + } + +#if 0 + if(g_screen_depth != 8) { + /* redraw whole superhires for now */ + g_full_refresh_needed = -1; + } +#endif + + video_update_color_raw(col_num, a2_color); +} + +void +video_update_colormap() +{ + int palette; + int full; + int i; + + full = g_installed_full_superhires_colormap; + + if(!full) { + palette = g_a2vid_palette << 4; + for(i = 0; i < 16; i++) { + video_update_color_raw(palette + i, g_lores_colors[i]); + } + x_update_physical_colormap(); + } +} + +void +video_update_status_line(int line, const char *string) +{ + char *buf; + const char *ptr; + int i; + + if(line >= MAX_STATUS_LINES || line < 0) { + printf("update_status_line: line: %d!\n", line); + exit(1); + } + + ptr = string; + buf = &(g_status_buf[line][0]); + g_status_ptrs[line] = buf; + for(i = 0; i < STATUS_LINE_LENGTH; i++) { + if(*ptr) { + buf[i] = *ptr++; + } else { + buf[i] = ' '; + } + } + + buf[STATUS_LINE_LENGTH] = 0; +} + +void +video_show_debug_info() +{ + word32 tmp1; + + printf("g_cur_dcycs: %f, last_vbl: %f\n", g_cur_dcycs, + g_last_vbl_dcycs); + tmp1 = get_lines_since_vbl(g_cur_dcycs); + printf("lines since vbl: %06x\n", tmp1); + printf("Last line updated: %d\n", g_vid_update_last_line); +} diff --git a/src/win32.rc b/src/win32.rc new file mode 100644 index 0000000..f4b5f2e --- /dev/null +++ b/src/win32.rc @@ -0,0 +1,109 @@ +#include +#include "winresource.h" + +// $Id: $ + +#ifndef IDC_STATIC +#define IDC_STATIC (-1) +#endif + +IDD_DLG_DISKCONF DIALOGEX 0, 0, 268, 182 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Disk Configuration" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,150,161,50,14 + PUSHBUTTON "Cancel",IDCANCEL,203,161,50,14 + LTEXT "S5D1",IDC_STATIC,19,46,19,8 + EDITTEXT IDC_EDIT_S5D1,43,42,156,14,ES_AUTOHSCROLL, + WS_EX_ACCEPTFILES + PUSHBUTTON "Browse",IDC_BTN_S5D1,203,42,50,14 + LTEXT "S5D2",IDC_STATIC,19,62,19,8 + EDITTEXT IDC_EDIT_S5D2,43,60,155,14,ES_AUTOHSCROLL + PUSHBUTTON "Browse",IDC_BTN_S5D2,203,60,50,14 + LTEXT "S6D1",IDC_STATIC,19,80,19,8 + EDITTEXT IDC_EDIT_S6D1,43,77,156,14,ES_AUTOHSCROLL + PUSHBUTTON "Browse",IDC_BTN_S6D1,203,77,50,14 + LTEXT "S6D2",IDC_STATIC,19,98,19,8 + EDITTEXT IDC_EDIT_S6D2,43,95,156,14,ES_AUTOHSCROLL + PUSHBUTTON "Browse",IDC_BTN_S6D2,203,96,50,14 + LTEXT "S7D1",IDC_STATIC,19,118,19,8 + EDITTEXT IDC_EDIT_S7D1,43,115,155,14,ES_AUTOHSCROLL + PUSHBUTTON "Browse",IDC_BTN_S7D1,203,115,50,14 + LTEXT "S7D2",IDC_STATIC,19,137,19,8 + EDITTEXT IDC_EDIT_S7D2,43,135,155,14,ES_AUTOHSCROLL + PUSHBUTTON "Browse",IDC_BTN_S7D2,203,135,50,14 + GROUPBOX "Disk settings",IDC_STATIC,7,7,254,148 + LTEXT "Configure your disk images for each drive. Disk image formats supported\nare *.2MG,*.PO and *.DSK. ", + IDC_STATIC,19,20,234,16 +END + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDC_KEGS32 MENU DISCARDABLE +BEGIN + POPUP "&Emulator" + BEGIN + MENUITEM "&Set Disk Configuration\tALT-F1", ID_FILE_DISK + MENUITEM "Send CTRL Open-Apple Reset\tCTRL-ALT-BREAK", + ID_FILE_SENDRESET + MENUITEM "Toggle &Joystick", ID_FILE_JOYSTICK + MENUITEM "Toggle Debug Statistics", ID_FILE_DEBUGSTAT + MENUITEM SEPARATOR + MENUITEM "E&xit\tALT-F4", ID_FILE_EXIT + END + POPUP "&Help" + BEGIN + MENUITEM "&About", ID_HELP_ABOUT + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Bitmap +// + +IDR_TOOLBAR BITMAP DISCARDABLE "wintoolbar.bmp" + +///////////////////////////////////////////////////////////////////////////// +// +// Accelerator +// + +IDR_ACCEL ACCELERATORS DISCARDABLE +BEGIN + VK_F1, ID_FILE_DISK, VIRTKEY, ALT, NOINVERT + VK_F4, ID_FILE_EXIT, VIRTKEY, ALT, NOINVERT +END + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDC_KEGS32 ICON DISCARDABLE "win32.ico" +KEGS32_ICON ICON DISCARDABLE "win32.ico" + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_ABOUT_DIALOG DIALOG DISCARDABLE 0, 0, 207, 82 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "About" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,78,61,50,14 + LTEXT "KEGS32: GS Emulator.\nBased on KEGS by Kent Dickey\nWindows Port by Chea Chee Keong\n\nThis software is free for non-commercial use.", + IDC_STATIC,38,7,162,45,NOT WS_GROUP + ICON "KEGS32_ICON",IDC_STATIC,7,7,21,20,0 +END + + diff --git a/src/win32snd_driver.c b/src/win32snd_driver.c new file mode 100644 index 0000000..e3e74a4 --- /dev/null +++ b/src/win32snd_driver.c @@ -0,0 +1,215 @@ +/************************************************************************/ +/* KEGS: Apple //gs Emulator */ +/* Copyright 2002 by Kent Dickey */ +/* */ +/* This code is covered by the GNU GPL */ +/* */ +/* The KEGS web page is kegs.sourceforge.net */ +/* You may contact the author at: kadickey@alumni.princeton.edu */ +/************************************************************************/ + +const char rcsid_win32snd_driver_c[] = "@(#)$KmKId: win32snd_driver.c,v 1.5 2002-11-19 03:09:59-05 kadickey Exp $"; + +#include "defc.h" +#include "sound.h" + +#ifndef __CYGWIN__ +# include +# include +#endif +#include + +extern int Verbose; + +extern int g_audio_rate; + +unsigned int __stdcall child_sound_loop_win32(void *param); +void check_wave_error(int res, char *str); + +#define NUM_WAVE_HEADERS 8 + +#ifndef __CYGWIN__ +HWAVEOUT g_wave_handle; +WAVEHDR g_wavehdr[NUM_WAVE_HEADERS]; +#endif + +extern int g_audio_enable; +extern word32 *g_sound_shm_addr; +extern int g_preferred_rate; + +int g_win32snd_buflen = 0x1000; + +void +win32snd_init(word32 *shmaddr) +{ + printf("win32snd_init\n"); + child_sound_loop(-1, -1, shmaddr); + + return; +} + +void +win32snd_shutdown() +{ + /* hmm */ + +} + +#ifndef __CYGWIN__ + +void CALLBACK +handle_wav_snd(HWAVEOUT hwo, UINT uMsg, DWORD dwInstance, DWORD dwParam1, + DWORD dwParam2) +{ + LPWAVEHDR lpwavehdr; + + /* Only service "buffer done playing messages */ + if(uMsg == WOM_DONE) { + lpwavehdr = (LPWAVEHDR)dwParam1; + if(lpwavehdr->dwFlags == (WHDR_DONE | WHDR_PREPARED)) { + lpwavehdr->dwUser = FALSE; + } + } + + return; +} +void +check_wave_error(int res, char *str) +{ + char buf[256]; + + if(res == MMSYSERR_NOERROR) { + return; + } + + waveOutGetErrorText(res, &buf[0], sizeof(buf)); + printf("%s: %s\n", str, buf); + exit(1); +} + +void +child_sound_init_win32() +{ + WAVEFORMATEX wavefmt; + WAVEOUTCAPS caps; + byte *bptr; + int bits_per_sample, channels, block_align; + int blen; + int res; + int i; + + memset(&wavefmt, 0, sizeof(WAVEFORMATEX)); + + wavefmt.wFormatTag = WAVE_FORMAT_PCM; + bits_per_sample = 16; + channels = 2; + wavefmt.wBitsPerSample = bits_per_sample; + wavefmt.nChannels = channels; + wavefmt.nSamplesPerSec = g_audio_rate; + block_align = channels * (bits_per_sample / 8); + wavefmt.nBlockAlign = block_align; + wavefmt.nAvgBytesPerSec = block_align * g_audio_rate; + + res = waveOutOpen(&g_wave_handle, WAVE_MAPPER, &wavefmt, 0, 0, + WAVE_FORMAT_QUERY); + + if(res != MMSYSERR_NOERROR) { + printf("Cannot open audio device\n"); + g_audio_enable = 0; + return; + } + + res = waveOutOpen(&g_wave_handle, WAVE_MAPPER, &wavefmt, + (DWORD)handle_wav_snd, 0, CALLBACK_FUNCTION | WAVE_ALLOWSYNC); + + if(res != MMSYSERR_NOERROR) { + printf("Cannot register audio\n"); + g_audio_enable = 0; + return; + } + + g_audio_rate = wavefmt.nSamplesPerSec; + + blen = (SOUND_SHM_SAMP_SIZE * 4 * 2) / NUM_WAVE_HEADERS; + g_win32snd_buflen = blen; + bptr = malloc(blen * NUM_WAVE_HEADERS); + if(bptr == NULL) { + printf("Unabled to allocate sound buffer\n"); + exit(1); + } + + for(i = 0; i < NUM_WAVE_HEADERS; i++) { + memset(&g_wavehdr[i], 0, sizeof(WAVEHDR)); + g_wavehdr[i].dwUser = FALSE; + g_wavehdr[i].lpData = &(bptr[i*blen]); + g_wavehdr[i].dwBufferLength = blen; + g_wavehdr[i].dwFlags = 0; + g_wavehdr[i].dwLoops = 0; + res = waveOutPrepareHeader(g_wave_handle, &g_wavehdr[i], + sizeof(WAVEHDR)); + check_wave_error(res, "waveOutPrepareHeader"); + } + + res = waveOutGetDevCaps((UINT)g_wave_handle, &caps, sizeof(caps)); + check_wave_error(res, "waveOutGetDevCaps"); + printf("Using %s\n", caps.szPname); + printf(" Bits per Sample = %d. Channels = %d\n", + wavefmt.wBitsPerSample, wavefmt.nChannels); + printf(" Sampling rate = %d, avg_bytes_per_sec = %d\n", + (int)wavefmt.nSamplesPerSec, (int)wavefmt.nAvgBytesPerSec); + + set_audio_rate(g_audio_rate); + +} + +void +win32_send_audio2(byte *ptr, int size) +{ + int found; + int res; + int i; + + found = 0; + for(i = 0; i < NUM_WAVE_HEADERS; i++) { + if(g_wavehdr[i].dwUser == FALSE) { + found = 1; + break; + } + } + + if(!found) { + /* all audio buffers busy, just get out */ + return; + } + + memcpy(g_wavehdr[i].lpData, ptr, size); + g_wavehdr[i].dwBufferLength = size; + g_wavehdr[i].dwUser = TRUE; + + res = waveOutWrite(g_wave_handle, &g_wavehdr[i], sizeof(g_wavehdr)); + check_wave_error(res, "waveOutWrite"); + + return; +} + +int +win32_send_audio(byte *ptr, int in_size) +{ + int size; + int tmpsize; + + size = in_size; + while(size > 0) { + tmpsize = size; + if(size > g_win32snd_buflen) { + tmpsize = g_win32snd_buflen; + } + win32_send_audio2(ptr, tmpsize); + ptr += tmpsize; + size = size - tmpsize; + } + + return in_size; +} + +#endif /* __CYGWIN */ diff --git a/src/windriver.c b/src/windriver.c new file mode 100644 index 0000000..787c9b6 --- /dev/null +++ b/src/windriver.c @@ -0,0 +1,637 @@ +/************************************************************************/ +/* KEGS: Apple //gs Emulator */ +/* Copyright 2002 by Kent Dickey */ +/* */ +/* This code is covered by the GNU GPL */ +/* */ +/* The KEGS web page is kegs.sourceforge.net */ +/* You may contact the author at: kadickey@alumni.princeton.edu */ +/************************************************************************/ + +const char rcsid_windriver_c[] = "@(#)$KmKId: windriver.c,v 1.8 2004-03-23 17:25:37-05 kentd Exp $"; + +#define WIN32_LEAN_AND_MEAN /* Tell windows we want less header gunk */ +#define STRICT /* Tell Windows we want compile type checks */ + +#include +#include +#include +#include +#include + +#include "defc.h" +#include "protos_windriver.h" + +extern int Verbose; + +extern int g_warp_pointer; +extern int g_screen_depth; +extern int g_force_depth; +int g_screen_mdepth = 0; + +extern int g_quit_sim_now; + +int g_use_shmem = 1; +int g_has_focus = 0; +int g_auto_repeat_on = -1; + +extern Kimage g_mainwin_kimage; + +HDC g_main_dc; +HDC g_main_cdc; +int g_main_height = 0; + +int g_win_capslock_down = 0; + +extern word32 g_palette_8to1624[256]; +extern word32 g_a2palette_8to1624[256]; + +extern word32 g_full_refresh_needed; + +extern int g_border_sides_refresh_needed; +extern int g_border_special_refresh_needed; +extern int g_status_refresh_needed; + +extern int g_lores_colors[]; +extern int g_cur_a2_stat; + +extern int g_a2vid_palette; + +extern int g_installed_full_superhires_colormap; + +extern int g_screen_redraw_skip_amt; + +extern word32 g_a2_screen_buffer_changed; + +HWND g_hwnd_main; +BITMAPINFO *g_bmapinfo_ptr = 0; +volatile BITMAPINFOHEADER *g_bmaphdr_ptr = 0; + +int g_num_a2_keycodes = 0; + +extern char *g_status_ptrs[MAX_STATUS_LINES]; + +int g_win_button_states = 0; + + +/* this table is used to search for the Windows VK_* in col 1 or 2 */ +/* flags bit 8 is or'ed into the VK, so we can distinguish keypad keys */ +/* regardless of numlock */ +int g_a2_key_to_wsym[][3] = { + { 0x35, VK_ESCAPE, 0 }, + { 0x7a, VK_F1, 0 }, + { 0x7b, VK_F2, 0 }, + { 0x63, VK_F3, 0 }, + { 0x76, VK_F4, 0 }, + { 0x60, VK_F5, 0 }, + { 0x61, VK_F6, 0 }, + { 0x62, VK_F7, 0 }, + { 0x64, VK_F8, 0 }, + { 0x65, VK_F9, 0 }, + { 0x6d, VK_F10, 0 }, + { 0x67, VK_F11, 0 }, + { 0x6f, VK_F12, 0 }, + { 0x69, VK_F13, 0 }, + { 0x6b, VK_F14, 0 }, + { 0x71, VK_F15, 0 }, + { 0x7f, VK_PAUSE, VK_CANCEL+0x100 }, + + { 0x32, 0xc0, 0 }, /* '`' */ + { 0x12, '1', 0 }, + { 0x13, '2', 0 }, + { 0x14, '3', 0 }, + { 0x15, '4', 0 }, + { 0x17, '5', 0 }, + { 0x16, '6', 0 }, + { 0x1a, '7', 0 }, + { 0x1c, '8', 0 }, + { 0x19, '9', 0 }, + { 0x1d, '0', 0 }, + { 0x1b, 0xbd, 0 }, /* '-' */ + { 0x18, 0xbb, 0 }, /* '=' */ + { 0x33, VK_BACK, 0 }, /* backspace */ + { 0x72, VK_INSERT+0x100, 0 }, /* Insert key */ +/* { 0x73, XK_Home, 0 }, alias VK_HOME to be KP_Equal! */ + { 0x74, VK_PRIOR+0x100, 0 }, /* pageup */ + { 0x47, VK_NUMLOCK, VK_NUMLOCK+0x100 }, /* clear */ + { 0x51, VK_HOME+0x100, 0 }, /* KP_equal is HOME key */ + { 0x4b, VK_DIVIDE, VK_DIVIDE+0x100 }, + { 0x43, VK_MULTIPLY, VK_MULTIPLY+0x100 }, + + { 0x30, VK_TAB, 0 }, + { 0x0c, 'Q', 0 }, + { 0x0d, 'W', 0 }, + { 0x0e, 'E', 0 }, + { 0x0f, 'R', 0 }, + { 0x11, 'T', 0 }, + { 0x10, 'Y', 0 }, + { 0x20, 'U', 0 }, + { 0x22, 'I', 0 }, + { 0x1f, 'O', 0 }, + { 0x23, 'P', 0 }, + { 0x21, 0xdb, 0 }, /* [ */ + { 0x1e, 0xdd, 0 }, /* ] */ + { 0x2a, 0xdc, 0 }, /* backslash, bar */ + { 0x75, VK_DELETE+0x100, 0 }, + { 0x77, VK_END+0x100, VK_END }, + { 0x79, VK_NEXT+0x100, 0 }, + { 0x59, VK_NUMPAD7, VK_HOME }, + { 0x5b, VK_NUMPAD8, VK_UP }, + { 0x5c, VK_NUMPAD9, VK_PRIOR }, + { 0x4e, VK_SUBTRACT, VK_SUBTRACT+0x100 }, + + // { 0x39, VK_CAPITAL, 0 }, // Handled specially! + { 0x00, 'A', 0 }, + { 0x01, 'S', 0 }, + { 0x02, 'D', 0 }, + { 0x03, 'F', 0 }, + { 0x05, 'G', 0 }, + { 0x04, 'H', 0 }, + { 0x26, 'J', 0 }, + { 0x28, 'K', 0 }, + { 0x25, 'L', 0 }, + { 0x29, 0xba, 0 }, /* ; */ + { 0x27, 0xde, 0 }, /* single quote */ + { 0x24, VK_RETURN, 0 }, + { 0x56, VK_NUMPAD4, VK_LEFT }, + { 0x57, VK_NUMPAD5, VK_CLEAR }, + { 0x58, VK_NUMPAD6, VK_RIGHT }, + { 0x45, VK_ADD, 0 }, + + { 0x38, VK_SHIFT, 0 }, + { 0x06, 'Z', 0 }, + { 0x07, 'X', 0 }, + { 0x08, 'C', 0 }, + { 0x09, 'V', 0 }, + { 0x0b, 'B', 0 }, + { 0x2d, 'N', 0 }, + { 0x2e, 'M', 0 }, + { 0x2b, 0xbc, 0 }, /* , */ + { 0x2f, 0xbe, 0 }, /* . */ + { 0x2c, 0xbf, 0 }, /* / */ + { 0x3e, VK_UP+0x100, 0 }, + { 0x53, VK_NUMPAD1, VK_END }, + { 0x54, VK_NUMPAD2, VK_DOWN }, + { 0x55, VK_NUMPAD3, VK_NEXT }, + + { 0x36, VK_CONTROL, VK_CONTROL+0x100 }, + { 0x3a, VK_SNAPSHOT+0x100, VK_MENU+0x100 },/* Opt=prntscrn or alt-r */ + { 0x37, VK_SCROLL, VK_MENU }, /* Command=scr_lock or alt-l */ + { 0x31, ' ', 0 }, + { 0x3b, VK_LEFT+0x100, 0 }, + { 0x3d, VK_DOWN+0x100, 0 }, + { 0x3c, VK_RIGHT+0x100, 0 }, + { 0x52, VK_NUMPAD0, VK_INSERT }, + { 0x41, VK_DECIMAL, VK_DECIMAL }, + { 0x4c, VK_RETURN+0x100, 0 }, + { -1, -1, -1 } +}; + +int +win_update_mouse(int x, int y, int button_states, int buttons_valid) +{ + int buttons_changed; + + buttons_changed = ((g_win_button_states & buttons_valid) != + button_states); + g_win_button_states = (g_win_button_states & ~buttons_valid) | + (button_states & buttons_valid); + if(g_warp_pointer && (x == A2_WINDOW_WIDTH/2) && + (y == A2_WINDOW_HEIGHT/2) && (!buttons_changed) ) { + /* tell adb routs to recenter but ignore this motion */ + update_mouse(x, y, 0, -1); + return 0; + } + return update_mouse(x, y, button_states, buttons_valid & 7); +} + +void +win_event_mouse(WPARAM wParam, LPARAM lParam) +{ + POINT pt; + word32 flags; + int buttons; + int x, y; + int motion; + + flags = wParam; + x = LOWORD(lParam) - BASE_MARGIN_LEFT; + y = HIWORD(lParam) - BASE_MARGIN_TOP; + + buttons = (flags & 1) + + (((flags >> 1) & 1) << 2) + + (((flags >> 4) & 1) << 1); +#if 0 + printf("Mouse at %d, %d fl: %08x, but: %d\n", x, y, flags, buttons); +#endif + motion = win_update_mouse(x, y, buttons, 7); + + if(motion && g_warp_pointer) { + /* move mouse to center of screen */ + pt.x = BASE_MARGIN_LEFT + A2_WINDOW_WIDTH/2; + pt.y = BASE_MARGIN_TOP + A2_WINDOW_HEIGHT/2; + ClientToScreen(g_hwnd_main, &pt); + SetCursorPos(pt.x, pt.y); + } +} + +void +win_event_key(HWND hwnd, UINT raw_vk, BOOL down, int repeat, UINT flags) +{ + word32 vk; + int a2code; + int is_up; + int capslock_down; + int i; + + if((flags & 0x4000) && down) { + /* auto-repeating, just ignore it */ + return; + } + + vk = raw_vk + (flags & 0x100); +#if 0 + printf("Key event, vk=%04x, down:%d, repeat: %d, flags: %08x\n", + vk, down, repeat, flags); +#endif + + /* remap a few keys here.. sigh */ + if((vk & 0xff) == VK_APPS) { + /* remap to command */ + vk = VK_MENU; + } + + if((vk & 0xff) == VK_CAPITAL) { + // Windows gives us up-and-down events of the actual key + // Use GetKeyState to get the true toggle state, and pass + // that on to the adb interface + capslock_down = GetKeyState(VK_CAPITAL) & 0x01; + if(capslock_down != g_win_capslock_down) { + g_win_capslock_down = capslock_down; + adb_physical_key_update(0x39, !capslock_down); + } + + return; // Do no more processing! + } + + /* search a2key_to_wsym to find wsym in col 1 or 2 */ + i = 0; + is_up = !down; + for(i = g_num_a2_keycodes-1; i >= 0; i--) { + a2code = g_a2_key_to_wsym[i][0]; + if((vk == g_a2_key_to_wsym[i][1]) || + (vk == g_a2_key_to_wsym[i][2])) { + vid_printf("Found vk:%04x = %02x\n", vk, a2code); + adb_physical_key_update(a2code, is_up); + return; + } + } + printf("VK: %04x unknown\n", vk); +} + +void +win_event_quit(HWND hwnd) +{ + g_quit_sim_now = 1; + my_exit(0); +} + +void +win_event_redraw() +{ + g_full_refresh_needed = -1; + g_a2_screen_buffer_changed = -1; + g_status_refresh_needed = 1; + g_border_sides_refresh_needed = 1; + g_border_special_refresh_needed = 1; +} + +LRESULT CALLBACK +win_event_handler(HWND hwnd, UINT umsg, WPARAM wParam, LPARAM lParam) +{ + switch(umsg) { + case WM_MOUSEMOVE: + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + case WM_MBUTTONDOWN: + case WM_MBUTTONUP: + case WM_RBUTTONDOWN: + case WM_RBUTTONUP: + win_event_mouse(wParam, lParam); + return 0; + case WM_PAINT: + win_event_redraw(); + break; + } + switch(umsg) { + HANDLE_MSG(hwnd, WM_KEYUP, win_event_key); + HANDLE_MSG(hwnd, WM_KEYDOWN, win_event_key); + HANDLE_MSG(hwnd, WM_SYSKEYUP, win_event_key); + HANDLE_MSG(hwnd, WM_SYSKEYDOWN, win_event_key); + HANDLE_MSG(hwnd, WM_DESTROY, win_event_quit); + } + +#if 0 + switch(umsg) { + case WM_NCACTIVATE: + case WM_NCHITTEST: + case WM_NCMOUSEMOVE: + case WM_SETCURSOR: + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + case WM_RBUTTONDOWN: + case WM_CONTEXTMENU: + case WM_RBUTTONUP: + case WM_MBUTTONDOWN: + case WM_MBUTTONUP: + case WM_PAINT: + + break; + default: + printf("Got umsg2: %d\n", umsg); + } +#endif + + return DefWindowProc(hwnd, umsg, wParam, lParam); +} + + +int +main(int argc, char **argv) +{ + WNDCLASS wndclass; + RECT rect; + int height; + + InitCommonControls(); + + wndclass.style = 0; + wndclass.lpfnWndProc = (WNDPROC)win_event_handler; + wndclass.cbClsExtra = 0; + wndclass.cbWndExtra = 0; + wndclass.hInstance = GetModuleHandle(NULL); + wndclass.hIcon = LoadIcon((HINSTANCE)NULL, IDI_APPLICATION); + wndclass.hCursor = LoadCursor((HINSTANCE) NULL, IDC_ARROW); + wndclass.hbrBackground = GetStockObject(WHITE_BRUSH); + wndclass.lpszMenuName = NULL; + wndclass.lpszClassName = "kegswin"; + + // Register the window + if(!RegisterClass(&wndclass)) { + printf("Registering window failed\n"); + exit(1); + } + + height = X_A2_WINDOW_HEIGHT + (MAX_STATUS_LINES * 16) + 32; + g_main_height = height; + + g_hwnd_main = CreateWindow("kegswin", "KEGSWIN - Apple //gs Emulator", + WS_TILED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX, + CW_USEDEFAULT, CW_USEDEFAULT, + X_A2_WINDOW_WIDTH, height, + NULL, NULL, GetModuleHandle(NULL), NULL); + + printf("g_hwnd_main = %p, height = %d\n", g_hwnd_main, height); + GetWindowRect(g_hwnd_main, &rect); + printf("...rect is: %ld, %ld, %ld, %ld\n", rect.left, rect.top, + rect.right, rect.bottom); + + g_main_dc = GetDC(g_hwnd_main); + + SetTextColor(g_main_dc, 0); + SetBkColor(g_main_dc, 0xffffff); + + g_main_cdc = CreateCompatibleDC(g_main_dc); + + g_screen_depth = 24; + g_screen_mdepth = 32; + + + // Call kegsmain + return kegsmain(argc, argv); +} + + +void +check_input_events() +{ + MSG msg; + + while(PeekMessage(&msg, g_hwnd_main, 0, 0, PM_NOREMOVE)) { + if(GetMessage(&msg, g_hwnd_main, 0, 0) > 0) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } else { + printf("GetMessage returned <= 0\n"); + my_exit(2); + } + } + + return; +} + + +void +x_update_color(int col_num, int red, int green, int blue, word32 rgb) +{ +} + +void +x_update_physical_colormap() +{ +} + +void +show_xcolor_array() +{ + int i; + + for(i = 0; i < 256; i++) { + printf("%02x: %08x\n", i, g_palette_8to1624[i]); + } +} + + +void +xdriver_end() +{ + printf("xdriver_end\n"); +} + + +void +x_get_kimage(Kimage *kimage_ptr) +{ + byte *ptr; + int width; + int height; + int depth, mdepth; + int size; + + width = kimage_ptr->width_req; + height = kimage_ptr->height; + depth = kimage_ptr->depth; + mdepth = kimage_ptr->mdepth; + + size = 0; + if(depth == g_screen_depth) { + /* Use g_bmapinfo_ptr, adjusting width, height */ + g_bmaphdr_ptr->biWidth = width; + g_bmaphdr_ptr->biHeight = -height; + kimage_ptr->dev_handle = CreateDIBSection(g_main_dc, + g_bmapinfo_ptr, DIB_RGB_COLORS, + (VOID **)&(kimage_ptr->data_ptr), NULL, 0); + } else { + /* allocate buffers for video.c to draw into */ + + size = (width*height*mdepth) >> 3; + ptr = (byte *)malloc(size); + + if(ptr == 0) { + printf("malloc for data failed, mdepth: %d\n", mdepth); + exit(2); + } + + kimage_ptr->data_ptr = ptr; + + kimage_ptr->dev_handle = (void *)-1; + + } + printf("kim: %p, dev:%p data: %p, size: %08x\n", kimage_ptr, + kimage_ptr->dev_handle, kimage_ptr->data_ptr, size); + + return; +} + + +void +dev_video_init() +{ + int extra_size; + int lores_col; + int a2code; + int i; + + printf("Preparing graphics system\n"); + + g_num_a2_keycodes = 0; + for(i = 0; i < 0x7f; i++) { + a2code = g_a2_key_to_wsym[i][0]; + if(a2code < 0) { + g_num_a2_keycodes = i; + } + } + + g_screen_depth = 24; + g_screen_mdepth = 32; + + extra_size = sizeof(RGBQUAD); + if(g_screen_depth == 8) { + extra_size = 256 * sizeof(RGBQUAD); + } + g_bmapinfo_ptr = (BITMAPINFO *)GlobalAlloc(GPTR, + sizeof(BITMAPINFOHEADER) + extra_size); + + g_bmaphdr_ptr = (BITMAPINFOHEADER *)g_bmapinfo_ptr; + g_bmaphdr_ptr->biSize = sizeof(BITMAPINFOHEADER); + g_bmaphdr_ptr->biWidth = A2_WINDOW_WIDTH; + g_bmaphdr_ptr->biHeight = -A2_WINDOW_HEIGHT; + g_bmaphdr_ptr->biPlanes = 1; + g_bmaphdr_ptr->biBitCount = g_screen_mdepth; + g_bmaphdr_ptr->biCompression = BI_RGB; + g_bmaphdr_ptr->biClrUsed = 0; + + video_get_kimages(); + + if(g_screen_depth != 8) { + // Allocate g_mainwin_kimage + video_get_kimage(&g_mainwin_kimage, 0, g_screen_depth, + g_screen_mdepth); + } + + for(i = 0; i < 256; i++) { + lores_col = g_lores_colors[i & 0xf]; + video_update_color_raw(i, lores_col); + g_a2palette_8to1624[i] = g_palette_8to1624[i]; + } + + g_installed_full_superhires_colormap = 1; + + ShowWindow(g_hwnd_main, SW_SHOWDEFAULT); + UpdateWindow(g_hwnd_main); + + printf("Done with dev_video_init\n"); + fflush(stdout); + +} + +void +x_redraw_status_lines() +{ + COLORREF oldtextcolor, oldbkcolor; + char *buf; + int line; + int len; + int height; + int margin; + + height = 16; + margin = 0; + + oldtextcolor = SetTextColor(g_main_dc, 0); + oldbkcolor = SetBkColor(g_main_dc, 0xffffff); + for(line = 0; line < MAX_STATUS_LINES; line++) { + buf = g_status_ptrs[line]; + if(buf != 0) { + len = strlen(buf); + TextOut(g_main_dc, 10, X_A2_WINDOW_HEIGHT + + height*line + margin, buf, len); + } + } + SetTextColor(g_main_dc, oldtextcolor); + SetBkColor(g_main_dc, oldbkcolor); +} + + +void +x_push_kimage(Kimage *kimage_ptr, int destx, int desty, int srcx, int srcy, + int width, int height) +{ + void *bitm_old; + POINT point; + + point.x = 0; + point.y = 0; + ClientToScreen(g_hwnd_main, &point); + bitm_old = SelectObject(g_main_cdc, kimage_ptr->dev_handle); + + BitBlt(g_main_dc, destx, desty, width, height, + g_main_cdc, srcx, srcy, SRCCOPY); + + SelectObject(g_main_cdc, bitm_old); +} + +void +x_push_done() +{ +} + +void +x_auto_repeat_on(int must) +{ +} + +void +x_auto_repeat_off(int must) +{ +} + +void +x_hide_pointer(int do_hide) +{ + if(do_hide) { + ShowCursor(0); + } else { + ShowCursor(1); + } +} diff --git a/src/winresource.h b/src/winresource.h new file mode 100644 index 0000000..64e56e3 --- /dev/null +++ b/src/winresource.h @@ -0,0 +1,42 @@ +//{{NO_DEPENDENCIES}} +// $Id: $ +// Microsoft Developer Studio generated include file. +// Used by win32.rc +// +#define IDD_ABOUT_DIALOG 101 +#define IDC_KEGS32 102 +#define IDR_TOOLBAR 103 +#define IDD_DLG_DISKCONF 104 +#define IDR_ACCEL 105 +#define ID_TOOLBAR 5000 +#define ID_STATUSBAR 5001 +#define IDC_EDIT_S5D1 10051 +#define IDC_EDIT_S5D2 10052 +#define IDC_EDIT_S6D1 10061 +#define IDC_EDIT_S6D2 10062 +#define IDC_EDIT_S7D1 10071 +#define IDC_EDIT_S7D2 10072 +#define IDC_BTN_S5D1 11051 +#define IDC_BTN_S5D2 11052 +#define IDC_BTN_S6D1 11061 +#define IDC_BTN_S6D2 11062 +#define IDC_BTN_S7D1 11071 +#define IDC_BTN_S7D2 11072 +#define ID_HELP_ABOUT 40001 +#define ID_FILE_EXIT 40002 +#define ID_FILE_DISK 40003 +#define ID_FILE_SENDRESET 40004 +#define ID_FILE_JOYSTICK 40005 +#define ID_FILE_DEBUGSTAT 40006 +#define ID_FILE_FULLSCREEN 40012 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 111 +#define _APS_NEXT_COMMAND_VALUE 40013 +#define _APS_NEXT_CONTROL_VALUE 1003 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/src/xdriver.c b/src/xdriver.c new file mode 100644 index 0000000..7e5f7be --- /dev/null +++ b/src/xdriver.c @@ -0,0 +1,1306 @@ +/************************************************************************/ +/* KEGS: Apple //gs Emulator */ +/* Copyright 2002 by Kent Dickey */ +/* */ +/* This code is covered by the GNU GPL */ +/* */ +/* The KEGS web page is kegs.sourceforge.net */ +/* You may contact the author at: kadickey@alumni.princeton.edu */ +/************************************************************************/ + +const char rcsid_xdriver_c[] = "@(#)$KmKId: xdriver.c,v 1.181 2004-03-23 17:25:25-05 kentd Exp $"; + +# if !defined(__CYGWIN__) && !defined(__POWERPC__) +/* No shared memory on Cygwin */ +# define X_SHARED_MEM +#endif /* CYGWIN */ + +#include +#include +#include +#include +#include +#include + +#ifdef X_SHARED_MEM +# include +# include +# include +#endif + +int XShmQueryExtension(Display *display); +void _XInitImageFuncPtrs(XImage *xim); + +#include "defc.h" +#include "protos_xdriver.h" + +#define FONT_NAME_STATUS "8x13" + +extern int Verbose; + +extern int g_warp_pointer; +extern int g_screen_depth; +extern int g_force_depth; +int g_screen_mdepth = 0; + +extern int _Xdebug; + +extern int g_send_sound_to_file; + +extern int g_quit_sim_now; + +int g_has_focus = 0; +int g_auto_repeat_on = -1; +int g_x_shift_control_state = 0; + + +Display *g_display = 0; +Visual *g_vis = 0; +Window g_a2_win; +GC g_a2_winGC; +XFontStruct *g_text_FontSt; +Colormap g_a2_colormap = 0; +Colormap g_default_colormap = 0; +int g_needs_cmap = 0; + +extern word32 g_red_mask; +extern word32 g_green_mask; +extern word32 g_blue_mask; +extern int g_red_left_shift; +extern int g_green_left_shift; +extern int g_blue_left_shift; +extern int g_red_right_shift; +extern int g_green_right_shift; +extern int g_blue_right_shift; + +#ifdef X_SHARED_MEM +int g_use_shmem = 1; +#else +int g_use_shmem = 0; +#endif + +extern Kimage g_mainwin_kimage; + +extern int Max_color_size; + +XColor g_xcolor_a2vid_array[256]; + +extern word32 g_palette_8to1624[256]; +extern word32 g_a2palette_8to1624[256]; + +int g_alt_left_up = 1; +int g_alt_right_up = 1; + +extern word32 g_full_refresh_needed; + +extern int g_border_sides_refresh_needed; +extern int g_border_special_refresh_needed; +extern int g_status_refresh_needed; + +extern int g_lores_colors[]; +extern int g_cur_a2_stat; + +extern int g_a2vid_palette; + +extern int g_installed_full_superhires_colormap; + +extern int g_screen_redraw_skip_amt; + +extern word32 g_a2_screen_buffer_changed; + +extern char *g_status_ptrs[MAX_STATUS_LINES]; + +Cursor g_cursor; +Pixmap g_cursor_shape; +Pixmap g_cursor_mask; + +XColor g_xcolor_black = { 0, 0x0000, 0x0000, 0x0000, DoRed|DoGreen|DoBlue, 0 }; +XColor g_xcolor_white = { 0, 0xffff, 0xffff, 0xffff, DoRed|DoGreen|DoBlue, 0 }; + +int g_depth_attempt_list[] = { 16, 24, 15, 8 }; + + +#define X_EVENT_LIST_ALL_WIN \ + (ExposureMask | ButtonPressMask | ButtonReleaseMask | \ + OwnerGrabButtonMask | KeyPressMask | KeyReleaseMask | \ + KeymapStateMask | ColormapChangeMask | FocusChangeMask) + +#define X_BASE_WIN_EVENT_LIST \ + (X_EVENT_LIST_ALL_WIN | PointerMotionMask | ButtonMotionMask) + +#define X_A2_WIN_EVENT_LIST \ + (X_BASE_WIN_EVENT_LIST) + +int g_num_a2_keycodes = 0; + +int a2_key_to_xsym[][3] = { + { 0x35, XK_Escape, 0 }, + { 0x7a, XK_F1, 0 }, + { 0x7b, XK_F2, 0 }, + { 0x63, XK_F3, 0 }, + { 0x76, XK_F4, 0 }, + { 0x60, XK_F5, 0 }, + { 0x61, XK_F6, 0 }, + { 0x62, XK_F7, 0 }, + { 0x64, XK_F8, 0 }, + { 0x65, XK_F9, 0 }, + { 0x6d, XK_F10, 0 }, + { 0x67, XK_F11, 0 }, + { 0x6f, XK_F12, 0 }, + { 0x69, XK_F13, 0 }, + { 0x6b, XK_F14, 0 }, + { 0x71, XK_F15, 0 }, + { 0x7f, XK_Pause, XK_Break }, + { 0x32, '`', '~' }, /* Key number 18? */ + { 0x12, '1', '!' }, + { 0x13, '2', '@' }, + { 0x14, '3', '#' }, + { 0x15, '4', '$' }, + { 0x17, '5', '%' }, + { 0x16, '6', '^' }, + { 0x1a, '7', '&' }, + { 0x1c, '8', '*' }, + { 0x19, '9', '(' }, + { 0x1d, '0', ')' }, + { 0x1b, '-', '_' }, + { 0x18, '=', '+' }, + { 0x33, XK_BackSpace, 0 }, + { 0x72, XK_Insert, 0 }, /* Help? */ +/* { 0x73, XK_Home, 0 }, alias XK_Home to be XK_KP_Equal! */ + { 0x74, XK_Page_Up, 0 }, + { 0x47, XK_Num_Lock, XK_Clear }, /* Clear */ + { 0x51, XK_KP_Equal, XK_Home }, /* Note XK_Home alias! */ + { 0x4b, XK_KP_Divide, 0 }, + { 0x43, XK_KP_Multiply, 0 }, + + { 0x30, XK_Tab, 0 }, + { 0x0c, 'q', 'Q' }, + { 0x0d, 'w', 'W' }, + { 0x0e, 'e', 'E' }, + { 0x0f, 'r', 'R' }, + { 0x11, 't', 'T' }, + { 0x10, 'y', 'Y' }, + { 0x20, 'u', 'U' }, + { 0x22, 'i', 'I' }, + { 0x1f, 'o', 'O' }, + { 0x23, 'p', 'P' }, + { 0x21, '[', '{' }, + { 0x1e, ']', '}' }, + { 0x2a, 0x5c, '|' }, /* backslash, bar */ + { 0x75, XK_Delete, 0 }, + { 0x77, XK_End, 0 }, + { 0x79, XK_Page_Down, 0 }, + { 0x59, XK_KP_7, XK_KP_Home }, + { 0x5b, XK_KP_8, XK_KP_Up }, + { 0x5c, XK_KP_9, XK_KP_Page_Up }, + { 0x4e, XK_KP_Subtract, 0 }, + + { 0x39, XK_Caps_Lock, 0 }, + { 0x00, 'a', 'A' }, + { 0x01, 's', 'S' }, + { 0x02, 'd', 'D' }, + { 0x03, 'f', 'F' }, + { 0x05, 'g', 'G' }, + { 0x04, 'h', 'H' }, + { 0x26, 'j', 'J' }, + { 0x28, 'k', 'K' }, + { 0x25, 'l', 'L' }, + { 0x29, ';', ':' }, + { 0x27, 0x27, '"' }, /* single quote */ + { 0x24, XK_Return, 0 }, + { 0x56, XK_KP_4, XK_KP_Left }, + { 0x57, XK_KP_5, 0 }, + { 0x58, XK_KP_6, XK_KP_Right }, + { 0x45, XK_KP_Add, 0 }, + + { 0x38, XK_Shift_L, XK_Shift_R }, + { 0x06, 'z', 'Z' }, + { 0x07, 'x', 'X' }, + { 0x08, 'c', 'C' }, + { 0x09, 'v', 'V' }, + { 0x0b, 'b', 'B' }, + { 0x2d, 'n', 'N' }, + { 0x2e, 'm', 'M' }, + { 0x2b, ',', '<' }, + { 0x2f, '.', '>' }, + { 0x2c, '/', '?' }, + { 0x3e, XK_Up, 0 }, + { 0x53, XK_KP_1, XK_KP_End }, + { 0x54, XK_KP_2, XK_KP_Down }, + { 0x55, XK_KP_3, XK_KP_Page_Down }, + + { 0x36, XK_Control_L, XK_Control_R }, + { 0x3a, XK_Print, XK_Sys_Req }, /* Option */ + { 0x37, XK_Scroll_Lock, 0 }, /* Command */ + { 0x31, ' ', 0 }, + { 0x3b, XK_Left, 0 }, + { 0x3d, XK_Down, 0 }, + { 0x3c, XK_Right, 0 }, + { 0x52, XK_KP_0, XK_KP_Insert }, + { 0x41, XK_KP_Decimal, XK_KP_Separator }, + { 0x4c, XK_KP_Enter, 0 }, + { -1, -1, -1 } +}; + +int +main(int argc, char **argv) +{ + return kegsmain(argc, argv); +} + + +#define MAKE_2(val) ( (val << 8) + val) + +void +x_update_color(int col_num, int red, int green, int blue, word32 rgb) +{ + XColor *xcol; + + xcol = &(g_xcolor_a2vid_array[col_num]); + xcol->red = MAKE_2(red); + xcol->green = MAKE_2(green); + xcol->blue = MAKE_2(blue); + xcol->flags = DoRed | DoGreen | DoBlue; +} + +void +x_update_physical_colormap() +{ + if(g_needs_cmap) { + XStoreColors(g_display, g_a2_colormap, + &g_xcolor_a2vid_array[0], Max_color_size); + } +} + +void +show_xcolor_array() +{ + int i; + + for(i = 0; i < 256; i++) { + printf("%02x: %08x\n", i, g_palette_8to1624[i]); + +#if 0 + printf("%02x: %04x %04x %04x, %02x %x\n", + i, xcolor_array[i].red, xcolor_array[i].green, + xcolor_array[i].blue, (word32)xcolor_array[i].pixel, + xcolor_array[i].flags); +#endif + } +} + + +int +my_error_handler(Display *display, XErrorEvent *ev) +{ + char msg[1024]; + XGetErrorText(display, ev->error_code, msg, 1000); + printf("X Error code %s\n", msg); + fflush(stdout); + + return 0; +} + +void +xdriver_end() +{ + + printf("xdriver_end\n"); + if(g_display) { + x_auto_repeat_on(1); + XFlush(g_display); + } +} + +void +show_colormap(char *str, Colormap cmap, int index1, int index2, int index3) +{ + XColor xcol; + int i; + int pix; + + printf("Show colormap: %08x = %s, cmap cells: %d,%d,%d\n", + (int)cmap, str, index1, index2, index3); + for(i = 0; i < index1 + index2 + index3; i++) { + pix = i; + if(i >= index1) { + pix = (i-index1)*index1; + if(i >= (index1 + index2)) { + pix = (i - index1 - index2)*index2*index1; + } + } + if(i == 0 && index1 < 250) { + pix = 0x842; + } + xcol.pixel = pix; + XQueryColor(g_display, cmap, &xcol); + printf("Cell %03x: pix: %03x, R:%04x, G:%04x, B:%04x\n", + i, (int)xcol.pixel, xcol.red, xcol.green, xcol.blue); + } +} + +void +x_badpipe(int signum) +{ + /* restore normal sigpipe handling */ + signal(SIGPIPE, SIG_DFL); + + /* attempt to xset r */ + system("xset r"); + my_exit(5); +} + +void +dev_video_init() +{ + int tmp_array[0x80]; + XGCValues new_gc; + XSetWindowAttributes win_attr; + XSizeHints my_winSizeHints; + XClassHint my_winClassHint; + XTextProperty my_winText; + XVisualInfo *visualList; + char **font_ptr; + char cursor_data; + word32 create_win_list; + int depth; + int len; + int cmap_alloc_amt; + int cnt; + int font_height; + int base_height; + int screen_num; + char *myTextString[1]; + word32 lores_col; + int ret; + int i; + int keycode; + + printf("Preparing X Windows graphics system\n"); + ret = 0; + + signal(SIGPIPE, x_badpipe); + + g_num_a2_keycodes = 0; + for(i = 0; i <= 0x7f; i++) { + tmp_array[i] = 0; + } + for(i = 0; i < 0x7f; i++) { + keycode = a2_key_to_xsym[i][0]; + if(keycode < 0) { + g_num_a2_keycodes = i; + break; + } else if(keycode > 0x7f) { + printf("a2_key_to_xsym[%d] = %02x!\n", i, keycode); + exit(2); + } else { + if(tmp_array[keycode]) { + printf("a2_key_to_x[%d] = %02x used by %d\n", + i, keycode, tmp_array[keycode] - 1); + } + tmp_array[keycode] = i + 1; + } + } + +#if 0 + printf("Setting _Xdebug = 1, makes X synchronous\n"); + _Xdebug = 1; +#endif + + g_display = XOpenDisplay(NULL); + if(g_display == NULL) { + fprintf(stderr, "Can't open display\n"); + exit(1); + } + + vid_printf("Just opened display = %p\n", g_display); + fflush(stdout); + + screen_num = DefaultScreen(g_display); + + len = sizeof(g_depth_attempt_list)/sizeof(int); + if(g_force_depth > 0) { + /* Only use the requested user depth */ + len = 1; + g_depth_attempt_list[0] = g_force_depth; + } + g_vis = 0; + for(i = 0; i < len; i++) { + depth = g_depth_attempt_list[i]; + + g_vis = x_try_find_visual(depth, screen_num, + &visualList); + if(g_vis != 0) { + break; + } + } + if(g_vis == 0) { + fprintf(stderr, "Couldn't find any visuals at any depth!\n"); + exit(2); + } + + g_default_colormap = XDefaultColormap(g_display, screen_num); + if(!g_default_colormap) { + printf("g_default_colormap == 0!\n"); + exit(4); + } + + g_a2_colormap = -1; + cmap_alloc_amt = AllocNone; + if(g_needs_cmap) { + cmap_alloc_amt = AllocAll; + } + g_a2_colormap = XCreateColormap(g_display, + RootWindow(g_display,screen_num), g_vis, + cmap_alloc_amt); + + vid_printf("g_a2_colormap: %08x, main: %08x\n", + (word32)g_a2_colormap, (word32)g_default_colormap); + + if(g_needs_cmap && g_a2_colormap == g_default_colormap) { + printf("A2_colormap = default colormap!\n"); + exit(4); + } + + /* and define cursor */ + cursor_data = 0; + g_cursor_shape = XCreatePixmapFromBitmapData(g_display, + RootWindow(g_display,screen_num), &cursor_data, 1, 1, 1, 0, 1); + g_cursor_mask = XCreatePixmapFromBitmapData(g_display, + RootWindow(g_display,screen_num), &cursor_data, 1, 1, 1, 0, 1); + + g_cursor = XCreatePixmapCursor(g_display, g_cursor_shape, + g_cursor_mask, &g_xcolor_black, &g_xcolor_white, 0, 0); + + XFreePixmap(g_display, g_cursor_shape); + XFreePixmap(g_display, g_cursor_mask); + + XFlush(g_display); + + win_attr.event_mask = X_A2_WIN_EVENT_LIST; + win_attr.colormap = g_a2_colormap; + win_attr.backing_store = WhenMapped; + win_attr.border_pixel = 1; + win_attr.background_pixel = 0; + if(g_warp_pointer) { + win_attr.cursor = g_cursor; + } else { + win_attr.cursor = None; + } + + vid_printf("About to a2_win, depth: %d\n", g_screen_depth); + fflush(stdout); + + create_win_list = CWEventMask | CWBackingStore | CWCursor; + create_win_list |= CWColormap | CWBorderPixel | CWBackPixel; + + base_height = X_A2_WINDOW_HEIGHT + (MAX_STATUS_LINES * 13); + + g_a2_win = XCreateWindow(g_display, RootWindow(g_display, screen_num), + 0, 0, BASE_WINDOW_WIDTH, base_height, + 0, g_screen_depth, InputOutput, g_vis, + create_win_list, &win_attr); + + XSetWindowColormap(g_display, g_a2_win, g_a2_colormap); + + XFlush(g_display); + +/* Check for XShm */ +#ifdef X_SHARED_MEM + if(g_use_shmem) { + ret = XShmQueryExtension(g_display); + if(ret == 0) { + printf("XShmQueryExt ret: %d\n", ret); + printf("not using shared memory\n"); + g_use_shmem = 0; + } else { + printf("Will use shared memory for X\n"); + } + } +#endif + + video_get_kimages(); + if(g_screen_depth != 8) { + video_get_kimage(&g_mainwin_kimage, 0, g_screen_depth, + g_screen_mdepth); + } + + if(!g_use_shmem) { + if(g_screen_redraw_skip_amt < 0) { + g_screen_redraw_skip_amt = 3; + } + printf("Not using shared memory, setting skip_amt = %d\n", + g_screen_redraw_skip_amt); + } + + /* Done with visualList now */ + XFree(visualList); + + for(i = 0; i < 256; i++) { + g_xcolor_a2vid_array[i].pixel = i; + lores_col = g_lores_colors[i & 0xf]; + video_update_color_raw(i, lores_col); + g_a2palette_8to1624[i] = g_palette_8to1624[i]; + } + + x_update_physical_colormap(); + + g_installed_full_superhires_colormap = !g_needs_cmap; + + myTextString[0] = "KEGS"; + + XStringListToTextProperty(myTextString, 1, &my_winText); + + my_winSizeHints.flags = PSize | PMinSize | PMaxSize; + my_winSizeHints.width = BASE_WINDOW_WIDTH; + my_winSizeHints.height = base_height; + my_winSizeHints.min_width = BASE_WINDOW_WIDTH; + my_winSizeHints.min_height = base_height; + my_winSizeHints.max_width = BASE_WINDOW_WIDTH; + my_winSizeHints.max_height = base_height; + my_winClassHint.res_name = "KEGS"; + my_winClassHint.res_class = "KEGS"; + + XSetWMProperties(g_display, g_a2_win, &my_winText, &my_winText, 0, + 0, &my_winSizeHints, 0, &my_winClassHint); + XMapRaised(g_display, g_a2_win); + + XSync(g_display, False); + + g_a2_winGC = XCreateGC(g_display, g_a2_win, 0, (XGCValues *) 0); + font_ptr = XListFonts(g_display, FONT_NAME_STATUS, 4, &cnt); + + vid_printf("act_cnt of fonts: %d\n", cnt); + for(i = 0; i < cnt; i++) { + vid_printf("Font %d: %s\n", i, font_ptr[i]); + } + fflush(stdout); + g_text_FontSt = XLoadQueryFont(g_display, FONT_NAME_STATUS); + vid_printf("font # returned: %08x\n", (word32)(g_text_FontSt->fid)); + font_height = g_text_FontSt->ascent + g_text_FontSt->descent; + vid_printf("font_height: %d\n", font_height); + + vid_printf("widest width: %d\n", g_text_FontSt->max_bounds.width); + + new_gc.font = g_text_FontSt->fid; + new_gc.fill_style = FillSolid; + XChangeGC(g_display, g_a2_winGC, GCFillStyle | GCFont, &new_gc); + + /* XSync(g_display, False); */ +#if 0 +/* MkLinux for Powermac depth 15 has bugs--this was to try to debug them */ + if(g_screen_depth == 15) { + /* draw phony screen */ + ptr16 = (word16 *)dint_main_win; + for(i = 0; i < 320*400; i++) { + ptr16[i] = 0; + } + for(i = 0; i < 400; i++) { + for(j = 0; j < 640; j++) { + sh = (j / 20) & 0x1f; + val = sh; + val = val; + *ptr16++ = val; + } + } + XPutImage(g_display, g_a2_win, g_a2_winGC, xint_main_win, + 0, 0, + BASE_MARGIN_LEFT, BASE_MARGIN_TOP, + 640, 400); + XFlush(g_display); + } +#endif + + + XFlush(g_display); + fflush(stdout); +} + +Visual * +x_try_find_visual(int depth, int screen_num, XVisualInfo **visual_list_ptr) +{ + XVisualInfo *visualList; + XVisualInfo *v_chosen; + XVisualInfo vTemplate; + int visualsMatched; + int mdepth; + int needs_cmap; + int visual_chosen; + int match8, match24; + int i; + + vTemplate.screen = screen_num; + vTemplate.depth = depth; + + visualList = XGetVisualInfo(g_display, + (VisualScreenMask | VisualDepthMask), + &vTemplate, &visualsMatched); + + vid_printf("visuals matched: %d\n", visualsMatched); + if(visualsMatched == 0) { + return (Visual *)0; + } + + visual_chosen = -1; + needs_cmap = 0; + for(i = 0; i < visualsMatched; i++) { + printf("Visual %d\n", i); + printf(" id: %08x, screen: %d, depth: %d, class: %d\n", + (word32)visualList[i].visualid, + visualList[i].screen, + visualList[i].depth, + visualList[i].class); + printf(" red: %08lx, green: %08lx, blue: %08lx\n", + visualList[i].red_mask, + visualList[i].green_mask, + visualList[i].blue_mask); + printf(" cmap size: %d, bits_per_rgb: %d\n", + visualList[i].colormap_size, + visualList[i].bits_per_rgb); + match8 = (visualList[i].class == PseudoColor); + match24 = (visualList[i].class == TrueColor); + if((depth == 8) && match8) { + visual_chosen = i; + Max_color_size = visualList[i].colormap_size; + needs_cmap = 1; + break; + } + if((depth != 8) && match24) { + visual_chosen = i; + Max_color_size = -1; + needs_cmap = 0; + break; + } + } + + if(visual_chosen < 0) { + printf("Couldn't find any good visuals at depth %d!\n", + depth); + return (Visual *)0; + } + + printf("Chose visual: %d, max_colors: %d\n", visual_chosen, + Max_color_size); + + v_chosen = &(visualList[visual_chosen]); + x_set_mask_and_shift(v_chosen->red_mask, &g_red_mask, + &g_red_left_shift, &g_red_right_shift); + x_set_mask_and_shift(v_chosen->green_mask, &g_green_mask, + &g_green_left_shift, &g_green_right_shift); + x_set_mask_and_shift(v_chosen->blue_mask, &g_blue_mask, + &g_blue_left_shift, &g_blue_right_shift); + + g_screen_depth = depth; + mdepth = depth; + if(depth > 8) { + mdepth = 16; + } + if(depth > 16) { + mdepth = 32; + } + g_screen_mdepth = mdepth; + g_needs_cmap = needs_cmap; + *visual_list_ptr = visualList; + + return v_chosen->visual; +} + +void +x_set_mask_and_shift(word32 x_mask, word32 *mask_ptr, int *shift_left_ptr, + int *shift_right_ptr) +{ + int shift; + int i; + + /* Shift until we find first set bit in mask, then remember mask,shift*/ + + shift = 0; + for(i = 0; i < 32; i++) { + if(x_mask & 1) { + /* we're done! */ + break; + } + x_mask = x_mask >> 1; + shift++; + } + *mask_ptr = x_mask; + *shift_left_ptr = shift; + /* Now, calculate shift_right_ptr */ + shift = 0; + x_mask |= 1; // make sure at least one bit is set + while(x_mask < 0x80) { + shift++; + x_mask = x_mask << 1; + } + + *shift_right_ptr = shift; + return; + +} + +int g_xshm_error = 0; + +int +xhandle_shm_error(Display *display, XErrorEvent *event) +{ + g_xshm_error = 1; + return 0; +} + +void +x_get_kimage(Kimage *kimage_ptr) { + if(g_use_shmem) { + g_use_shmem = get_shm(kimage_ptr); + } + if(!g_use_shmem) { + get_ximage(kimage_ptr); + } +} + +int +get_shm(Kimage *kimage_ptr) +{ +#ifdef X_SHARED_MEM + XShmSegmentInfo *seginfo; + XImage *xim; + int (*old_x_handler)(Display *, XErrorEvent *); + int width; + int height; + int depth; + + width = kimage_ptr->width_req; + height = kimage_ptr->height; + depth = kimage_ptr->depth; + + seginfo = (XShmSegmentInfo *)malloc(sizeof(XShmSegmentInfo)); + xim = XShmCreateImage(g_display, g_vis, depth, ZPixmap, + (char *)0, seginfo, width, height); + + /* check mdepth! */ + if(xim->bits_per_pixel != kimage_ptr->mdepth) { + printf("get_shm bits_per_pix: %d != %d\n", + xim->bits_per_pixel, g_screen_mdepth); + } + + vid_printf("xim: %p\n", xim); + kimage_ptr->dev_handle = xim; + kimage_ptr->dev_handle2 = seginfo; + if(xim == 0) { + return 0; + } + + /* It worked, we got it */ + seginfo->shmid = shmget(IPC_PRIVATE, xim->bytes_per_line * xim->height, + IPC_CREAT | 0777); + vid_printf("seginfo->shmid = %d\n", seginfo->shmid); + if(seginfo->shmid < 0) { + XDestroyImage(xim); + return 0; + } + + /* Still working */ + seginfo->shmaddr = (char *)shmat(seginfo->shmid, 0, 0); + vid_printf("seginfo->shmaddr: %p\n", seginfo->shmaddr); + if(seginfo->shmaddr == ((char *) -1)) { + XDestroyImage(xim); + return 0; + } + + /* Still working */ + xim->data = seginfo->shmaddr; + seginfo->readOnly = False; + + /* XShmAttach will trigger X error if server is remote, so catch it */ + g_xshm_error = 0; + old_x_handler = XSetErrorHandler(xhandle_shm_error); + + XShmAttach(g_display, seginfo); + XSync(g_display, False); + + + vid_printf("about to RMID the shmid\n"); + shmctl(seginfo->shmid, IPC_RMID, 0); + + XFlush(g_display); + XSetErrorHandler(old_x_handler); + + if(g_xshm_error) { + XDestroyImage(xim); + /* We could release the shared mem segment, but by doing the */ + /* RMID, it will go away when we die now, so just leave it */ + printf("Not using shared memory\n"); + return 0; + } + + kimage_ptr->data_ptr = (byte *)xim->data; + vid_printf("Sharing memory. xim: %p, xim->data: %p\n", xim, xim->data); + + return 1; +#else + return 0; /* No shared memory */ +#endif /* X_SHARED_MEM */ +} + +void +get_ximage(Kimage *kimage_ptr) +{ + XImage *xim; + byte *ptr; + int width; + int height; + int depth; + int mdepth; + + width = kimage_ptr->width_req; + height = kimage_ptr->height; + depth = kimage_ptr->depth; + mdepth = kimage_ptr->mdepth; + + ptr = (byte *)malloc((width * height * mdepth) >> 3); + + vid_printf("ptr: %p\n", ptr); + + if(ptr == 0) { + printf("malloc for data failed, mdepth: %d\n", mdepth); + exit(2); + } + + kimage_ptr->data_ptr = ptr; + + xim = XCreateImage(g_display, g_vis, depth, ZPixmap, 0, + (char *)ptr, width, height, 8, 0); + +#ifdef KEGS_LITTLE_ENDIAN + xim->byte_order = LSBFirst; +#else + xim->byte_order = MSBFirst; +#endif + _XInitImageFuncPtrs(xim); /* adjust to new byte order */ + + /* check mdepth! */ + if(xim->bits_per_pixel != mdepth) { + printf("shm_ximage bits_per_pix: %d != %d\n", + xim->bits_per_pixel, mdepth); + } + + vid_printf("xim: %p\n", xim); + + kimage_ptr->dev_handle = xim; + + return; +} + + +void +x_redraw_status_lines() +{ + char *buf; + int line; + int height; + int margin; + word32 white, black; + + height = g_text_FontSt->ascent + g_text_FontSt->descent; + margin = g_text_FontSt->ascent; + + white = (g_a2vid_palette << 4) + 0xf; + black = (g_a2vid_palette << 4) + 0x0; + if(g_screen_depth != 8) { + white = (2 << (g_screen_depth - 1)) - 1; + black = 0; + } + XSetForeground(g_display, g_a2_winGC, white); + XSetBackground(g_display, g_a2_winGC, black); + + for(line = 0; line < MAX_STATUS_LINES; line++) { + buf = g_status_ptrs[line]; + if(buf == 0) { + /* skip it */ + continue; + } + XDrawImageString(g_display, g_a2_win, g_a2_winGC, 0, + X_A2_WINDOW_HEIGHT + height*line + margin, + buf, strlen(buf)); + } + + XFlush(g_display); +} + + + +void +x_push_kimage(Kimage *kimage_ptr, int destx, int desty, int srcx, int srcy, + int width, int height) +{ + XImage *xim; + + xim = (XImage *)kimage_ptr->dev_handle; + +#ifdef X_SHARED_MEM + if(g_use_shmem) { + XShmPutImage(g_display, g_a2_win, g_a2_winGC, xim, + srcx, srcy, destx, desty, width, height, False); + } +#endif + if(!g_use_shmem) { + XPutImage(g_display, g_a2_win, g_a2_winGC, xim, + srcx, srcy, destx, desty, width, height); + } +} + +void +x_push_done() +{ + XFlush(g_display); +} + + +#define KEYBUFLEN 128 + +int g_num_check_input_calls = 0; +int g_check_input_flush_rate = 2; + +int +x_update_mouse(int raw_x, int raw_y, int button_states, int buttons_valid) +{ + int x, y; + + x = raw_x - BASE_MARGIN_LEFT; + y = raw_y - BASE_MARGIN_TOP; + + if(g_warp_pointer && (x == A2_WINDOW_WIDTH/2) && + (y == A2_WINDOW_HEIGHT/2) && (buttons_valid == 0) ) { + /* tell adb routs to recenter but ignore this motion */ + update_mouse(x, y, 0, -1); + return 0; + } + return update_mouse(x, y, button_states, buttons_valid & 7); +} + +void +check_input_events() +{ + XEvent ev; + int len; + int motion; + int buttons; + int refresh_needed; + + g_num_check_input_calls--; + if(g_num_check_input_calls < 0) { + len = XPending(g_display); + g_num_check_input_calls = g_check_input_flush_rate; + } else { + len = QLength(g_display); + } + + motion = 0; + refresh_needed = 0; + while(len > 0) { + XNextEvent(g_display, &ev); + len--; + if(len == 0) { + /* Xaccel on linux only buffers one X event */ + /* must look for more now */ + len = XPending(g_display); + } + switch(ev.type) { + case FocusIn: + case FocusOut: + if(ev.xfocus.type == FocusOut) { + /* Allow keyrepeat again! */ + vid_printf("Left window, auto repeat on\n"); + x_auto_repeat_on(0); + g_has_focus = 0; + } else if(ev.xfocus.type == FocusIn) { + /* Allow keyrepeat again! */ + vid_printf("Enter window, auto repeat off\n"); + x_auto_repeat_off(0); + g_has_focus = 1; + } + break; + case EnterNotify: + case LeaveNotify: + /* These events are disabled now */ + printf("Enter/Leave event for winow %08x, sub: %08x\n", + (word32)ev.xcrossing.window, + (word32)ev.xcrossing.subwindow); + printf("Enter/L mode: %08x, detail: %08x, type:%02x\n", + ev.xcrossing.mode, ev.xcrossing.detail, + ev.xcrossing.type); + break; + case ButtonPress: + vid_printf("Got button press of button %d!\n", + ev.xbutton.button); + buttons = (1 << ev.xbutton.button) >> 1; + motion |= x_update_mouse(ev.xbutton.x, ev.xbutton.y, + buttons, buttons & 7); + + break; + case ButtonRelease: + buttons = (1 << ev.xbutton.button) >> 1; + motion |= x_update_mouse(ev.xbutton.x, ev.xbutton.y, 0, + buttons & 7); + break; + case MotionNotify: + if(ev.xmotion.window != g_a2_win) { + printf("Motion in window %08x unknown!\n", + (word32)ev.xmotion.window); + } + motion |= x_update_mouse(ev.xmotion.x, ev.xmotion.y, 0, + 0); + break; + case Expose: + refresh_needed = -1; + break; + case NoExpose: + /* do nothing */ + break; + case KeyPress: + case KeyRelease: + handle_keysym(&ev); + break; + case KeymapNotify: + break; + case ColormapNotify: + vid_printf("ColormapNotify for %08x\n", + (word32)(ev.xcolormap.window)); + vid_printf("colormap: %08x, new: %d, state: %d\n", + (word32)ev.xcolormap.colormap, + ev.xcolormap.new, ev.xcolormap.state); + break; + default: + printf("X event 0x%08x is unknown!\n", + ev.type); + break; + } + } + + if(motion && g_warp_pointer) { + XWarpPointer(g_display, None, g_a2_win, 0, 0, 0, 0, + BASE_MARGIN_LEFT + (A2_WINDOW_WIDTH/2), + BASE_MARGIN_TOP + (A2_WINDOW_HEIGHT/2)); + } + + if(refresh_needed) { + printf("Full refresh needed\n"); + g_a2_screen_buffer_changed = -1; + g_full_refresh_needed = -1; + + g_border_sides_refresh_needed = 1; + g_border_special_refresh_needed = 1; + g_status_refresh_needed = 1; + + /* x_refresh_ximage(); */ + /* redraw_border(); */ + } + +} + +void +x_hide_pointer(int do_hide) +{ + if(do_hide) { + XDefineCursor(g_display, g_a2_win, g_cursor); + } else { + XDefineCursor(g_display, g_a2_win, None); + } +} + + +void +handle_keysym(XEvent *xev_in) +{ + KeySym keysym; + word32 state; + int keycode; + int a2code; + int type; + int is_up; + + keycode = xev_in->xkey.keycode; + type = xev_in->xkey.type; + + keysym = XLookupKeysym(&(xev_in->xkey), 0); + + state = xev_in->xkey.state; + + vid_printf("keycode: %d, type: %d, state:%d, sym: %08x\n", + keycode, type, state, (word32)keysym); + + x_update_modifier_state(state); + + is_up = 0; + if(type == KeyRelease) { + is_up = 1; + } + +#if 0 + if(keysym == XK_Alt_L || keysym == XK_Meta_L) { + g_alt_left_up = is_up; + } + + if(keysym == XK_Alt_R || keysym == XK_Meta_R) { + g_alt_right_up = is_up; + } + + if(g_alt_left_up == 0 && g_alt_right_up == 0) { + printf("Sending sound to file\n"); + g_send_sound_to_file = 1; + } else { + if(g_send_sound_to_file) { + printf("Stopping sending sound to file\n"); + close_sound_file(); + } + g_send_sound_to_file = 0; + } +#endif + + /* first, do conversions */ + switch(keysym) { + case XK_Alt_R: + case XK_Meta_R: + case XK_Mode_switch: + case XK_Cancel: + keysym = XK_Print; /* option */ + break; + case XK_Alt_L: + case XK_Meta_L: + case XK_Menu: + keysym = XK_Scroll_Lock; /* cmd */ + break; + case NoSymbol: + switch(keycode) { + /* 94-95 are for my PC101 kbd + windows keys on HPUX */ + case 0x0095: + /* left windows key = option */ + keysym = XK_Print; + break; + case 0x0096: + case 0x0094: + /* right windows key = cmd */ + keysym = XK_Scroll_Lock; + break; + /* 0072 is for cra@WPI.EDU who says it's Break under XFree86 */ + case 0x0072: + /* 006e is break according to mic@research.nj.nec.com */ + case 0x006e: + keysym = XK_Break; + break; + + /* 0x0042, 0x0046, and 0x0048 are the windows keys according */ + /* to Geoff Weiss on Solaris x86 */ + case 0x0042: + case 0x0046: + /* flying windows == open apple */ + keysym = XK_Scroll_Lock; + break; + case 0x0048: + /* menu windows == option */ + keysym = XK_Print; + break; + } + } + + a2code = x_keysym_to_a2code(keysym, is_up); + if(a2code >= 0) { + adb_physical_key_update(a2code, is_up); + } else if(a2code != -2) { + printf("Keysym: %04x of keycode: %02x unknown\n", + (word32)keysym, keycode); + } +} + +int +x_keysym_to_a2code(int keysym, int is_up) +{ + int i; + + if(keysym == 0) { + return -1; + } + + if((keysym == XK_Shift_L) || (keysym == XK_Shift_R)) { + if(is_up) { + g_x_shift_control_state &= ~ShiftMask; + } else { + g_x_shift_control_state |= ShiftMask; + } + } + if(keysym == XK_Caps_Lock) { + if(is_up) { + g_x_shift_control_state &= ~LockMask; + } else { + g_x_shift_control_state |= LockMask; + } + } + if((keysym == XK_Control_L) || (keysym == XK_Control_R)) { + if(is_up) { + g_x_shift_control_state &= ~ControlMask; + } else { + g_x_shift_control_state |= ControlMask; + } + } + + /* Look up Apple 2 keycode */ + for(i = g_num_a2_keycodes - 1; i >= 0; i--) { + if((keysym == a2_key_to_xsym[i][1]) || + (keysym == a2_key_to_xsym[i][2])) { + + vid_printf("Found keysym:%04x = a[%d] = %04x or %04x\n", + (int)keysym, i, a2_key_to_xsym[i][1], + a2_key_to_xsym[i][2]); + + return a2_key_to_xsym[i][0]; + } + } + + return -1; +} + +void +x_update_modifier_state(int state) +{ + int state_xor; + int is_up; + + state = state & (ControlMask | LockMask | ShiftMask); + state_xor = g_x_shift_control_state ^ state; + is_up = 0; + if(state_xor & ControlMask) { + is_up = ((state & ControlMask) == 0); + adb_physical_key_update(0x36, is_up); + } + if(state_xor & LockMask) { + is_up = ((state & LockMask) == 0); + adb_physical_key_update(0x39, is_up); + } + if(state_xor & ShiftMask) { + is_up = ((state & ShiftMask) == 0); + adb_physical_key_update(0x38, is_up); + } + + g_x_shift_control_state = state; +} + +void +x_auto_repeat_on(int must) +{ + if((g_auto_repeat_on <= 0) || must) { + g_auto_repeat_on = 1; + XAutoRepeatOn(g_display); + XFlush(g_display); + adb_kbd_repeat_off(); + } +} + +void +x_auto_repeat_off(int must) +{ + if((g_auto_repeat_on != 0) || must) { + XAutoRepeatOff(g_display); + XFlush(g_display); + g_auto_repeat_on = 0; + adb_kbd_repeat_off(); + } +}